暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

知识点滴 -- old_snapshot_threshold 参数开启导致索引无法使用案例

原创 金仓数据库 2025-01-17
401

最近遇到一个sql 优化问题,现场反馈 一张50万数据的表创建索引后根据条件查询只返回2行数据,但是执行计划显示并没有走索引,尝试了各种方式比如hint、强制关闭seqscan等操作,都不起作用。

一、无法使用索引的场景

1. 开启 old_snapshot_threshold 参数

alter system set old_snapshot_threshold=1;

2. 开启两个会话,验证无法使用索引的场景

会话一会话二

test=# begin;
BEGIN
test=# delete from t1 where id=12;
DELETE 1

test=# create index idx_t2 on t2(id);
CREATE INDEX
test=# analyze t2;
ANALYZE
test=# explain select * from t2 where id=12;
-------------------------------------------------------
Seq Scan on t2 (cost=0.00..20904.00 rows=1 width=37)
       Filter: (id = 12)

commit;



test=# explain select * from t2 where id=12;
 ------------------------------------------------------------------
Index Scan using idx_t2 on t2 (cost=0.42..8.44 rows=1 width=37)
        Index Cond: (id = 12)

可以发现,当存在比创建索引事务更早的活动事务时,在活动事务结束前,新创建的索引是无法使用的。

3. 查看pg_index.indcheckxmin 

test=# select xmin,indisvalid,indcheckxmin,indisready,indislive from pg_index where indexrelid = 'idx_t2'::regclass;
  xmin  | indisvalid | indcheckxmin | indisready | indislive
--------+------------+--------------+------------+-----------
 101644 | t          | t            | t          | t

可以看到 pg_index.indcheckxmin 为 true 。该字段的含义如下:

在此索引对应的 sys_index 行的 xmin 小于当前所有活动事务 snapshot 的 backend_xmin 数值之前,任何查询都不能使用此索引。

二、old_snapshot_threshold 参数

Oracle 有个 undo_retension 参数,表示事务修改操作undo信息保留的时长。当查询时长超过undo_retension值时,就会包“snapshot too old”错误。PostgreSQL old_snapshot_threshold 参数就相当于 Oracle undo_retension 参数。

PG 中是通过vacuum回收脏数据,也就是我们常说的dead tuple,但是有时候长事物会持有一些dead tuple很长时间,这样就不能及时做vacuum回收脏数据,就使得数据库中的表很容易膨胀,并占用额外的存储空间,还会导致相关SQL操作性能下降。所以为了避免以上结果,从9.6引入了old_snapshot_threshold参数。当old_snapshot_threshold默认值为-1,是关闭的。当我们设置该为具体时间的时候,vacuum 就不会等待长时间的查询结束,会继续对dead tuple进行处理,如果刚好有事物正好在使用这些dead tuple,那么就会报错snapshot too old。

三、为什么索引不可用?

1. backend_xid 和 backend_xmin

sys_stat_activity.backend_xid 表示已申请事务号的事务,例如有增删改,DDL等操作的事务。backend_xid 从申请事务号开始持续到事务结束。

sys_stat_activity. backend_xmin 表示SQL开始执行(也可能是事务开始时。视隔离级别而定)时的系统活动事务的xid最小值(snapshot),例如查询语句,查询游标。backend_xmin 从SQL开始持续到SQL结束,如果是游标的话,持续到游标关闭。

2. HOT 更新

KingbaseES当前的多版本的方案,是在update和delete元组时并不是直接的删除一行,而是通过xid和标记手段来设置这个元组对以后的事务不可见。对于update 操作,如果原数据块无法容纳“新”记录,也就是在其他块插入一条记录,那需要在索引插入一条entry。而如果The Heap Only Tuple (HOT) update,也就是原数据块可以容纳新记录,那索引无需新加一条entry,只需要表原数据块 slot 链接到新 slot 。

3. old_snapshot_threshold 引发 HOT 断链

由于 开启了old_snapshot_threshold 参数,即使在有活动事务的情况下 vacuum 操作也会进行,这会导致发生HOT 断链的情况,这时候HOT链中索引的键值已更改,但是为了保证数据的一致性,要保证键值发生变化前的事务就不能引用该索引。

四、问题解决

遇到这种情况,可以手动结束 所有 backend_xid 小于 xmin (创建索引的事务ID)的事务。
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论