PostgreSQL可命中表达式索引但YugabyteDB无法命中的问题
问题描述
执行以下测试脚本时,PostgreSQL 运行最后一条SELECT查询可正常命中创建的表达式索引,跳过排序操作直接快速获取符合条件的前10行结果,YugabyteDB 执行相同语句时无法命中该索引,存在性能差异。
drop table if exists entry2; CREATE TABLE entry2 (comp_id int, path varchar, index varchar, archtype varchar, other JSONB, PRIMARY KEY (comp_id, path,index)); DO $$ BEGIN FOR counter IN 1..200000 BY 1 LOOP insert into entry2 values (counter,'/content[open XXX- XXX-OBSERVATION.blood_pressure.v1,0]','0','open XXX- XXX-OBSERVATION.blood_pressure.v1','{"data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value" :132,"data[at0001]/events[at0006]/data[at0003]/items[at0005]/value/value": 92}'); insert into entry2 values (counter,'/content[open XXX- XXX-OBSERVATION.blood_pressure.v1,0]','1','open XXX- XXX-OBSERVATION.blood_pressure.v1',('{"data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value" :'||(130+ counter) ||',"data[at0001]/events[at0006]/data[at0003]/items[at0005]/value/value": 90}')::jsonb); insert into entry2 values (counter,'/content[open XXX- XXX-OBSERVATION.heart_rate-pulse.v1,0]','0','open XXX- XXX-OBSERVATION.heart_rate-pulse.v1','{"data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value" :132,"/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/value": 113}'); END LOOP; END; $$; drop index if exists blood_pr; create index blood_pr on entry2(((other ->> 'data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value')::integer )); explain analyse select (other ->> 'data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value')::integer from entry2 where (other ->> 'data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value')::integer > 140 order by (other ->> 'data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value')::integer::integer limit 10 ;
差异原因
- 核心原因是表达式匹配规则不同:测试脚本的
ORDER BY子句中,目标表达式多写了一次冗余的::integer类型转换,实际排序表达式为((other ->> 'xxx')::integer)::integer。PostgreSQL优化器内置了表达式等价化简能力,可自动识别对整数类型重复做整数转换属于无意义操作,判定该表达式和索引定义的(other ->> 'xxx')::integer完全等价,因此可以直接利用索引的有序性同时完成范围过滤、排序操作,跳过全表扫描和显式排序步骤。YugabyteDB当前版本的优化器未实现这类冗余类型转换的自动消除逻辑,会将带双重转换的表达式判定为与索引定义的表达式不一致,因此无法命中索引。 - 架构层面的兼容差异:YugabyteDB是分布式数据库,二级索引与主表按独立分片规则分布式存储,早期版本对PostgreSQL表达式索引的语义等价推导、索引有序性利用的适配程度低于单机PostgreSQL,仅当查询中的表达式与索引定义严格字面一致时,才会选择索引扫描路径。
适配方案
- 最简修复:删除
ORDER BY子句中冗余的::integer转换,保证WHERE、SELECT、ORDER BY子句中使用的表达式与索引定义的表达式严格一致,修改后优化器可正常识别索引匹配关系,利用索引有序性直接返回前10条结果,性能与PostgreSQL表现一致。修改后的查询语句如下:
explain analyse select (other ->> 'data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value')::integer from entry2 where (other ->> 'data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value')::integer > 140 order by (other ->> 'data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value')::integer limit 10 ;
- 性能优化:可将索引创建为覆盖索引,把查询依赖的
other字段加入索引包含列,避免索引扫描后的回表开销,索引创建语句参考:
create index blood_pr on entry2(((other ->> 'data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value')::integer )) include (other);
- 版本升级:升级到YugabyteDB 2.18及以上稳定版本,该版本对PostgreSQL兼容层的表达式匹配逻辑做了大量优化,对常见冗余类型转换、表达式等价推导的支持更完善,可减少这类场景下的人工改写成本。
内容的提问来源于stack exchange,提问作者FranckPachot
相关产品推荐
相关产品推荐

