LightDB-PostgreSQL事务之并发与隔离

前言

PostgreSQL 支持多用户并发访问,并且保证多个用户同时访问相同的数据时不会造成数据的不一致性。当多个用户同时访问相同的数据时,如果不进行任何隔离控制,可能导致以下问题:

事务并发问题

  • 脏读(dirty read),一个事务能够读取其他事务未提交的修改。例如,B 的初始余额为 0;A 向 B 转账 1000 元但没有提交;此时 B 能够看到 A 转过来的 1000 元,并且成功取款 1000 元;然后 A 取消了转账;银行损失了 1000 元。
  • 不可重复读(nonrepeatable read),一个事务读取某个记录后,再次读取该记录时数据发生了改变(被其他事务修改并提交)。例如,B 查询初始余额为 1000,取款 1000;同时 A 向 B 转账 1000 元并且提交;B 再次查询发现余额还是 1000 元,以为取款机出错了(当然,通过查询流水记录可以发现真相;数据库的状态仍然是一致的)。
  • 幻读(phantom read),一个事务按照某个条件查询一些数据后,再次执行相同查询时结果的数量发生了变化(另一个事务增加或者删除了某些数据并且完成提交)。幻读和非重复读有点类似,都是由于其他事务修改数据导致的结果变化。
  • 更新丢失(lost update),第一类:当两个事务更新相同的数据时,第一个事务被提交,然后第二个事务被撤销;那么第一个事务的更新也会被撤销(所有隔离级别都不允许发生这种情况)。第二类:当两个事务同时读取某一记录,然后分别进行修改提交;就会造成先提交的事务的修改丢失。

事务隔离级别

为了解决并发问题,SQL 标准定义了 4 种不同的事务隔离级别(从低到高):
事务隔离
事务的隔离级别从低到高依次为:

  • Read Uncommitted(读未提交):最低的隔离级别,实际上就是不隔离,任何事务都可以看到其他事务未提交的修改;该级别可能产生各种并发异常。不过,PostgreSQL 消除了 Read Uncommitted 级别时的脏读,因为它的实现等同于 Read Committed。
  • Read Committed(读已提交):一个事务只能看到其他事务已经提交的数据,解决了脏读问题,但是存在不可重复读、幻读和第二类更新丢失问题。这是 PostgreSQL 的默认隔离级别。
  • Repeated Read(可重复读):一个事务对于同某个数据的读取结果不变,即使其他事务对该数据进行了修改并提交;不过如果其他事务删除了该记录,则无法再查询到数据(幻读)。SQL 标准中的可重复读可能出现幻读,但是 PostgreSQL 在可重复读级别消除了幻读。
  • Serializable(可串行化):最高的隔离级别,事务串行化执行,没有并发。

只有 Serializable 真正实现了事务的完全隔离,但是不支持并发的数据库系统应用场景非常有限。因此,需要对并发性能和隔离性进行平衡,PostgreSQL 的默认隔离级别为 Read Committed;此时,可以避免脏读,同时拥有不错的并发性能。

使用SHOW命令可以查看当前的隔离级别:

show transaction_isolation;

transaction_isolation|
---------------------+
read committed       |

1 row(s) fetched.

如果需要修改当前事务的隔离级别,可以在事务的最开始执行SET TRANSACTION命令:

begin;
SET TRANSACTION ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED };

LightDB 默认隔离级别(READ COMMITTED)演示

LightDB 默认级别(READ COMMITTED)不会发生脏读,但是存在不可重复读幻读更新丢失问题

事务1事务2
begin;select balance from accounts where id = 1; – 返回6000.0000
begin;update accounts set balance = balance + 1000 where id = 1; select balance from accounts where id = 1; - 返回7000.0000
select balance from accounts where id = 1; – 仍然返回 6000,没有脏读
commit; – 提交事务
select balance from accounts where id = 1; – 此时返回 7000,出现不可重复读
select * from accounts where id=4; – 返回 UserC
begin;delete from accounts where id = 4;commit; – 删除 UserC 并提交事务
select * from accounts where id=4; – 没有结果,出现幻读
select balance from accounts where id = 1; – 此时返回 7000
begin; select balance from accounts where id = 1; – 此时返回 7000
update accounts set balance = 6000 where id = 1; – 更新为 6000
update accounts set balance = 8000 where id = 1; – 等待事务 1 提交
commit;
commit;
select balance from accounts where id = 1; – 返回 8000,而不是自己修改成的 6000,更新丢失

在以上过程中,PostgreSQL 使用了锁加 MVCC(Multiversion Concurrency Control)技术来实现数据的隔离和一致性。MVCC 简单来说,就是保留每次数据修改之前的旧版本,根据隔离级别决定读取哪个版本的数据。这种实现的最大好处就是读操作永远不会阻塞写操作、写操作永远不会阻塞读操作。

如果一个事务已经修改某个数据而且未提交,则另一个事务不允许同时修改该数据(必须等待),写操作一定是相互阻塞的,需要按照顺序执行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值