对于 MVCC 数据库,事务会看到已提交数据的虚拟快照,从语句开始(在 READ COMMITED 中)或事务(在 REPEATABLE READ 或 SERIALIZABLE 中)开始。在 Oracle 9i 中,此功能得到了增强,因此我们可以定义更早的快照。这称为闪回查询。让我们看看在 PostgreSQL 中在某些情况下可以等效的东西。
我将为此示例运行多个并发会话
psql -v PROMPT1="$((1+$(jobs|wc -l)))%R%x%# "
这将连接psql并将提示设置为作业编号。
1=# drop table if exists demo;
DROP TABLE
1=# create table demo as select 1 n, transaction_timestamp(), statement_timestamp();
SELECT 1
1=# select * from demo;
n | transaction_timestamp | statement_timestamp
---+-------------------------------+-------------------------------
1 | 2021-08-22 18:07:19.934717+00 | 2021-08-22 18:07:19.934717+00
(1 row)
我创建了一个包含 1 行的演示表,这是在我自动提交时提交的。
现在我将打开一个我将保持打开状态的事务,以便快照仍然可用(这里没有像 Oracle UNDO_RETENTION 这样的东西)。我使用 pg_export_snapshot 函数获得了快照的内部标识(没有什么像 Oracle TIMESTAMP_TO_SCN)
1=# begin transaction;
BEGIN
1=*# set transaction isolation level serializable;
SET
1=*# select pg_export_snapshot();
pg_export_snapshot
---------------------
00000003-0015B343-1
(1 row)
这是使用该功能的基础,要小心,特别是如果您使用流式复制,保持事务打开可能是一个问题。此功能的目标是运行多个会话以查看相同的快照。例如,在不影响一致性的情况下并行化数据导出。
我现在打开另一个来模拟更多活动:
1=*# ^Z [1]+ Stopped /usr/local/pgsql/bin/psql -v PROMPT1="$((1+$(jobs|wc -l)))%R%x%# " [opc@C ~]$ [opc@C ~]$ psql -v PROMPT1="$((1+$(jobs|wc -l)))%R%x%# " 2=#
我在演示表中插入一个新行,并提交此更改:
2=# begin transaction;
BEGIN
2=*# insert into demo select 1 n, transaction_timestamp(), statement_timestamp();
INSERT 0 1
2=*# commit;
COMMIT
我本可以退出会话,但只需将其置于后台并在此处打开一个新会话:
2=# ^Z [2]+ Stopped /usr/local/pgsql/bin/psql -v PROMPT1="$((1+$(jobs|wc -l)))%R%x%# " [opc@C ~]$ psql -v PROMPT1="$((1+$(jobs|wc -l)))%R%x%# "
从那里,默认快照是语句的开始,我看到 2 行,因为两个插入都已提交。
3=# select * from demo;
n | transaction_timestamp | statement_timestamp
--------+-------------------------------+-------------------------------
1 | 2021-08-22 18:07:19.934717+00 | 2021-08-22 18:07:19.934717+00
1 | 2021-08-22 18:09:34.615897+00 | 2021-08-22 18:09:35.317214+00
(2 rows)
这是我们有更多控制权的地方。我将打开一个可序列化的事务,它的快照不是当前的,而是我不久前用 pg_export_snapshot() 得到的
3=# begin transaction;
BEGIN
3=*# set transaction isolation level serializable;
SET
3=*# set transaction snapshot '00000003-0015B343-1';
SET
这在 READ COMMITED 中是没有意义的,因为这个快照会被立即丢弃。
就在这里。当时,只有一行被提交:
3=*# select * from demo;
n | transaction_timestamp | statement_timestamp
--------+-------------------------------+-------------------------------
1 | 2021-08-22 18:07:19.934717+00 | 2021-08-22 18:07:19.934717+00
(1 row)
什么是导出快照?只是快照结构中的一些元数据,以及导出它的事务和进程的标识:
[postgres]$ cat /data/pgdata/pg_snapshots/00000003-0015B343-1 vxid:3/1422147 pid:2663858 dbid:14973 iso:3 ro:0 xmin:2133213514 xmax:2133213514 xcnt:0 sof:0 sxcnt:0 rec:0
使用 SET TRANSACTION SNAPSHOT 导入,只有在导出事务仍然存在时才能完成。一旦采用此快照,即使导出事务终止,您也可以继续使用此快照。
在您尝试将其用于奇异问题的疯狂伪解决方案之前,请记住它的设计原因:跨并行会话的一致读取。您有一个会话需要在可重复读取快照上执行一些工作(例如导出数据),并且您希望将工作分配给来自同一时间点快照的多个会话读取。如果你用 pg_dump 导出,这会被 with–jobs选项自动使用。
为了退出导出快照的会话,可能很想使用 PREPARE TRANSACTION,但这是不允许的 ( ERROR: cannot PREPARE a transaction that has exported snapshots)




