我正在评估 Spanner 的读取密集型用例。我正在使用 Postgres 方言。我有一个单节点设置(1000 个处理单元),其中一个表的行数少于一百万行。查表是通过主键是一个 sha256 编码的字符串来随机化键分布。在我的测试中,
我正在运行一个独立的 go-lang 客户端应用程序,该应用程序正在执行 100 个 go-routine 工作线程以使用主键查询数据库(select * from foo where ID IN('...', '...')) 。每个查询在 IN 子句中最多可查找 ID。
通过此设置,我获得了大约 1K QPS,CPU 达到 100%。 Spanner 宣称单个节点的 QPS 约为 22K,但我并没有达到这个水平。此设置中的最大扫描行数接近 20K,但平均扫描行数接近 2.5K。我什至不知道为什么最大扫描行是 20K,因为我总是只查询最多 10 个 ID 主键。 Spanner 后端指标仅在这个级别有用,但没有给我任何关于为什么我们无法超越 1K QPS 限制的指示。
我尝试了多次迭代,其中包括工作池大小、具有 10 个 gRPC 通道和批量大小的会话池(最大 1000)的多种变化,但结果或多或少是相同的。
感谢任何意见和指导并分享您的经验。
导致您看不到所需吞吐量的原因可能有多种。
让我们从一些基础数学开始,看看您的client在此设置中可以处理的最大读取数是多少:
根据您的问题,您似乎正在生成一个包含随机文字列表的查询。所以类似
select * from foo where id in ('key1', 'key2', 'key3')
。这将要求 Spanner 在每次执行查询时解析和计划查询,因为每次执行的 SQL 字符串都不同。相反,您应该使用参数化查询,并将键作为参数值发送。这将使 Spanner 缓存查询计划,并且只需要解析和计划查询 10 次。
因此生成 10 个如下所示的查询:
select * from foo where id in ($1, $2, $3)
。
您写道您的表包含“少于 100 万行”。 Spanner 是为规模而构建的,“更多”数据实际上比更少更好,因为它会让 Spanner 更好地分发数据。大约 1mio 行通常就足够了,但您的读取需要分布在整个键空间中才能实现最大吞吐量。您是否验证过您的随机密钥选择确实是随机的?您能否尝试在表中使用更多数据? 热身/持续时间
Spanner 通过创建
splits根据实际使用情况将数据分布在多个服务器上。这是一个动态的过程,需要一些时间。建议在生产启动之前预热数据库。对于基准也是如此。有关更多详细信息,请参阅 https://cloud.google.com/spanner/docs/pre-warm-database。