这里只是介绍封锁协议,因为其他的其实不算很难理解,网上优秀的博客有很多。
在运用X锁和S锁这两种基本封锁对数据对象加锁时,还需要约定一些规则。例如,何时申请X锁或S锁、持锁时间、何时释放等。这些规则称为封锁协议。通常使用三级封锁协议来在不同程度上解决并发操作的不正确调度带来的更新丢失、不可重复读和读“脏”数据等不一致性问题,这就是封锁协议
。
一级封锁协议
一级封锁协议是指,事务T在修改数据R之前必须先对其加X锁,直到事务结束才释放。一级封锁协议可以防止丢失修改,因为一个事务修改的时候会上x锁,其他事务不能进行修改,但是此时其他事务进行读取的时候并不会去竞争锁,所以能直接读取,就造成了脏读
。
二级封锁协议
二级封锁协议是指,在一级封锁协议基础上增加事务T在读数据R之前必须先对其加S锁,读完后即可释放S锁(无需等待事务结束)
。二级封锁协议出防止了丢失修改,还可以进一步防止读“脏”数据。因为此时T事务在修改数据上了X锁,T1事务想要读取的时候必须要上S锁,就需要等待了。
三级封锁协议
三级封锁协议是指,在一级封锁协议的基础上增加事务T在读数据R之前必须先对其加S锁,直到事务结束才释放。三级封锁协议出防止了丢失修改和读“脏”数据外,还可以进一步防止了不可重复读。
终极大招,Serializable(串行化)
现在还剩最后一个问题,就是幻读。上面的几种锁协议,都是建立在行锁的基础之上,不会去锁住整个表,这就会出现幻读的情况。
当一个事务A更新一个表中的所有数据的时候,这时候他会对所有的行上锁,其修改成功,然后返回来查看,发现有两条数据没有被修改,这两条数据是事务B刚刚插入的,因为A只锁住了他要修改的行,但是不会影响其他事务的插入,这就导致出现了两个没有被修改的数据,这个数据是被插入的,并部是真的没有被修改。
解决这个问题的办法,就只有锁表了,这种方式基本不会使用,将数据库操作决定的串行化了,严重拖慢性能
理解
1)第一个协议的意思就是,规定一个事务,如果当前事务打算修改这个数据R,就必须先对其加X锁,要想修改数据,就要先读取数据,读取之前就要加上X锁。什么?你问为什么要读数据?不读数据的话怎么会存在更新丢失问题,那就是直接覆盖了。
丢失修改:两个事务T1和T2同时读入同一数据并修改,T2提交的结果破坏了T1提交的结果,导致T1的修改被丢失。
这样是解决了更新丢失问题,为什么解决了?因为我们要求如果一个事务想要去修改数据,读数据之前要将上x锁,所以如果两个事务都要去修改的话,前一个在读的时候加上了锁,另外一个事物在读取的时候也需要加锁,但是X锁只能上一个,他就只能等了,所以就解决了更新丢失问题
2)第二个协议又加上了一个限制,如果有个事务想要读取数据,必须要先加S锁,我开始就很疑惑,我一个事务都加上了X锁了,加个S锁干什么,这其实是我走进了一个误区,我一直以为这是针对的同一个事务而言。
首先我们要知道,在一级协议中,我们只规定了写数据时加的锁,并没有规定读数据需不需要加锁。所以说,如果我一个事务A正在修改数据,加上了X锁,然后另外一个事务B打算来读取数据,他不需要去加锁,也就不存在竞争问题,所以,他能够读取到A事务没有提交的数据,导致了脏读。说到这我想你应该大概明白了其中的含义了吧,二级协议就是在一级协议的基础上规定,你一个事务要读取数据之前,必须要加S锁,如果另外一个事务正在写,就会导致当前读事务被阻塞了,也就解决了脏读问题。
3)三级协议同样是补充了二级协议,二级协议在一个事务中可能会多次获取和释放S锁,这就导致了可能在一个事务中多次获取的数据不一样,这就是不可重复读。因为我这个读事务一直在事务中,讲道理应该每次获取到的数据都是一样的,但是因为二级协议规定的是读取完之后马上释放锁,就导致了这个问题。所以三级协议规定,在整个事务中都要加上S锁,所以想要进行修改的事务就不能正常的加上X锁,然后修改数据,所以读锁读到的数据始终是一样的。