数据库入门指南:浅谈数据库事务处理 | 青训营

82 阅读5分钟

事务的特性:ACID

  • 原子性(Atomicity):事务中的诸多操作要么都做要么都不做(日志恢复技术)
  • 一致性(Consistency)
  • 隔离性(Isolation):一个事务的执行不能被其他事务干扰(隔离事务级别)
  • 持续性(Durability):一旦事务提交,永久有效

一致性是由原子性、隔离性、持续性保证的,是指得到正确的结果。

如果没有原子性,事务可能只做一半,不能保证一致性,如果没有隔离性,事务可能被其他事务影响,也不能保证一致性

隔离事务级别

隔离事务级别是单副本情况的并发控制,对于分布式数据库,还需讨论一致性级别

隔离性依次提高,并发量依次降低:

  • 读未提交
  • 读已提交
  • 可重复读
  • 可串行化

多事务并发执行的问题:

(1)脏写

(2)脏读:事务1更新了记录,但没有提交,事务2读取了更新后的行,然后事务T1回滚,现在T2读取无效。

(3)不可重复读:事务1读取记录时,事务2更新了记录并提交,事务1再次读取时可以看到事务2修改后的记录;

(4)幻读:事务1读取记录时事务2增加了记录并提交,事务1再次读取时可以看到事务2新增的记录;

(零)没有并发控制

存在问题:脏写,更新遗失(写写不互斥)。

(一)读未提交(Read uncommitted)

写事务阻止其他写事务(写写互斥,读写不互斥,写会加排他锁,读不会加任何锁),避免了更新遗失。但是没有阻止其他读事务,允许脏读(dirty reads),即事务可以 看到其他事务“尚未提交”的修改。

解决问题:脏写。

存在的问题:脏读。即读取到不正确的数据,因为另一个事务可能还没提交最终数据,这个读事务就读取了中途的数据

解决办法就是下面的“可读取确认”。

(二)读已提交(Read committed)

写事务阻止其他写事务(写写互斥,读写不互斥,写会加排他锁,读不会加任何锁),但是写事务会创建一个数据项的新的版本快照,在写事务提交前,虽然不排斥读事务,但其他的读事务只能读到数据项旧的快照版本,也叫快照隔离(MVCC多版本并发控制)。

快照隔离确保读取数据的尝试无须等待(非阻塞读) ,如果使用锁机制实现读已提交,那么显然在写事务提交前读操作必须等待

但是写事务之间仍有必要使用锁机制进行互斥,如果我们完全放弃锁机制而基于快照,那么可能使得两个写事务都只能看到旧的数据,造成更新遗失(写时序异常

解决问题:脏读。

存在的问题:不可重复读。即在同一次事务内的两次读取结果不同,可能第一次id为1的人叫“李三”,然后有写事务被提交,第二次读id为1的人就叫了“李四”。两次读操作读到了不同的快照版本。

解决办法就是下面的“可重复读”。

(三)可重复读(Repeatable read)

写事务阻止其他写事务(写写互斥,读写不互斥,写会加排他锁,读不会加任何锁),但是可重复复读隔离级别是事务级别的快照,每次读取的都是当前事务的快照版本,即使当前数据被其他事务修改了(commit),也只会读取当前事务版本的数据。

解决问题:不可重复读。

存在的问题:幻读。我们对当前事务的快照是对已存在数据库内的数据项的快照,因此对这个数据项是可重复读的,但可能会有新的数据项被插入数据库,导致一个事务第一次读取可能读取到了10条记录,但是第二次可能读取到11条,这就是幻读。

我们也可以使用对行加读写锁的方式实现可重复读,即行锁,行锁相对较表锁,粒度更细,使得对不同行的更新可以并发进行,互不干扰,提高了并发度,但是我们只能对已存在的的行加锁,就不能阻止新的行被插入,因此仍然会有幻读问题。

可重复读相当于实现了对已有数据项操作的可串行,但会有幻读问题,解决办法就是下面的“串行化”。

(四)可串行化(Serializable)

读加共享锁,写加排他锁(读者写者问题,共享锁用来事务读数据,排他锁用来事务写数据)。这样读取事务可以并发,但是读写,写写事务之间都是互斥的,这样执行与所有操作串行执行结果完全一致,所以叫可串行化,这种情况下数据是完全正确的,但是会影响并发量。

这里的锁显然是表锁

快照隔离的优势是确保读取数据的尝试无须等待,但快照是基于数据项的,我们很难对庞大的整张表生成快照(代价大),因此,快照隔离很难解决幻读问题而实现真正的可串行化,因此在MySQL中,MVCC只在读取已提交(Read Committed)可重复读(Repeatable Read) 两个事务级别下有效(MVCC主要包括Read View和undo日志)。

总结

快照隔离的优势:取数据的尝试无须等待(非阻塞读,直接读取快照)

快照隔离的问题:幻读、写时序异常 (不等前一个写事务完成提交,后一个写事务就读到旧的快照版本开始写,造成其中一个更新遗失);