Google Cloud Spanner无索引字段查询差异与扫描机制技术咨询
Spanner 无索引字段查询的差异问题解析
表结构定义
CREATE TABLE Account ( ID STRING(100) NOT NULL, BSB STRING(20) NOT NULL, AccNum STRING(100) NOT NULL, ) PRIMARY KEY(ID);
背景信息
- 表仅以
ID作为主键,BSB和AccNum无索引、无唯一约束 - 表存储1000万条记录,
BSB重复率高,AccNum近似唯一 - 查询表现:
- 执行
SELECT * FROM account WHERE AccNum = "001":查询计划显示"table scan on Account, rows returned:1",未触发全表扫描,耗时8ms - 执行
SELECT * FROM account WHERE BSB = "100":查询计划显示"table scan on Account Full scan, row returned: 3,000,000",触发全表扫描,耗时30s
- 执行
问题解答
1. 为何第二条SQL触发全表扫描,第一条却未触发?
核心原因是Spanner的查询优化器会根据过滤条件的预期返回行数选择执行策略:
- 对于
AccNum = "001",因为AccNum近似唯一,优化器预判返回行数极少(几乎1条),会采用分片并行扫描+提前终止的方式:Spanner的表按主键分片存储,它会同时扫描各个分片,一旦找到匹配记录就停止扫描剩余分片,所以不会触发全表扫描,耗时极短。 - 对于
BSB = "100",BSB重复率高,优化器预判会返回大量数据(300万条),此时分片扫描+提前终止的效率反而更低,不如直接全表扫描所有分片来获取全部匹配数据,因此触发全表扫描,耗时更长。
2. 若WHERE子句中的字段无索引,是否必然触发全表扫描?该认知是否正确?
这个认知不正确。
无索引不代表一定会全表扫描,Spanner的查询优化器会结合字段的统计信息(比如重复率、基数)、过滤条件的预期返回行数来选择最优执行策略:
- 如果预判返回行数极少,会采用分片并行扫描+找到匹配后提前终止的方式,避免全表扫描;
- 只有当预判返回行数较多时,才会选择全表扫描,因为此时提前终止的收益可以忽略,全表扫描的整体效率更高。
3. 第一条SQL未触发全表扫描,Spanner实际是如何查找对应记录的?
具体执行逻辑如下:
- 查询优化器基于
AccNum的统计信息(近似唯一),判断该条件只会返回极少数记录,因此选择并行分片扫描策略。 - Spanner将
Account表按主键ID分成多个分片,同时向所有分片发起扫描请求,每个分片在自己的范围内查找AccNum = "001"的记录。 - 一旦任意一个分片找到匹配的记录,Spanner会立即停止向剩余分片发送扫描请求,或通知已在扫描的分片终止扫描。
- 最后将找到的记录返回给客户端,整个过程因为提前终止,只扫描了部分分片而非全表,因此耗时很短。
内容的提问来源于stack exchange,提问作者pellucid
相关产品推荐
相关产品推荐

