PostgreSQL长事务与失效的索引查询浅析介绍

 更新时间:2022年09月16日 10:35:38   作者:foucus、  
pg中的长事务会影响表中垃圾回收,导致表的年龄增长无法freeze。能消耗事务的只有当执行了一些DML或者DDL操作后才能算是我们通常说的长事务。否则只能算是我们常说的长连接,当然长连接也有很多弊端,例如占用内存、cpu等资源

最近刚写了一篇文章介绍了下长事务,以及一些长事务常见的危害,如无法及时的垃圾回收导致表膨胀之类的问题,最近刚好又碰到一个问题也是长事务所导致的。

上周六早上接到同事电话,说某个库CPU一直很高,看了下全是某张大表的全表扫描导致,但是奇怪的是相关的查询都有用到索引列,不知道为啥查询全部都没走索引。

当我连上去查看时发现确实如此,如果只是某个查询不走索引那可能是SQL本身写的有问题,但是这张表相关的所有SQL都不走索引,那自然会想到是索引本身的原因了。那是不是索引失效了呢?经过检查发现这张表上的索引状态均正常,并且我还将索引重建了,可仍然没起作用。

正当我迷茫的时候,偶然间再去执行相关SQL的时候发现竟然又都走索引了,这又是啥情况。。

我啥都没做你就自己恢复了,那不是显得我很呆?不行,必须得搞清楚啥原因。

当我再去查看相关索引的时候发现,该索引的pg_index中的indcheckxmin列均为true,这个字段我之前有写过一篇索引失效的文章里介绍过。那么什么情况下索引的该属性会被设置为true呢?两种情况:

  1. 当前事务中表上存在broken HOT chains;
  2. 当old_snapshot_threshold被设置时。

之前我们也介绍过,如果索引的该属性为true那么在创建索引的事务中该索引是不可用的,不过这种场景我们基本不太会遇到,因为在实际应用中我们基本不会在事务中创建完索引然后不提交该事务直接去使用。

而关于indcheckxmin的详细解释是:直到此pg_index行的xmin低于查询的TransactionXmin之前,查询都不能使用此索引。那么什么情况下会出现这种问题呢?长事务!

当我们创建索引的时候如果索引的indcheckxmin被设置为true,且数据库中此时存在长事务,那么直到该长事务提交前,该索引会一直不可用。

下面我们来模拟这种情况:

--会话一:打开一个长事务

bill=# begin;
BEGIN
bill=*# delete from t;
DELETE 1000
bill=*#

--会话二:创建索引

由于old_snapshot_threshold参数被设置,所以创建的索引indcheckxmin被设置为true了。

bill=# show old_snapshot_threshold ;
 old_snapshot_threshold
------------------------
 1h
(1 row)
bill=# create index idx_t1 on t1(id);
CREATE INDEX
bill=# select indisvalid,indcheckxmin,indisready,indislive from pg_index where indexrelid = 'idx_t1'::regclass;
 indisvalid | indcheckxmin | indisready | indislive
------------+--------------+------------+-----------
 t          | t            | t          | t
(1 row)

使用该索引列进行查询:

索引的确无法使用。

bill=# explain analyze select * from t1  where id = 100;
                                           QUERY PLAN
-------------------------------------------------------------------------------------------------
 Seq Scan on t1  (cost=0.00..209.00 rows=51 width=37) (actual time=0.010..0.692 rows=51 loops=1)
   Filter: (id = 100)
   Rows Removed by Filter: 9949
 Planning Time: 0.150 ms
 Execution Time: 0.706 ms
(5 rows)
bill=# set enable_seqscan = off;
SET
bill=# explain analyze select * from t1  where id = 100;
                                                    QUERY PLAN
-------------------------------------------------------------------------------------------------------------------
 Seq Scan on t1  (cost=10000000000.00..10000000209.00 rows=51 width=37) (actual time=0.063..0.732 rows=51 loops=1)
   Filter: (id = 100)
   Rows Removed by Filter: 9949
 Planning Time: 0.089 ms
 Execution Time: 0.796 ms
(5 rows)

提交该长事务后再次查询:

索引变得可用了。

