线上有一张大表table(id int pk,col1 ,col2),想更新其中若干列:
update table set col1='a' where col2>0
因为[col2>0]涉及到多行,说明条件必然不包含主键或者唯一键值。如果是RC隔离级别,只有行锁。而如果RR隔离级别,那么就有以下两种情况:
1.col2上有索引:行锁+范围锁
2.col2上不含索引:锁全表
为了防止长时间锁表(这里锁的含义是X锁,即排它锁。本来加了X锁就不能再加S锁了,也就是说不能读。但是mysql的MVCC可以采用乐观锁的方式读),最好的结果应该是行锁。
所以创建一个表table2来存放满足条件的数据:
insert into table2 select id from table where col2>0;
这样就可以拿着id去table更新了,这样就是行锁,不会影响其他行的写。
接下来就有两个选择了:
1.一条一条更新,每更新一条就commit一次。
2.用join进行批量更新。
两个方法共同点都是行锁,那么有其他区别吗?
一、整体锁时间肯定2小于1.但是2是连续的时间,而1是分散的时间,所以相对而言,对用户体验来说1会好一些。
二、如果被更新列col1上有索引的话,1的方式可能会不断地对索引打断裂变,而2的方式是一次性完成的,所以IO上2的性能更好。
三、2会写入更多的redo和undo,失败以后全部回滚,消耗也会更大。但这在业务上也是个优点,就是失败了再重新执行就好了,而单条的话还需要记录错误点。
所以我建议,采用一种折中的办法,就是把大批量切割成小批量执行。