我们其实多多少少都应该知道锁是干什么的,无论是在JVM中还是在Mysql中还是其他场景都有锁的应用,为的就是在解决资源竞争的问题,主包之前在第四篇的时候就有锁过X锁和S锁,他们属于表级锁。
我们先来说说表的区分,从数据库操作来区分的话 ,就是读锁和写锁,顾名思义,就是读取操作引起的锁,以及写操作造成的锁。从锁的粒度来区分的话 ,就是表锁、页锁、行锁,顾名思义也就是锁住整个表,锁住这条记录,锁住整个数据页。从锁的态度来区分的话 ,就是悲观锁和乐观锁,这个也很好理解,悲观锁就是认为一定有人和他抢资源,一定加锁,乐观锁就是认为不太可能有人抢资源,不加锁但是在事务提交的时候检查内容是否被修改过。从加锁的方式来说 ,就是显式锁和隐式锁,意思很明显,显式就是我们的sql里面手段加了锁,隐式的就是sql中没有mysql自动帮我加的。其他的就是全局锁和死锁了,全局就是把整个数据库都锁住了,死锁就是互相需要对方锁住的资源,互相都无法提交直接的事务,造成的僵持局面。
接下来我们从锁的粒度的角度来看看各个锁的作用到底是啥。这里先提一句就是,锁的粒度越大,并发就越低,但是资源消耗也越低,同时死锁的情况也越低,反之就是粒度越小,并发越高,资源消耗越高(因为管理的锁变多了),同时死锁的情况也变高(因为更多的锁互相竞争)。
表锁
锁类型 | 符号 | 作用 | 典型场景 | 冲突对象 |
---|---|---|---|---|
表共享读锁 | S | 允许多读阻塞写 | 备份操作 | X锁 |
表独占写锁 | X | 独占访问权限 | DDL操作 | 所有锁 |
意向共享锁 | IS | 预示行级S锁 | 行锁前置 | X表锁 |
意向排他锁 | IX | 预示行级X锁 | 行锁前置 | S/X表锁 |
元数据锁 | MDL | 保护表结构 | DDL操作 | 所有DDL |
S锁和X锁这边就不多说了,https://blog.csdn.net/qq_45041250/article/details/150849222主包的这篇文章中有详细的说明介绍,我们主要讲一下其他的。
IS锁与IX锁
意向锁虽然是表级锁,但是却是可以和表锁和行锁共存的,他的目的就是为了方便大粒度加锁的,因为我们如果要给一个表加X锁,那么这个表就不能有行X锁,不然不就冲突了吗,如果按照老办法,就是一行一行的检测有没有X锁,那这样太慢了,所以就有了这个意向锁,当一个数据被加锁后,就会给上一层添加一个IX锁,也就是行X锁就会给对应的表上一个IX锁,这样就可以解决全表扫描的尴尬了。
元数据锁
这个和表级别X/S锁是有区别的,元数据锁MDL是管表的结构的,X/S是管表的内容的。
对比维度 | MDL锁 | 表X/S锁 |
---|---|---|
锁定目标 | 表结构定义 | 表数据内容 |
加锁时机 | 语句开始执行时 | 事务需要操作时 |
作用层级 | 连接/会话级 | 事务级 |
主要用途 | 保护表结构不被修改 | 控制数据并发访问 |
自动释放 | 语句/事务结束 | 事务结束 |
主键锁
这里再多说一个主键锁吧,这个东西的作用就是见名知意,就是控制主键的,只要有插入操作就会需要这个锁,但是8.0之后就是默认不加这个锁了,因为表锁太影响并发了,所以去掉了,但是有时候尤其是主从复制这种可能会有间隙,就是主键不连续。
页锁
这个其实也是没什么好说的,因为我们只要了解了X锁和S锁其他的都不是很难理解了,这里只需要注意B树锁就行了,有了上面MDL锁的理解,对这个理解起来也就简单了,同样的页的X锁和S锁管的是数据,B树锁管的是索引,一旦发生索引变更就会加这个锁,来确保索引的一致性。
锁类型 | 作用 | 典型场景 | 版本变化 |
---|---|---|---|
页共享锁 | 缓冲池页保护 | 页读取 | 5.7+优化 |
页排他锁 | 页修改保护 | 页写入 | 8.0减少使用 |
B树锁 | 索引结构保护 | 分裂/合并 | 5.6重要 |
行锁
锁类型 | 符号 | 作用 | 典型场景 | 冲突对象 |
---|---|---|---|---|
记录锁 | S/X | 锁定单行 | 精确更新 | 同行X锁 |
间隙锁 | Gap | 锁定范围间隙 | 防止幻读 | 区间插入 |
临键锁 | Next-Key | 记录+间隙 | RR隔离级 | 区间操作 |
插入意向锁 | II | 特殊间隙锁 | 并发插入 | 同间隙锁 |
谓词锁 | Predicate | 空间索引 | GIS查询 | 空间操作 |
记录锁就不说了,懂得都懂我们看看其他的。
间隙锁
这个从表格上也是可以知道个大概,这个就是为了解决幻读而产生的一种锁,因为我们加锁是在有数据的情况下加的,插入的数据还没有被刷盘那就是等于虚幻的数据,那这个是肯定不能加锁的,所以才会有间隙锁,这个间隙锁就不分X和S锁了,因为本质都是一样的,阻止insert行为,假如我们开启了一个事务,并且手动点给一个select 操作加锁了,那么他就会被加一个间隙锁,因为默认情况下select是不会加锁的,在加锁的这个区间(该记录在索引中的前一条记录 到当前记录之间的间隙)无法插入数据。确保其他事务或者查询得到的数据出现错误。
临键锁
这个其实就是间隙锁和记录锁加起来的组合体,因为我们知道间隙锁只管insert,不管其他的,只管当前行数据和索引前一条之间的间隙,但是不会锁当前行数据的,而这个临键锁就是既锁间隙,又锁行记录。也是我们mysql默认的锁。
对比维度 | 临键锁(Next-Key Lock) | 间隙锁(Gap Lock) |
---|---|---|
锁定范围 | 记录+前导间隙 | 纯间隙(不包含记录本身) |
组成结构 | 行锁 + 间隙锁的组合 | 独立的间隙锁定 |
默认行为 | InnoDB RR隔离级的默认锁 | 需要特殊条件触发 |
幻读防护 | 完全防止幻读 | 部分防止幻读 |
锁定强度 | 更强(锁定记录和间隙) | 较弱(仅锁间隙) |
插入意向锁
这个锁其实属于一直隐式锁,不需要我们手动声明的,这个就是加入一条数据已经加了间隙锁或者临键锁,那么这个时候需要在加锁的区域进行插入操作的话就会加这个锁,只要前面的锁一释放,就执行插入操作,只要插入的主键是不同的,就不会冲突,也就是多个事务可以同时对这行记录加插入意向锁,但是插入意向锁并不是意向锁,因为意向锁是表级锁。
谓词锁
这个就当做了解吧,谓词锁是MySQL 8.0+针对空间索引和JSON路径查询的专用锁,用于锁定满足特定逻辑条件的所有数据行,防止在GIS地理围栏或JSON字段查询时出现幻读问题。
核心价值 :让WHERE ST_Contains(polygon, point)
或WHERE JSON_EXTRACT(data, '$.path')
这类基于逻辑条件的查询能够像基于主键的查询一样保证数据一致性。
特性 | 传统行锁 | 谓词锁 | 优势对比 |
---|---|---|---|
锁定范围 | 物理行记录 | 逻辑条件匹配 | 范围+500% |
适用场景 | 精确值查询 | 空间/GIS/JSON | 功能+300% |
索引要求 | 依赖B-Tree | 支持R-Tree等 | 灵活性+200% |
隐式锁
这里再多嘴一下吧,隐式锁(Implicit Lock) 是InnoDB引擎特有的优化机制,当新记录插入时,系统会自动赋予该记录一个隐式X锁,无需显式加锁。
特性 | 显式锁 | 隐式锁 | 优势对比 |
---|---|---|---|
加锁方式 | 主动申请 | 自动附加 | 性能+30% |
锁检测 | 立即冲突 | 延迟转换 | 并发+50% |
生命周期 | 事务控制 | 即时释放 | 资源占用-40% |
也就是不需要我们手动加的,而且主包认为不仅仅是插入操作,DML语句都会加一个隐式锁,同时也会隐式加一个事务,但另一个事务需要访问或者修改我们增删改的数据时,就会先检查这个数据也没有被事务持有,如果有那么这个隐式锁就会转化为X锁。
总结
本篇主要讲述了各种锁,以及其作用,锁是完成数据库隔离性的重要工具,当然需要结合MVCC。