关于MySQL insert into ... select 的锁情况
摘要:
一直以为"insert into tb select * from tbx" 这样的导入操作是会把tbx表给锁住的,在锁期间是不允许任何操作(保证一致性)。看完这篇写的之后,发现其实我错了一半。tbx表是会被锁住,但这个锁有2种情况,现在逐一进行分析:
分析
环境:

root@127.0.0.1 : test 02:10:40>select @@global.tx_isolation,@@session.tx_isolation; +-----------------------+------------------------+ | @@global.tx_isolation | @@session.tx_isolation | +-----------------------+------------------------+ | REPEATABLE-READ | REPEATABLE-READ | +-----------------------+------------------------+ 1 row in set (0.00 sec) root@127.0.0.1 : test 02:10:50>select @@version; +------------+ | @@version | +------------+ | 5.6.10-log | +------------+ 1 row in set (0.00 sec)

1:按照主键排序插入的情况
直接插入,不加排序字段(默认):

session1:执行操作,表只有5W条记录 root@127.0.0.1 : test 02:10:51>insert into uu select * from user; session2:查看操作锁的情况(锁的行数) root@127.0.0.1 : (none) 02:13:30>pager grep "lock(s)" PAGER set to 'grep "lock(s)"'root@127.0.0.1 : (none) 02:18:08>show engine innodb status; #被锁的行数逐步增加 274 lock struct(s), heap size 31160, 17746 row lock(s), undo log entries 17474
root@127.0.0.1 : (none) 02:18:16>show engine innodb status; 500 lock struct(s), heap size 63928, 32572 row lock(s), undo log entries 32074
root@127.0.0.1 : (none) 02:18:17>show engine innodb status; 676 lock struct(s), heap size 80312, 44308 row lock(s), undo log entries 43635

用主键升序插入:
情况和1一样。即默认的"select * from tb" 和 "select * from tb order id(PK) ASC " 是一样的情况。
用主键倒序插入:
情况和1一样。即默认的"select * from tb" 和 "select * from tb order id(PK) DESC" 是一样的情况,这里说的一样是锁方式一样(都是逐步,只是顺序不一样)。
从上面可知:通过主键排序或则不加排序字段的导入操作"insert into tb select * from tbx",是会锁tbx表,但他的锁是逐步地锁定已经扫描过的记录。
2:按照非主键排序插入的情况

session1:执行操作 root@127.0.0.1 : test 02:33:00>insert into uu select * from user order by createTime ; session2:查看操作锁的情况(行数) root@127.0.0.1 : (none) 02:27:29>pager grep "lock(s)" root@127.0.0.1 : (none) 02:27:54>show engine innodb status; #被锁的行数一样,不变(整张表) 773 lock struct(s), heap size 80312, 50771 row lock(s), undo log entries 1843
root@127.0.0.1 : (none) 02:33:19>show engine innodb status; 773 lock struct(s), heap size 80312, 50771 row lock(s), undo log entries 17680
root@127.0.0.1 : (none) 02:33:20>show engine innodb status; 773 lock struct(s), heap size 80312, 50771 row lock(s), undo log entries 22260
root@127.0.0.1 : (none) 02:33:21>show engine innodb status; 773 lock struct(s), heap size 80312, 50771 row lock(s), undo log entries 28960

从上面可知:通过非主键排序的导入操作"insert into tb select * from tbx",是会锁tbx表,但他的锁是一开始就会锁定整张表。
总之,"insert into tb select * from tbx" 的导入操作是会锁定原表,但是锁是有2种情况:“逐步锁”,“全锁”。
验证:
针对1的情况:逐步锁定扫描过的记录,那操作未扫描的数据会怎么样?

session1:执行操作 root@127.0.0.1 : test 02:55:27>insert into uu select * from user; Query OK, 49998 rows affected (9.06 sec) session2:测试操作锁的情况 root@127.0.0.1 : test 02:54:49>delete from user where id = 33333;update user set username='TEST' where id = 44444;insert into user(id,username,company) values(1000,'ASD','ABCASDA'); Query OK, 0 rows affected (0.00 sec) #可以删除未扫描(锁)的数据(id=33333) Query OK, 0 rows affected (0.00 sec) Rows matched: 1 Changed: 0 Warnings: 0 #可以更新为扫描(锁)的数据(id=44444) Query OK, 1 row affected (8.09 sec)#插入(更新,删除)操作被锁了,因为该记录已经被扫描到(id=1000) session3:查看操作的锁情况: root@127.0.0.1 : (none) 02:55:33>show engine innodb status; LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s) 272 lock struct(s), heap size 31160, 17574 row lock(s), undo log entries 17305 1 row in set (0.09 sec) root@127.0.0.1 : (none) 02:55:35>show engine innodb status; LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s) 448 lock struct(s), heap size 47544, 29109 row lock(s), undo log entries 28664 1 row in set (0.01 sec) root@127.0.0.1 : (none) 02:55:37>show engine innodb status; LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s) 612 lock struct(s), heap size 63928, 40034 row lock(s), undo log entries 39425 1 row in set (0.00 sec) root@127.0.0.1 : (none) 02:55:39>show engine innodb status; 1 row in set (0.01 sec)

