除了MySQL默认的“Repeatable Read”隔离级别,还有一个常用的“Read Committed(RC读取提交内容)”,在这个隔离级别下再做下演示:
1)重新开启两个cmd窗口
此时处于“Read Committed”隔离级别。
2)开启事务,左右两边执行普通不加锁的select语句。
3)(左边)先执行select加X锁,然后再执行(右边)的语句。
和“RR”隔离级别一样,只有(左边)提交事务以后(右边)才可以获取到锁,这个楠神不演示了。
一个事务(A)未提交,其他事务是看不到(A)事务修改的数据的,这个也不演示了。
我们直接去演示和“RR”隔离级别不一样的地方。
4)(左边)执行update,(右边)执行普通的select语句,然后(左边)commit提交,(右边)再次执行普通的select语句
(右边)虽然事务还没有结束,普通的select语句可以获取到最新的数据。这个地方就和“RR”隔离级别完全不一样了。普通的select语句只要是已提交事务修改的数据都能获取到。所以在“RC”隔离级别下,数据是不可重复读的。
再去看下最高级别的隔离——“Serializable”
1)重新开启两个cmd窗口
此时处于“Serializable”隔离级别。
2)开启事务,左右两边执行普通不加锁的select语句。
3)当在(左边)执行select加X锁时
报错了“锁等待超时”,如果是在(右边)执行select加X锁呢:
依然报错。
为什么都会报错?只有一种解释,在“Serializable”隔离级别下普通的select语句默认加了“共享锁S”。
(左边)1先加了一个“共享锁S”,(右边)2也可以为相同的数据集加一个“共享锁S”。在“共享锁S”下,是不能加“排他锁X”,只有等到“共享锁S”全部解除了才能加“排他锁X”。
所以说,“Serializable”隔离级别虽说可以最高地确保数据安全,但它的弊端太大:读写太容易冲突,易产生死锁,并发性急剧下降。
4)看下面的死锁
当(右边)4执行加排他锁时,由于(左边)事务没有结束,(左边)的共享锁依然存在,(右边)一直等待……
此时,(左边)5也要加排他锁,可由于(右边)事务没有结束,(右边)的共享锁也还在,那此时就会出现一种僵持状态。(右边)需要等待(左边)解除共享锁,(左边)也在等待(右边)解除共享锁,此时就产生了死锁。
(右边)的排他锁是先准备加的,所以MySQL就强制把(左边)的事务结束了。
额外说明:
共享锁在事务结束时会解锁,当只有一个事务对某一数据集加“共享锁”,然后又加“排他锁”,是可以加成功的。“共享锁”会变成“排他锁”,事务自己肯定不会影响自己,事务的锁只会影响其他事务。
在没有把握的情况下平时还是少用共享锁。
在写SQL语句时,要尽量避免死锁的产生。死锁后面还会重点讲解。