全局锁、表级锁、行级锁
前言:
在开发过程中经常会碰到事务与事务间对于数据处理的并发问题,以及会碰到一些数据库锁表,程序调用被阻塞等问题,我一般项目开发采用关系型数据库是mysql,如何对mysql关系型数据库锁的理解和掌握很有必要,可以在开发中更加高效敏捷的开发,接下来我会对于mysql数据库默认innodb引擎默认中的数据库锁进行分析以及的隔离级别可重复复的原理进行分析。
共享锁和排它锁的理解
- 共享锁(读锁): 如果事务T对数据A加上共享锁后,则其他事务只能对数据A再加共享锁,不能加排他锁。获取共享锁的事务只能读数据,不能修改数据。
- 排它锁(写锁): 如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获取排他锁的事务既能读数据,又能修改数据。
全局锁、表级锁、行级锁
-
全局锁:
一般是用于全库的逻辑备份。
MySQL提供加全局读锁的方法:Flush tables with read lock(FTWRL)
这个命令可以使整个库处于只读状态。使用该命令之后,数据更新语句、数据定义语句和更新类事务的提交语句等操作都会被阻塞。但是存在一定的风险:- 如果在主库备份,在备份期间不能更新,业务停摆。
- 如果在从库备份,备份期间不能执行主库同步的binlog,导致主从延迟。
-
表锁:
MySql里面的表级锁有两种,一种是表锁,另一种是元数据锁(meta data lock, MDL)
(表锁和MDL锁是两个不同的锁,同时对于两个锁分别都存在读锁和写锁两个类型锁。)- 表锁:
可以直接运行命令 lock tables read/write进行加锁,使用 unlock tables 主动解锁。
这个命令会限制其他的线程对于表的读和写操作,同时本线程也不可以进行对于其他表的操作,这个影响比较大,一般我们不宜采用。实际中,如果存在行锁的话,一般情况不会使用表锁。 - 元数据锁 MDL(metadata lock):
元数据锁主要是面向DML和DDL之间的并发控制,如果对一张表做增删改查(DML)操作的同时,有一个线程在做DDL操作,不加控制的话,就会出现错误和异常。同时元数据锁不需要我们显式的加,系统默认会加。
在执行的时候的情况:当对一个表做增删改查操作(DML)的时候,加 MDL 读锁;当要对表做结构变更操作(DDL)的时候,加 MDL 写锁 读锁和写锁增加的作用:- 读锁之间不排斥,可以有多个线程同时对于一张表增删改查。
- 读写锁之间,写锁之间是互斥的,用来保证变更表结构操作的安全性,因此,如果两个线程同时给一个表加字段,其中一个要等另一个执行完才能开始执行。
同时还有,现在一个线程开启事务,给表进行查询,或者数据的增删改,给表加了读锁,线程还没有提交,这个时候还有另一个线程提交事务给表进行修改,需要加入写锁 ,这个时候第二个线程的事务就会一直等待,同时后面进来的线程事务不管是要加读锁还是写锁都会被阻塞。等到第一个事务提交事务后,释放了MDL才会执行第二个事务。(这里也用到了两阶段锁是概念)
** 知识点补充:**- 对于支持行锁的引擎(InnoDB),增删改添加的锁一般针对行锁,所以在表级所的层次,对于MDL锁添加的是读锁,对于表锁这里就不存在。
- 对于不支持行锁的引擎,一般情况采用表锁。
这里你或许会提出疑问:
不支持行锁的引擎(myisam),只能使用表锁,而表锁同一张表在同一时刻只能有一个更新。但是上面讲的表级锁中的MDL锁,DML语句会产生MDL读锁,而MDL读锁不是互斥的,也就是说一张表可以同时有多个DML语句操作。
答案:真正的解答是这样的: 给出答案,这个是不矛盾的,我们的理解是MDL锁和表锁是两个不同的结构。两个表级锁都分别存在“读锁” 和 “写锁”。你要在myisam 表上更新一行,那么会加MDL读锁和表的写锁;然后同时另外一个线程要更新这个表上另外一行,也要加MDL读锁和表写锁。对于MDL读锁都同时加上去了,第二个线程的写锁是加不了的,需要等第一个写锁释放后才能加上。
- 表锁:
-
行锁:
结合开头的共享锁和排他锁的解释:
对于MDL语句的锁机制:执行增删改语句,会给涉及到的数据添加上排他锁,其他事物不可以再给这些数据行添加任何锁。但是可以通过select语句查询,select语句默认不会加任何锁,如果需要添加排他锁和共享锁可以通过添加 排他锁: select …for update语句,加共享锁可以使用select … lock in share mode语句
补充:对于MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(DML)前,会自动给涉及的表加写锁- 什么是两阶段锁:
在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。因此如果事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。 - 死锁检测:
在一个事务执行前,如果这个事务需要对于某行加入行锁才会去做死锁检测。
死锁的处理方法:
一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数 innodb_lock_wait_timeout 来设置。
另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑。
提出一个问题,对于一个语句的更新筛选字段没有走索引,执行过程是怎么样?
答案:针对于一个更新语句,如果我们更新一条语句没有走索引,即筛选条件不是索引的值,innodb内部是全表根据主键索引逐行扫描 逐行加锁,在事务提交的时候统一释放锁,在事务未提交的时候,其他的更新需要进行等待。
````java 到这里我们已经对于数据库的锁进行分析和理解了。同时还有```
- 什么是两阶段锁:
到这里我们对于数据库的锁进行了分析和理解,同时mysql存在一些其他锁机制,这里就不进行讨论和分析了。
如果有兴趣的话可以自己查阅相关文档。下一篇我们将对于数据库的隔离级别进行分析,mysq在innodb引擎
下才有的事务隔离级别是可重复读,下一篇我们将对于可重复读进行分析。
希望文章对于大家有所帮助,如果有什么理解不到位的欢迎大家在下方留言。