当前位置: 首页 > news >正文

Mysql基础(②锁)

分类锁类型说明示例 / 特点
按粒度行级锁仅锁定单行数据,粒度最细MySQL InnoDB 的 FOR UPDATE 行锁,并发性能高,锁冲突概率低
表级锁锁定整张表,粒度最粗MySQL MyISAM 的表锁、InnoDB 的 AUTO_INCREMENT 锁,并发性能低
页级锁锁定数据页(数据库存储的基本单位),粒度介于行和表之间MySQL BDB 引擎,平衡并发和锁开销,但可能出现页内锁冲突
按功能共享锁(S 锁)允许事务读取数据,多个事务可同时持有SELECT ... LOCK IN SHARE MODE(MySQL),读操作不阻塞其他读操作
排他锁(X 锁)允许事务修改数据,仅一个事务可持有SELECT ... FOR UPDATE(MySQL),写操作阻塞其他所有读写操作
意向锁表级锁,标识事务对表中 rows 的锁类型(为行锁做准备)InnoDB 的意向共享锁(IS)、意向排他锁(IX),自动添加,无需手动操作
乐观锁非真正的锁,通过版本号或时间戳实现,假设冲突概率低UPDATE ... WHERE version = ?,适合读多写少场景,无锁等待开销

行级锁

你打开编辑(BEGIN 事务),系统自动给这条客户记录加行锁,同事此时点编辑会卡住(等你释放锁)。你改完点保存(COMMIT),锁释放,同事才能编辑。

先创建一张简单的表并插入数据:

