可能出现的问题
丢失更新:两个事务同时修改一个数据,一个覆盖另一个。
- 比如事务T1:10 - 1 和 T2:10 - 2 并发执行,T2的结果覆盖T1,得到8,或者T1覆盖T2,得到9。
污读:事务T1更新了数据R,T2读了R,T1回滚,T2得到的数据和数据库不一致了
不可重读:事务T1读取了数据R,T2读取并更新了数据R,T1再次读取R发现两次读取不一致。
幻读:事务T1在读取某个范围的记录时,T2在该范围内插入了一条记录,T1再次读取到这一范围时,产生幻行。
锁类型
X锁(排他型封锁Exclusive Lock,写封锁)
- 禁止一切并发操作,其他事务需要等解锁之后在执行
S锁(共享型封锁Share Lock, 读封锁)
- 允许其他事务对同一对象查询,但不能对该数据对象修改
封锁协议
一级封锁协议:在事务修改数据对象时加X锁,直到事务结束。 解决丢失更新的问题。
二级封锁协议:一级封锁协议 + 事务在读时加S锁,读完释放S锁。防止污读。
三级封锁协议:一级封锁协议 + 事务在读时加S锁,读完不释放S锁,事务结束后才释放。防止污读和不可重读。
四种隔离级别
Read Uncommitted(未提交读):事务中的修改,即使没有提交,对别的事务也是可见的。
Read Committed(提交读):只能看到已经提交的事务做出的修改。
Repeatable Read(可重复读):解决污读和不可重读,理论上无法解决幻读的问题,但实际上InnoDB使用了多版本并发控制(MVCC multi version concurrency control)解决了幻读的问题。是MySQL的默认隔离级别。
Serializable(可串行):强制使事务串行执行。
隔离级别 | 可能污读 | 可能不可重读 | 可能幻读 | 加锁读 |
---|---|---|---|---|
Read Uncommitted | 是 | 是 | 是 | 否 |
Read Committed | 否 | 是 | 是 | 否 |
Repeatable Read | 否 | 否 | 是 | 否 |
Serializable | 否 | 否 | 否 | 是 |
MVCC是通过保存数据在某个时间点的快照实现的,也就是说,不管一个事务执行多长时间,该事务看到的数据都是一致的,而开始时间不同的事务,同一时刻看到的同一张表可能不同。
InnoBD的MVCC是通过每行记录后面隐藏的两个列实现的,这两个列一个是创建时间,一个是过期时间(或删除时间),储存的不是实际的时间,而是系统版本号,每开始一个新事务,系统版本号就会自动递增。
具体实现:
- select:只查找版本号小于当前事务版本的数据行,且过期时间为空或大于当前事务版本。
- insert:为插入的每一行都保存当前版本号作为行版本号
- delete:为删除的每一行都保存当前版本号作为行删除标识
- update:插入一条新记录,保存当前版本号为行版本号,保存当前版本号为原来行的删除标识(相当于insert + delete)
加了这两列(这两个版本号)使得大多数读操作都不需要加锁!