数据表中原有的数据:SELECT * FROM transaction_test;
id name age
20 TEST 10
开启一个事务A:只是执行 GEGIN TRANSACTION IOSLATION LEVEL REPEATABLE READ; 不要提交,后面还有操作。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0.001s
开启事务B:执行插入操作并提交。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
INSERT INTO transaction_test VALUES (20, 'TEST----', 20);
COMMIT;
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0s
INSERT INTO transaction_test VALUES (20, 'TEST----', 20)
> Affected rows: 1
> Time: 0s
COMMIT
> OK
> Time: 0.002s
此时在事务A中执行查询操作。结果如下。
id name age
20 TEST 10
20 TEST---- 20
场景二
数据库表中的数据:SELECT * FROM transaction_test;
id name age
20 TEST 10
20 TEST---- 20
开启事务A,只执行一下操作
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM transaction_test;
返回结果:
id name age
20 TEST 10
20 TEST---- 20
开启事务B,插入一条数据,并提交。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
INSERT INTO transaction_test VALUES (120, 'YYYYYYY', 120);
COMMIT;
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0.001s
INSERT INTO transaction_test VALUES (120, 'YYYYYYY', 120)
> Affected rows: 1
> Time: 0.001s
COMMIT
> OK
> Time: 0.002s
开启事务C查询,结果如下:
id name age
20 TEST 10
20 TEST---- 20
120 YYYYYYY 120
此时在事务A中再次执行查询:结果如下:
id name age
20 TEST 10
20 TEST---- 20
场景三
数据库表中原有数据:
id name age
20 TEST 10
20 TEST---- 20
120 YYYYYYY 120
开启事务A,并执行如下操作,不提交。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT pg_sleep(10);
SELECT * FROM transaction_test;
查询结果如下:
id name age
20 TEST 10
20 TEST---- 20
120 YYYYYYY 120
开启事务B,执行插入操作并提交。注意该步操作必须在pg_sleep执行结束前完成。
开启事务C,执行查询操作:
id name age
20 TEST 10
20 TEST---- 20
120 YYYYYYY 120
265 香港加油 355
事务A中再次执行查询操作: 查询结果如下:
id name age
20 TEST 10
20 TEST---- 20
120 YYYYYYY 120
数据库表中没有数据。
开启事务A:执行插入操作,不提交。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
INSERT INTO transaction_test VALUES (265, '香港加油', 355);
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0.001s
INSERT INTO transaction_test VALUES (265, '香港加油', 355)
> Affected rows: 1
> Time: 0.002s
开启事务B,执行查询操作:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM transaction_test;
查询无结果。
此时提交事务A。再在事务B中执行查询操作。依然没有结果。
在该隔离级别下,一个查询可以看到该事务前面未提交的数据。
开启事务,执行以下操作:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM transaction_test;
INSERT INTO transaction_test VALUES (265, '香港加油', 355);
SELECT * FROM transaction_test;
执行结果如下:
第一个查询一句没有记录。
第二个查询语句结果如下:
id name age
265 香港加油 355
数据库表中已有数据:
id name age
265 香港加油 355
开启事务A:执行如下操作,先不回滚。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
UPDATE transaction_test SET name = '中国加油' WHERE id = 265;
结果如下:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0.001s
UPDATE transaction_test SET name = '中国加油' WHERE id = 265
> Affected rows: 1
> Time: 0.007s
开启事务B,执行同样数据的修改操作。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
UPDATE transaction_test SET name = '中国加油-YYDS' WHERE id = 265;
结果如下:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0.004s
注意此时,事务B中的修改操作并没有返回信息。
在事务A中执行回滚操作。事务B中的修改操作立刻返回信息如下:
UPDATE transaction_test SET name = '中国加油-YYDS' WHERE id = 265
> Affected rows: 1
> Time: 92.628s
场景2
数据库中原有数据:
id name age
265 中国加油-YYDS 355
1 香港加油 1
开启事务A执行以下操作:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
DELETE FROM transaction_test WHERE id = 1;
结果如下:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0.001s
DELETE FROM transaction_test WHERE id = 1
> Affected rows: 1
> Time: 0.001s
开启事务B,执行以下操作。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
UPDATE transaction_test SET name = '中国加油' WHERE id = 1;
结果如下:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0.001s
注意此时事务B中的修改操作没有返回信息,被阻塞了。在事务A中执行回滚操作。
事务B中的修改操作立刻返回信息:
UPDATE transaction_test SET name = '中国加油' WHERE id = 1
> Affected rows: 1
> Time: 147.549s
提交事务B后,数据表中数据如下:
id name age
265 中国加油-YYDS 355
1 中国加油 1
数据表原有数据:
id name age
265 中国加油-YYDS 355
1 中国加油 1
开启事务A,执行以下操作,先不提交。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
UPDATE transaction_test SET name = '中国加油-YYDS' WHERE id = 1;
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0s
UPDATE transaction_test SET name = '中国加油-YYDS' WHERE id = 1
> Affected rows: 1
> Time: 0s
开启事务B,执行以下操作
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
UPDATE transaction_test SET name = '中国加油-YYDS-2' WHERE id = 1;
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0s
此时事务B的修改操作没有信息返回,被阻塞了。
提交事务A。事务B的修改操作立刻返回信息如下:
UPDATE transaction_test SET name = '中国加油-YYDS-2' WHERE id = 1
> ERROR: could not serialize access due to concurrent update(由于并发更新,无法序列化访问)
> Time: 164.355s
事务B如果执行的是删除操作,同样的结果。
场景2 – 删除提交
表中原有数据:
id name age
1 中国加油-YYDS 1
20 TEST 10
开启事务A执行以下操作
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
DELETE FROM transaction_test WHERE id = 1;
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0s
DELETE FROM transaction_test WHERE id = 1
> Affected rows: 1
> Time: 0.001s
开启事务B,执行以下操作:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
UPDATE transaction_test SET name = '中国加油-YYDS' WHERE id = 1;
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0s
事务B中的更新操作没有信息返回,被阻塞了。此时提交事务A。事务B中的更新操作返回如下信息:
UPDATE transaction_test SET name = '中国加油-YYDS' WHERE id = 1
> ERROR: could not serialize access due to concurrent update
> Time: 69.744s
场景3 – 事务A修改提交后,事务B加锁
开启事务A:执行以下操作,先不提交。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
DELETE FROM transaction_test WHERE id = 20;
结果如下:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0.001s
DELETE FROM transaction_test WHERE id = 20
> Affected rows: 2
> Time: 0s
开启事务B,执行加锁操作:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM transaction_test FOR UPDATE;
结果如下:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0.003s
注意事务B的 SELECT FOR UPDATE 操作没有信息返回,说明事务B被阻塞了。这是提交事务A。事务B立刻返回如下信息。
SELECT * FROM transaction_test FOR UPDATE
> ERROR: could not serialize access due to concurrent update
> Time: 5.819s
场景4 – 只是锁住提交
数据表中原有数据:
id name age
20 TEST 10
开启事务A执行以下操作,不提交。
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM transaction_test FOR UPDATE;
结果如下:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM transaction_test FOR UPDATE;
id name age
20 TEST 10
开启事务B,执行如下操作:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
DELETE FROM transaction_test WHERE id = 20;
结果如下:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ
> OK
> Time: 0s
注意此时事务B中的删除操作没有信息返回,被阻塞。提交事务A。事务B中的删除操作立刻返回信息:
DELETE FROM transaction_test WHERE id = 20
> Affected rows: 1
> Time: 710.841s
提交事务B。符合条件的数据正在被删除。
结论
在可重复读的隔离级别中,第一个事务阻塞第二个事务,只有两个事务作用的行有交叉才会发生阻塞。
第一个事务不论是修改提交或者是删除提交,被阻塞的第二个事务都不能修改、删除或者锁住第一个事务所影响的行。并返回 ERROR: could not serialize access due to concurrent update。
第一个事务如果是仅仅锁着然后提交释放锁(select for update/share),则第二个被阻塞的事务可以进行作用最初发现的行。
注意事项
使用这个事务隔离级别的应用必须重试因序列化失败导致失败的事务。
即:返回 ERROR: could not serialize access due to concurrent update(由于并发更新,无法序列化访问) 这个错误,应当中断当前事务,并从头重试整个事务。
只读事务永远不会发生序列化冲突。