最近看了很多关于数据库的事务隔离级别和并发问题之间的博客 很多只提到对于某些问题可以采用什么隔离级别解决 但是没有详细解释为什么可以这么解决 。下面做出详细解释
数据库事务:对数据库的一系列操作
特性:acid
我们详解的就是事务的隔离性部分
首先了解关于锁的两个概念
我们在对数据库的操作无外乎四种操作:增 删 改 查 他们可以被看做两大类: 一个是读, 一个是写。我们对数据库的操作对数据库上锁来保证不会出现脏读 幻读 不可重复读 锁分为以下两种
共享锁(读锁):S锁 若事务T对数据A加上S锁 则T在只能读A不能修改A 其他的事务只能在对A加S锁 直到T释放A上的锁为止 。 简单的说:当我在读一本书(数据)的时候 ,不允许任何人对这本书乱改乱画,但我愿意分享这本书和其他人一起看
排他锁(写锁):X锁 若事务T 对数据A加上X锁 则除了T可以读和写之外 任何事务不能对A进行读和写 简单的说:当我在写一本书(数据)的时候,其他人不要读我这本没有写好的书,书上的内容可能用词不太恰当(数据还在计算,读到的数据可能只是中间结果); 其他人也不要对这本书修改(两个人同时修改一个数据,数据出错)
S锁和X锁 对于没有加锁的事务来说都是放行的 上述的对于锁描述过程仅适用于加锁的事务
数据不一致出现的问题:
1. 脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。(读到了rollback之前的数据或者中间过渡的数据)
2. 不可重复读 :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两 次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不 可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果 只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。(A事务对数据的两次相同读操作竟然不一样 由于在两次读的间隙 其他事务修改了A要读的数据) 为什么要读两次呢? 开发过程中涉及到验算 需要读两次数据验证
3. 幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象 发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。 如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。
幻读和不可重复读类似 幻读为重复读取多条数据 不可重复读为
在不加锁的情况下可能出现上述三种情况 为了避免上述情况 需要在保证尽量不降低效率的情况下
对于任何数据的处理 肯定是先读后写
加锁的位置大体分为:读前 写前 读后 写后 事务提交后
一级封锁协议(隔离级别0001):在事务修改的数据添加X锁(读前和写前不允许其他事务对其操作) 直到事务结束释放
可以避免丢失更新 (丢失修改) 因为丢失修改问题的根源在于事务T2读到了事务T1未修改的数据 只要在T1修改数据之前不允许其他事务读 就可避免 而X锁完美的解决了这个问题
但是不可以避免不可重复读和脏读
首先对于重复读来说:事务T2对数据修改 事务T1进行了一次读 和 又一次读的操作 t2虽然加锁了 但不影响T1读 因为T1只有读操作,不需要加锁 x锁无法限制没有锁的事务T1 因此不能避免重复读错误
对于脏读来说 同上 读脏数据的事务仅仅是有读操作 不需要加锁 因此不能避免读取中间数据(脏数据)
二级封锁协议(0010)在一级的基础之上 添加了 在读取之前添加S锁 读完立刻释放S锁
这次我们看看能不能解决不可重复读和脏读
对于不可重复读: 事务T1第一次读取 给数据添加了S锁 当事务T2想要对数据修改时 ,触发上面提到的封锁协议(即:修改数据需要给数据加X锁) 但是S锁的特性是不允许给加了S锁对象的数据添加X锁 因此此时T2需要等待 当第一次读完之后根据规则S锁会被释放 此时T2就可以趁虚而入了 并加上X锁 对数据进行修改 修改完之后 T2事务结束 T1再读的时候 已经出现错误了 因此 不能避免不可重复读
对于脏读:事务T1对数据首先进行修改的话需要加X锁 这个锁一旦加上, 就不允许加其他任何对此数据的锁 ,T2事务想要趁虚而入读取的话就必须添加S锁 ,X锁当然不允许了 因此此事务不会受到T2事务的趁虚而入 。 因此可以防止脏读
三层封锁协议 (0100) :注意二层封锁协议的后面一句话 “读完立刻释放S锁” 三层封锁协议则是修改了这句话变成“事务结束时才释放”
那么这么做能避免不可重复读吗 ?
当然 当事务T1在进行第一次读取的时候 就给事务加上了S锁 这个锁不允许X锁的添加 因此T2就被拒之门外了 同时S锁直到事务结束时才会释放 因此避免了不可重复读的错误。