数据库都有哪些锁?
目录
数据库都有哪些锁?
一、按锁的区间划分
- 间隙锁(gap locks) :
- 是开区间的,是一个在索引记录之间的间隙上的锁。
- 作用是保证某个间隙内的数据在锁定情况下不会发生任何变化。例如在默认隔离级别(可重复读)下,当使用非唯一索引搜索或没有索引时等情况会产生间隙锁。
- 临键锁(next – key locks)
- 是行锁 + 间隙锁,即临键锁是是一个左开右闭的区间。
- InnoDB 的默认事务隔离级别是可重复读,在这种级别下,如果使用特定语句(如
select... for update
等)会触发临键锁,可以防止幻读。
二、按锁的粒度划分
- 表级锁(table – level lock)
- 直接给整个表添加锁。如
select * from student where name = 'tom' for update
(InnoDB 在不通过索引检索数据时也是表锁 )。 - 开销小,加锁快;不会出现死锁;但锁定粒度大,发生锁冲突的概率最高,并发度最低。
- MyISAM 在执行查询语句(select)前,会自动给涉及的所有表加读锁,在执行更新操作(update、delete 、insert 等)前,会自动给涉及的表加写锁。
- 直接给整个表添加锁。如
- 行级锁(record locks)
- InnoDB 中给指定的行添加锁:如
select * from student where id > 10 for update
。 - 是通过给索引上的索引项加锁来实现的,如果没有索引则会类似表锁(比如通过隐藏的聚簇索引) 。
- 行锁的劣势是开销大、加锁慢、会出现死锁;优势是锁的粒度小,发生锁冲突的概率低;处理并发的能力强。
- InnoDB 中给指定的行添加锁:如
- 页级锁
- 页级锁的颗粒度介于行级锁与表级锁之间。
- 主要应用于 BDB 存储引擎(现在使用相对较少)。
三、按锁级别划分
- 共享锁(share lock,即 S 锁)
- 又称读锁,允许一个事务去读取一行,阻止其他事务获得相同数据集的排它锁。若事务 t 对数据对象 a 加上 S 锁,则事务 t 可以读 a,但不能修改 a,其他事务只能对再对 a 加 S 锁,而不能加 X 锁 ,直到 t 释放 a 上的锁。这保证了其他事务可以读 a,但在释放 a 上的 S 锁之前不能对 a 做任何修改。
- 排它锁 / 独占锁(exclusive lock,即 X 锁)
- 又称写锁,允许获取排它锁的事物更新数据,阻止其他事务取得相同的数据集共享读锁和排它写锁。若事务 t 对数据对象 a 加上 X 锁,事物 t 可以读 a 也可以修改 a,其他事务不能再对 a 加任何锁,直到 t 释放 a 上的锁。
- 意向锁
- 意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的 IS 锁;
- 意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的 IX 锁。意向锁是 InnoDB 自动加的,不需要用户干预。
四、按加锁方式分类
- 自动锁(automatic locks) :数据库自动根据操作和场景加的锁。
- 显示锁(lock tables) :通过特定的命令(如
lock tables...
)手动显示加的锁。
五、按锁的使用方式分类
- 乐观锁(optimistic lock) : 并不是真正的锁机制,通常是通过在表中增加版本号等字段来实现,在更新时检查版本等标识是否符合预期来判断是否发生并发冲突等。
- 悲观锁(pessimistic lock) :通过实实在在的锁来控制并发访问,如前面提到的共享锁、排它锁等都属于悲观锁策略。
六、其他特殊锁
- 死锁:两个或多个事务相互等待对方持有的资源,从而导致都无法继续执行的情况。
- 全局锁:
- 对整个数据库实例加锁,让整个数据库处于只读状态。如 MySQL 提供了
flush tables with read lock(ftwrl)
命令加全局读锁,加锁之后整个数据库实例处于只读状态,相关数据操作命令都会被阻塞。一般仅用于全库备份等特殊场景(且在 InnoDB 等支持一致性读的引擎中全库备份不一定需要 )。
- 对整个数据库实例加锁,让整个数据库处于只读状态。如 MySQL 提供了