一、全局锁:
顾名思义,全局锁执行后,整个库都锁定。有读锁和写锁,加锁和解锁方式如下:
加锁:flush tables WITH READ LOCK;
解锁:UNLOCK TABLES;
使用场景:做全库的逻辑备份;
二、表级锁:
有读锁和写锁,加锁和解锁方式如下:
加锁:LOCK TABLES `role` READ;
解锁:UNLOCK TABLES;
使用场景:(1)读密集型;(2)写作不频繁的场景;(3)数据量不大的简单应用;(4)全表更新或者删除;
触发表锁的命令:(1)ALTER TABLE;(2)DROP TABLE 或者 TRUNCATE TABLE;(3)LOCK TABLES;(4)全表扫描或者大范围扫描;(5)加全局锁;
风险:(1)性能下降;(2)多表操作,如果没有按照一定顺序操作表,可能会导致死锁;
三、行级锁:
粒度上区分:共享锁和排他锁。
主要由INNODB存储引擎提供,包含共享锁(读锁,也是S锁)和排他锁(写锁,也是X锁)。实际使用中,还有一种间隙锁,会锁定行锁前后的间隙,防止数据插入。
使用场景:(1)高并发读写操作;(2)单行操作;(3)短期锁;(4)实现并发控制;(5)复杂的事务处理;
触发表锁的命令:
(1)select ... for update:添加一个排他锁;
(2)select ... LOCK IN SHARE MODE:添加一个共享锁;
(3)INSERT、UPDATE、DELETE:添加排他锁;
风险:(1)死锁;(2)锁升级:事务锁定行过多,可能导致上升到表锁;(3)锁等待;(4)资源消耗;(5)难调试和排查;(6)事务隔离级别;
模式上区分:乐观锁和悲观锁。
乐观锁:认为冲突概率低,操作数据时,不会锁定数据,只有在提交修改时,才检查。如果数据已被修改,则回滚,否则提交。
mysql并没有内置乐观锁机制,需要在代码中实现,常见的实现方式是,使用时间戳或者版本号字段,记录每一次对数据的修改。
标志SQL:(1)select id,version from .xxx ...;(2)update xxx set ... where ... and version = 旧version;
乐观锁使用场景:(1)低冲突环境;(2)读多写少场景;(3)短事务操作;(4)分布式系统;(5)互联网应用;
风险:(1)冲突检测:只有提交时才检测,有冲突会导致所有都回滚;(2)依赖于版本管理;
悲观锁:一种并发控制方法。认为冲突概率高,每次读写前都加锁;
使用方式:参考前面提到的共享锁(S锁)和排他锁(X锁);
使用场景:(1)写操作比较多;(2)并发冲突高;(3)业务需要强一致性的场景;
缺点:(1)性能开销;(2)并发降低;(3)死锁;(4)锁超时;
四、意向锁
意向锁是表锁。为了协调行锁和表锁的关系,支持多粒度锁并存(即行锁和表锁)。
意向锁也分共享锁和排他锁。
作用:当事务A有行锁时,mysql自动给该表添加意向锁。当事务B申请整张表的写锁时,只需要判断是否存在意向锁,而不需要判断所有行锁。
意向锁的兼容互斥性:
意向共享锁(IS) | 意向排他锁(IX) | |
共享锁(S) | 兼容 | 互斥 |
排他锁(X) | 互斥 | 互斥 |
五、临键锁
可以理解为一种特殊的间隙锁,临键锁可以解决幻读的问题。
当事务拥有某一行记录的临键锁时,会锁住一段左开右闭的区间。比如后面截图中的3条数据,就生成了4个临键锁,临键锁如下:(1)(-∞,20];(2)(20, 25];(3)(25, 30];(4)(30, +∞];
当更新age=25的记录时,不能增加或者修改age为(25, 30]之间的数。
sql语句说明如图:
创建测试表,并插入相应数据,然后开启事务,并更新年龄为25的记录。
没有commit更新的情况下,新开一个窗口,执行插入语句,年龄在25-30之间,结果插入被阻塞,这就是因为临键锁。
六、记录锁
mysql的innodb引擎的一种锁定机制,用于锁定和控制单个行记录的访问。
记录锁作用在索引上,对于没有主键和唯一键的表,innodb会自动添加隐藏的聚簇索引,并在该索引上加锁。
七、间隙锁
where条件是一个范围时,数据库会锁定区间数据,主要是解决幻读问题。
使用场景:(1)防止幻读;(2)范围查询;
缺点:(1)性能影响;(2)死锁;(3)复杂性;(4)锁定范围过大,可能导致不必要的锁定冲突;