ThinkSSL🔒 一键申购 5分钟快速签发 30天无理由退款 购买更放心 广告
[TOC] &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pg的事务快照功能在9.2版本开始支持,允许事务共享它当时的snapshot给其他的事务使用。 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SET TRANSACTION SNAPSHOT 命令允许新的事务使用与一个现有事务相同的快照运行。已经存 在的事务必须已经把它的快照用`pg_export_snapshot`函数导出。该函数会返回一个快照标识符,SET TRANSACTION SNAPSHOT需要被给定一个快照标识符来指定要导入的快照。在这个命令中该标识符必须被写成一个字符串,例如'000003A1-1'。`SET TRANSACTION SNAPSHOT`只能在一个事务的 开始执行,并且要在该事务的第一个查询或者数据修改语句(`SELECT`、`INSERT`、`DELETE`、`UPDATE`、`FETCH`或`COPY`)之前执行。此外,该事务必须已经被设置 为`SERIALIZABLE`或者`REPEATABLE READ`隔离级别(否则,该快照将被立刻抛弃, 因为`READ COMMITTED`模式会为每一个命令取一个新快照)。 如果导入事务使用了`SERIALIZABLE`隔离级别,那么导入快照 的事务必须也使用该隔离级别。还有,一个非只读可序列化事务不能导入来自只读事务的快照。 # <span style="font-size:15px">**实例1:export的事务是repeatable read 隔离级别** </span> 1. session 1开启repeatable read隔离级别事务,并使用pg_export_snapshot函数导出快照标识 ``` postgres=# begin TRANSACTION ISOLATION LEVEL repeatable read; BEGIN postgres=*# SELECT pg_export_snapshot(); pg_export_snapshot --------------------- 00000004-0000003C-1 (1 row) postgres=*# select * from tbl; data ------ C B (2 rows) ``` 2. session 2 新增数据并commit ``` [postgres@iZbp1bum6107bp8mgzkeunZ ~]$ psql psql (13.0) Type "help" for help. postgres=# INSERT INTO tbl VALUES('A'); INSERT 0 1 postgres=# select * from tbl; data ------ C B A (3 rows) ``` 3. session 3 :默认的read committed的隔离级别下,能看到session 2提交的修改 ,但是在新事务中,通过SET TRANSACTION SNAPSHOTsession 1中export snapshot的事务后,看不到session 2中新增的数据 ``` // 默认的read committed的隔离级别下,能看到session 2提交的修改 postgres=# select * from tbl; data ------ C B A (3 rows) // 导入 session 1 导出的快照,发现导入失败,因为快照导入事务必须具有可序列化或可重复读取的隔离级别 postgres=# SET TRANSACTION SNAPSHOT '00000004-0000003C-1'; WARNING: SET TRANSACTION can only be used in transaction blocks ERROR: a snapshot-importing transaction must have isolation level SERIALIZABLE or REPEATABLE READ // 设置事务隔离级别为repeatable read postgres=# BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ; BEGIN // 导入session 1的快照之后,发现看不到session 2的数据 postgres=*# SET TRANSACTION SNAPSHOT '00000004-0000003C-1'; SET postgres=*# select * from tbl; data ------ C B (2 rows) ``` 在session 1 导出快照之后,会在`$PGDATA/pg_snapshots`目录下生成与snapshot同名的文件,该文件就是快照文件。事务commit之后,文件就会被删除。 ``` [root@iZbp1bum6107bp8mgzkeunZ pg_snapshots]# l 00000004-0000003C-1 [root@iZbp1bum6107bp8mgzkeunZ pg_snapshots]# cat 00000004-0000003C-1 vxid:4/60 pid:17797 dbid:13580 iso:2 ro:0 xmin:575 xmax:575 xcnt:0 sof:0 sxcnt:0 rec:0 ``` **结论:** &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;session 1 export snapshot。session 2 通过SET TRANSACTION SNAPSHOT导入session 1 的snapshot。这样不管session 1 export snapshot后有没有别的session 提交事务,都不影响session 2。 <br> # <span style="font-size:15px">**实例2:export的事务是read committed隔离级别**</span> 1. session 1开启read committed隔离级别的事务,插入数据并导出快照 ``` // 事务开始前,插入一条数据 postgres=# insert into tbl values ('A'); INSERT 0 1 // 开启read committed隔离级别的事务 postgres=# begin TRANSACTION ISOLATION LEVEL read committed; BEGIN postgres=*# SELECT pg_export_snapshot(); pg_export_snapshot --------------------- 00000004-00000048-1 (1 row) postgres=*# select * from tbl; data ------ A (1 row) postgres=*# insert into tbl values ('B'); INSERT 0 1 postgres=*# SELECT pg_export_snapshot(); pg_export_snapshot --------------------- 00000004-00000048-2 (1 row) postgres=*# select * from tbl; data ------ A B (2 rows) ``` 2. session 2中查看,只能看到A数据,不能看到B数据,因为B数据在session 1中的事务中还没提交 ``` postgres=# select * from tbl; data ------ A (1 row) ``` 3. session 3 中新增一条数据并commit,session 1的事务中可以看到数据 ``` // session 3 插入一条数据 postgres=# INSERT INTO tbl VALUES('C'); INSERT 0 1 // session 1中可以看到这条数据 postgres=*# select * from tbl; data ------ A B C (3 rows) postgres=*# SELECT pg_export_snapshot(); pg_export_snapshot --------------------- 00000004-00000048-3 ``` 4. session 2 中导入快照00000004-00000048-1,则只能查到数据A ``` postgres=# begin TRANSACTION ISOLATION LEVEL repeatable read; BEGIN postgres=*# SET TRANSACTION SNAPSHOT '00000004-00000048-1'; SET postgres=*# select * from tbl; data ------ A (1 row) ``` 5. session 3 中导入快照00000004-00000048-2,也只能查到数据A ``` postgres=# begin TRANSACTION ISOLATION LEVEL repeatable read; BEGIN postgres=*# SET TRANSACTION SNAPSHOT '00000004-00000048-2'; SET postgres=*# select * from tbl; data ------ A (1 row) ``` 6. session 4 中导入快照00000004-00000048-3,则能查看到A、C的数据 ``` postgres=# begin TRANSACTION ISOLATION LEVEL repeatable read; BEGIN postgres=*# SET TRANSACTION SNAPSHOT '00000004-00000048-3'; SET postgres=*# select * from tbl; data ------ A C (2 rows) postgres=*# commit; COMMIT ``` 7. session 5 insert一条新的数据D ``` // session 5 新增一条数据,只查询到A、C、D,因为B在session 1中的事务中未提交 postgres=# insert into tbl values ('D'); INSERT 0 1 postgres=# select * from tbl; data ------ A C D (3 rows) // session 2和session 3、session 4的查询结果跟前面一致 // 而session 1中可以看到D postgres=*# select * from tbl; data ------ A B C D (4 rows) ``` 8. session 1执行commit,其他事务中依然看不到其他数据 ![](https://img.kancloud.cn/f6/38/f638c3df58987a2eb65bd54c2e35e83a_1489x905.png) **结论:** repeatable read和serializable是看不到事务中其他事务提交的数据的, read committed则可以看到 <br> **总结:** * import事务快照时,其实只是把执行export事务的当时的txid_current_snapshot传递过来. 不会传递事务的隔离属性如(read committed或repeatable read或serializable)。 * 共享事务snapshot的事务之间,除了存在自己修改的数据的差异之外,对于执行export的事务如果是read committed的,它看到的数据和执行import的事务看到的数据也是存在差异的。