事务的实现原理可以解读为 RDBMS 采取何种技术确保事务的 ACID 特性。
事务的实现原理可以解读为 RDBMS 采取何种技术确保事务的 ACID 特性。PostgreSQL 主要使用 MVCC 和 WAL 两项技术实现 ACID 特性。
实际上,MVCC 和 WAL 这两项技术都比较成熟,主流关系型数据库中都有相应的实现,但每个数据库中具体的实现方式往往存在较大的差异。
在 PostgreSQL 中,每个事务都有一个唯一的事务 ID,被称为 XID。除了被 BEGIN - COMMIT/ROLLBACK 包裹的一组语句会被当作一个事务对待外,不显示指定 BEGIN - COMMIT/ROLLBACK 的单条语句也是一个事务。
数据库中的事务 ID 递增。可通过 txid_current() 函数获取当前事务的 ID。
PostgreSQL 中,对于每一行数据(称为一个 tuple),包含有 4 个隐藏字段。这四个字段是隐藏的,但可直接访问。
下面通过实验具体看看这些标记如何工作。在此之前,先创建测试表
CREATE TABLE test
(
id INTEGER,
value TEXT
);
开启一个事务,查询当前事务 ID(值为 3277),并插入一条数据,xmin 为 3277,与当前事务 ID 相等。符合上文所述——插入 tuple 时记录 xmin,记录未被删除时 xmax 为 0
postgres=> BEGIN;
BEGIN
postgres=> SELECT TXID_CURRENT();
txid_current
--------------
3277
(1 row)
postgres=> INSERT INTO test VALUES(1, 'a');
INSERT 0 1
postgres=> SELECT *, xmin, xmax, cmin, cmax FROM test;
id | value | xmin | xmax | cmin | cmax
----+-------+------+------+------+------
1 | a | 3277 | 0 | 0 | 0
(1 row)
继续通过一条语句插入 2 条记录,xmin 仍然为当前事务 ID,即 3277,xmax 仍然为 0,同时 cmin 和 cmax 为 1,符合上文所述 cmin/cmax 在事务内随着所执行的语句递增。虽然此步骤插入了两条数据,但因为是在同一条语句中插入,故其 cmin/cmax 都为 1,在上一条语句的基础上加一。
INSERT INTO test VALUES(2, 'b'), (3, 'c');
INSERT 0 2
postgres=> SELECT *, xmin, xmax, cmin, cmax FROM test;
id | value | xmin | xmax | cmin | cmax
----+-------+------+------+------+------
1 | a | 3277 | 0 | 0 | 0
2 | b | 3277 | 0 | 1 | 1
3 | c | 3277 | 0 | 1 | 1
(3 rows)
将 id 为 1 的记录的 value 字段更新为’d’,其 xmin 和 xmax 均未变,而 cmin 和 cmax 变为 2,在上一条语句的基础之上增加一。此时提交事务。
UPDATE test SET value = 'd' WHERE id = 1;
UPDATE 1
postgres=> SELECT *, xmin, xmax, cmin, cmax FROM test;
id | value | xmin | xmax | cmin | cmax
----+-------+------+------+------+------
2 | b | 3277 | 0 | 1 | 1
3 | c | 3277 | 0 | 1 | 1
1 | d | 3277 | 0 | 2 | 2
(3 rows)
postgres=> COMMIT;
COMMIT
开启一个新事务,通过 2 条语句分别插入 2 条 id 为 4 和 5 的 tuple。
BEGIN;
BEGIN
postgres=> INSERT INTO test VALUES (4, 'x');
INSERT 0 1
postgres=> INSERT INTO test VALUES (5, 'y');
INSERT 0 1
postgres=> SELECT *, xmin, xmax, cmin, cmax FROM test;
id | value | xmin | xmax | cmin | cmax
----+-------+------+------+------+------
2 | b | 3277 | 0 | 1 | 1
3 | c | 3277 | 0 | 1 | 1
1 | d | 3277 | 0 | 2 | 2
4 | x | 3278 | 0 | 0 | 0
5 | y | 3278 | 0 | 1 | 1
(5 rows)
此时,将 id 为 2 的 tuple 的 value 更新为’e’,其对应的 cmin/cmax 被设置为 2,且其 xmin 被设置为当前事务 ID,即 3278