锁
- 锁的介绍:锁是计算机协调
多个进程或线程
并发访问某一个数据的机制。- 在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。
如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题
。锁冲突也是影响数据库并发访问性能的一个重要因素。
- mysql中锁的分类:按照
锁的粒度
分,分为以下三类:- 全局锁:锁定数据库中的所有表。
- 表级锁:每次操作锁住整张表。
- 行级锁:每次操作锁住对应的行数据。
一、全局锁
-
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,以及更新操作的事务提交语句都将被
阻塞
。- DML(Data Manipulation Language):用来对数据库中
表
的数据记录进行增删改
操作的。 - DDL(Data Definition Language,数据定义语言)(如CREATE, ALTER, DROP等):用于定义或更改数据库结构,而不是数据本身。
- DQL(Data Query Language(数据查询语言)):数据查询语言,用来查询数据库中表的记录。
- DCL(Data Control Language(数据控制语言)):用来管理数据库用户、控制数据库的访问权限。
- DML(Data Manipulation Language):用来对数据库中
-
其典型的使用场景:是做
全库的逻辑备份
,对数据库所有的表
进行锁定
,从而获取一致性视图
,保证数据的完整性。
为什么全库逻辑备份,就需要加全局锁
- 不加全局锁,可能存在的问题:数据不一致问题(就是比如再进行数据备份的时候,
先备份了库存表
,在订单表和日志表还没有备份的时候,在业务系统中,执行了下单操作,扣减库存,生成订单(此时订单表数据新增了),然后业务中执行插入订单日志操作(日志表也新增了数据),最后在备份订单表和日志表的数据的时候,就导致了库存表与订单表、日志表数据的不一致了。) - 使用MySQL的全局锁之后:
- 对数据库进行进行逻辑备份之前,先对整个数据库加上全局锁,一旦加了全局锁之后,其他的DDL、DML全部都处于阻塞状态,但是可以执行DQL语句,也就是处于只读状态,而数据备份就是查询操作。那么数据在进行逻辑备份的过程中,数据库中的数据就是不会发生变化的,这样就保证了数据的一致性和完整性。
给数据库加全局锁的语法
-
- 加全局锁
# mysql命令
flush tables with read lock;
-
- 数据备份
# mysqldump不是mysql的语句,而是mysql中提供的一个工具
# -h指定主机地址
# -u指定用户名
# -p指定登录的密码
# dadao表示要进行数据库备份的数据库名称
# dadao.sql表示要备份在哪了,备份的文件是什么名称
mysqldump -h192.168.70.130 -uroot –p1234 dadao > dadao.sql
-
- 释放锁
unlock tables ;
全局锁的特点
-
数据库中加全局锁,是一个比较繁重的操作,会存在以下问题:
- 如果在
主
库上备份,在数据备份期间主库和从库都不能执行更新,业务基本上就得停摆。 - 如果在
从
库上备份,在数据备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟。
- 如果在
-
在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致性数据备份。
mysqldump --single-transaction -uroot –p123456 dadao > dadao.sql
二、表级锁
- 表级锁的介绍:表级锁,每次操作
锁住整张表
。锁定粒度大,发生锁冲突
的概率最高,并发度最低
。应用在MyISAM、InnoDB、BDB等存储引擎中。 - 表级锁的分类:
- 表锁
- 元数据锁(meta data lock,MDL)
- 意向锁
表锁
-
表锁的分类:
- 表共享读锁(read lock)
- 表独占写锁(write lock),也叫排他锁
-
表锁相关的语法:
# 加锁:
# lock tables 表名... read/write。
# 加读锁
lock tables order read;
# 加写锁
lock tables order write;
# 释放锁:
# 执行unlock tables语句
# 或者是 客户端断开连接
读锁的特点
写锁的特点
- 结论:读锁不会阻塞其他客户端的读,但是会阻塞写。写锁既会阻塞其他客户端的读,又会阻塞其他客户端的写。
元数据锁
-
元数据锁(meta data lock )简写MDL。(没有明确的锁类型)
-
MDL加锁过程是
系统自动控制
,无需显式使用
,在访问一张表的时候会自动
加上。- MDL锁主要作用是
维护表元数据的数据一致性
,在表上有未提交的事务的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性。- 这里的元数据,可以简单理解为就是一张表的
表结构
。 也就是说,某一张表涉及到未提交的事务
时,是不能
够修改这张表的表结构
的。
- 这里的元数据,可以简单理解为就是一张表的
- 在MySQL5.5中引入了MDL,
-
当对一张表进行
增删改查
的时候,会自动加MDL的读锁(共享锁); -
当对
表结构进行变更操作
的时候,会自动加MDL的写锁(排他锁);
-
- MDL锁主要作用是
-
常见的SQL操作时,所添加的元数据锁:
-
当执行SELECT、INSERT、UPDATE、DELETE等语句时,添加的是元数据共享锁(SHARED_READ /SHARED_WRITE)之间是
兼容
的。
-
当执行SELECT语句时,添加的是元数据共享锁(SHARED_READ),会阻塞元数据排他锁(EXCLUSIVE),之间是互斥的。
-
查看数据库中的元数据锁的情况:
select object_type,object_schema,object_name,lock_type,lock_duration from
performance_schema.metadata_locks ;
意向锁
- 意向锁介绍:为了避免DML在执行时加的
行锁与表锁的冲突
,在InnoDB中引入了意向锁,使得在添加表锁时不用检查每行数据是否加锁
,使用意向锁来减少表锁的检查。
没有意向锁的情况
假如没有意向锁,客户端一对表加了行锁后,客户端二如何给表加表锁呢,来通过示意图简单分析一下:
- 首先客户端一,开启一个事务,然后执行DML操作,在执行DML语句时会对涉及到的行加行锁。
- 当客户端二,想对这张表
加表锁
时,会检查当前表是否有对应的行锁
,如果没有,则添加表锁。(此时就会遍历检查,会从第一行数据开始检查是否有行锁,以及行锁是否可以添加表锁,一直检查到最后一行数据,效率较低。)
有意向锁后的情况
- 客户端一,在执行DML操作时,会对涉及的行
加行锁
,同时也会对该表加上意向锁
- 而其他客户端,在对这张表加表锁的时候,会
根据该表上所加的意向锁
来判定是否可以成功加表锁
,而不用逐行判断行锁情况了。如果判断为不可以加表锁时,则会阻塞。
意向锁的分类
- 意向共享锁(IS): 由语句select … lock in share mode添加 。与表锁共享锁(read) 兼容,与表锁排他锁(write) 互斥。
- 意向排他锁(IX): 由insert、update、delete、select…for update添加 。与表锁共享锁(read)及排他锁(write) 都互斥,意向锁之间不会互斥。
# 加意向共享锁
select * from score where id = 1 lock in share mode;
# 加意向排它锁(自动添加的)
update score set math = 66 where id = 1;
一旦事务提交了,意向共享锁、意向排他锁,都会自动释放。
- 查看意向锁及行锁的加锁情况:
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from
performance_schema.data_locks;
-
意向共享锁与表读锁是兼容的
-
意向排他锁与表读锁、写锁都是互斥的
三、行级锁
- 行级锁:每次操作锁定对应的
行
数据。锁定的粒度最小,发生锁冲突的机率最低,并发度最高。主要应用于Innodb引擎中。 - InnoDB的数据是基于索引组织的,
行锁
是通过对索引上的索引项
加锁来实现的,而不是对记录加的锁。对于行级锁,主要分为以下三类:-
行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。
-
间隙锁(Gap Lock):锁定索引记录的间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。
-
临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。
-
行锁
- InnoDB实现了以下两种类型的行锁:
- 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的
排它锁
。 - 排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的
共享锁和排他锁
。
- 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的
- 两种行锁的兼容情况如下:
- 常见的SQL语句在执行时所加的行锁如下:
-
默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用 next-key锁(临键锁) 进行搜索和索引扫描,以防止幻读。
- 针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁。
- InnoDB的行锁是针对于索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁。
-
查看意向锁及行锁的加锁情况:
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from
performance_schema.data_locks;
- 普通的select语句执行时不会加锁
- select…lock in share mode,加共享锁,共享锁与共享锁之间兼容。
- 共享锁与排他锁之间互斥。
- 排它锁与排他锁之间互斥
- 无索引行锁会自动升级为表锁
间隙锁和临键锁
- 默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用 next-key锁(临键锁) 进行搜索和索引扫描,以防止幻读。
-
在索引上进行等值查询(唯一索引),给不存在的记录加锁时, 锁会从next-key锁优化为间隙锁 。
-
在索引上进行等值查询(非唯一普通索引),向右遍历时,最后一个值不满足查询需求时,next-key lock会退化为间隙锁。
-
在索引上进行范围查询(唯一索引):会添加对应的临键锁。
-
对于>和>=范围查询,InnoDB会对索引的最大值(supremum)加上next-key lock,这实际上锁定了索引最大值后面的间隙。
- 例如:假设要查询id>=19的数据,首先将19加了行锁,然后给25加上临键锁(包含25及25之前的间隙),正无穷的临键锁(正无穷及之前的间隙)。
-
间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。