Mysql 锁机制详情

news2024/10/9 14:08:28

一:事务四要素 

  • 原子性(Atomicity):要么全部完成,要么全部不完成(通过redo log 日志实现);
  • 一致性(Consistency):一个事务单元需要提交之后才会被其他事务可见(通过undo log 日志实现);
  • 隔离性(Isolation):并发事务之间不会互相影响,设立了不同程度的隔离级别,通过适度的破坏一致性,得以提高性能(通过 lock 实现);
  • 持久性(Durability):事务提交后即持久化到磁盘不会丢失(通过redo logundo log日志实现)。

二:并发事务问题:

脏读:没有提交的事务被其他事务读取到了

不可重复读:同一个事务对同一条记录读取两遍,两次读出来的结果竟然不一样。(针对update语句)

幻读:同样的条件,第一次和第二次读出来的记录数不一样(针对insert语句)

丢失更新:当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题–最后的更新覆盖了由其他事务所做的更新。

三:隔离级别

四种不同的隔离级别:

  • 读未提交(Read Uncommitted):可以读取未提交的记录,会出现脏读,幻读,不可重复读,所有并发问题都可能遇到;
  • 读已提交(Read Committed):事务中只能看到已提交的修改,不会出现脏读现象,但是会出现幻读,不可重复读;(大多数数据库的默认隔离级别都是 RC,但是 MySQL InnoDb 默认是 RR)
  • 可重复读(Repeatable Read):MySQL InnoDb 默认的隔离级别,解决了不可重复读问题,但是任然存在幻读问题;(MySQL 的实现有差异,后面介绍)
  • 序列化(Serializable):最高隔离级别,啥并发问题都没有。

存在的问题

“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。

查看和设置 MySQL 的隔离级别

查看MySQL 正在使用什么隔离级别

select @@tx_isolation;

 这里要注意的是在 MySQL 中变量一般分为两类:

  • 用户变量 :用户变量的变量名格式为 @variable,
  • 系统变量:系统变量的格式为 @@variable,tx_isolation 是系统变量,所以变量名为 @@tx_isolation,而系统变量又分为以下两种:
    • 全局变量:默认情况下使用 select @@variable 查询出来的是会话变量的值,也可以写作 select @@session.variable 或者 select @@local.variable
    • 会话变量:如果要查询全局变量的值,则使用 select @@global.variable

设置隔离级别

  • set transaction isolation level read uncommitted;
  • set transaction isolation level read committed;
  • set transaction isolation level repeatable read;
  • set transaction isolation level serializable;

四:锁分类

  锁是实现隔离级别的几种方式之一,除了锁,实现并发问题的方式还有时间戳时间戳时间戳,多版本控制多版本控制多版本控制等等,这些也可以称为无锁的并发控制无锁的并发控制。传统的隔离级别是基于锁实现的,这种方式叫做 基于锁的并发控制(Lock-Based Concurrent Control,简写 LBCC)

1. 按锁粒度分:

  • 全局锁:锁整Database,由MySQL的server层实现
  • 表级锁:锁某Table,由MySQL的server层实现
  • 行级锁:锁某Row的索引,也可锁定行索引之间的间隙,由存储引擎实现【InnoDB】

2. 按锁功能分:

  • 共享锁Shared Locks(S锁,也叫读锁)
  • 排他锁Exclusive Locks(X锁,也叫写锁)

五:MySQL 加锁流程

