MySQL锁,你真的理解么?

MySQL锁

一、基本概念

锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中的数据也是一种供多个用户使用的共享资源,html

当多个用户并发地存取数据时,在数据库加锁的目的能够保证数据库数据的一致性。mysql

  • 锁的类型

在数据库中,对数据的操做其实只有两种,即读和写,因此可用共享锁和互斥锁实现,即共享锁间之间是兼容的,而互斥锁间不兼容。web

  • 共享锁(读锁):容许事务读
  • 互斥锁(写锁):容许事务删除或者更新一行数据

共享锁表明了读操做、互斥锁表明了写操做,因此咱们能够在数据库中并行读,可是只能串行写,只有这样才能保证不会发生线程竞争,实现线程安全。sql

  • 锁的粒度

咱们按照锁的粒度能够分为全局锁、表锁和行锁,不一样的存储引擎拥有的锁粒度是不一样的 数据库

在下面的文章中,咱们按照锁粒度的划分分别学习全局锁、表级锁和行锁编程

二、全局锁

2.1基本概念

从名称上就能够看出,全局锁是对整个数据库实例加锁。MySQL提供了一个加全局读锁的方法,即Flush tables with read lock (FTWRL)。当对数据库使用这个命令时,数据库将会处于只读状态,以后的其它线程的语句:如数据更新语句(数据的增删改)DML,数据的定义语句(表结构的修改)DDL均会被阻塞。安全

2.2使用场景

全局锁的典型使用场景是作数据库逻辑备份,也就是将整个库中的全部表都select出来存成文本。在备份过程当中的整个库要处于只读状态,这会存在如下问题并发

  • 若是你在主库上备份,那么在备份期间都不能执行更新,业务基本上就能中止
  • 若是你在从库上备份,那么备份期间从库不能执行主库同步过来的binlog,会致使主从延迟。

若是选用的引擎不支持事务(如MyISAM),在对数据库作逻辑备份时只能选择FTWRL。若是引擎支持事务,而且支持可重复读的隔离级别(如InnoDB),咱们就能够用一致性视图来作备份,MySQL自带的逻辑备份工具是mysqldump。当mysqldump使用参数-single-transaction时,导入数据以前就能够启动一个事务,来确保拿到一致性视图。在MVCC的支持下,导入数据的过程当中是能够正常更新的。编辑器

2.3lock/unlock命令

  • 全局锁加锁命令
flush tables with read lock
复制代码
  • 释放全局锁的命令
unlock tables
复制代码

断开链接也能释放全局锁工具

三、表级锁

MySQL的表级锁有两种:表锁,元数据锁(meta data lock MDL),

3.1表锁

表锁的语法是:lock tables ... read/write,能够利用unlock tables主动释放锁,也能够在链接断开时自动释放。须要注意的是,lock tables除了限制其它线程的读写,也会限制自身接下来的操做对象

举例

若是在某个线程 A 中执行 lock tables t1 read, t2 write这个语句,则其余线程写 t一、读写 t2 的语句都会被阻塞。与此同时,线程 A 在执行 unlock tables 以前,也只能执行读 t一、读写 t2 的操做。连写 t1 都不容许,天然也不能访问其余表。

这种操做整个表的影响很大,因此对于使用InnoDB引擎下,通常不适用lock tables命令控制并发。

3.2MDL

MDL全称是metadata lock,即元数据锁,它的做用主要是为了维护表中数据的一致性,即用于解决DDL操做与DML操做的一致性。

  • 解决的问题

其主要解决了2个问题,一个是事务隔离问题,好比在可重复隔离级别下,会话A在2次查询期间,会话B对表结构作了修改,两次查询结果就会不一致,没法知足可重复读的要求;另一个是数据复制的问题,好比会话A执行了多条更新语句期间,另一个会话B作了表结构变动而且先提交,就会致使slave在重作时,先重作alter,再重作update时就会出现复制错误的现象。

