一. 前言
数据库并发会导致各种问题(即多线程下导致的同步问题),需要用lock来解决。
一般会导致的问题:脏读,不可重复读,第一类更新丢失,第二类更新丢失
为了解决这些问题,需要使用锁机制,数据库一般有三级封锁协议。本文阐述了这些问题具体是什么,锁机制如何解决这些问题。
二. 数据库并发导致的问题
①脏读(dirty read):事务A update了data,事务B读取了update后的data,此时事务A rollback,导致B读取的是错误的数据,即dirty data
②不可重复读(unrepeatable read):B读取了data,A读取了data并且进行了update,此时B再次读取data,两次获取到的data并不一致。
③第一类更新丢失:A读取了data①,B读取了data并且进行update,commit。A同时又对data进行update,而这个update是在①的基础上操作的,最终B的更新就会被丢失。
④第二类更新丢失:B读取了data,A读取了data。B对data进行update,然后commit。接着A又对data进行update,commit。同样的,B的更新会被丢失。
三. 数据库的锁机制
数据库有两种锁,S锁和X锁:
S锁(shared):事务可读不可写。(其他事务可以同时添加S锁)
X锁(exclusive):事务可读可写。(其他事务不可以同时添加任何锁)
锁之间的兼容性,设定有两个锁a1,a2,用f(a1, a2)来表示两个锁的兼容性,如果f = 1,则表示兼容。如果f = 0,则不兼容。
f(s, s) = 1;其他诸如:f(x, x)= f(x, s)= f(s, x) = 0;
即X锁不能与其他锁同时存在。而s锁与s锁之间可以同时存在。
1.一级锁协议:事务T修改数据R之前,先加X锁,直到事务结束时释放(无论是正常结束commit还是非正常结束rollback)
作用:可防止 更新丢失问题。
不能防止: 脏读,不可重复读,幻读等。
PS:任何数据库都至少满足一级锁的协议,因为更新丢失是不能接受的错误,所以更新丢失一般只存在于理论讨论中,实际应用中基本不会出现这个问题。
2.二级锁协议:在一级锁的条件下,T在读取R之前先加S锁,读完后释放。
作用:可防止更新丢失,脏读。
不能防止: 不可重复读,幻读。
3.三级锁协议:在一级锁的条件下,T在读取R之前先加S锁,事务结束后释放。
作用:可防止 更新丢失,脏读,不可重复读,幻读。
四. 具体例子来说明锁的作用机制:
①不使用锁导致的更新丢失问题:
②使用一级锁解决更新丢失问题:
③为什么一级锁不能解决脏读问题:
④使用二级锁解决脏读问题:
⑤为什么二级锁不能解决不可重复读问题:
⑥三级锁解决不可重复读:
三级锁相当于,完全串行化。即一个事务开始了,其他事务都完全不能开始进行,无论是读还是写。因此就上述的例子,一定是A完全执行完毕,才轮到B执行。那么就必定不会出现不可重复读的问题。(三级锁的完全串行化,也就是,为了解决并行化带来的问题,直接不使用并行,改为串行执行。)