MySQL进阶10—锁机制4

概述

本章,您将学习到 MySQL 8 中有关锁机制的知识,内容包括:

  • 锁机制的分类
  • 演示 InnoDB 存储引擎的行锁
  • 演示 InnoDB 存储引擎的表锁
  • 锁的查看
  • 有关乐观锁、全局锁和死锁的内容
  • MVCC

由于内容较多,本文档仅来说明乐观锁、全局锁和死锁的内容。

乐观锁

乐观锁:又名乐观并发控制(optimistic concurrency control,缩写 "OCC"),也是一种并发控制的方法。和悲观锁相反,乐观锁假定认为操作的数据在一般情况下不会造成冲突,所以不会对操作的数据进行加锁,只有到数据提交时才通过一种机制(常见的是对记录的数据进行版本比对)来验证数据是否冲突。同样的,悲观锁是一种机制思想,并不是实际操作过程中特指的某一个锁。

乐观锁的这种机制思想适用于那种读多写少的场景。

一般而言,乐观锁需要开发者自己手动去实现,常见的有:

  • 版本号

    实现步骤如下:

    1. 在表中添加一个 version 字段(整数类型)
    2. 读取数据时,同时获取 version 值
    3. 更新数据时,检查当前 version 是否与读取时一致。若一致,执行更新操作并 version + 1;若不一致,说明数据已被其他事务修改,更新失败
    # 读取数据
    SELECT id,data,version FROM example WHERE id = 1;
    
    # 更新数据
    UPDATE example 
    SET data = 'new data', version = version + 1 
    WHERE id = 1 AND version = 5;
  • 时间戳

    ‌实现步骤‌如下:

    1. 在数据表中添加 timestamp 字段(DATETIME 类型)
    2. 读取数据时,同时获取 timestamp 值
    3. 更新数据时,检查当前 timestamp 是否与读取时一致。若一致,执行更新操作并让时间戳字段更新到当前时间;若不一致,说明数据已被其他事务修改,更新失败
    # 读取数据
    SELECT id,name,timestamp FROM user WHERE id = 1;
    
    # 更新数据
    UPDATE user SET name = 'New Name', timestamp = NOW()
    WHERE id = 1 AND timestamp = '2025-12-17 14:00:00';
  • 版本号 + 时间戳

全局锁

全局锁:对当前 MySQL 实例中的所有表进行加锁,使它们变成只读状态,后续的 DDL、DML 以及涉及到事务的提交都会被阻塞,仅允许 DQL。

当要对全库进行逻辑备份时,为了保证数据的完整性和一致性,通常都会使用全局锁。需要注意的是,全局锁会让业务中断,因此在绝大部分的场景下,都应该尽量避免使用全局锁。

相关语法:

# 开启全局锁
flush tables with read lock;

# 解除全局锁
unlock tables;

逻辑备份:即备份的数据以 SQL 语句的形式进行存储

死锁

死锁:指两个或多个事务在同一个资源上相互占用,事务之间都在等待对方释放锁,这种死循环的现象就被称为死锁。

死锁几乎出现在每个关系型数据库当中,在 MySQL 8 的 InnoDB 存储引擎中,死锁的错误提示为 —— "Deadlock found when trying to get lock; try restarting transaction"

下面演示了死锁的出现以及存储引擎遇到死锁现象时的策略(依旧是使用前文的 locks 库):

use locks;

desc tlock1;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int         | NO   | PRI | NULL    | auto_increment |
| name  | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

select * from tlock1;
+----+------+
| id | name |
+----+------+
|  1 | kone |
|  7 | john |
| 15 | lee  |
+----+------+
3 rows in set (0.00 sec)
Session 1 Session 2
use locks;
set autocommit=0;
start transaction;
update tlock1 set name='jack' where id=1;
use locks;
set autocommit=0;
start transaction;
update tlock1 set name='zhang' where id=7;
update tlock1 set name='juliet' where id=7;
update tlock1 set name='mike' where id=1;
Query OK, 1 row affected (23.96 sec)
Rows matched: 1 Changed: 1 Warnings: 0
rollback;
rollback;
  • Session 1 中,update tlock1 set name='jack' where id=1; 表示添加了 MDL 读锁(行级排他锁)
  • Session 1 中,update tlock1 set name='juliet' where id=7; 会阻塞,虽然 Session 2 中 update tlock1 set name='zhang' where id=7; 也是 MDL 读锁(行级排他锁),但是由于更新的是同一行数据,因此会阻塞
  • Session 2 中,update tlock1 set name='mike' where id=1; 执行时出现了死锁,并输出 "ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction"
  • 由于发现了死锁,因此 Session 1 中的 update tlock1 set name='juliet' where id=7; 会执行成功,因为存储引擎发现了死锁并应用了相应的策略

当 MySQL 8 的 InnoDB 存储引擎出现死锁现象后,会有两种可选的应对策略:

  1. 进入到等待时间 - 等待的时间由系统变量 innodb_lock_wait_timeout 决定,默认 50s 。注意!该系统变量不仅是发生阻塞时的最长等待时间,也是死锁的最长等待时间。超时之后不会发生回滚,因为系统变量 innodb_rollback_on_timeout 的默认值为 OFF(0)
  2. 死锁检测并回滚 - 当发生死锁时,主动回滚某一个事务,优先回滚小事务(数据修改量最小的事务),若事务修改的数据量相同,则回滚等待时间最长的事务。由系统变量 innodb_deadlock_detect 的值来决定是否打开死锁检测,默认值为 ON(1)
select @@global.innodb_lock_wait_timeout;
+-----------------------------------+
| @@global.innodb_lock_wait_timeout |
+-----------------------------------+
|                                50 |
+-----------------------------------+
1 row in set (0.00 sec)

select @@global.innodb_rollback_on_timeout;
+-------------------------------------+
| @@global.innodb_rollback_on_timeout |
+-------------------------------------+
|                                   0 |
+-------------------------------------+
1 row in set (0.00 sec)

select @@global.innodb_deadlock_detect;
+---------------------------------+
| @@global.innodb_deadlock_detect |
+---------------------------------+
|                               1 |
+---------------------------------+
1 row in set (0.00 sec)
Avatar photo

关于 陸風睿

GNU/Linux 从业者、开源爱好者、技术钻研者,撰写文档既是兴趣也是工作内容之一。Q - "281957576";WeChat - "jiulongxiaotianci",Github - https://github.com/jimcat8
用一杯咖啡支持我们,我们的每一篇[文档]都经过实际操作和精心打磨,而不是简单地从网上复制粘贴。期间投入了大量心血,只为能够真正帮助到您。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