当用户执行这条 SQL 时,MySQL Server 和 InnoDb 之间的执行流程:

        从图中可以看到当 UPDATE 语句被发给 MySQL 后,MySQL Server 会根据 WHERE 条件读取第一条满足条件的记录,然后 InnoDB 引擎会将第一条记录返回并加锁(current read),待 MySQL Server 收到这条加锁的记录之后,会再发起一个 UPDATE 请求,更新这条记录。一条记录操作完成,再读取下一条记录,直至没有满足条件的记录为止。因此,MySQL 在操作多条记录时 InnoDB 与 MySQL Server 的交互是一条一条进行的,加锁也是一条一条依次进行的,先对一条满足条件的记录加锁,返回给 MySQL Server,做一些 DML 操作,然后在读取下一条加锁,直至读取完毕。理解这一点,对我们后面分析复杂 SQL 语句的加锁过程将很有帮助。

         注意,如果 SQL 语句无法使用索引时会走主索引实现全表扫描,这个时候 MySQL 会给整张表的所有数据行加记录锁。如果一个 WHERE 条件无法通过索引快速过滤,存储引擎层面就会将所有记录加锁后返回,再由 MySQL Server 层进行过滤。不过在实际使用过程中,MySQL 做了一些改进,如果是 RC 隔离级别,在 MySQL Server 过滤条件发现不满足后,会调用 unlock_row 方法,把不满足条件的记录锁释放掉(违背了 2PL 的约束)。这样做可以保证最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的。如果是 RR 隔离级别,一般情况下 MySQL 是不能这样优化的,除非设置了 innodb_locks_unsafe_for_binlog 参数,这时也会提前释放锁,并且不加 GAP 锁,这就是所谓的 semi-consistent read。。可见在没有索引时,不仅会消耗大量的锁资源,增加数据库的开销,而且极大的降低了数据库的并发性能,所以说,更新操作一定要记得走索引。

六:基本的加锁规则

虽然 MySQL 的锁各式各样,但是有些基本的加锁原则是保持不变的,譬如:快照读是不加锁的,更新语句肯定是加排它锁的,RC 隔离级别是没有间隙锁的等等。这些规则整理如下:

  • 常见语句的加锁

    • SELECT ... 语句正常情况下为快照读,不加锁;
    • SELECT ... LOCK IN SHARE MODE 语句为当前读,加 S 锁;
    • SELECT ... FOR UPDATE 语句为当前读,加 X 锁;
    • 常见的 DML 语句(如 INSERT、DELETE、UPDATE)为当前读,加 X 锁;
    • 常见的 DDL 语句(如 ALTER、CREATE 等)加表级锁,且这些语句为隐式提交,不能回滚;
  • 表锁

    • 表锁(分 S 锁和 X 锁)
    • 意向锁(分 IS 锁和 IX 锁)
    • 自增锁(一般见不到,只有在 innodb_autoinc_lock_mode = 0 或者 Bulk inserts 时才可能有)
  • 行锁

    • LOCK_REC_NOT_GAP:只锁记录,记录锁(分 S 锁和 X 锁)
    • LOCK_GAP:间隙锁,锁两个记录之间的 GAP,防止记录插入(分 S 锁和 X 锁);
    • LOCK_ORDINARY:也称为 Next-Key Lock,锁一条记录及其之前的间隙,这是 RR 隔离级别用的最多的锁,从名字也能看出来(分 S 锁和 X 锁);
    • LOCK_INSERT_INTENSION:插入意向 GAP 锁,插入记录时使用,是 LOCK_GAP 的一种特例。
  • 行锁分析

    • 行锁都是加在索引上的,最终都会落在聚簇索引上;
    • 加行锁的过程是一条一条记录加的;
  • 锁冲突

    • S 锁和 S 锁兼容,X 锁和 X 锁冲突,X 锁和 S 锁冲突;
  • 不同隔离级别下的锁

    • 上面说 SELECT ... 语句正常情况下为快照读,不加锁;但是在 Serializable 隔离级别下为当前读,加 S 锁;
    • RC 隔离级别下没有间隙锁和 Next-key 锁(特殊情况下也会有:purge + unique key);

七:全局锁

全局锁是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句, 已经更新操作的事务提交语句都将被阻塞。其典型的使用场景是做全库的逻辑备份,对所有的表进行锁 定,从而获取一致性视图,保证数据的完整性。

加全局锁的命令为:

flush tables with read lock;

释放全局锁的命令为:

unlock tables;

或者断开加锁session的连接,自动释放全局锁。

