学习目标
- 了解读锁和写锁
- select for update 是读锁还是写锁
- MVCC是什么
- 事务的隔离级别
事务操作
我们同时开启两个事务作测试时发现:
通过例1、2、3发现MySQL关于事务默认是开启了防止脏读、幻读和可重复读。
- 脏读:变更操作能够读取
- 幻读:插入或删除能够读取
可以知道默认事务隔离级别是RR(Repeat READ)可重复读,即防止了脏读、幻读。
在RR级别下其实还有其它三个级别,
- 比RR弱的:Read uncommitted 可以脏读和幻读。Read commited 防止脏读
- 比RR强的:Serializable完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞,效率极低
例1
两个窗口同时开启事务,如下:
窗口1中事务1执行update时查看结果如下:
窗口2中事务2执行查看如下:
发现事务2查看结果没有发生改变
例2
即使事务1提交后
事务2仍然读取的是过去的结果
例3
事务1插入数据
事务2查看不到
事务2删除数据
事务1查不到
写锁和读锁
我们知道Mysql在Innodb引擎下支持事务,是因为相较于MyISAM引擎存在行级锁,可以做到排它。
排它锁又称:写锁。当一个事务对某几个上写锁时,不允许其他事务写,但允许读。更不允许其他事务给这几行上任何锁。包括写锁。。在insert、updte和delete时默认都是会加上写锁,比如例1。在查询时我们也可以通过select * from table for update
方式上写锁。
读锁,又称共享锁,可以读,不能执行其它写操作,所以不能写锁。可以上读锁。一般通过select * from table where lock in share mode
上读锁。
例1
事务1更新
事务2同样行更新,发现被阻塞
例2
事务1提交
事务2执行更新
在例2中发现update时事务1的提交被事务2紧随其后的提交,同行数据的更新被替换。
解决方法:
当一个事务读取数据时,锁住数据行,在提交之前,其他事务不能执行
乐观锁
通过加版本号判断方式防止阻塞,提高程序执行效率
事务特性
ACID,是指数据库管理系统(DBMS)在写入或更新资料的过程中,为保证事务(transaction)是正确可靠的,所必须具备的四个特性:原子性(atomicity,或称不可分割性)、一致性(consistency)、隔离性(isolation,又称独立性)、持久性(durability)
-
原子性:自不必多说,保证事务中所有操作一荣俱荣,一损俱损。
-
隔离性:在上述例中已经证明,事务执行间数据相互隔离
-
一致性:原子性和隔离性可以保证数据一致性
-
持久性:自不必多说,事务提交数据持久到数据库中。
MVCC
MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
那么Mysql是如何帮助我们实现事务的隔离,防止脏读和幻读的呢?
我们知道MVCC主要解决的是快照读的问题
-
当前读
像select lock in share mode(共享锁), select for update ; update, insert ,delete(排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁 -
快照读
像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本
通过多版本控制实现的。简单理解就是,给每条数据加上版本号。
update:
insert: