关于mysql insert into...select锁的问题

本文探讨了在MySQL5.6和5.7环境下,不同事务隔离级别(REPEATABLE-READ与READ-COMMITTED)对行锁产生的影响。在REPEATABLE-READ下,事务会逐步产生行锁;而在READ-COMMITTED下,则不会产生行锁。同时,两种操作都会产生元数据锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

环境:

mysql 5.6和5.7

binlog_format = 'ROW';

1:当隔离级别为REPEATABLE-READ时,逐步产生行锁

---TRANSACTION 14970094, ACTIVE 34 sec fetching rows
mysql tables in use 2, locked 2
15510 lock struct(s), heap size 1384656, 136906 row lock(s), undo log entries 121400

2:当隔离级别为READ-COMMITTED时,不产生行锁

---TRANSACTION 14973455, ACTIVE 9 sec inserting
mysql tables in use 2, locked 1
2 lock struct(s), heap size 1136, 0 row lock(s), undo log entries 97603

注:两种操作都会产生元数据锁

 

 

<think>嗯,用户想了解MySQL 8.0.25中使用INSERT INTO SELECT语句涉及的next-key行为以及解决方法。我需要先回忆一下MySQL机制,尤其是InnoDB的next-key。 首先,next-key是InnoDB在可重复隔离级别下用来防止幻的一种机制。它结合了记录和间隙定一个范围。当执行INSERT INTO SELECT,如果源表有数据被扫描,可能会对扫描到的范围加next-key,特别是当使用唯一索引或非唯一索引的候。 然后,用户提到的版本是8.0.25,需要确认这个版本是否有相关的变化。根据引用[2],MySQL 8.0.22开始,DML操作取grant表,但这里讨论的是普通的表操作。可能需要查看MySQL官方文档关于的部分。 接下来,INSERT INTO SELECT在复制数据,如果目标表有触发器或外键约束,或者源表有索引扫描,可能会导致更长间的定,从而引发性能问题甚至死。比如,当从大表中SELECT数据插入到另一个表,如果源表的索引范围被定,其他事务可能被阻塞。 解决方法可能包括调整事务隔离级别,比如使用已提交来减少间隙的使用,或者优化索引,减少扫描范围。另外,分批处理数据可能有助于减少的持有间。引用[3]提到MySQL 8.0提高了可靠性,可能在管理方面有所优化,但具体到8.0.25需要确认。 用户可能遇到的情况是,在执行INSERT INTO SELECT,源表被长定,影响其他事务。需要解释这种情况下next-key的行为,并提供具体的解决方案,比如使用主键顺序扫描、降低隔离级别、拆分事务等。同,结合用户提供的引用,可能引用相关版本文档来支持解答。</think>### MySQL 8.0.25中`INSERT INTO SELECT`的Next-Key行为分析 #### 1. Next-Key的基本机制 在InnoDB存储引擎中,`INSERT INTO SELECT`语句在**可重复(REPEATABLE READ)**隔离级别下,若涉及索引范围扫描,可能触发Next-Key。Next-Key是记录(Record Lock)和间隙(Gap Lock)的组合,用于防止幻(Phantom Read)[^2]。 例如: ```sql INSERT INTO target_table SELECT * FROM source_table WHERE indexed_column BETWEEN 100 AND 200; ``` 如果`source_table`的`indexed_column`是非唯一索引,InnoDB会对该范围内所有记录及间隙加,阻止其他事务插入符合此范围的新数据。 #### 2. 具体行为表现 - **主键/唯一索引扫描**:若`SELECT`条件使用主键或唯一索引精确匹配(如`WHERE id = 5`),仅加记录。 - **非唯一索引扫描**:若使用非唯一索引或全表扫描,会对扫描范围加Next-Key。 - **无索引扫描**:全表扫描,可能对表中所有间隙加,导致严重竞争[^3]。 #### 3. 典型问题场景 ```sql -- 事务1(长间运行) INSERT INTO orders_archive SELECT * FROM orders WHERE create_time >= '2023-01-01'; -- 事务2(被阻塞) INSERT INTO orders (create_time) VALUES ('2023-01-02'); ``` 若`orders.create_time`有非唯一索引,事务1会定`2023-01-01`之后的间隙,导致事务2被阻塞。 #### 4. 优化解决方案 ##### 方法1:调整隔离级别 将事务隔离级别改为**已提交(READ COMMITTED)**,该级别下InnoDB使用间隙: ```sql SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; BEGIN; INSERT INTO target_table SELECT ...; COMMIT; ``` 注意:需评估幻风险是否可接受。 ##### 方法2:索引优化 为`SELECT`条件添加高效索引,减少定范围: ```sql ALTER TABLE source_table ADD INDEX idx_column(column); ``` ##### 方法3:分批处理 使用主键范围分批操作,减少单次持有间: ```sql INSERT INTO target_table SELECT * FROM source_table WHERE id BETWEEN 1 AND 1000; INSERT INTO target_table SELECT * FROM source_table WHERE id BETWEEN 1001 AND 2000; ``` ##### 方法4:使用`LIMIT`与循环 通过应用层控制分批逻辑: ```python last_id = 0 batch_size = 1000 while True: cursor.execute(f""" INSERT INTO target_table SELECT * FROM source_table WHERE id > {last_id} ORDER BY id LIMIT {batch_size} """) last_id = get_last_insert_id() if affected_rows < batch_size: break ``` #### 5. 监控状态 通过以下命令实监控行为: ```sql -- 查看当前信息 SELECT * FROM performance_schema.data_locks; -- 查看阻塞关系 SELECT * FROM sys.innodb_lock_waits; ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值