Mysql的InnoDB存储引擎支持事务,默认是行锁。因为这个特性,所以数据库支持高并发,但是如果InnoDB更新数据的时候不是行锁,而是表锁的话,那么其并发性会大打折扣,而且也可能导致你的程序出错。
而导致行锁变为表锁的情况之一就是:
SQL的更新(update)或者删除(delete)语句中未使用到索引,导致在InnoDB在对数据进行相应操作的时候必须把整个表锁起来进行检索(表锁)。而如果使用了索引的话,InnoDB只会通过索引条件检索数据,而只锁住索引对应的行(行锁)。
下面记录一下我遇到的问题:
描述:
- 系统中有一个实时的定时任务,当有条件触发的时候,会更新对应的表,就先叫其为A表;
- 但是同时有一个任务有时候会对A表有写操作,因此当进行测试的时候,有时候会不固定的出“Lock wait timeout exceeded”的错误。
当出现这个问题的时候,从很多的地方进行了分析,然后都无法得到正确的解决方案(因为描述1模块不是我写的,所以没有去查看更新表的代码操作)
导致原因:
在描述1中定时任务更新表A的时候,更新条件中没有使用索引,导致当进行定时任务更新表的时候形成了表锁。然后因为表A数据量比较大,检索较慢,然后导致了描述2中对表A的写操作的等锁超时。
最常见的索引:
- 主键:众所周知,自带最高效的索引属性
- 唯一索引:属性值重复率为0,可以作为业务主键
- 普通索引:属性值重复率大于0,不能作为唯一指定条件
注意:对于普通索引,当“重复率”低时,甚至接近主键或者唯一索引的效果时,依然是行锁;但是如果“重复率”高时,Mysql不会把这个普通索引当做索引,即会造成一个没有索引的SQL,从而形成表锁。