元数据锁是server层的锁,表级锁,每执行一条DML、DDL语句时都会申请MDL锁,DML操做须要MDL读锁,DDL操做须要MDL写锁(MDL加锁过程是系统自动控制,没法直接干预,读读共享,读写互斥,写写互斥),申请MDL锁的操做会造成一个队列,队列中写锁获取优先级高于读锁。

NOTE:一旦出现写锁等待,不但当前操做会被阻塞,同时还会阻塞后续该表的全部操做(读操做固然也会被阻塞)。事务一旦申请到MDL锁后,直到事务执行完才会将锁释放。

MDL锁一旦发生可能会对数据库的业务带来其它的影响,由于后续对该表的访问都会被阻塞,形成链接积压。那么咱们该如何避免MDL锁的发生呢?

  • MDL锁的优化
  • 避免长事务
  • 在DDL操做中设定等待时间

四、行锁

行锁每次锁定的是一行数据,行级锁定不是MySQL本身实现锁定的方式,是由存储引擎实现的(InnoDB)本身实现的。

InnoDB实现了如下两种类型的行锁

  • 共享锁(读锁)

容许一个事务去读一行,阻止其余事务得到相同数据集的排他锁。若事务T对数据对象A加上共享锁,则事务T能够读A但不能修改A,而其余事务只能再对对象A加共享锁,而不能加排他锁,直到事务T释放A上的共享锁。这保证了其余事务能够读A,但在事务T释放A上的S锁以前不能对A作任何修改。

  • 排他锁(写锁)

容许获取排他锁的事务更新数据,阻止其余事务取得相同的数据集共享读锁和排他写锁。若事务T对数据对象A加上排他锁,事务T能够读A也能够修改A,其余事务不能再对A加任何锁,直到T释放A上的锁

NOTE:排他锁指的是一个事务在一行数据加上排他锁后,其余事务不能再在其上加其余的锁。mysql InnoDB引擎默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,若是加排他锁可使用select …for update语句,加共享锁可使用select … lock in share mode语句。因此加过排他锁的数据行在其余事务种是不能修改数据的,也不能经过for update和lock in share mode锁的方式查询数据,但能够直接经过select …from…查询数据,由于普通查询没有任何锁机制。

为了使得表锁和行锁共存,实现多粒度锁机制,InnoDB存在了两种内部使用的的意向锁

  • 意向共享锁

事务打算给数据行共享锁,事务在给一个数据行加共享锁前必须先取得该表的意向共享锁。

  • 意向排他锁

事务打算给数据行加排他锁,事务在给一个数据行加排他锁前必须先取得该表的意向排他锁

NOTE:意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任何锁。

  • 死锁

当并发系统中不一样线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,就会致使这几个线程都进入无限等待的状态,称为死锁

案例

事务A 事务B
begin;update t set k=k+1 where id=1; begin
update t set k=k+1 where id=2;
update t set k=k+1 where id=2;
update t set k=k+1 where id=1;

事务 A 在等待事务 B 释放 id=2 的行锁,而事务 B 在等待事务 A 释放 id=1 的行锁。 事务 A 和事务 B 在互相等待对方的资源释放,就是进入了死锁状态。

解除死锁的策略

  • 设置超时时间参数innodb_lock_wait_timeout,
  • 将参数 innodb_deadlock_detect 设置为 on,发起死锁检测,当发现死锁后,主动回滚死锁链条中的某一个事务,让其它事务得以继续执行。
  • 间隙锁(Next-key)

当咱们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的 索引项加锁。对于键值在条件范围内但并不存在的记录叫作“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁 (Next-Key锁)。

使用间隙锁的目的是防止幻读

关注公众号:10分钟编程,让咱们天天博学一点点

公众号回复success,领取独家整理的学习资源,JAVA、大数据全套视频资料

参考

[1]林晓斌.《MySQL实战45讲》

[2]https://www.cnblogs.com/kunjian/p/11993708.html

[3]http://mysqlpub.com/thread-5383-1-1.html

[4]http://blog.csdn.net/c466254931/article/details/53463596

相关文章
相关标签/搜索