对于MySQL你必需要了解的锁知识

1、前言

MySQL 的锁按照范围能够分为全局锁、表锁、行锁,其中行锁是由数据库引擎实现的,并非全部的引擎都提供行锁,MyISAM 就不支持行锁,因此文章介绍行锁会以InnoDB引擎为例来介绍行锁。html

2、全局锁

MySQL 提供全局锁来对整个数据库实例加锁。mysql

语法:sql

FLUSH TABLES WITH READ LOCK

这条语句通常都是用来备份的,当执行这条语句后,数据库全部打开的表都会被关闭,而且使用全局读锁锁定数据库的全部表,同时,其余线程的更新语句(增删改),数据定义语句(建表,修改表结构)和更新类的事务提交都会被阻塞。数据库

在mysql 8.0 之后,对于备份,mysql能够直接使用备份锁。安全

语句:session

LOCK INSTANCE FOR BACKUP

UNLOCK INSTANCE

这个锁的做用范围更广,这个锁会阻止文件的建立,重命名,删除,包括 REPAIR TABLE TRUNCATE TABLE, OPTIMIZE TABLE操做以及帐户的管理都会被阻塞。固然这些操做对于内存临时表来讲是能够执行的,为何内存表不受这些限制呢?由于内存表不须要备份,因此也就不必知足这些条件。多线程

3、表锁

Mysql的表级别锁分为两类,一类是元数据锁(Metadata Lock,MDL),一种是表锁。线程

元数据锁(MDL) 不须要显式使用,在访问一个表的时候会被自动加上。这个特性须要MySQL5.5版本以上才会支持,当对一个表作增删改查的时候,该表会被加MDL读锁;当对表作结构变动的时候,加MDL写锁。MDL锁有一些规则:code

  • 读锁之间不互斥,因此能够多线程多同一张表进行增删改查。
  • 读写锁、写锁之间是互斥的,为了保证表结构变动的安全性,因此若是要多线程对同一个表加字段等表结构操做,就会变成串行化,须要进行锁等待。
  • MDL的写锁优先级比MDL读锁的优先级,可是能够设置max_write_lock_count系统变量来改变这种状况,当写锁请求超过这个变量设置的数后,MDL读锁的优先级会比MDL写锁的优先级高。(默认状况下,这个数字会很大,因此不用担忧写锁的优先级降低)
  • MDL的锁释放必需要等到事务结束才会释放

因此咱们在操做数据库表结构时候必需要注意不要使用长事务,这里具体是什么意思呢?我举个例子说明下:htm

![](

上图表示演示了4个session执行语句,首先SessionA开启了事务没有提交,接着sessionB执行查询,由于是获取MDL读锁,因此互相不影响,能够正常执行,SessionC新增一个字段,因为MDL写和读是互斥的,因此SessionC会被阻塞,以后SessionD开始执行一个查询语句,因为SessionC的阻塞,因此SessionD也阻塞了。因此,咱们模拟的SessionA的事务是长事务,而后后面执行了修改表结构,会致使后续对该表全部的读写操做都不可行了。因此在实际场景中,若是业务请求比较频繁的时候,对表结构进行修改的时候就有可能致使该库的线程被阻塞满。

表锁 的语法以下:

LOCK TABLES
    tbl_name [[AS] alias] lock_type
    [, tbl_name [[AS] alias] lock_type] ...

lock_type: {
    READ [LOCAL]
  | [LOW_PRIORITY] WRITE
}

UNLOCK TABLES

表锁分为读锁和写锁,读锁不互斥,可是获取读锁不能写入数据,其余没有获取到读锁的session也是能够读取表的,因此读锁的目的就是限制表被写。若是表被读锁锁住后,再执行插入语句会报错,报错以下:

1099 - Table 'XXXX' was locked with a READ lock and can't be updated

写锁被获取后能够对表进行读写,写锁是互斥的,一旦某个session获取到表的写锁,另外的session没法访问这个表,直到写锁被释放。

表的解锁可使用unlock tables 解锁,也能够客户端口自动解锁。lock tables锁表会独占式的锁住表,除了限制其余线程对该表的读写,也会限制本线程接下来的操做对象。

4、行锁(InnoDB)

MySQL的行锁是在引擎层面实现的,因此这里讨论的也是InnoDB引擎下的行锁,下面会详细介绍InnoDB下常见的几种行锁

4.1 共享锁

共享锁能容许事务获取到锁后进行读操做,共享锁是不互斥的,一个事务获取到共享锁后,另一个事务也能够获取共享锁,获取共享锁后不能进行写操做。

4.2 排它锁

排他锁容许事务获取到锁后进行更新一行或者删除某一行操做,排他锁顾名思义是互斥的,一个事务获取到排他锁后,其余事务不能获取到排他锁,直到这个锁被释放。

4.3 意向锁

InnoDB支持多种粒度的锁,容许行锁和表锁共存,这里说的意向锁实际上是一种表级别的锁,可是我把它放在行锁里面是由于它不会单独存在,它的出现确定会伴随着行锁(共享锁或者排他锁),它主要的目的就是表示将要锁定表中的行或者正在锁定表中的行。

意向锁根据和行锁的组合能够分为:

  • 意向排他锁:代表将要在表中的某些行获取排他锁

  • 意向共享锁:代表将要在表中的某些行获取共享锁

意向锁的获取必须在行锁获取以前,也就是说获取共享锁以前必须先要获取共享意向锁,对于排他锁也是同样的道理。

那么这个意向锁到底有什么做用呢?

解释这个以前,咱们先看看意向锁和行锁以前的兼容关系:

--- 排他锁(X) 意向排他锁(IX) 共享锁(S) 意向共享锁(IS)
排他锁(X) 冲突 冲突 冲突 冲突
意向排他锁(IX) 冲突 兼容 冲突 兼容
共享锁(S) 冲突 冲突 兼容 兼容
意向共享锁(IS) 冲突 兼容 兼容 兼容

咱们假设有2个事务A和事务B,事务获取到了共享锁,锁住了表中的某一行,这一行只能读,不能写,如今事务B要申请整个表的写锁。若是事务B申请成功,那么确定是能够对表中全部的行进行写操做的,那么确定与A获取的行锁冲突。数据库为了不这种冲突,就会进行冲突检测,那么如何去检测呢?有两种方式:

  • 判断表是否已经被其余事务用表级锁锁住。
  • 判断表中的每一行是否被行锁锁住。

判断表中的每一行须要遍历全部记录,效率太差,因此数据库就用第一种方式去作冲突检测,也就是用到了意向锁。

总结

本文主要从MySQL的加锁范围来分析了MySQL的锁,MySQL根据加锁范围能够分为全局锁、表锁、行锁。全局锁和表锁是MySQL本身实现,行锁都是由引擎层面去实现。InnoDB下的行锁主要分为共享锁和排他锁。共享锁请求后,行只能读,共享锁之间不互斥。排他锁获取后能更新和删除行,排他锁与其余锁都互斥。最后我在行锁的基础上提到了意向锁,意向锁主要表示正在锁住行或者即将锁住行,为了在锁冲突检测中提升效率。固然InnoDB下还有其余锁,好比间隙锁,记录锁,Next-Key锁等,这些都不在本文的探讨范围以内,若有兴趣的同窗能够自行研究。

参考

相关文章
相关标签/搜索