数据库的隔离等级算是一个比较重要的知识点,这里做一下学习记录。
基础介绍
- 共享锁(S):发生在数据查找之前,多个事务的共享锁之间可以共存
- 排他锁(X):发生在数据更新之前,排他锁是一个独占锁,与其他锁都不兼容
- 更新锁(U):发生在更新语句中,更新锁用来查找数据,当查找的数据不是要更新的数据时转化为S锁,当是要更新的数据时转化为X锁
- 意向锁:发生在较低粒度级别的资源获取之前,表示对该资源下低粒度的资源添加对应的锁,意向锁有分为:意向共享锁(IS) ,意向排他锁(IX),意向更新锁(IU),共享意向排他锁(SIX),共享意向更新锁(SIU),更新意向排他锁(UIX)
并行事务存在的问题
-
更新丢失:
和别的事务读到相同的东西,分别写该记录,然后存在先写的数据被后来写入的数据覆盖了。(谁写的快谁的更新就丢失了)
-
脏读:
指在一个事务处理过程里读取了另一个未提交的事务中的数据,读取数据不一致。说具体点就是指事务A对数据进行增删改操作,但未提交,另一事务B可以读取到未提交的数据。如果事务A这时候回滚了,则第二个事务B读取的即为脏数据。
-
不可重复读:
两次读之间有别的事务修改。也就是说,当前事务A先进行了一次数据读取,然后再次读取到的数据是别的事务B修改成功的数据,导致两次读取到的数据不匹配。
-
幻读:
两次读之间有别的事务增删。指的是在同一事务下,连续执行两次同样的SQL语句第二次的SQL语句可能返回之前不存在的行。比如事务A首先根据条件索引得到N条数据,然后事务B改变了这N条数据之外的M条或者增添了M条符合事务A搜索条件的数据,导致事务A再次搜索发现有N+M条数据了,就产生了幻读。
-
不可重复读和幻读的区别:
两者有些相似,但是前者针对的是update或delete,后者针对的insert。
上述问题对应的四种隔离等级
-
READ UNCOMMITTED (读未提交):
不处理。
-
READ COMMITTED (读已提交):
只读提交的数据,无脏读;
-
REPEATABLE READ (可重复读):
加行锁,两次读之间不会有修改,无脏读无重复读;
-
SERIALIZABLE (串行化):
加表锁,全部串行,无所有问题。
以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。
在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。
四种隔离等级实现原理
- READ UNCOMMITTED (读未提交)
- 事务对当前被读取的数据不加锁;
- 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级共享锁,直到事务结束才释放。
- READ COMMITTED (读已提交)
- 事务对当前被读取的数据加 行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;
- 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
- REPEATABLE READ (可重复读)
- 事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放;
- 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
- SERIALIZABLE (串行化)
- 事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;
- 事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。