尽量申请自增 id 后,释放自增锁(上一篇)。
insert 轻量对普通才有效。有些 insert :其他资源加锁,申请自增 id 后,不能马上释放。
一、insert … select 语句
可重复读隔离级别binlog_format=statement:insert into? t2(c,d) select c,d from t;
所有行和间隙加锁,保证日志和数据一致性。执行序列:
B先执行, t 主键索引加了 (-∞,1] 这个 next-key lock,执行完A 的 insert 执行。
没有锁,B 的 insert先执行,后写入 binlog。binlog_format=statement ,binlog 里面就记录了这样的语句序列:
insert into t? values(-1,-1,-1);
insert into? t2(c,d) select c,d from t;
到了备库执行,把 id=-1 写到表 t2 中,主备不一致。
二、insert 循环写入
insert …select 只锁需访问资源,不是锁全表。
2.1?insert into?t2?扫描行数Rows_examined=1,受 limit 1 影响
insert into? t2(c,d)? (select c+1, d from t force? index(c) order by c desc limit 1);? ?//插入一行数据, c 值= c 最大值+ 1。
加锁范围:?索引 c 上 (3,4] 和 (4,supremum]这两个 next-key lock,主键索引 id=4 这行。
索引 c 倒序,扫描第一行,结果写入 t2 。
2.2 插入表 t :Rows_examined = 5? ,用临时表
insert into? t(c,d)? (select c+1, d from t force? index(c) order by c desc limit 1);?
Using temporary用临时表:t 内容读出,写入临时表。
rows =1,猜测:子查询结果读出来(扫描 1 行),写入临时表,从临时表读出来(扫描 1 行),写回表 t 中。扫描行数 2(不是5),猜测不对。
2.3 用临时表原因
边遍历边更新,读出直接写回原表,遍历过程,读到刚插记录(如果参与计算逻辑,跟语义不符)。
Innodb_rows_read增加 4。 Memory 全表扫描表 t。
1. 创建临时表,字段 c 和 d
2.? 索引 c 扫描表 t,依次取 c=4、3、2、1回表,读到 c 和 d 写入临时表。Rows_examined=4。
3.? ?limit 1,只取临时表第一行,插入 t 。Rows_examined加 1,= 5。
索引 c 上间隙都加上共享 next-key lock。其他事务不能insert。
2.4 优化办法
1:没在子查询中直接使用 limit 1,遍历整表 。应先 insert into临时表 temp_t,只需要扫描一行;取出插入t1。
2:数据量小,用内存临时表:
三、insert 唯一键冲突
可重复读(repeatable read)。 B insert 锁等待。
A 唯一键冲突,冲突索引上加锁。 next-key lock 由右边界值定义。 A 持有索引 c 上 (5,10] 共享 next-key lock(读锁)。
主键、唯一索引冲突加都 next-key lock。加读锁,避免这行被别的事务删掉。
3.1 经典死锁场景
session A rollback 时,C 几乎同时发现死锁并返回。
1.? T1 ,启动? A,insert 语句,索引c=5 加记录锁。c是唯一索引,退化为记录锁(如果你的印象模糊了,可以回顾下第 21 篇文章介绍的加锁规则)。
2.? T2 , B 相同 insert 语句,唯一键冲突,加读锁?; C 也在索引c 上,c=5 记录上加读锁。
3.? T3 时刻,A 回滚。 B 和 C 执行插入操作,加写锁。等待对方行锁,死锁。
四、insert into … on duplicate?key update
主键冲突后直接报错,如改写成
insert into t? values(11,10,10)?on?duplicate key update?d=100;? 给索引 c 上 (5,10]?加排他 next-key lock(写锁)。
插入碰到唯一键约束,执行后面更新语句。
多个列违反了唯一性约束,按照索引的顺序,修改跟第一个索引冲突的行。
已有 (1,1,1) 和 (2,2,2)
先判断主键 id 是的,跟 id=2 这一行冲突,修改 id=2 行。
需要注意的是,执行这条语句affected rows 返回的是 2,很容易造成误解。真正更新的只有一行,insert 和 update 都认为自己成功了,计数都加1
小结
insert … select 拷贝数据。可重复读, select 扫描记录的间隙加读锁。
insert 和 select 对象是同一个表,循环写入。引入用户临时表做优化。
insert 唯一键冲突,冲突唯一值上加共享 next-key lock(S 锁)。尽快提交或回滚事务,避免加锁时间长。
问题
两个表之间拷贝数据什么方法,注意事项?优势?
下一篇文章。