说到全局锁用于备份这个事情,还是很危险的。因为如果在主库上加全局锁,则整个数据库将不能写 入,备份期间影响业务运行,如果在从库上加全局锁,则会导致不能执行主库同步过来的操作,造成主 从延迟。 对于innodb这种支持事务的引擎,使用mysqldump备份时可以使用--single-transaction参数,利用 mvcc提供一致性视图,而不使用全局锁,不会影响业务的正常运行。而对于有MyISAM这种不支持事务 的表,就只能通过全局锁获得一致性视图,对应的mysqldump参数为--lock-all-tables。

举个栗子:

# 提交请求锁定所有数据库中的所有表,以保证数据的一致性,全局读锁【LBCC】
mysqldump -uroot -p --host=localhost --all-databases --lock-all-tables >
/root/db.sql
# 一致性视图【MVCC】
mysqldump -uroot -p --host=localhost --all-databases --single-transaction >
/root/db.sql

八:表级锁

MySQL的表级锁有四种:

  • 表读锁(Table Read Lock)
  • 表写锁(Table Write Lock)
  • 元数据锁(meta data lock,MDL)
  • 自增锁(AUTO-INC Locks)

看表锁定状态

show status like 'table%';

  • table_locks_immediate:产生表级锁定的次数;
  • table_locks_waited:出现表级锁定争用而发生等待的次数; 

手动增加表锁:

lock table 表名称 read(write),表名称2 read(write),其他;
# 举例:
lock table t read; #为表t加读锁
lock table t write; #为表t加写锁

查看表锁情况:

show open tables;

删除表锁:

unlock tables;

元数据锁

        元数据锁不需要显式指定,在访问一个表的时候会被自动加上,锁的作用是保证读写的正确性。 可以想象一下:如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个表结构做变更, 删了一列,那么查询线程拿到的结果跟表结构对不上,肯定是不行的。 因此,在 MySQL 5.5 版本中引入了元数据锁,当对一个表做增删改查操作的时候,加 元数据 读锁;当 要对表做结构变更操作的时候,加 元数据 写锁。

  • 读锁是共享的,是互相不阻塞的:因此你可以有多个线程同时对一张表加读锁,保证数据在读取的 过程中不会被其他线程修改。
  • 写锁则是排他的:也就是说一个写锁会阻塞其他的写锁和读锁,用来保证变更表结构操作的安全 性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。

AUTO_INC 锁

AUTO_INC 锁又叫自增锁(一般简写成 AI 锁),它是一种特殊类型的表锁,当插入的表中有自增列(AUTO_INCREMENT)的时候可能会遇到。当插入表中有自增列时,数据库需要自动生成自增值,在生成之前,它会先为该表加 AUTO_INC 表锁,其他事务的插入操作阻塞,这样保证生成的自增值肯定是唯一的。AUTO_INC 锁具有如下特点:

  • AUTO_INC 锁互不兼容,也就是说同一张表同时只允许有一个自增锁;
  • 自增锁不遵循二段锁协议,它并不是事务结束时释放,而是在 INSERT 语句执行结束时释放,这样可以提高并发插入的性能。
  • 自增值一旦分配了就会 +1,如果事务回滚,自增值也不会减回去,所以自增值可能会出现中断的情况。

