MySQL每日易言-锁

本文介绍了MySQL中的全局锁、表级锁(包括表锁和元数据锁)、行锁以及死锁的概念和处理。强调了在实际开发中合理使用锁以提高性能和避免死锁的重要性。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

MySQL最开始添加锁是为了处理各种并发场景,但是有些锁的范围较广,不能很好的契合实际的开发需求,因此锁的粒度在不断减小,但是可用性是不断提升,合理的分配锁可以实现高效率访问数据资源,因此MySQL也就衍生出了各种锁。本文参考《MySQL实战45讲》,仅作学习。


一、全局锁

全局锁明面上就是为整个数据库都添加一个锁,最常见的就是当需要对数据库进行全局备份的时候需要去添加一个全局读锁:flush tables with read lock(FTWRL);加锁后DDL和DML操作都会被阻塞,同时事务也无法进行提交。

理论上当我们需要全局备份的时候使用事务(single-transaction)也可以实现的,因为在可重复读的隔离级别下会对当时的库数据打一个快照,可以保证数据一致性。但并不是所有的引擎都有事务的,如MyISAM就不支持事务,也就只能使用加全局读锁去备份。

还有一种方法是设置全局只读:set global readonly=true,但是这种方法有一个很大的弊端:如果使用FTWRL,当数据库发生连接断开时,这个全局读锁会被自动释放,而将库设置为readonly后断开连接后数据库会依然保持readonly,致使数据库长时间处于不可写的情况,可能会影响公司的业务。

二、表级锁

顾名思义就是对库中的某张表进行上锁,常见的表锁有两种:表锁和元数据锁(MDL)

1.表锁

添加表锁的语法是:lock tables … read/write。当事务A设置读锁或者写锁后,A就只能访问当前表,是不可以访问其他表的,同时A设置读锁后写操作也是被禁的,除非执行unlock tables

2.元数据锁

元数据锁MDL(meta data lock)主要是用来保证在查询的过程中不应受到表结构改变的影响。如当我们查询A表的时候另一个人对表结构进行修改,就会导致我们查找的数据并不一定是我们想要的结果。MDL是隐式使用,并不需要用户手动开启。

MySQL5.5版本后引入了MDL,当我们对表进行DML和DQL时会添加MDL读锁,对表进行DDL时会添加MDL写锁;读锁和写锁、写锁和写锁是互斥的,就导致如果有两个线程要执行DDL操作时会阻塞其中的一个线程,等到另一个线程提交或释放后才可以继续执行。

三、行锁

行锁的粒度比表锁小,就导致行锁在使用上也就比表锁更加灵活,这也是InnoDB 相较于MyISAM的优点。

行锁在事务中通常是要遵循两阶段锁的事务提交协议:当我们开启事务并update一条数据后,会默认为这条数据添加行锁,并且一般情况下行锁并不是不需要就会立刻释放的,而是要等到事务结束时才会释放。

虽然行锁和两阶段锁都是用于处理并发事务的机制,但是他们是不同的概念。行锁是一种锁的粒度,而两阶段锁是一种事务提交协议。由于存在两阶段锁,当我们在事务A中修改ID=1的用户信息时,如果没有提交事务,那么事务B修改这条数据的时候会被阻塞,知道事务A提交时事务B才能执行。

所以在日常开发中,应该把最容易产生冲突的加锁操作往后放,减少对某段数据加锁的时间,不仅可以最大程度的减少事务之间的锁等待,同时也可以提高并发的性能。

四、死锁与死锁检测

由于加锁可能会导致其他事务阻塞,这是就有可能出现一种情况:有两个或多个事务相互持有对方需要的数据,但是又因为两阶段锁被相互阻塞,最终导致了死锁的结果,如下图所示:

transaction Atransaction B
begin;beigin;
update user set age=age+1 where id = 1;
update user set age=age+1 where id = 2;
update user set age=age+1 where id = 2;(block)
update user set age=age+1 where id = 1;(block)

在上述情况中,当事务A要修改id=2的数据时,因为事务B之前已经update过这条数据,会给这行数据加写锁并且要在提交事务的时候才会解锁(两阶段锁),因此事务A会被阻塞,而事务B也是因为事务A对需要的数据加过锁,就导致事务A和事务B相互阻塞,最终出现了死锁的情况,如果项目中有失败后重试的设置,就会导致每一个新来的事务都被阻塞,最后导致内容全部都是阻塞的线程。

因此为了避免上述情况通常会设置死锁检测机制:当一个线程要执行的时候要先将其阻塞,判断自己的加入会不会导致出现死锁问题,时间复杂度为O(n),这种方式在理论上是可以避免出现死锁问题,但实际开发中我们不仅要避免死锁问题,同时还要考虑CPU占用问题,当大量线程并发更新同一行时,即便是不会出现死锁问题,也会因为检测而耗费大量的内存和时间,因此解决方案大多是存在两面性的。

同时也存在其他方式去解决死锁问题,如设置并发的最大数量或者是将容易发生死锁的字段拆分成多个字段,在每天统计的时候在进行合并都可以解决上述问题,但是也都存在弊端,因此处理死锁需要根据实际开发需求去进行设计而非某种技术就可以实现的。


总结

锁的使用需要根据实际需求进行设计,虽然很多时候都是需要加锁的,但是频繁的使用锁不仅会提升CPU的占用率同时也会影响性能,因此合理的设计要比实习的开发更加繁琐。

本文主要介绍了一些常见的锁的类型,但在实际开发中更多使用的应该是行锁或者间隙锁,这些会在下篇文章中详解!

  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值