vlambda博客
学习文章列表

图解mysql事务的四个隔离级别

什么是隔离级别

  说到隔离级别,就要先谈到事务,因为隔离级别是基于事务而存在的.


事务

  事务指的是多个数据同时修改时,要么一起成功,要么一起失败。事务就像是小时候玩超级玛丽一样,你每次过关,都必须在没有死亡的情况下才能过关,只要有一次死亡,那么这一关就得重新开始;超级玛丽不存在中途继续的情况,重新开始就意味着回滚(rollback),过关就代表提交(commit)

myisam

  众所周知,,myisam是不支持事务的,所以myisam的每次修改数据时都是串行化的,也就是表锁;


innodb

  mysql中只有innodb才支持事务,innodb支持表锁和行锁,innodb锁的对象是索引,聊到innodb的索引,肯定要牵扯到事务,事务有4个属性,称为ACID属性

图解mysql事务的四个隔离级别

原子性(Actimicity): 事务是原子操作,要么同时修改,要么同时回滚

一致性(Consistent):事务完成时必须保证数据一致性

隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作时影响独立的环境之下

持久性(Durable):也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。


隔离级别能解决什么问题

  而今天我们就要讲mysql的隔离性,隔离级别是为了解决脏读、不可重复读、幻读问题的,四个隔离级别分别为:

图解mysql事务的四个隔离级别

  需要注意的是,隔离级别虽然解决了安全问题,但是并发效率却随着级别的升高而降低;也就是说越安全、效率越低;串行化可解决所有问题,但效率最低


事务出现的问题

  mysql是一个并发数据库,也就是支持多个用户同时修改的情况,那么有多个用户修改,就会有多个事务,多事务高并发的情况下会出现什么问题呢?直接告诉你吧,就是上面讲的,会出现脏读、不可重复读、幻读的情况;


脏读

  比如有下图的用户表,表中只有一条数据,


  1. A事务第一次读这条数据,amount为100,没问题,

  2. 然后在这中间,有一个B事务修改了其中一条信息,将amount改为98,但是这时候B事务还没提交数据,

  3. 在这间隙里,A事务又查询了一次数据,查到了B事务未提交的数据,

  这就是脏读;如果A事务将这个未提交的数据拿来使用,而事务B又回滚的话,将会出现很大的问题;会出现数据不一致的问题

图解mysql事务的四个隔离级别

不可重复读

还是那一行数据;


  • A事务第一次读这条数据,amount为100,没问题,

  • 然后在这中间,有一个B事务修改了其中一条信息,将amount改为98,并且提交了B事务

  • A事务在读这条数据时,得到的结果是98,

  同一个事务,两次查询到的数据不一样,如果有多次查询同一条数据,每次查询到的数据都不一样,你是不是会很抓狂?不可重复读的危害其实也不大,因为oracle默认的隔离级别就是不可重复读;所以oracle的效率要比mysql高;

图解mysql事务的四个隔离级别

幻读

还是那一行数据;

  1. A事务第一次查询所有的数据,结果只有一行,没问题

  2. 然后在这中间,有一个B事务插入了一条新的数据;

  3. A事务在查询所有数据,结果变成了2行数据,就像出现了幻觉一样;

  这种现象叫做幻读;幻读会有数据一致性的问题,明明同一个事务,查询出来的数据却不一致,

图解mysql事务的四个隔离级别

  行锁可以解决不可重复读的问题,但是不能解决幻读问题;如果要解决幻读,有2种方法


  • 使用间隙锁

  • 使用串行化隔离级别

四大隔离级别

1、读未提交

  read uncommitted 只要修改了数据,不管有没提交,其他事物都可以马上看到数据,用java来举例,就相当于每个数据都加了volatile关键字保证了其他线程的可见性;

  mysql 修改隔离级别为 读未提交 命令如下

set session transaction isolation level read uncommitted;

读未提交的缺陷


  是在并发场景下,这种隔离级别没有解决任何问题,所以基本没什么人用;


2、已提交读/读已提交

  READ COMMITTED 已提交读也可以成为读已提交,已提交读可以解决脏读的问题,它的级别比读未提交要高,所以,将隔离级别设置为读已提交后,其他事物修改了数据必须提交后,当前事务才可以看到;

mysql 修改隔离级别为 读已提交 命令如下

set session transaction isolation level read committed;

已提交读的缺陷


  虽然读已提交可以解决脏读问题,但是不能解决不可重复读的问题,但是它的效率高,所以oracle、sql Server等数据库的默认隔离级别就是读已提交;


3.可重复读:

   Repeatable Read 可重复读是mysql 的默认隔离级别,可重复读是为了解决同一个事务下,每次查询的结果必须是一致的

  1. 比如A事务读取id为1的数据,amount为100

  2. B事务将amount修改为98

  3. A事务再去查询时amount 还是100 ,不受B事务修改的影响

  这样就做到了不同事务之间的行数据彻底隔离;互不影响;设置可重复读的sql如下

set session transaction isolation level repeatable read;

图解mysql事务的四个隔离级别

可重复读的缺陷


  可重复读虽然能保证事务的一致性,因为可重复读只能保证同一行数据的事务,也就是行锁,如果我插入的新的行记录,就无法保证一致性了,也就无法解决幻读的问题,


4.、串行化

  Serializable 安全性最高的隔离界别,效率也最慢,这种隔离级别就相当于是表锁,每次修改数据时都会将整张表锁住,哎,那这不就是myisam了吗?没错,串行化就是和myisam一样了;串行化的隔离级别,在java中就和synchronized 关键字一样了;每次修改的时候只能有一个线程进行修改,其他线程只能在外面排队等着;那么多人排成串在那等,效率肯定是最慢的了

图解mysql事务的四个隔离级别

  mysql 设置可串行化隔离级别的sql如下

set session transaction isolation level serializable;


往期 · 推荐





我知道你

在看