数据库基础知识详解二:乐观/悲观锁、封锁级别、三级封锁协议以及两段锁协议
写在文章前:本系列文章用于博主自己归纳复习一些基础知识,同时也分享给可能需要的人,因为水平有限,肯定存在诸多不足以及技术性错误,请大佬们及时指正。
4.乐观锁和悲观锁
乐观锁和悲观锁在数据库和多线程并发中常被提及,但它们并不是某两个特定的锁,而是两个锁的宏观理念。
-
悲观锁:认为数据随时会被修改,因此每次读取数据之前都会上锁,防止其它事务读取或修改数据。应用于数据更新比较频繁的场景。
-
乐观锁:操作数据时不会上锁,但是更新时会判断在此期间有没有别的事务更新这个数据,若被更新过,则失败重试。适用于读多写少的场景。
乐观锁的实现方式有:
(1)加一个版本号或者时间戳字段,每次数据更新时同时更新这个字段。
(2)先读取想要更新的字段或者所有字段,更新的时候比较一下,只有字段没有变化才进行更新。
5.常见的封锁级别
意向锁是 InnoDB (MySQL加的数据引擎)自动加的, 不需要用户干预。 对于UPDATE、DELETE和INSERT 语句, InnoDB 会自动给涉及数据集加排他锁(X)。对于普通SELECT 语句,InnoDB 不会加任何锁。事务可以通过以下语句显式给记录集加共享锁或排他锁: 共享锁(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE。 其他 session 仍然可以查询记录,并也可以对该记录加 share mode 的共享锁。但是如果当前事务需要对该记录进行更新操作,则很有可能造成死锁。 排他锁(X):SELECT * FROM table_name WHERE … FOR UPDATE。其他 session 可以查询该记录,但是不能对该记录加共享锁或排他锁,而是等待获得锁。
排它锁(Exclusive Lock)/ X锁:事务对数据加上X锁时,只允许此事务读取和修改此数据,并且其它事务不能对该数据加任何锁。
共享锁(Shared Lock)/ S锁:加了S锁后,该事务只能对数据进行读取而不能修改,并且其它事务只能加S锁,不能加X锁。
意向锁(Intention Locks):一个事务在获得某个数据行对象的 S 锁之前,必须先获得整个表的 IS 锁或更强的锁。一个事务在获得某个数据行对象的 X 锁之前,必须先获得整个表的 IX 锁。IS/IX 锁之间都是兼容的。
好处:如果一个事务想要对整个表加X锁,就需要先检测是否有其它事务对该表或者该表中的某一行加了锁,这种检测非常耗时。有了意向锁之后,只需要检测整个表是否存在IX/IS/X/S锁就行了锁的作用:用于管理对共享资源的并发访问,保证数据库的完整性和一致性。
封锁粒度:
MySQL 中提供了两种封锁粒度:行级锁以及表级锁。
简单来说即锁住整张表或者锁住一行的数据。
封锁粒度小 | 封锁粒度大 | |
---|---|---|
优点 | 锁定的数据量较少,发生锁竞争的可能较小,系统的并发性更好。 | 系统开销较小。(加锁、释放锁、检查锁的状态都需要消耗资源)。 |
缺点 | 系统开销较大大。 | 锁定的数据量较多,发生锁竞争的可能较大,系统的并发性较差。 |
6.三级封锁协议
-
一级封锁协议:事务在修改数据之前必须先对其加X锁,直到事务结束才释放。
可以解决丢失修改问题(两个事务不能同时对一个数据加X锁,避免了修改被覆盖)
-
二级封锁协议:在一级的基础上,事务在读取数据之前必须先加S锁,读完后释放。
可以解决脏读问题(如果已经有事务在修改数据,就意味着已经加了X锁,此时想要读取数据的事务并不能加S锁,也就无法进行读取,避免了读取脏数据)
-
三级封锁协议:在二级的基础上,事务在读取数据之前必须先加S锁,直到事务结束才能释放。
可以解决不可重复读问题(避免了在事务结束前其它事务对数据加X锁进行修改,保证了事务期间数据不会被其它事务更新)。
7.两段锁协议
什么是两段锁协议?
定义:事务必须严格分为两个阶段对数据进行加锁和解锁的操作,第一阶段加锁,第二阶段解锁。
也就是说一个事务中一旦释放了锁,就不能再申请新锁了。
可串行化调度是指,通过并发控制,使得并发执行的事务结果与某个串行执行的事务结果相同。
事务遵循两段锁协议是保证可串行化调度的充分条件(注意不是充要条件,而是充分条件。即事务遵循两段锁协议一定能保证可串行化调度,但保证可串行化调度不一定需要遵循两段锁协议)。