背景
群发短信业务对短信服务商发送状态回传进行处理时,导致死锁频繁发生。主要业务交互是,短信服务商每次最多将200个手机号的实际发送状态通过API接口进行回传,回传的手机号是不定序的。
原因
虽然在批量更新时对乱序的手机号进行了排序,但是忘记了对手机号加索引。
分析
MySQL的行锁是基于索引的,由于手机号没有索引,所以更新时的查询走的是全表扫描,即聚簇索引,此时会直接在聚簇索引上加锁。
update tb_record set state = 1 where tel = 'tel'
本例的事务隔离级别为【READ-COMMITTED】,在不同的事务隔离级别下会有不同的表现。
按照主键的顺序依次扫描,每取一行,则对该行进行加锁,然后判断是否满足查询条件,若不满足则释放锁,满足时不释放锁。
注意以上的加锁顺序是按照主键(即索引)顺序,因此,即使对手机号进行了排序,也是无法保证两个事务的加锁顺序是一致的。
出现事务一等待事务2,事务二等待事务一的循环等待回路,发生死锁。