MySQL之事务的隔离性

最近看了不少关于MySQL的文章,对MySQL中的事务的概念和原理也有了更加深刻的理解。所以这里也结合自己的一些理解和实战,记录一下。

MySQL的事务的四大原则

首先,我们先聊一聊事务的四大原则, 即大家耳熟能详的 ACID。

  1. 原子性(Atomicity):事务内的一系列的CRUD操作,对于一个事务而言,是一个原子操作。要么全部成功,要么全部失败。
  2. 隔离性(Isolation):隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

隔离性的核心就是,A事务中所有的CURD操作,对于B事务而言是不可见的。

  1. 持久性(Durability):当事务正确完成后,它对于数据的改变是永久性的。
  2. 一致性(Consistency):事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。

看似很难以理解,其实只要保证了上面的三大特性(AID),一致性就自然而然的保证了。
eg:拿转账来说,假设用户A和用户B两者的钱加起来一共是20000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是20000,这就是事务的一致性。

事务的隔离级别

前面提到了事务的四大特性,在单机情况下(我们这里讨论的都是单机下的事务,而不是分布式事务),原子性和持久性,在MySQL中是很好得到保证的。但是事务的隔离性在高并发的情况下,要保证并发性能,务必要对事务做一定的优化。因此这里详细探讨一下事务的隔离性.

前面提到,隔离性的核心就是,A事务中所有的CURD操作,对于B事务而言是不可见的。那么这里就存在一个可见性的问题,先引入一个问题:

  1. 提交(或者回滚)之前可见,还是之后可见?
  • 提交之前可见:即A事务的操作,在提交(回滚)之前,对于B事务是可见的。这就是脏读========对应的隔离级别就是未提交读(RU)。最低级别的隔离级别,基本上没有任何隔离性操作,相反并发性能最高。
  • 提交(回滚)之前不可见,但提交(回滚)之后可见:即A事务的操作,
    在提交(回滚)之前,对于B事务是不可见的,但是在提交之后,对于B事务是可见的,也就是B事务可以查询到A事务提交的记录。这就是不可重复读,顾名思义,就是两次读取的操作不一致,所以叫做不可重复读========= 对应的事务隔离级别是提交读(RC),有一定的事务隔离,并发性降低。
  1. 上面讨论的两个隔离级别,针对的都是,A事务的操作,在什么情况下可见,但是,事务的隔离性要求的是A事务的操作,对于B事务是不可见的。由此又引入一个问题,不可见的粒度是多大呢?
  • 仅仅A事务中对数据的修改和更新对B事务不可见:什么意思呢,就是说A事务中修改了x,y,z记录,那么x,y,z记录对于B事务是不可见的。但是表中的a,b,c等其他记录,对于B事务还是可见的。这种情况下会导致幻读的问题,具体后面再讲,先记住概念。======== 对应的事务隔离级别是可重复读(RR),性能可观,也是MySQL默认的事务隔离级别。
  • 如果A事务导致表中的所有记录对于B事务不可见:这种情况下就是所有事务串行化,不会有任何的并发问题,但是性能也最差。========= 对应事务隔离级别中的 串行化.

在上面的叙述中,已经大致阐述了数据库事务隔离级别中存在的几种并发问题以及针对这些并发问题相应的事务隔离级别。下面我们就结合实际操作,详细阐述一下这些事务的概念:

  1. 未提交读(Read Uncommited,即RU):即A事务可以读取到B事务没有提交的问题。此时,如果B事务回滚,那么相当于A事务就读到了数据库中没有的数据,也就是并发问题中的脏读现象。
  1. 设置事务隔离级别为未提交读(RU)set session transaction isolation level Read uncommitted
  2. 在A窗口(Session)中执行开启A事务,同时查询id=1的记录
    MySQL之事务的隔离性_第1张图片
    此时可以看到,A事务中,id=1查询到的name=”xiaoming“。
  3. 在B窗口中,开启B事务,同时修改id=1的记录的name=”fangfang“。
    MySQL之事务的隔离性_第2张图片
  4. 此时,当A事务中再次查询id=1的记录,会发现,name已经被修改了。如果这个时候B事务rollback,则A事务读取到了数据库中不存在的数据。即为脏读
    MySQL之事务的隔离性_第3张图片
  1. 提交读(Read Commited,即RC):即一个事务A只能读取到其他事务B已经提交的数据。没有提交或者rollback的数据,A事务无法读取到。
  1. 修改数据库的事务隔离级别为RC:set session transaction isolation level Read committed
  2. A事务开始事务,同时查询id=1的记录,发现name=”xiaoming“
    MySQL之事务的隔离性_第4张图片
  3. B 事务开启事务,同时修改id=1的记录的name=”fangfang”,但是此时B事务没有提交
    MySQL之事务的隔离性_第5张图片
  4. 此时A事务再次查询id=1 的记录,发现name仍然为“xiaoming”,没有读到未提交的数据。
    MySQL之事务的隔离性_第6张图片
  5. B事务提交事务(commit),数据入库
    MySQL之事务的隔离性_第7张图片
  6. 这个时候,再到A事务中查询id=1的记录,发现name=“fangfang”。读取到了提交的数据,同时同一个事务中两次相同的读操作,却读到了不同的数据,这种并发现象就是不可重复读
    MySQL之事务的隔离性_第8张图片

Tips:不可重复读和脏读的区别:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
3. 可重复读(Read Repeatable,即RR):即一个事务A,两次读取同一条记录,无论是否有B事务对该记录进行了修改,并且提交。A事务始终读取到的都是和A事务开始时的一致。即A事务一旦读取到了一条记录,就不会再次读取到该记录的其他状态。

  1. 修改事务隔离级别为可重复读(RR):set session transaction isolation level Repeatable Read;
  2. A事务开始事务,同时查询id=1的数据
    MySQL之事务的隔离性_第9张图片
  3. B事务修改id=1的记录,同时提交修改(commit)。
    MySQL之事务的隔离性_第10张图片
  4. A事务再次查询id=1的记录,发现name还是原来的值“fangfang”。则表明此时为可重复读的状态。
    MySQL之事务的隔离性_第11张图片
  5. 此时,B事务向数据库中插入一条新的记录,id=6.
    MySQL之事务的隔离性_第12张图片
  6. A事务中,查询id=6的记录时,发现没有改记录,但是插入时,却提示主键冲突。表明此时发生了并发问题中的幻读现象
    MySQL之事务的隔离性_第13张图片

Tips:幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项(记录),而幻读针对的是不同的数据项(记录),简而言之,就是不可重复读是两个事物对同一个记录的修改造成的,而幻读是由另一个事物插入了新的记录造成的。

  1. 序列化(Serializable):即所有的事务串行执行,不存在任何的并发问题。但是并发性能也最低,生产中几乎不可能使用,所以也不做过多赘述。

前面分析了很多的数据事务的概念,以及通过实际的操作阐述了不同的隔离级别下的并发问题,那么问题来了:MySQL通过什么实现了不同的事务隔离级别呢?
请看下一章

你可能感兴趣的:(MySQL,数据库)