锁表
前几天,有个表锁表了。
数据库是ORACLE,执行的语句是:
select * from t1 where a=1 for update;
update t1 set a =2 where a =1;
执行这两个语句的时候,一个是锁表,一个是误操作。
问题所在
- 开启了事务,然后执行了select for update强制加锁
- 没有及时释放事务,导致这个字段无法被查询
- 当无法获得锁的时候,其他的sql会锁定,进入到等待状态
不命中索引导致的锁表
经过查询,mysql的说法比较奇怪:
当执行的时候显式指定主键,则锁主键;如果没有指定主键,如果是二级索引,那么锁一定范围的数据。
如果没有索引,则锁全表。
逻辑上:如果是INNODB模式,肯定会维护一个主键索引。
当执行
select * from t1 where a=‘%张’ for update;
由于不是主键,所以基本上会锁一定范围的数据。如果没有索引或者不走索引,锁全表。
ORACLE基本上也是一样的:
- 如果不指定主键则锁全表。
- 如果是二级索引,那么锁一定范围的数据。
- 如果是主键,只锁指定行。
- 没有查到该条数据,则不锁表。
*
- 假如命中的是热点sql或者字典表,问题就大了
- 如果命中的是多个数据,那么被查询到的的所有数据会被锁
- 如果是RR级别,会加入间隙锁,插入数据命中这个范围的也会被锁
锁全表的话:
无法进行新增或者更新操作,所有新增和更新会等待锁释放。
所以会影响正常事务的运行。
commit没有及时提交
执行命令的时候,会断网导致没有上传COMMIT或者ROLLBACK
也就是说:当程序执行如下SQL的时候:
start transaction
select * from t1 where a=1 for update;
update t1 set a =2 where a =1;
commit;
由于系统断网,只上传了两个命令:
start transaction
select * from t1 where a=1 for update;
然后,这个事务就挂着事务把全表给锁了。
同时多表联查的select执行update。
这个是有很高风险的。
一个是:UPDATE会锁INSERT和UPDATE两个语句,而不是单锁UPDATE。
如果说UPDATE的时候按照二级主键进行加锁,而数据一个在表头一个在表位,或者新增的数据正好在间隙之间,就会锁表。