Google이 빅 쿼리 테이블을 저장하는 방식과 사용자에게 미치는 영향
게시 됨: 2016-02-24이것은 뛰어난 개발자인 Adam Knox가 제공한 블로그의 기술 토크 설치입니다.
Big Query는 SQL과 매우 유사한 구문을 사용하지만 실제로는 대용량 데이터를 처리하는 솔루션과는 상당히 다른 전술을 사용합니다. Big Query는 무차별 대입하는 경향이 있고 실제로 인덱스를 사용하지 않기 때문에 데이터가 많다는 것은 많은 프로세서와 처리 시간을 의미합니다. 이를 해결하기 위해 Google은 쿼리 설계 방식에 영향을 줄 수 있는 데이터를 저장하는 다른 방법을 사용했습니다.
파일 형식
테이블이 생성되면 Google은 ColumnIO 형식을 사용하여 데이터를 저장합니다. 각 열은 반복되는 필드를 제외하고 별도의 파일로 저장됩니다. 열이 있는 테이블이 있는 경우: name(문자열), phoneNumbers(반복 정수); 이름은 다른 이름과 함께 한 줄에 저장되고 해당 이름과 관련된 모든 전화 번호는 전화 번호와 함께 한 줄에 저장됩니다. 열이 너무 커지면 분할될 수도 있지만 워크플로에는 영향을 주지 않습니다.
이것을 알면 실제로 관심이 없는 열을 처리하지 않고 더 빠른 쿼리를 실행할 수 있는 기능을 제공하고 경우에 따라 이전에는 쿼리를 실행할 수 없었습니다. 또한 처리된 데이터의 양에 따라 시스템이 청구하기 때문에 쿼리가 더 저렴해질 수 있으며, 쿼리에 열이 포함되지 않으면 해당 데이터는 처리되지 않습니다.
파일 스토리지
테이블을 만든 경우 파일이 생성되면 3개의 다른 데이터 센터와 각 데이터 센터 내의 3개의 다른 위치에 저장되기 때문에 테이블이 아무데도 가지 않는 것이 합리적으로 안전하다고 느낄 수 있습니다. Big Query는 프로세서에 문제를 일으키기 때문에 데이터에 쉽게 액세스할 수 있어야 합니다. 이에 대한 주의 사항은 임시 데이터 세트입니다. 데이터 세트 내의 테이블에 만료 시간을 지정하는 옵션이 있으며 테이블은 이 만료 날짜보다 오래되면 사라집니다.
테이블 계층
프로젝트에는 각각 테이블을 포함할 수 있는 데이터세트를 저장할 수 있는 기능이 있습니다. Big Query 구문에서 테이블에 액세스하기 위한 전체 명령은 [projectname:datasetname.tablename]
이지만 현재 작업 중인 프로젝트에서 테이블에 액세스하는 경우 dataset.tablename
으로 단축할 수 있습니다.
쿼리가 일반적으로 테이블의 모든 행에 대해 실행되기 때문에 관련 데이터를 별도의 테이블로 나누면(예: 날짜별 또는 특정 이벤트 발생 후) 유지 관리가 더 쉬운 쿼리를 만드는 데 도움이 될 수 있습니다. 즉, 여러 테이블을 보고 싶을 수 있으므로 [projectname:datasetname.__TABLES__]
에서 액세스할 수 있고 데이터 세트의 각 테이블에 대한 정보를 포함하고 다음 테이블을 집계하는 데 사용할 수 있는 각 데이터 세트에 숨겨진 메타 테이블이라는 것이 있습니다. 쿼리.
TABLE_QUERY
명령은 집계에 대한 쿼리를 실행하기 전에 여러 개의 유사한 테이블을 집계할 수 있도록 하는 명령이며 __TABLES__
의 모든 열을 사용하여 이 집계를 형성할 수 있습니다. 예를 들어, 프로젝트에 대한 2016년 1월 1일 이후의 응답 시간에 대한 아이디어가 필요한 경우 최소, 중앙값 및 최대 응답 시간을 보여주는 다음 쿼리를 실행할 수 있습니다.
SELECT QUANTILES((protoPayload.endTime - protoPayload.startTime), 3) AS responseTimeBucketsInMilliseconds FROM (TABLE_QUERY(appengine_logs, "table_id CONTAINS 'appengine_googleapis_com_request_log' AND creation_time > 1451606400000"))
각각 정수를 포함하는 두 개의 열만 사용되기 때문에 이것은 여전히 합리적으로 저렴한 쿼리($0.0003)입니다. 비록 28+에 액세스하고 있지만 아래 쿼리에 따르면 1.857TB이며 모든 필드에 액세스하는 데 약 9$의 비용이 듭니다. .
SELECT SUM(size_bytes)/1000000000 FROM [repcore-prod:appengine_logs.__TABLES__] WHERE table_id CONTAINS 'appengine_googleapis_com_request_log' AND creation_time > 1451606400000
파일 업데이트
Big Query는 새 행을 테이블로 스트리밍할 수 있기 때문에 변경 사항을 로깅하고 해당 변경 사항을 분석할 때 유용합니다. 그러나 인덱스가 없기 때문에 특정 행을 빠르고 자주 조회하는 데는 형편없는 데이터 저장 장치이며 테이블의 행을 수정하거나 제거할 수 없기 때문에 변경될 것으로 예상되는 개체의 단일 표현을 저장하는 데에도 적합하지 않습니다. .

