Google 如何存储您的大查询表以及它如何影响您
已发表: 2016-02-24这是我们博客的技术讲座安装,由非凡的开发人员 Adam Knox 带给您。
尽管 Big Query 使用的语法与 SQL 非常相似,但它实际上与处理大量数据的解决方案有很大不同。 Big Query 倾向于暴力破解,实际上并不使用索引,因此大量数据意味着大量处理器和处理时间。 为了实现这一目标,Google 采用了一种不同的数据存储方式,这可能会影响您设计查询的方式。
文件格式
创建表后,Google 会使用其 ColumnIO 格式来存储您的数据。 每列都存储为一个单独的文件,但重复字段除外。 如果您有一个包含列的表:名称(字符串),电话号码(重复整数); 该名称将与其他名称存储在同一行中,并且与该名称关联的所有电话号码将与电话号码一起存储在同一行中。 如果列太大,也可以拆分列,但这不会影响工作流程。
知道这一点可以通过不处理您实际上不关心的列来运行更快的查询,并且在某些情况下甚至使以前无法运行查询成为可能。 查询也可能变得更便宜,因为系统按处理的数据量收费,如果查询中不包含列,则不会处理该数据。
文件存储
如果您创建了一个表,您会感到相当安全,因为一旦创建了文件,它们就会存储在三个不同的数据中心,甚至每个数据中心内的三个不同位置。 由于 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 给它的名字,让它在一天后消失。 如果您遇到“响应太大而无法返回”,那是因为他们正在保护您免受自己的伤害。 创建巨大的表很容易(尤其是交叉连接)。 如果发生这种情况并且您期望它会发生,那么您需要为表命名并启用“允许大结果”选项。 如果您重复一个没有新行流式传输的查询,那么如果它们仍然存在,您将只会获得缓存的结果。
