ACID
如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性:
原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
原子性的核心是:利用一个undo日志来记录事务可以回滚的各个数据版本。即当目前执行的事务发生故障时,需要回滚,可以根据undo日志中的记录来知道事务回滚到哪一步停止。这样来保证事务由当下状态回滚到开始执行前的状态。一致性(Consistency)
一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
数据库隔离级别
当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性,在介绍数据库提供的各种隔离级别之前,我们先看看如果不考虑事务的隔离性,会发生的几种问题:
脏读
脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。不可重复读
不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。幻读
幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
MySQL数据库为我们提供的四种隔离级别:
- Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
事务隔离级别最高,并发性最差,利用排它锁实现。针对同一资源,将所有的请求事务进行排序,一一个顺序执行。最高限度的保证了数据的一致性。
- Repeatable read (可重复读):可避免脏读、不可重复读的发生。
利用共享锁和排写锁实现。读读并行,禁止任何写事务并行
- Read committed (读已提交):可避免脏读的发生。
允许读后写并行。读到的任何数据都是提交的数据,避免读到中间的未提交的数据。但是无法避免不可重复读。因为读事务第一次读取数据之后,另一个写事务可能会修改此事务,导致读事务第二次读取数据和第一次所读到的不一样。但是这种场景并不是经常出现,系统的一致性可以接受,因此多数数据库的默认级别是读已提交。
- Read uncommitted (读未提交):最低级别,任何情况都无法保证。
对所有事务只加写锁,不加读锁。即只有写写不可以并行。但是这回导致读事务读到一些中间状态的数据,即脏读。
InnoDB行锁实现方式
InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!
MVCC
MVCC(多版本并发控制),是一种不利用锁机制实现的隔离级别,主要实现了在保证数据的一致性的前提下,实现了读写的并行。之前提到的读已提交,虽然实现了读写并行,读未提交实现了写读并行,读写并行;但是两种隔离级别均没有保证数据的一致性,分别出现不可重复读和脏读。
mvcc的原理是给每一数据的更新都有一个版本号。当写事务正在进行时,此时过来一个读事务,读事务会首先生成一个版本号,即该事物想读取哪一个版本的数据。然后,写事务更新数据,读事务读取之前相应版本的数据,而保证了不出现不可重复读和脏读的情况。