-
MySql的锁,数据库锁设计的初衷是处理并发问题
-
作为多用户共享的资源,当出现并发访问时,数据库需要合理的控制资源的访问规则,锁就是实现访问规则的数据结构
根据加锁的范围,Mysql里面的锁大致可以分为:
-
全局锁
-
表级锁
-
行锁
(1)全局锁
全局锁就是对整个数据库实例加锁,整个库就会处于只读状态。
(1)Mysql提供了一个加全局读锁的方法:
命令:Flush tables with read lock(FTWRL).
使用这个命令后,整个数据库处于只读状态,之后其他线程的数据更新语句(数据增删改),数据定义语句(建表,修改表结构等)和更新类事务提交语句,都会被阻塞。
(2)全局锁的应用场景是:给全库做逻辑备份。(也就是把整个库的表都select出来存成文本)
(3)使用全局锁的局限:
-
如果是在主库上进行备份,那么备份期间,数据库数据都不能更新,业务就得停摆。
-
如果是在从库上进行备份,那么备份期间从库不能执行从主库同步过来的binlog(给某行给某行某个字段做了什么修改),会导致主从延迟。
(4)备份为什么一定要加锁?
如果不加锁,在备份的过程中,有个线程进来修改了部分数据,然后前面备份的数据和后面备份的数据会对应不上。也就是说,备份的库不是一个逻辑时间点,视图的逻辑不一致。
(5)有一个方法可以拿到一致性视图:
在可重复读的隔离级别下开启一个事务,因为可重复读,只要这个事务不提交,那么事务开启后看到数据一直都是刚开启时的数据,就可以拿到一致性视图。
官方自带的逻辑备份工具是mysqldump,当mysqldump使用参数-single-transaction的时候,导出数据之前就会启动一个事务,确保拿到一致性视图。由于MVCC的支持,这个过程中数据是可以正常更新的。
-
FTWRL确保不会有其他线程对数据做更新,然后对整个库做备份,备份过程中,整个库处于只读状态。(所有引擎都可以使用,不支持事务的引擎用这个方式)
-
mysqldump自带备份工具,需要支持事务的引擎,才能使用。
(6)既然只要全库只读,为什么不使用set global readonly=true的方式呢?
-
一是,在有些系统中,readonly的值会被用来做其他逻辑,比如判断一个库是主库还是备库,因此不建议使用。
-
二是,在异常处理机制上有差异:
如果使用FTWRL命令后,客户端发生异常,Mysql会自动释放这个全局锁,整个库就可以回归到正常状态。
如果将readonly的值设置为true的话,客户端发生异常,整个库就会一直处在readonly状态,会使整个库长时间处于不可更新状态。
(2)表级锁
表级锁有两种:
-
表锁
-
元数据锁(MDL)
(1)表锁的语法是lock tables ...read/write
与FTWRL类似,可以用unlock tables主动释放锁,也可以在客户端断开的时候自动释放。
lock tabeles 除了会限制别的线程的读写外,还会限制本线程的操作。
例子:某个线程A执行lock tables t1 read,t2 write; 这个语句,则其他线程些写t1,读写t2的语句都会被阻塞。
同时线程A在执行unlock tables之前,也只能执行读t1,读写t2的操作,写t1都不允许。
对于InnoDB引擎这种支持行锁的引擎,一般不使用表锁来控制并发,锁住整个表的代价太大。
表锁一般是用在数据库引擎不支持行锁的时候才会被用到!!!
(2)元数据锁(MDL)
MDL不需要显式使用,在访问一个表的时候会自动加上。
MDL的作用是:防止DML(表数据增删改查)和DDL(表结构修改)并发冲突。
Mysql5.5版本中引入的MDL,当对一个表做增删改查时,加MDL读锁,当要对表结构变更时,加MDL写锁。
-
读锁之间不互斥,因此可以有多个线程可以同时对一张表增删改查。
-
读锁和写锁之间,写锁和写锁之间是互斥的,用来保证变更表结构操作的安全性。
(如果有两个线程同时给一个表加字段,其中一个要等另一个执行完才能开始执行)
在事务中的MDL锁在语句执行开始时申请,语句结束后并不会马上释放,而是要等整个事务提交再释放。
长事务一直不提交,那么会一直占着MDL锁,如果你要变更的表正好有长事务在执行,要么你暂停表结构变更(DDL),要么杀掉这个长事务。