MySQL-锁的划分(表锁、行锁、页锁)

本文详细介绍了MySQL的锁机制,包括从操作类型上划分的读锁/共享锁、写锁/排他锁,以及从操作粒度上划分的表锁、行锁和页锁。重点讲解了表级的共享锁、排他锁和意向锁,以及行级的记录锁、间隙锁和临键锁。此外,还提到了元数据锁(MDL)在DDL操作中的作用,以及乐观锁和悲观锁的概念和应用场景。最后讨论了死锁的发生、解决方法以及如何避免死锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从操作类型上来划分锁

从操作类型上来划分:读锁/共享锁、写锁/排他锁

从操作粒度上来划分锁

从操作粒度上来划分:表锁、行锁

表锁:

  • 表级别的共享锁、排他锁

    共享锁排他锁
    共享锁兼容不兼容
    排他锁不兼容不兼容
  • 意向锁:协调表锁与行锁的一种表级锁

    例如:事务A给id = 1的数据加了一条行锁,这时事务B想给该表加一个排他锁是不可以的,但是事务B又不知道该表中有没有行锁,故需要一条条的去遍历数据,看该表中的数据到底加没加行锁,这样做很耗时性能差。

    引入了人意向锁,当事务A给id = 1的数据加了一条行排他锁,这时数据库就会自动的创一个表级别的意向排他锁,这时事务B想给该表加一个排他锁,只需要看该表有没有意向排他锁即可,无需遍历表中的数据。

  • 元数据锁(MDL):当执行DDL操作时需要用元数据锁来保护

    例如:当一个事务在执行DML增删改时,另一个事务想要进行DDL(alter table、drop table)操作,这是不允许的,元数据锁会让该DDL操作处于阻塞状态。

行锁

  1. 记录锁:对表中的某条记录加锁,其他的事务就不能操作这条记录了

    例如:事务A对id = 0的记录加了排他锁并且未释放锁,这时事务B也想要操作这条记录,此时事务B就会处于阻塞状态,直到事务A释放锁后才能对这条记录操作。

  2. 间隙锁:用于解决幻读问题的,防止插入幻影记录

    例如:事务A在id = 0到id = 5之间(0,5)开区间加一个间隙锁,这时事务B就添加不了id = 2的数据

    可能会出现死锁:事务A在id = 0到id = 5之间(0,5)开区间加一个间隙锁,事务B也在id = 0到id = 5之间(0,5)开区间加一个间隙锁,这时事务B执行添加id = 2的数据的操作就会处于阻塞状态(因为事务A加了间隙锁,不让事务B插入记录);事务A执行添加id = 3的数据的操作也会处于阻塞状态(因为事务B加了间隙锁,不让事务A插入记录),事务A和B执行添加记录的操作都处于阻塞状态,形成死锁。

  3. 临键锁:是记录锁和间隙锁的结合体,相当于开区间+端点形成闭区间

    例如:在id = 5这条记录(前一条记录是id = 0)上加一个临键锁,这时就在id = 5这条记录上加了一个记录锁,也在id = 0到id = 5(0,5)之间加了间隙锁,等价于在(0,5]区间加了锁

  4. 插入意向锁:当事务A加了间隙锁,事务B添加记录处于阻塞时,会产生一个插入意向锁,当间隙锁释放了,事务B的添加操作就能执行。

页锁

锁的开销和锁的粒度都介于表锁与行锁之间,并发度一般

从对待锁的态度上划分

乐观锁、悲观锁并不是锁,是一种设计思想

  1. 悲观锁:对并发问题池悲观态度,认为并发是大概率事情

    S锁、X锁、行锁、页锁都是悲观锁

  2. 乐观锁:对并发问题持乐观态度,认为并发是小概率事件

    乐观锁是通过程序实现的,版本号机制、时间戳机制

    举例:版本号机制

    事务A执行读操作,读的version是1,这时事务B也进行读操作,读的version也是1,事务B先对共享数据修改,修改完version + 1变为2,然后事务A再对共享数据修改时,发现version = 2和当时读的version = 1不一致,这时事务A就会从新读version = 2的数据,再进行修改,修改完version + 1,这样通过版本号就能决绝并发问题

    举例:时间戳机制

    和版本号类似,只是将版本号替换成了时间戳,读的时间戳和修改时的时间戳要一样

小结:

悲观锁适合写多读少的场景,防止读-写,写-写冲突

乐观锁适合读多写少的场景,优点在于时程序实现,不存在死锁

从加锁的方式上划分

显示锁: 能够通过指令查看到的,比如了加了S锁、X锁

隐式锁:不能够通过指令查看到的,针对于插入数据的情况,防止脏读脏写

例如:事务A开启后,执行插入一行记录的操作,事务A还未提交,此时事务B想要访问刚刚插入的那条数据,这时事务B就会帮助事务A生成一个所结构,给刚插入的记录加上一个隐式锁(延迟加载),直到事务A提交。如果不加隐式锁的话,此时事务A是未提交的,事务B操作这条插入的记录就会出现脏写、脏读的问题。

死锁

死锁发生在两个及以上的事务中,彼此握着对方想要获取的锁,等待对方释放锁,形成死锁。

解决死锁的两种方式:

  1. 超时等待:设置一个超时时间,当阻塞的时间超过这个超时时间,就会回滚这个事务。
    • 优点:能解决死锁问题
    • 缺点:在高并发场景中,可能出现死锁的情况比较多,这样总的等到时间就特别多。如果减少超时时间,这样会导致原本正常的阻塞的事务被误认为是死锁而被回滚了。
  2. 死锁检测:数据库会进行死锁检测,当发现死锁问题,就会回滚成本较低的那个事务。
    • 优点:能解决死锁问题
    • 缺点:在高并发场景中,会进行大量的死锁检测,耗时间耗资源

如何避免死锁

  1. 合理设计索引,减少索引定位的行(使锁住的行尽量的少)减少锁竞争
  2. 把一个大事务拆分成小事务,占用锁的时间变少,减少冲突
  3. 调整SQL执行逻辑,避免update、delete这种占用锁时间长的操作在事务之前
  4. 降低隔离级别
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值