Mysql行锁失效情况分析

本文通过Mysql8.0.32的测试展示了行锁在特定条件下如何升级为表锁。在更新使用主键的记录时,不同窗口的并发更新可以并行执行,而使用非索引字段更新会导致行锁失效,升级为表锁,造成其他操作阻塞,直至提交事务释放锁。

一、概述

数据库版本:使用Mysql8.0.32作为测试版本,InnoDB引擎。
本文阐述Mysql行锁失效升级为表的场景。

二、测试

  • 使用远程终端打开两个数据库服务器(Linux)窗口,使用如下命令登录:
mysql -u root -p

回车,输入数据库密码.

  • 测试数据库为: test
show databases;
use test;
  • 关闭自动提交
set autocommit=0;
  • 测试数据表为:
CREATE TABLE `t_run_work_order`  (
  `id` bigint(0) UNSIGNED NOT NULL AUTO_INCREMENT,
  `work_no` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '工单编号',
  `data_guid` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '报警ID',
  `dept_id` bigint(0) NOT NULL COMMENT '组织结构ID',
  `flow_id` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '流程ID',
  `node_id` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '当前流程',
  `work_title` varchar(40) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '工单标题',
  `work_state` char(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '状态',
  `is_exception` char(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '是否异常',
  `is_invalid` char(1) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '是否有效',
  `create_time` datetime(0) NOT NULL COMMENT '创建时间',
  `version` int(0) NOT NULL DEFAULT 0 COMMENT '版本号',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `u_data_guid`(`data_guid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 11903 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = 'XX信息表' ROW_FORMAT = Dynamic;

其中id是主键。

插入两条id分别为7742、7743的记录。

  • 对比测试:
    在左侧窗口执行:
mysql> update t_run_work_order set work_title='OTHE-L'' where id='7742';
Query OK, 1 row affected (16.49 sec)
Rows matched: 1  Changed: 1  Warnings: 0

在右侧窗口执行:

update t_run_work_order set work_title='OTHE-R' where id='7742';

我们发现,右侧命令会阻塞。
阻塞原因:左侧命令执行后,尚未提交,锁住了id为7742的记录;
接下来,在左侧窗口执行:

commit;

观察右侧窗口,右侧窗口命令执行成功。

那么,如果我们同时对不同的记录进行操作呢?

在左侧窗口执行:

mysql> update t_run_work_order set work_title='OTHE-L'' where id='7742';
Query OK, 1 row affected (16.49 sec)
Rows matched: 1  Changed: 1  Warnings: 0

在右侧窗口执行:

update t_run_work_order set work_title='OTHE-R' where id='7743';
Query OK, 1 row affected (12.49 sec)
Rows matched: 1  Changed: 1  Warnings: 0

我们发现,两边都执行成功,没有阻塞。
这说明:分表对不同主键的记录进行更新操作,互不影响。因为他们只是锁住了各自的记录,也就是行锁。

接下来,我们测试一下使用非索引字段作为条件进行更新操作。
在左侧窗口执行:

mysql> update t_run_work_order set work_title='OTHE-L'' where data_guid='56c24ba9-49c7-471c-b77f-a3f38043d429'';
Query OK, 1 row affected (16.49 sec)
Rows matched: 1  Changed: 1  Warnings: 0

在右侧窗口执行:

update t_run_work_order set work_title='OTHE-R' where data_guid='56c24ba9-49c7-471c-b77f-a3f38043d428';

右侧窗口阻塞。

我们在左侧窗口执行

commit;

命令提交。然后观察右侧窗口,右侧窗口立即执行成功。
这说明了,对使用非索引字段作为条件对记录进行操作时,行锁会失效,升级为表锁,此时,其他更新、删除操作都会处于阻塞状态,直到锁释放。

### MySQL 实验平台中的解决方案 在 MySQL 中,的实现主要依赖于存储引擎的支持。InnoDB 是支持的存储引擎之一[^4]。然而,在某些特定场景下,MySQL 可能会选择表而非,这通常与查询优化器的决策有关[^3]。以下内容将详细探讨如何在实验平台中解决与相关的问题。 #### 1. 确保使用 InnoDB 存储引擎 为了确保能够正常工作,首先需要确认所使用的表采用的是 InnoDB 存储引擎。可以通过以下命令检查表的存储引擎: ```sql SHOW TABLE STATUS WHERE Name = 'your_table_name'; ``` 如果发现存储引擎为 MyISAM,则需要将其转换为 InnoDB: ```sql ALTER TABLE your_table_name ENGINE=InnoDB; ``` #### 2. 强制索引以避免全表扫描 在某些情况下,MySQL 的查询优化器可能会忽略索引,转而执全表扫描。这种为会导致失效,转而使用表。为避免这种情况,可以强制使用索引: ```sql SELECT * FROM test FORCE INDEX (your_index_name) WHERE condition; ``` 需要注意的是,即使强制使用索引,也有可能因为优化器的原因导致仍然使用表。因此,建议对查询语句进性能分析,确保其符合预期。 #### 3. 调整事务隔离级别 事务隔离级别会影响为。例如,在 REPEATABLE-READ 隔离级别下,InnoDB 使用两阶段协议,可能导致更多的冲突[^4]。可以通过调整事务隔离级别来减少冲突的概率: ```sql SET TRANSACTION ISOLATION LEVEL READ COMMITTED; ``` READ COMMITTED 隔离级别下,InnoDB 不会持有间隙(Gap Lock),从而降低死的可能性[^2]。 #### 4. 批量操作优化 当需要对大量数据进修改时,一次性加可能会导致长时间的等待。为减少冲突的概率和时间,可以将批量操作拆分为多次小批量操作[^5]。例如: ```sql -- 每次更新 100 条记录 UPDATE test SET column = value WHERE id BETWEEN start AND end LIMIT 100; ``` 通过这种方式,可以有效减少冲突的时间和概率。 #### 5. 监控等待情况 在实验平台中,可以通过以下命令监控等待的情况,以便及时发现问题并优化查询: ```sql SHOW ENGINE INNODB STATUS; ``` 该命令会输出当前 InnoDB 的状态信息,包括等待队列、事务信息等。 --- ### 示例代码:批量插入避免冲突 以下是一个示例代码,展示如何通过分批插入数据来减少冲突: ```python import mysql.connector # 连接数据库 conn = mysql.connector.connect(user='root', password='password', host='localhost', database='test') cursor = conn.cursor() # 分批插入数据 batch_size = 100 start_id = 1 end_id = 1000 while start_id <= end_id: sql = f"INSERT INTO test (id, name, adress) SELECT {start_id} + n.n, 'yyg', 'zhongxian' FROM (SELECT 0 AS n UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL ... UNION ALL SELECT {batch_size - 1}) n WHERE {start_id} + n.n <= {end_id};" cursor.execute(sql) conn.commit() start_id += batch_size cursor.close() conn.close() ``` --- ### 结论 通过确保使用 InnoDB 存储引擎、强制索引、调整事务隔离级别、优化批量操作以及监控等待情况,可以在实验平台中有效解决与 MySQL 相关的问题。这些方法不仅可以提高系统的并发性能,还能减少冲突带来的负面影响。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

oyezitan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值