本文主要是介绍Innodb 行锁与表锁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
行锁与表锁
InnoDB默认是行级别的锁,当有明确指定的主键时候,是行级锁。否则是表级别。
#for update的注意点
for update 仅适用于InnoDB,并且必须开启事务,在begin与commit之间才生效。
要测试for update的锁表情况,可以利用MySQL的Command Mode,开启二个视窗来做测试。
1、只根据主键进行查询,并且查询到数据,主键字段产生行锁。
begin;
select * from goods where id = 1 for update;
commit;
2、只根据主键进行查询,没有查询到数据,不产生锁。
begin;
select * from goods where id = 1 for update;
commit;
3、根据主键、非主键含索引(name)进行查询,并且查询到数据,主键字段产生行锁,name字段产生行锁。
begin;
select * from goods where id = 1 and name='prod11' for update;
commit;
4、根据主键、非主键含索引(name)进行查询,没有查询到数据,不产生锁。
begin;
select * from goods where id = 1 and name='prod12' for update;
commit;
5、根据主键、非主键不含索引(name)进行查询,并且查询到数据,如果其他线程按主键字段进行再次查询,则主键字段产生行锁,如果其他线程按非主键不含索引字段进行查询,则非主键不含索引字段产生表锁,如果其他线程按非主键含索引字段进行查询,则非主键含索引字段产生行锁,如果索引值是枚举类型,mysql也会进行表锁,这段话有点拗口,大家仔细理解一下。
begin;
select * from goods where id = 1 and name='prod11' for update;
commit;
6、根据主键、非主键不含索引(name)进行查询,没有查询到数据,不产生锁。
begin;
select * from goods where id = 1 and name='prod12' for update;
commit;
7、根据非主键含索引(name)进行查询,并且查询到数据,name字段产生行锁。
begin;
select * from goods where name='prod11' for update;
commit;
8、根据非主键含索引(name)进行查询,没有查询到数据,不产生锁。
begin;
select * from goods where name='prod11' for update;
commit;
9、根据非主键不含索引(stock)进行查询,并且查询到数据,stock字段产生表锁。
begin;
select * from goods where stock='1000' for update;
commit;
10、根据非主键不含索引(stock)进行查询,没有查询到数据,stock字段产生表锁。
begin;
select * from goods where stock='2000' for update;
commit;
11、只根据主键进行查询,查询条件为不等于,并且查询到数据,主键字段产生表锁。
begin;
select * from goods where id <> 1 for update;
commit;
11.1
begin;
select * from goods where id < 5 and id <> 1 for update;
commit;
这条查询仅仅会返回2 ~ 4之间的行,但是实际上获取了 1 ~ 4之间的行的排他锁。InnoDB会锁住第1行 ---- 底层存储引擎的操作是“从索引的开头开始获取满足条件id < 5的记录” ,服务器并没有告诉InnoDB可以过滤第1行的where条件, 需要MySQL服务器将存储引擎返回行以后再应用where过滤条件(Extra Using where)。
12、只根据主键进行查询,查询条件为不等于,没有查询到数据,主键字段产生表锁。
begin;
select * from goods where id <> 1 for update;
commit;
13、只根据主键进行查询,查询条件为 like,并且查询到数据,主键字段产生表锁。
begin;
select * from goods where id like '1' for update;
commit;
14、只根据主键进行查询,查询条件为 like,没有查询到数据,主键字段产生表锁。
begin;
select * from goods where id like '1' for update;
commit;
总结
1、InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。
2、由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。
3、当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。
4、即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。
5、检索值的数据类型与索引字段不同,虽然MySQL能够进行数据类型转换,但却不会使用索引,从而导致InnoDB使用表锁。通过用explain检查两条SQL的执行计划,我们可以清楚地看到了这一点。(此条本地验证后发现 数据库类型为varchar时 查询用 数字类型时用不了索引 。但数据库类型为int、 datetime时 查询类型为字符串 也可以走索引)
这篇关于Innodb 行锁与表锁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!