显然,AUTO_INC 表锁会导致并发插入的效率降低,为了提高插入的并发性,MySQL 从 5.1.22 版本开始,引入了一种可选的轻量级锁(mutex)机制来代替 AUTO_INC 锁,我们可以通过参数 innodb_autoinc_lock_mode 控制分配自增值时的并发策略。参数 innodb_autoinc_lock_mode 可以取下列值:

  • innodb_autoinc_lock_mode = 0 (traditional lock mode)

    • 使用传统的 AUTO_INC 表锁,并发性比较差。
  • innodb_autoinc_lock_mode = 1 (consecutive lock mode)

    • MySQL 默认采用这种方式,是一种比较折中的方法。
    • MySQL 将插入语句分成三类:Simple inserts、Bulk inserts、Mixed-mode inserts。通过分析 INSERT 语句可以明确知道插入数量的叫做 Simple inserts,譬如最经常使用的 INSERT INTO table VALUE(1,2) 或 INSERT INTO table VALUES(1,2), (3,4);通过分析 INSERT 语句无法确定插入数量的叫做 Bulk inserts,譬如 INSERT INTO table SELECT 或 LOAD DATA 等;还有一种是不确定是否需要分配自增值的,譬如 INSERT INTO table VALUES(1,'a'), (NULL,'b'), (5, 'C'), (NULL, 'd') 或 INSERT ... ON DUPLICATE KEY UPDATE,这种叫做 Mixed-mode inserts
    • Bulk inserts 不能确定插入数使用表锁;Simple inserts 和 Mixed-mode inserts 使用轻量级锁 mutex,只锁住预分配自增值的过程,不锁整张表。Mixed-mode inserts 会直接分析语句,获得最坏情况下需要插入的数量,一次性分配足够的自增值,缺点是会分配过多,导致浪费和空洞。
    • 这种模式的好处是既平衡了并发性,又能保证同一条 INSERT 语句分配的自增值是连续的。
  • innodb_autoinc_lock_mode = 2 (interleaved lock mode)

    • 全部都用轻量级锁 mutex,并发性能最高,按顺序依次分配自增值,不会预分配。
    • 缺点是不能保证同一条 INSERT 语句内的自增值是连续的,这样在复制(replication)时,如果 binlog_format 为 statement-based(基于语句的复制)就会存在问题,因为是来一个分配一个,同一条 INSERT 语句内获得的自增值可能不连续,主从数据集会出现数据不一致。所以在做数据库同步时要特别注意这个配置。

MySQL 表锁的加锁规则如下:

  • 对于读锁

    • 持有读锁的会话可以读表,但不能写表;
    • 允许多个会话同时持有读锁;
    • 其他会话就算没有给表加读锁,也是可以读表的,但是不能写表;
    • 其他会话申请该表写锁时会阻塞,直到锁释放。
  • 对于写锁

    • 持有写锁的会话既可以读表,也可以写表;
    • 只有持有写锁的会话才可以访问该表,其他会话访问该表会被阻塞,直到锁释放;
    • 其他会话无论申请该表的读锁或写锁,都会阻塞,直到锁释放。

锁的释放规则如下:

  • 使用 UNLOCK TABLES 语句可以显示释放表锁;
  • 如果会话在持有表锁的情况下执行 LOCK TABLES 语句,将会释放该会话之前持有的锁;
  • 如果会话在持有表锁的情况下执行 START TRANSACTION 或 BEGIN 开启一个事务,将会释放该会话之前持有的锁;
  • 如果会话连接断开,将会释放该会话所有的锁。

九:行级锁

InnoDB的行级锁,按照锁定范围来说,分为四种

  • 记录锁(Record Locks):锁定索引中一条记录。
  • 间隙锁(Gap Locks):要么锁住索引记录中间的值,要么锁住第一个索引记录前面的值或者 最后一个索引记录后面的值。
  • 临键锁(Next-Key Locks):是索引记录上的记录锁和在索引记录之前的间隙锁的组合(间 隙锁 + 记录锁)。
  • 插入意向锁(Insert Intention Locks):做insert操作时添加的对记录id的
1. 记录锁

记录锁(Record Locks)仅仅锁住索引记录的一行,在单条索引记录上加锁。记录锁锁住的永远是索 引,而非记录本身,即使该表上没有任何显示索引,那么innodb会在后台创建一个隐藏的聚簇索引索 引,那么锁住的就是这个隐藏的聚簇索引索引。

2. 间隙锁

(1)间隙锁(Gap Locks),仅仅锁住一个索引区间(开区间,不包括双端端点)。

(2)在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括该索引记录 本身。

(3)间隙锁可用于防止幻读,保证索引间隙不会被插入数据。

(4)在可重复读(REPEATABLE READ)这个隔离级别下生效。

值得注意的是,间隙锁和间隙锁之间是互不冲突的,间隙锁唯一的作用就是为了防止其他事务的插入,所以加间隙 S 锁和加间隙 X 锁没有任何区别。

3. 临键锁

(1)临键锁(Next-Key Locks)相当于记录锁 + 间隙锁【左开右闭区间】,例如(5,8]

