3级4种隔离级别
- 数据库操作中为了有效保证并发读取数据的正确性,定义事务隔离级别。可用多版本或者锁实现。
- mysql默认 REPEATABLE_READ , Oracle默认 READ_COMMITTED 。
无隔离
- 一个事务可以操作另一个未提交事务的数据,
- 问题:
- 脏读:事务A读取事务B未提交的数据。
- 事务B更新多个对象,事务A读到其中部分对象。
- 事务B回滚,事务A读到事务B即将回滚的数据。
- 脏写:事务A覆盖事务B未提交的数据。
- 不同事务的并发混在一起。
- 脏读:事务A读取事务B未提交的数据。
- 使用场景:
- 只需要保证数据可回滚或者可持久化。如只有insert,无update场景。
弱隔离
读提交
- 事务A在事务B提交后才能读取事务B修改的数据。
- 解决问题:
- 脏写。行级锁,事务修改完成提交之后释放。
- 脏读。行级锁、数据多版本。
- 问题:
- 不可重复读。读倾斜。
- 由于其他事务修改单对象,导致查询该对象的状态不一致。如其他事务update、delete同一条数据。
- 由于其他事务修改单对象,导致查询该对象的状态不一致。如其他事务update、delete同一条数据。
- 不可重复读。读倾斜。
可重复读
- 事务A相同条件两次读取的数据相同。
- 解决问题:
- 不可重复读。MVCC解决方案。
在innodb可重复读隔离级别下,普通的查询是快照读,是不会看到别的事务插入或者修改的数据
所以说MVCC可以解决幻读。幻读在“当前读”下才会出现。
- 问题:
- 更新丢失。两个事务并发select->modify->update,更新冲突,LWW产生更新丢失。
- 由于多事务对单对象操作产生直接竞争,如update和delete操作。
- 幻读。写倾斜,事务A select-> 事务B改变部分数据-> 事务A相同条件查询获得的结果集不一样。
- 由于多事务对一组对象中的不同部分操作产生间接竞争,如insert或者update、delete不同数据。
- case1:insert新对象。第二次查询多了一条数据。需要区间锁,参考
- case2:update不同对象。需要串行事务
- 由于多事务对一组对象中的不同部分操作产生间接竞争,如insert或者update、delete不同数据。
- 更新丢失。两个事务并发select->modify->update,更新冲突,LWW产生更新丢失。
串行化
- 解决问题:
- 更新丢失。
- 使用原子写操作。如
UPDATE table SET a = a + 1
- 悲观锁。分布式锁等。
- 乐观锁。CAS+重试。
- 如果CAS的Where条件运行在旧的快照上,仍然无法防止更新丢失。
- 自动检测更新丢失。PostgreSQL的可重复读、Oracle的可串行化、SQL Server的快照级别,支持检测更新丢失并终止违规事务。
- 多主写入,不使用LWW,合并冲突。
- 使用原子写操作。如
- 幻读。三种串行化方案。
- 更新丢失。
实际执行串行
- 方式:
- 单线程执行,如redis。
- 分区串行,扩展到多实例、多CPU。
- 存储过程。
- 条件:
- 事务简短,如OLTP,否则影响其他事务。
- 涉及数据可以完全加载到内存,减少I/O瓶颈。
- 单个分区写入吞吐低,否则I/O瓶颈。
- 跨分区事务尽量少,如同步更新二级索引。
两阶段加锁
- X锁和S锁。写写、读写互斥,读读并发。
- 如果只锁对象,无法解决幻读。
- DB访问延时不确定性大。
- 谓词锁。锁查询条件。
- 索引区间锁。扩大保护范围,简化谓词锁。比如mysql的next-key lock。
可串行化的快照隔离
- 乐观锁原则,事务提交时检查查询结果是否改变,判断是否需要中止事务。
- 是否读到过期版本MVCC快照。
- 写是否影响之前的读。读加SSI锁,类似S锁但是不阻塞写事务,提交时检查冲突。
- 实现:PostgreSQL的SSI。
读写锁
排他锁
- 排他锁又称写锁。
- 如果事务T对数据加上排他锁后,则其他事务不能再对数据加任何类型的锁。
- 获准排他锁的事务既能读数据,又能修改数据。
- 查询语句可以使用排他锁,对查询的数据每一行加上排他锁。
SELECT ... FOR UPDATE;
共享锁
- 共享锁又称读锁。实现读读并发。
- 如果事务T对数据加上共享锁后,则其他事务只能对数据再加共享锁,不能加排他锁。
- 获准共享锁的事务只能读数据,不能修改数据。
- 查询语句可以使用排他锁,读取同一版本的数据。
SELECT ... LOCK IN SHARE MODE;
锁实现4种隔离级别
- 读未提交:任何操作都不加锁。
- 读提交:数据的修改删除加排他锁。
- 重复读:数据读取之后加共享锁,无法锁住Insert,会幻读。
- 串行:不能通过行锁解决,需事务串行。
- 参考。
并发控制一般使用锁和数据多版本。
提高并发的思路:
- 普通锁:串行执行。
- 读写锁:读读并发。
- 数据多版本:读写并发。