请勿滥用 2PC, 忘记提交prepared transaction对PostgreSQL造成的危害.

1 minute read

背景

我在数据库中开启了一个2PC事务,但是不去管他,会发生什么呢?有什么危害?

postgres=# begin;  
BEGIN  
postgres=# insert into t6 values (1);  
INSERT 25622 1  
postgres=# prepare transaction 'a';  
PREPARE TRANSACTION  
postgres=# select * from txid_current_snapshot();  
     txid_current_snapshot       
-------------------------------  
 639903995:639904018:639903995  
(1 row)  
postgres=# select * from pg_prepared_xacts ;  
 transaction | gid |           prepared            |  owner   | database   
-------------+-----+-------------------------------+----------+----------  
   639903995 | a   | 2015-09-24 10:03:53.900569+08 | postgres | postgres  
(1 row)  

危害1, 膨胀

因为vacuum 在回收垃圾时,判断dead tuple可以回收的前提是,dead tuple是在最早未提交事务之前产生的。

所以,在这个事务之后,产生的DEAD TUPLE都无法被回收,即使VACUUM FULL也无法回收。

源码分析请参考

《PostgreSQL垃圾回收代码分析 - why postgresql cann’t reclaim tuple is HEAPTUPLE_RECENTLY_DEAD》

危害2, 年龄

年龄同样会受到威胁,最多只能降低到最早未提交的事务。

例如:

postgres=# select age(datfrozenxid),datfrozenxid,datname from pg_database where datname=current_database();  
 age | datfrozenxid | datname    
-----+--------------+----------  
  36 |    639903995 | postgres  
(1 row)  

无法降低年龄了

postgres=# vacuum freeze;  
VACUUM  
postgres=# select age(datfrozenxid),datfrozenxid,datname from pg_database where datname=current_database();  
 age | datfrozenxid | datname    
-----+--------------+----------  
  36 |    639903995 | postgres  
(1 row)  

随着事务流逝,年龄越来越大

postgres=# insert into t6 values (1);  
INSERT 25655 1  
postgres=# insert into t6 values (1);  
INSERT 25656 1  
postgres=# insert into t6 values (1);  
INSERT 25657 1  
postgres=# insert into t6 values (1);  
INSERT 25658 1  
postgres=# insert into t6 values (1);  
INSERT 25659 1  
postgres=# select age(datfrozenxid),datfrozenxid,datname from pg_database where datname=current_database();  
 age | datfrozenxid | datname    
-----+--------------+----------  
  41 |    639903995 | postgres  
(1 row)  
postgres=# vacuum freeze;  
VACUUM  
postgres=# select age(datfrozenxid),datfrozenxid,datname from pg_database where datname=current_database();  
 age | datfrozenxid | datname    
-----+--------------+----------  
  41 |    639903995 | postgres  
(1 row)  

危害3, 持锁,DDL当然也是下不去的。

包括vacuum full, alter table, ….

最后,还需要提醒,基于流复制的备库,2PC事务会复制过去,激活后就可以看到。

所以2PC是非常坚强的,停库后起来还在,切换到备库也还在。

监控必须建立起来,对于长时间不提交的prepared transaction,及时告警。

参考

1. 《PostgreSQL垃圾回收代码分析 - why postgresql cann’t reclaim tuple is HEAPTUPLE_RECENTLY_DEAD》

2. http://www.postgresql.org/docs/9.4/static/sql-prepare-transaction.html

3. http://www.postgresql.org/docs/9.4/static/sql-commit-prepared.html

4. http://www.postgresql.org/docs/9.4/static/sql-rollback-prepared.html

5. http://www.postgresql.org/docs/9.4/static/view-pg-prepared-xacts.html

6. http://www.postgresql.org/docs/9.4/static/runtime-config-resource.html#GUC-MAX-PREPARED-TRANSACTIONS

Flag Counter

digoal’s 大量PostgreSQL文章入口