-- 创建表(InnoDB引擎支持行级锁)
CREATE TABLE `user_account` (`id` int(11) NOT NULL PRIMARY KEY,`name` varchar(50) NOT NULL,`balance` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;-- 插入3条数据(3行)
INSERT INTO `user_account` VALUES 
(1, '张三', 1000),
(2, '李四', 2000),
(3, '王五', 3000);

演示 1:行级锁只锁单行,不影响其他行

我们打开两个 MySQL 窗口(模拟两个事务),按步骤执行:

窗口 1(事务 A):

-- 开启事务
BEGIN;-- 修改id=1的行(自动加行级锁)
UPDATE user_account SET balance = balance - 100 WHERE id = 1;-- 不提交事务(锁不释放)

窗口 2(事务 B):

-- 开启事务
BEGIN;-- 尝试修改id=1的行(会被阻塞,因为被事务A锁定)
UPDATE user_account SET balance = balance + 100 WHERE id = 1;
-- 执行后会卡住,等待锁释放

窗口 2 继续操作:

-- 尝试修改id=2的行(可以正常执行,不受影响)
UPDATE user_account SET balance = balance - 200 WHERE id = 2;
-- 结果:Query OK, 1 row affected (0.00 sec)
-- 说明:行级锁只锁了id=1,不影响id=2的行

最后步骤:

窗口 1 提交事务(释放锁):COMMIT;

窗口 2 之前卡住的语句会立即执行,然后也提交:COMMIT;

演示 2:验证锁释放后的结果

两个事务都提交后,查询表数据:

SELECT * FROM user_account;

运行结果:

+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
| 1  | 张三   | 900     |  -- 事务A扣了100
| 2  | 李四   | 1800    |  -- 事务B扣了200
| 3  | 王五   | 3000    |  -- 未操作,不变
+----+--------+---------+

关键结论

  1. 行级锁只会锁定被操作的那一行(如 id=1),其他行(如 id=2、3)可以正常操作。

  2. 锁会一直持有,直到事务提交(COMMIT)或回滚(ROLLBACK)才释放。

  3. 如果两个事务操作同一行,后执行的会等待前一个事务释放锁,避免数据混乱。

表级锁

数据库中常见的表级锁有两种:

读锁(共享锁,Shared Lock):多个事务可以同时持有,持有期间只能读数据,不能修改,也不允许其他事务加写锁。
写锁(排他锁,Exclusive Lock):只有一个事务可以持有,持有期间可以读写数据,其他事务既不能读(加读锁)也不能写(加写锁)。

假设我们有一张表 user,数据如下:

idnamebalance
1张三1000
2李四2000

示例 1:加读锁(共享锁)

事务 A(终端 1)操作:

-- 开启会话,给user表加读锁(只能读,不能写)
LOCK TABLES user READ;-- 可以正常读取数据
SELECT * FROM user WHERE id = 1;
-- 结果:id=1, name=张三, balance=1000

事务 B(终端 2)操作:

-- 其他事务可以加读锁并读取(读锁共享)
LOCK TABLES user READ;
SELECT * FROM user WHERE id = 1; -- 正常返回结果-- 但不能修改数据(会被阻塞,直到锁释放)
UPDATE user SET balance = 1500 WHERE id = 1;
-- 此处会卡住,等待事务A释放锁

事务 A 释放锁:

-- 释放锁
UNLOCK TABLES;

此时事务 B 的 UPDATE 语句会立即执行(如果没有其他锁)。

示例 2:加写锁(排他锁)

事务 A(终端 1)操作:

-- 给user表加写锁(独占,可读写)
LOCK TABLES user WRITE;-- 可以读取和修改数据
UPDATE user SET balance = 1500 WHERE id = 1;
SELECT * FROM user WHERE id = 1; -- 结果:balance=1500

事务 B(终端 2)操作:

-- 尝试读数据(加读锁)会被阻塞
LOCK TABLES user READ;
-- 此处卡住,无法执行,直到事务A释放锁-- 尝试修改数据也会被阻塞
UPDATE user SET balance = 2500 WHERE id = 2;
-- 同样卡住

事务 A 释放锁:

UNLOCK TABLES;

此时事务 B 的操作会立即执行。

页级锁

假设我们使用支持页级锁的数据库(如启用 BDB 引擎的 MySQL,仅作演示),创建一张表并插入数据:

-- 创建使用BDB引擎的表(支持页级锁)
CREATE TABLE `page_demo` (`id` int(11) NOT NULL PRIMARY KEY,`name` varchar(50) NOT NULL
) ENGINE=BDB DEFAULT CHARSET=utf8;-- 插入8行数据,假设数据库按4行一页存储:
-- 页1:id=1~4,页2:id=5~8
INSERT INTO `page_demo` VALUES 
(1, '数据1'), (2, '数据2'), (3, '数据3'), (4, '数据4'),
(5, '数据5'), (6, '数据6'), (7, '数据7'), (8, '数据8');

场景 1:操作同一页的数据(页 1)

终端 1(事务 A):修改页 1 中的数据(id=2)

-- 开启事务(BDB引擎会自动加页级锁)
BEGIN;
-- 修改id=2(属于页1),触发页1的锁定
UPDATE page_demo SET name='修改后的数据2' WHERE id=2;
-- 不提交事务(保持锁)

终端 2(事务 B):尝试操作页 1 中的其他数据(id=3)

BEGIN;
-- 尝试修改id=3(同属页1),会被阻塞(页1被锁)
UPDATE page_demo SET name='修改后的数据3' WHERE id=3;
-- 执行后会卡住,等待终端1释放锁

终端 2 继续尝试:操作另一页(页 2)的数据(id=5)

-- 修改id=5(属于页2),可以正常执行(页2未被锁)
UPDATE page_demo SET name='修改后的数据5' WHERE id=5;
-- 结果:Query OK, 1 row affected (0.00 sec)

释放锁:终端 1 提交事务

COMMIT; -- 释放页1的锁

此时终端 2 中被阻塞的修改 id=3 的语句会立即执行。

运行结果

+----+-----------------+
| id | name            |
+----+-----------------+
| 1  | 数据1           |  -- 未操作
| 2  | 修改后的数据2   |  -- 事务A修改
| 3  | 修改后的数据3   |  -- 事务B修改(锁释放后执行)
| 4  | 数据4           |  -- 未操作
| 5  | 修改后的数据5   |  -- 事务B修改(页2未被锁)
| 6  | 数据6           |  -- 未操作
| 7  | 数据7           |  -- 未操作
| 8  | 数据8           |  -- 未操作
+----+-----------------+

http://www.dtcms.com/a/342635.html

相关文章:

  • 想在手机上操作服务器?cpolar让WaveTerminal终端随身携带,效率倍增
  • 高并发短信网关平台建设方案概述
  • 打造医疗新质生产力
  • nodejs安装后 使用npm 只能在cmd 里使用 ,但是不能在poowershell使用,只能用npm.cmd
  • ES_多表关联
  • Linux 信号 (Signals)
  • 鱼眼相机去畸变的算法原理(一)
  • WEB服务器(静态/动态网站搭建)
  • 循环神经网络实战:用 LSTM 做中文情感分析(二)
  • Mokker AI:一键更换照片背景的AI神器
  • 鸿蒙生态开发全栈指南
  • mac的m3芯片安装mysql
  • 统计全为1的正方形子矩阵-二维dp
  • 机器学习中的两大核心算法:k 均值聚类与集成学习
  • c# 和 c++ 怎样结合
  • 基于springboot的美术馆管理系统
  • 迁移docker容器的mysql数据库到本地
  • CQRS 的优缺点
  • 【图像算法 - 20】慧眼识病:基于深度学习与OpenCV的植物叶子疾病智能识别系统
  • uniapp跨域怎么解决
  • uniapp 获取手机状态栏的高度
  • 2025-08-21 Python进阶1——控制流语句
  • K 均值聚类:从概念到实践的无监督学习之旅
  • 面试后的跟进策略:如何提高录用几率并留下专业印象
  • 暂停更新的高速下载网盘,作者可能不再维护
  • Oracle: cannot decrease column length because some value is too big
  • .NET Core MongoDB 查询数据异常及解决
  • 分布式集群压测+grafana+influxdb+Prometheus详细步骤
  • 详细说明http协议特别是conten-length和chunk编码,并且用linux的命令行演示整个过程
  • Python读取和设置PNG图片的像素值