数据库的并发操作带来数据的不一致性有 丢失修改,不可重复读,读脏数据。其中不可重复读有三种情况。另外两种为:1 事务T1按照一定条件读取了某些数据,T2删除了部分记录,T1再次按相同条件读取记录时,发现有数据莫名消失。2 事务T1按照一定条件读取了某些数据,T2插入了符合T1条件的数据,T1再次按照相同条件读数据时,发现莫名多出数据。这两种情况叫做幻影读。
T1 | T2 | T1 | T2 | T1 | T2 |
read A=1 A=A+1
Write A
|
read A=1 A=A+1 Write A
| read A read B C=A+B=2
read A read B C=A+B=3 |
read A A=A+1 Write A
| read A=1 A=A+1 Write A
rollback |
read A=2
|
a丢失修改 | b不可重复读 | c读脏数据 |
所以需要封锁技术控制并发操作
锁类型:
X锁,写锁, 排他锁 ,exclusive locks用于数据写操作前进行锁定。 如果事务T对数据A加上X锁,就只允许事务T读取和修改数据A,其他事务不能对数据A再加任何锁,但可以读。直到事务T释放A上的锁才能
S锁,读锁, 共享锁 ,share locks 用于数据读操作前进行锁定。 如果事务T对数据A加上了S锁,事务T就只能读取数据A但不可以修改,其他事务可以再对数据A加S锁执行读取操作,只要数据A上有S锁,任何事物都只能对其加S锁而不能加X锁修改。
封锁粒度和意向锁这里就不讨论了。
锁的持续时间:
在基于锁的并发控制中,隔离级别决定了锁的持有时间。"C"-表示锁会持续到事务提交。 "S" –表示锁持续到当前语句执行完毕。如果锁在语句执行完毕就释放则另外一个事务就可以在这个事务提交前修改锁定的数据,从而造成混乱。
隔离级别 | 写操作 | 读操作 | 范围操作 (...where...) |
未授权读 | S | S | S |
授权读 | C | S | S |
可重复读 | C | C | S |
可序列化 | C | C | C |
由这两种锁组合形成的四种隔离级别也有叫封锁协议
一级封锁协议--未授权读取(Read Uncommitted)
事务T在修改数据A之前必须先对其加X锁,直到事务结束才释放X锁
假设A和B初始都为1
T1 | T2 | T1 | T2 | T1 | T2 |
Xlock A A=A+1 Write A commit Unlock A
|
等待 等待 等待 等待 Xlock A A=A+1 Write A commit Unlock A
| read A read B C=A+B=2
read A read B C=A+B=3
|
Xlock A A=A+1 Write A commit Unlock A
| Xlock A A=A+1 Write A
rollback Unlock A
|
read A=2
|
a正确 | b不可重复读 | c读脏数据 |
其中c读脏数据是这样理解的, 事件T1对A加了排他锁,对数据A进行了修改,若事件T2对A申请锁,则会wait;若事件T2不对A申请任何锁,则即使A加对A加了排他锁,T2也会无视。
二级封锁协议--授权读取(Read Committed)
一级封锁协议加上事务T在读取数据R之前必须先对其加S锁,读完后方可释放S锁
T1 | T2 | T1 | T2 | T1 | T2 |
Xlock A A=A+1 Write A commit Unlock A
|
等待 等待 等待 等待 Xlock A A=A+1 Write A commit Unlock A
| Slock A Slock B read A read B C=A+B=2 Unlock A Unlock B
等待 等待 Slock A Slock B read A read B C=A+B=3 Unlock A Unlock B |
等待 等待 等待 Xlock A A=A+1 Write A commit Unlock A
| Xlock A A=A+1 Write A
rollback Unlock A
|
等待 等待 等待 · · · · Slock A read A Unlock A |
a正确 | b不可重复读 | c正确 |
三级封锁协议--可重复读取(Repeatable Read)
一级封锁协议加上事务T在读取数据A之前必须先对其加S锁,直到事务结束才释放。
T1 | T2 | T1 | T2 | T1 | T2 |
Xlock A A=A+1 Write A commit Unlock A
|
等待 等待 等待 等待 Xlock A A=A+1 Write A commit Unlock A
| Slock A Slock B read A read B C=A+B=2
Slock A Slock B read A read B C=A+B=3 Unlock A Unlock B
|
等待 等待 等待 Xlock A A=A+1 Write A commit Unlock A
| Xlock A A=A+1 Write A
rollback Unlock A
|
等待 等待 等待 · · · · Slock A read A Unlock A |
a正确 | b T1读操作是正确的 但是T1范围读是会出现幻影读的 | c正确 |
三级锁的在使用范围条件的时候会发生幻影读
T1 | T2 |
select count[id] from user where id>10 Slock id [id>10] count[id]=0 commit Unlock
select count[id] from user where id>10 Slock id [id>10] count[id]=0 commit Unlock |
insert into user values [id=11] commit
|
幻影读 |
两段封锁协议--序列化(Serializable)
对任何数据进行读写之前,必须对该数据加锁包括使用where的范围读。在释放一个封锁之后,事务不再申请和获得任何锁,只能继续释放锁,直到事务结束。
T1 | T2 | T1 | T2 | T1 | T2 |
Xlock A A=A+1 Write A commit Unlock A
|
等待 等待 等待 等待 Xlock A A=A+1 Write A commit Unlock A
| Slock A Slock B read A read B C=A+B=2
read A read B C=A+B=3 Unlock A Unlock B
|
等待 等待 等待 Xlock A A=A+1 Write A commit Unlock A
| Xlock A A=A+1 Write A
rollback Unlock A
|
等待 等待 等待 · · · · Slock A read A Unlock A |
a正确 | b正确--不带范围的操作 | c正确 |
两段锁在使用范围操作的时候不会发生幻影读
T1 | T2 |
select count[id] from user where id>10 Slock id [id>10] count[id]=0
select count[id] from user where id>10 count[id]=1 commit Unlock
|
等待 等待 等待 等待 等待 等待 insert into user values [id=11] commit
|
正确 |