(2)默认情况下,innodb使用临键锁来锁定记录,但在不同的场景中会退化

(3)当查询的索引含有唯一属性的时候,临键锁会进行优化,将其降级为记录锁,即仅锁住索引本身, 不是范围。

4. 插入意向锁

(1)插入意向锁(Insert Intention Locks)是一种在 INSERT 操作之前设置的一种特殊的间隙锁。

(2)插入意向锁表示了一种插入意图,即当多个不同的事务,同时往同一个索引的同一个间隙中插入数 据的时候,它们互相之间无需等待,即不会阻塞。

(3)插入意向锁不会阻止插入意向锁,但是插入意向锁会阻止其他间隙写锁(排他锁)、记录锁。

行锁的兼容矩阵

其中,第一行表示已有的锁,第一列表示要加的锁,它们之间的兼容矩阵如下图所示:

十:MySQL 中观察行锁

有两种方式可以在 MySQL 中观察行锁,Mysql5.7版本

  • select * from information_schema.innodb_locks;
  • show engine innodb status;
    • 如果发生死锁,也可以通过这个命令来定位死锁发生的原因。不过在这之前需要先打开 Innodb 的锁监控,可以看到更加详细的锁信息:
set global innodb_status_output = ON;
set global innodb_status_output_locks = ON;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2199209.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS调整元素大小

为了增强用户体验,CSS3 中新增了一个非常实用的 resize 属性,该属性允许用户通过拖动的方式来自由缩放元素的尺寸,在此之前要实现类似的效果还需要借助大量的 JavaScript 代码。resize 属性的语法格式如下: resize: none|both|ho…

【含开题报告+文档+PPT+源码】基于SpringBoot乡村助农益农平台的设计与实现

开题报告 近年来,随着社会经济的快速发展和人民生活水平的提高,人们对优质农产品的需求越来越高。然而,传统的农产品销售管理模式存在一些问题。首先,农产品供应链信息不透明,导致生产者难以了解市场需求和价格变动趋…

【笔记】6.2 玻璃的成型

