当尝试优化查询以根据位置获取商店记录时,我陷入了一些奇怪的事情(我认为),获取大型数据集需要大量的 CPU 时间。
基本上我有 > 1000 条记录需要迭代以找到用户位置 3000 米范围内的商店,我在管理控制台中得到了相当多的数字。
这导致了一些数据存储测试,这些测试产生了一些有趣的数字以获取 1000 条记录。
我有 6 种测试方法要单独运行,并从管理控制台和应用程序统计中占用 CPU 时间,结果是这样的(在生产中):
r = db.GqlQuery("SELECT __key__ FROM StoreRecords").fetch(1000)
# appstats: real=120ms cpu=182ms api=845ms
# admin console: 459ms 1040cpu_ms 845api_cpu_ms
r = db.GqlQuery("SELECT __key__ FROM StoreRecords").fetch(100)
# appstats: real=21ms cpu=45ms api=95ms
# admin console: 322ms 134cpu_ms 95api_cpu_ms
r = db.GqlQuery("SELECT * FROM StoreRecords").fetch(1000)
# appstats: real=1208ms cpu=1979ms api=9179ms
# admin console: 1233ms 10054cpu_ms 9179api_cpu_ms
r = db.GqlQuery("SELECT * FROM StoreRecords").fetch(100)
# appstats: real=57ms cpu=82ms api=929ms
# admin console: 81ms 1006cpu_ms 929api_cpu_ms
r = model.StoreRecords.all().fetch(1000)
# appstats: real=869ms cpu=1526ms api=9179ms
# admin console: 1061ms 9956cpu_ms 9179api_cpu_ms
r = model.StoreRecords.all().fetch(100)
# appstats: real=74ms cpu=86ms api=929ms
# admin console: 97ms 1025cpu_ms 929api_cpu_ms
这里我最多只取 1000 条记录,但需要全部取回(大约 4-5000 条)。
我的问题是:
- 获取 1000 条记录真的需要将近 20 (10054cpu_ms + 9179api_cpu_ms) 秒吗?
- 为什么应用程序时间和管理控制台时间不同?根据我的配额计算什么?
通过将获取的记录作为 protobuf 推送到内存缓存,可以轻松解决这个问题。但我很好奇应用程序和管理控制台之间的高使用率和时间差异。
奖励问题: 为什么提取 1000 条记录总是导致 9179api_cpu_ms?
请您参考如下方法:
为什么检索大量记录需要大量资源令人惊讶?这是一个 O(n) 过程,您真的不应该在每个请求的基础上执行此操作。按顺序回答您的问题:
- 它使用多少 CPU 时间取决于记录的性质,但这个结果并不奇怪。请注意,它接近 20 CPU 秒,而不是挂钟秒。另请注意,当新的计费模型推出时,您需要为数据存储操作和实例小时数付费,这是您应该优化的内容。
- 管理控制台会显示您计费所依据的权威数字。 appstats 数字较低,因为它们只计算 API 调用期间花费的时间,而不计算执行您自己的代码所花费的时间。
如果您的记录集很小并且相当静态,您应该将它们缓存在实例内存中,而不是每次都获取它们或将它们存储在内存缓存中。如果它们更大且动态,您应该使用 GeoModel 之类的东西,这样您就可以进行地理查询并仅获取相关记录。
获取 1000 条记录始终需要相同数量的 API CPU 时间,因为它是数据存储访问成本的表示方式 - 这不是实际花费的时间。新模型通过将其分解为单独的计费操作来解决此问题。