从上面看出,刚好说明了1的情况:逐步的锁定已经扫描过的记录。
默认、主键升序的select :从第一行开始扫描到最后,即第一行开始锁直到最后。
主键倒序select :从最后一行开始扫描到最前,即最后一行开始锁直到第一行。
针对2的情况:锁定整张表,那就是表锁;不能进行任何操作,直到锁释放了?

session1:执行操作 root@127.0.0.1 : test 03:23:06>insert into uu select * from user order by company; Query OK, 49994 rows affected (13.70 sec) session2:测试操作锁的情况 root@127.0.0.1 : test 03:22:44>delete from user where id = 33337;update user set username='TESAAST' where id = 44443;insert into user(id,username,company) values(1000,'ASD','ABCASDA'); Query OK, 1 row affected (9.58 sec) #直接被锁住了,等待session1释放了。 Query OK, 0 rows affected (0.00 sec) Rows matched: 1 Changed: 0 Warnings: 0 #同上 Query OK, 1 row affected (0.00 sec) #同上 session3:查看操作的锁情况: root@127.0.0.1 : (none) 03:22:45>pager grep "lock(s)" PAGER set to 'grep "lock(s)"' root@127.0.0.1 : (none) 03:23:20>show engine innodb status; 773 lock struct(s), heap size 80312, 50765 row lock(s), undo log entries 4433 1 row in set (0.02 sec) root@127.0.0.1 : (none) 03:23:28>show engine innodb status; LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s) 773 lock struct(s), heap size 80312, 50765 row lock(s), undo log entries 25383 1 row in set (0.06 sec) root@127.0.0.1 : (none) 03:23:32>show engine innodb status; LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s) 773 lock struct(s), heap size 80312, 50765 row lock(s), undo log entries 42464 1 row in set (0.01 sec)

从上面看出,刚好说明了2的情况:一开始就会锁定整张表的记录,不能进行任何操作,直到锁释放了。
总结:
类似"insert into tb select * from tbx" 的操作,最好确保tbx表不被做dml操作,不然很可能出现锁等待的情况。另:通过设置隔离级别:read committed & ROW(binlog_format)可以让dml和该语句并发操作。

session1:执行操作 root@127.0.0.1 : test 04:05:08>insert into uu select * from user order by company; Query OK, 49990 rows affected (14.09 sec) session2:测试操作锁的情况 root@127.0.0.1 : test 04:04:57>delete from user where id = 33318;update user set username='TESAAeST' where id = 44423;insert into user(id,username,company) values(1000,'ASD','ABCASDA'); Query OK, 0 rows affected (0.00 sec) Query OK, 0 rows affected (0.00 sec) Rows matched: 1 Changed: 0 Warnings: 0 Query OK, 1 row affected (0.00 sec) session3:查看操作的锁情况: root@127.0.0.1 : (none) 03:22:45>pager grep "lock(s)" PAGER set to 'grep "lock(s)"' root@127.0.0.1 : test 04:05:23>show engine innodb status; 1 lock struct(s), heap size 376, 0 row lock(s), undo log entries 6256 1 row in set (0.05 sec) root@127.0.0.1 : test 04:05:28>show engine innodb status; 1 lock struct(s), heap size 376, 0 row lock(s), undo log entries 32958 1 row in set (0.01 sec) root@127.0.0.1 : test 04:05:35>show engine innodb status; 1 lock struct(s), heap size 376, 0 row lock(s), undo log entries 33784 1 row in set (0.00 sec) root@127.0.0.1 : test 04:05:36>show engine innodb status; 1 lock struct(s), heap size 376, 0 row lock(s), undo log entries 34789 1 row in set (0.00 sec)

