You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:39:17