玻璃熔体的成型方法,有压制法(例如,制作水杯、烟灰缸等)、压延法(例如,制作压花玻璃等)、浇铸法(例如,制作光学玻璃、熔铸耐火材料、铸石等) 、吹制法(例如,制作瓶罐等空心玻璃)、拉制法(例如,制作窗用玻璃、玻璃管、玻璃纤维等)、离心法(例如,制作玻璃棉等)、喷吹法(例如,制作…

Authentication Lab | IP Based Auth Bypass

关注这个靶场的其它相关笔记:Authentication Lab —— 靶场笔记合集-CSDN博客 0x01:IP Based Auth Bypass 前情提要 有些开发人员为了图方便,会给站点设置一个 IP 白名单,如果访问站点的用户的 IP 在白名单内,则允许访…

低代码平台那么多,如何知道哪个比较好用

在当今数字化时代,低代码平台如雨后春笋般涌现,为企业的软件开发带来了新的机遇和挑战。面对众多的低代码平台,如何判断哪个比较好用呢? 首先,功能全面性是关键考量因素。一个好用的低代码平台应具备丰富的功能模块,包…

【ACM独立出版】第四届计算机、物联网与控制工程国际学术会议(CITCE 2024)

第四届计算机、物联网与控制工程国际学术会议(CITCE 2024) The 4th International Conference on Computer, Internet of Things and Control Engineering(CITCE 2024) 重要信息 大会官网:www.citce.org 大会时间:2024年11月1-…

C++面试之继承和多态

(倒反天罡) 概念选择题答案(0411-0422) 1 C 2 A 3 B 4 B 5 D 6 D 7 D 8 C 9 D 10 D 11 C 12 E 13 C 继承有什么作用? 继承可以重写或重定义父类中的一些方…

Redis 实现 查找附近的人 功能

文章目录 概述Redis 中 Geospatial(地理位置)Demo例子总结 概述 使用 Redis 实现“查找附近的人”功能,通常会依赖 Redis 的 Geo(地理位置) 数据类型来存储用户的经纬度,并基于此进行地理范围查询。Redis …

最新价值5000元的V2M2引擎传奇源码2024BLUE升级版 团购

最新团购的V2M2引擎源码2024年BLUE升级版 特点优势是最新XE12编辑器,微端,各种自定义UI 无限仿GOM引擎功能 参考地址:最新价值5000元的V2M2引擎传奇源码2024BLUE升级版[原始团购版]_1234FCOM专注游戏工具及源码例子分享下载地址:BlueCodePXL…

.gitignore文件不起作用,GitHub和GitHubDeskTOP

问题:使用github或者github桌面版的时候配置的.gitignore文件不起作用,.gitignore文件是最新的,没有问题,只是提交的的时候Library、UserSettings这些文件还都一直在,尝试了大家说的清缓存等等方法,仍然没有…

代码随想录训练营Day28 | 39. 组合总和 | 40.组合总和II | 131.分割回文串

学习文档:代码随想录 (programmercarl.com) 学习视频:代码随想录算法公开课 | 最强算法公开课 | 代码随想录 (programmercarl.com) Leetcode 39. 组合总和 题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找…

springboot医院预约挂号系统

基于springbootvue实现的医院预约挂号系统 (源码L文ppt)4-085 4.1系统功能模块设计 医院预约挂号系统与数据分析系统在设计与实施时,采取了模块性的设计理念,把相似的系统的功能整合到一个模组中,以增强内部的功能…

谷粒商城踩坑

1.mysql表名大小写问题(P16) 问题描述:代码要求的是表名大写,但实际上数据库是小写的,就报错: 问题解决:未解决 mysql版本为5.7.44。网上有两种解决方式,但是都解决不了 1.1 解决方…

国药准字:您购药的可靠指南

益安宁丸为同溢堂药业有限公司所独家生产的一款中成药,在内地市场和港澳地区均有上市。 益安宁丸真品的唯一标准:无论港版还是内地版,包装盒必然有国药准字Z20063087标识,但凡没有国药准字标识的必为假药。

【C++】:bind绑定器和function函数对象机制

欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 文章目录 引言function函数对象function引入细讲function体验function在工程实践中的优势 模拟实现function函数对象机制bind绑定器基本语法示例1. 绑定普通函数2. 使用占位符3. 绑定成员函数4. 绑定 lambda 表…

【Android Studio】基础入门(一)—— 创建第一个 Android 项目

文章目录 前言一、创建项目二、启动模拟器三、运行程序 前言 Android Studio是谷歌官方推出的免费集成开发环境,专为安卓应用开发而设计,集成了代码编写、调试、界面设计及性能分析等多种强大功能,支持Java和Kotlin语言,极大提升了…

【vue2.7.16系列】手把手教你搭建后台系统__搭建开发环境(1)

安装开发环境 安装nodejs 使用nvm管理多个node版本。nvm-npm-nrm-配置介绍。本系列文章使用node14.19.3,安装好node后,npm也就安装好了,npm版本是v6.14.7 安装vue/cli 5.0.8 npm install -g vue/cli创建项目 vue create admin # 选项&am…

Compose 一个联系人索引列表实现

一、前言 该功能是一个左侧为字符索引列表,右侧为联系人列表的实现,点击左侧字符索引可以自动滑动到对应联系人条目。该功能代码实现绝大多数有通义灵码完成。实现效果如下: 关键代码 data class Contact(val name: String)class Contact…

unix中如何申请进程调度的优先级

一、前言 unix系统中,进程的调度是由内核决定的。在一个系统中,进程的优先级越高,表示其在一定时间中占用cpu的时间越久。本文将介绍unix系统如何修改以及获取进程的优先级。 二、nice值 nice值是unix系统中用于表征进程优先级的一个参数。…

MES系统中的正向追溯与反向追溯管理

随着制造业的日益发展,生产过程中的质量控制和管理变得尤为关键。MES系统作为一种实现车间生产管理和控制的重要工具,其追溯功能在生产过程中起着至关重要的作用。 一、MES系统概述 MES系统是一套面向制造企业车间执行层的生产信息化管理系统。它通过对…