테이블 분할
어떤 상황에서는 단일 열에 액세스하는 것조차 Big Query에서 처리하기에는 너무 많습니다. 목록 생성 날짜의 순서로 목록 ID를 반환하는 다음과 같은 사용할 수 없는 쿼리를 제공합니다.
SELECT lid FROM [repcore-prod:datastore.LIS] ORDER BY ct
기술적으로 최근에 이것이 성공할 수 있게 되었지만 더 높은 청구 계층을 활성화해야 하기 때문에 Vendasta 직원에게는 불가능했습니다. 사전 주문된 인덱스가 없기 때문에 소량의 데이터를 매우 집중적으로 처리하므로 이 경우 처리 비용을 청구합니다. 결제 계층 변경은 Big Query UI의 대화형 쿼리에서 "옵션" 버튼 아래의 "무제한 허용"(활성화된 경우)을 선택하여 수행할 수 있습니다.
쿼리가 가능한 한 효율적이라고 가정하면 이러한 종류의 엄격한 제한을 피하기 위해 테이블 크기를 줄이는 데 몇 가지 접근 방식이 남아 있습니다. 첫 번째는 테이블 시간 데코레이터이고 두 번째는 해시를 사용하는 것입니다.
여기에서 테이블 데코레이터에 대해 자세히 알아볼 수 있습니다. LIMIT/WHERE/HAVING/OMIT
같은 명령을 사용하여 결과를 필터링하는 것과 달리 테이블 데코레이터는 주어진 범위 밖의 데이터에 액세스하지도 않기 때문에 테이블 쿼리 비용을 실제로 줄입니다. 불행히도 이 방법은 Vendasta에서 거의 쓸모가 없습니다. 왜냐하면 우리가 실제로 스트리밍하는 ListingHistoryModel과 같은 테이블의 경우에도 여전히 전체 테이블을 삭제하고 NDB에서 가져온 복제본으로 매일 교체하기 때문입니다. 오늘의 항목에만 관심이 있는 경우 ListingHistoryModel입니다.
로깅을 살펴보는 것은 Vendasta에서 매우 도움이 될 수 있는 한 곳입니다. 실제 로그 내용의 크기로 인해 계산 및 메모리 제한에 부딪히기 쉽고 로그 내용 열만 쿼리해도 비용이 많이 듭니다. 로그를 사용하면 일반적으로 시간 데코레이터가 도움이 되는 특정 시점에만 관심을 갖습니다.
쿼리 결과
쿼리를 실행할 때마다 새 테이블이 생성되고 때때로 보이지 않는 숨겨진 테이블이 생성됩니다. 그렇게 선택하면 결과 테이블에 이름을 지정하여 유지하거나 Google에서 지정한 이름으로 유지하고 하루 후에 사라지도록 할 수 있습니다. "응답이 너무 커서 반환할 수 없습니다"라는 메시지가 표시되면 응답이 자신을 보호하기 때문입니다. 거대한 테이블을 만드는 것은 쉽습니다(특히 교차 조인 사용). 이러한 일이 발생하고 발생할 것으로 예상했다면 테이블 이름을 지정하고 "대규모 결과 허용" 옵션을 활성화해야 합니다. 새 행이 스트리밍되지 않은 쿼리를 반복하면 여전히 주변에 있는 경우 캐시된 결과를 얻을 수 있습니다.