bill=# explain analyze select * from t1  where id = 100;
                                                  QUERY PLAN
---------------------------------------------------------------------------------------------------------------
 Index Scan using idx_t1 on t1  (cost=0.29..54.48 rows=51 width=37) (actual time=0.013..0.052 rows=51 loops=1)
   Index Cond: (id = 100)
 Planning Time: 0.061 ms
 Execution Time: 0.067 ms
(4 rows)

果然是长事务的坑啊!

由于我们的库中基本都打开了old_snapshot_threshold参数,导致创建的索引的indcheckxmin一定是true。但这其实并不会有什么太大影响,问题在于在创建索引的同时数据库中存在长事务,这就导致了索引在创建完之后第一时间变得不可用了,需要直到该长事务被提交后才可用。

后来和同事求证发现他们之前也经常碰到这种CPU变高然后又自己降下来的情况,之前并没有注意是这张表的全表扫描导致的。由于该表是由pg_pathman创建的自动分区,每天都会自己去创建一个新的分区,因此如果每天自动创建分区的时候存在长事务,那么创建完之后相关的分区上的索引均是不可用的,这也是为什么查询不走索引然后又自己恢复的原因了。

这个案例其实我们在实际生产中遇到的可能性还是很大的,如果你的数据库打开了old_snapshot_threshold参数,同时没有做好长事务的监控,那么创建的索引就会出现这种不可用的情况。长事务危害不浅啊!

到此这篇关于PostgreSQL长事务与失效的索引查询浅析介绍的文章就介绍到这了,更多相关PostgreSQL长事务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • postgres之jsonb属性的使用操作

    postgres之jsonb属性的使用操作

    这篇文章主要介绍了postgres之jsonb属性的使用操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Postgresql 如何选择正确的关闭模式

    Postgresql 如何选择正确的关闭模式

    这篇文章主要介绍了Postgresl 如何选择正确的关闭模式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • PostgreSQL教程(九):事物隔离介绍

    PostgreSQL教程(九):事物隔离介绍

    这篇文章主要介绍了PostgreSQL教程(九):事物隔离介绍,本文主要针对读已提交和可串行化事物隔离级别进行说明和比较,需要的朋友可以参考下
    2015-05-05
  • PostgreSQL中数据批量导入导出的错误处理

    PostgreSQL中数据批量导入导出的错误处理

    在 PostgreSQL 中进行数据的批量导入导出是常见的操作,但有时可能会遇到各种错误,下面将详细探讨可能出现的错误类型、原因及相应的解决方案,并提供具体的示例来帮助您更好地理解和处理这些问题,需要的朋友可以参考下
    2024-07-07
  • Postgresql 通过出生日期获取年龄的操作

    Postgresql 通过出生日期获取年龄的操作

    这篇文章主要介绍了Postgresql 通过出生日期获取年龄的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 使用PostgreSQL数据库建立用户画像系统的方法

    使用PostgreSQL数据库建立用户画像系统的方法

    这篇文章主要介绍了使用PostgreSQL数据库建立用户画像系统,下面使用一个具体的例子来说明如何使用PostgreSQL的json数据类型来建立用户标签数据,需要的朋友可以参考下
    2022-10-10
  • PostgreSQL如何查看事务所占有的锁实操指南

    PostgreSQL如何查看事务所占有的锁实操指南

    这篇文章主要给大家介绍了关于PostgreSQL如何查看事务所占有锁的相关资料,文中通过代码以及图文介绍的非常详细,对大家学习或者使用PostgreSQL具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-10-10
  • postgresql 实现获取所有表名,字段名,字段类型,注释

    postgresql 实现获取所有表名,字段名,字段类型,注释

    这篇文章主要介绍了postgresql 实现获取所有表名,字段名,字段类型,注释操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • postgresql 如何查看pg_wal目录下xlog文件总大小

    postgresql 如何查看pg_wal目录下xlog文件总大小

    这篇文章主要介绍了postgresql 如何查看pg_wal目录下xlog文件总大小的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • PostgreSQL存储过程循环调用方式

    PostgreSQL存储过程循环调用方式

    这篇文章主要介绍了PostgreSQL存储过程循环调用方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01

最新评论