参考:
http://www.cnblogs.com/zhoujinyi/archive/2013/04/28/3049382.html
http://blog.csdn.net/muzizhuben/article/details/46533799
http://blog.chinaunix.net/uid-9950859-id-181376.html
关于MySQL insert into ... select 的锁情况的更多相关文章
- 实战演示疑惑 mysql insert到底加什么锁
innodb的事务隔离级别是可重复读级别且innodb_locks_unsafe_for_binlog禁用,也就是说允许next-key lock 实验来自网上. ( 如果你没有演示出来,请check ...
- insert into select * from 锁表
mysql[192.168.11.187] processid[249] root@localhost in db[zjzc] hold transaction time 197 112069858, ...
- MYSQL INSERT INTO SELECT 不插入重复数据
INSERT INTO `b_common_member_count` (uid) SELECT uid FROM `b_common_member` WHERE uid NOT IN (SELECT ...
- 【数据库系列】MySql中的select的锁表范围
由于InnoDB预设的是Row-Level Lock,只有明确指定主键的时候MySql才会执行Row lock,否则MySql将会执行Table Lock. 1.明确指定主键则是行锁 2.明确指定主键 ...
- mysql insert into select 语法
Insert into Table2(field1,field2,...) select value1,value2,... from Table1 这样就对了
- MySQL : INSERT INTO SELECT
INSERT INTO wx_announcement_push ( title, content, STATUS, del_flag, user_login_name ) SELECT '大家好', ...
- mysql insert锁机制【转】
最近再找一些MySQL锁表原因,整理出来一部分sql语句会锁表的,方便查阅,整理的不是很全,都是工作中碰到的,会持续更新 笔者能力有限,如果有不正确的,或者不到位的地方,还请大家指出来,方便你我,方便 ...
- 语法:MySQL中INSERT INTO SELECT的使用(转)
1. 语法介绍 有三张表a.b.c,现在需要从表b和表c中分别查几个字段的值插入到表a中对应的字段.对于这种情况,可以使用如下的语句来实现: INSERT INTO db1_name (fi ...
- MySQL复制表-INSERT INTO SELECT
基础table为: mysql> select * from staff; +----+----------+-------+ | id | name | slary | +----+----- ...
随机推荐
- 移动端meta标签缓存设置
1.<meta charset="utf-8"> 2.<meta content="width=device-width, initial-scale= ...
- python学习之路基础篇(第六篇)
一.算法 冒泡排序 两两比较 打的沉下去,小的浮上来 从而把数字从小到大排列出来 选择排序 随机取一个索引作为最大值,然后和列表中的其他索引进行比较,如果l[0]<l[1],则将l[1]修改为 ...
- delphi 线程教学第五节:多个线程同时执行相同的任务
第五节:多个线程同时执行相同的任务 1.锁 设,有一个房间 X ,X为全局变量,它有两个函数 X.Lock 与 X.UnLock; 有如下代码: X.Lock; 访问资源 P; ...
- makefile 和shell文件相互调用
shell 文件内调用makefile文件: #!/bin/bash cd ctemplate-2.1./configuresudo make -f installcd ../cd Templat ...
- Docker新手入门:基本用法
Docker新手入门:基本用法 1.Docker简介 1.1 第一本Docker书 工作中不断碰到Docker,今天终于算是正式开始学习了.在挑选系统学习Docker以及虚拟化技术的书籍时还碰到了不少 ...
- 每CPU变量
最好的同步技术是把设计不需要同步的临界资源放在首位,这是一种思维方法,因为每一种显式的同步原语都有不容忽视的性能开销.最简单也是最重要的同步技术包括把内核变量或数据结构声明为每CPU变量(per-cp ...
- Android图表库MPAndroidChart(二)——线形图的方方面面,看完你会回来感谢我的
Android图表库MPAndroidChart(二)--线形图的方方面面,看完你会回来感谢我的 在学习本课程之前我建议先把我之前的博客看完,这样对整体的流程有一个大致的了解 Android图表库MP ...
- JAVA面向对象-----接口的概述
接口的概述 **接口(interface):**usb接口,主要是使用来拓展笔记本的功能,那么在java中的接口主要是使用来拓展定义类的功能,可以弥补java中单继承的缺点. class Pencil ...
- 2.4、Android Studio使用主题编辑器设计主题
Android Studio包含一个叫主题编辑器的可视的助手,可以提供以下功能: 1. 创建和更改你的app主题 2. 为不同的资源适应主题 3. 普通的UI颜色更改的实时显示 主题编辑器 这一节描述 ...
- Android初级教程:屏幕分辨率
在app编码中经常需要获取手机的屏幕分辨率(宽*高),原来我直接上网拷贝代码,但在使用过程中却发现诸多不便. 不便一:下面代码中的getWidth和getHeight在adt上提示deprecated ...