mysql InnoDB加锁测试

以下测试基于mysql5.7,隔离级别为:REPEATABLE READ
建表语句如下:

create table mytest (
  a int primary key AUTO_INCREMENT,
  b int ,
  c int,
  d int,
  KEY idx_b (b),
  KEY idx_c (c),
  UNIQUE idx_d (d)

) ENGINE=InnoDB;
insert into mytest (b, c,d) VALUES (1,1,1),(3,1,3),(5,3,6),(7,6,10),(10,8,12);

表数据如下:

索引列加锁

以下测试省略开启事务的sql
设client 1,分别执行如下测试sql(只是写在一起但是分别执行,每条测试sql都会单独开启事务)

select * from mytest where b=3 for update ;#锁普通索引
select * from mytest where c=1 for update ;#锁普通索引
select * from mytest where d=3 for update ;#锁唯一索引
select * from mytest where b=10 for update ;#锁普通索引最大值
insert into mytest (b, c,d) value (11,4,13);#测试插入意向锁

client2,分别执行以下测试sql

1. select * from mytest where b=3 lock in share mode ;#冲突(select * from mytest where b=3 for update ;)
2. insert into mytest (b, c,d) value (2,9,17);#gap lock冲突(select * from mytest where b=3 for update ;),不冲突(select * from mytest where c=1 for update ;),说明只锁对应的辅助索引和主键索引
3. insert into mytest (b, c,d) value (3,9,17);#不冲突(select * from mytest where c=1 for update ;),说明只锁对应的辅助索引和主键索引
4. insert into mytest (a,b, c,d) value (2,3,9,18);#聚集索引冲突(select * from mytest where c=1 for update ;),说明只锁对应的辅助索引和主键索引
5. select * from mytest where d=3 for update ;#聚集索引冲突(select * from mytest where c=1 for update ;)
6. select * from mytest where b=3 for update ;#聚集索引冲突(select * from mytest where c=1 for update ;)
7. insert into mytest (b, c,d) value (4,9,13);#gap lock冲突(select * from mytest where b=3 for update ;)
8. insert into mytest (b, c,d) value (11,9,2);#不冲突(select * from mytest where b=3 for update ;),说明唯一索引没有gap lock
9. insert into mytest (b, c,d) value (12,9,13);#gap lock冲突(select * from mytest where b=10 for update ;)说明对于正无穷范围的插入也有gap lock

10. insert into mytest (b, c,d) value (12,5,14);#不冲突(insert into mytest (b, c,d) value (11,4,13)),说明插入意向锁对于事务同时插入同一个gap,只要位置不同就不会引起冲突
11. insert into mytest (b, c,d) value (11,4,13);#冲突(insert into mytest (b, c,d) value (11,4,13)),插入意向锁对于事务同时插入同一个gap,位置相同会引起冲突

括号内是对应的client1中的测试sql
使用以下sql语句查询事务状态。

select l.*, t.trx_state,t.trx_weight,t.trx_query,w.* from information_schema.INNODB_TRX t left join information_schema.INNODB_LOCKS l
  on t.trx_id=l.lock_trx_id left join information_schema.INNODB_LOCK_WAITS w
    on t.trx_id=w.requesting_trx_id;

对应状态如下图(序号对应client2测试sql的序号):

非索引列加锁

建表语句如上,drop 索引idx_b,对应client1测试sql如下:

select * from mytest where b=3 for update ;#drop索引后测试

clients测试sql:

1. select * from mytest where b=8 for update ;#聚集索引冲突(select * from mytest where b=3 for update ;)
2. select * from mytest where b=1 for update ;#聚集索引冲突(select * from mytest where b=3 for update ;)
3. select * from mytest where c=1 for update ;#聚集索引冲突(select * from mytest where b=3 for update ;)
4. insert into mytest (b, c,d) value (11,9,13);#聚集索引冲突(select * from mytest where b=3 for update ;)

对应状态如下图(序号对应client测试sql的序号):

结论

对于有索引的列,加锁方式为:

  • 如果对辅助索引加锁,也会锁住对应的聚集索引,而不会锁住其他辅助索引
  • 对于主键和唯一索引,只会锁住这条record,不会锁住前后gap
  • 对于某个索引值加锁,实际上对这个record以及前后两个gap都会加锁。假设索引列有(a,b,c)三个值,对b加锁实际上是对(a,b]以及[b,c)都会加锁
  • 事务插入到一个gap时获取的锁为插入意向锁,插入意向锁可以被多个事务同时获得,不同事务同时插入到同一个gap只要插入位置不同就不会引起冲突。

如果对非索引列加锁,mysql会对整张表的聚集索引全部上锁,并对所有聚集索引之间所有gap加锁,任何插入或更新都会造成锁等待,这种情况千万要避免。

原文地址:https://www.cnblogs.com/bo2256321/p/10503545.html

时间: 2024-11-13 19:50:25

mysql InnoDB加锁测试的相关文章

MySQL InnoDB加锁超时回滚机制(转)

add by zhj: 看来我对MySQL的理解还有待深入,水还是挺深的啊,MySQL给记录加锁时,可以通过innodb_lock_wait_timeout参数设置超时时间, 如果加锁等待超过这个时间,就会回滚,但回滚的话有两种方式:第一种:回滚当前加锁的这条语句:第二种:回滚整个事务.这两种方式是通过参数 innodb_rollback_on_timeout来控制的.如果是OFF,表示加锁超时回滚时,只回滚加锁超时的那条SQL语句:如果是ON,表示回滚整个事务.默认 是OFF.在<MySQL

Mysql Innodb Cluster测试

本文介绍mysql 8版本下的Innodb Cluster配置与测试过程,其核心还是mysql的组复制功能,通过使用mysql shell简化了组复制的配置过程,同时使用mysql route中间件实现了故障的自动转移以及读写分离等功能.之前测试mysql组复制的时候有提出过中间件的问题,mysql-route是个不错的解决方案,前文传送门:http://blog.51cto.com/ylw6006/1976829 一.环境介绍 操作系统:centos linux 7.2 64bitmysql社

[转]关于MYSQL Innodb 锁行还是锁表

关于mysql的锁行还是锁表,这个问题,今天算是有了一点头绪,mysql 中 innodb是锁行的,但是项目中居然出现了死锁,锁表的情况.为什么呢?先看一下这篇文章. 做项目时由于业务逻辑的需要,必须对数据表的一行或多行加入行锁,举个最简单的例子,图书借阅系统.假设 id=1 的这本书库存为 1 ,但是有 2 个人同时来借这本书,此处的逻辑为 Select   restnum from book where id =1 ;     -- 如果 restnum 大于 0 ,执行 update  U

MySQL InnoDB四个事务级别 与 脏读、不重复读、幻读

MySQL InnoDB事务隔离级别脏读.可重复读.幻读 希望通过本文,可以加深读者对ySQL InnoDB的四个事务隔离级别,以及脏读.不重复读.幻读的理解. MySQL InnoDB事务的隔离级别有四级,默认是"可重复读"(REPEATABLE READ). ·        未提交读(READUNCOMMITTED).另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读). ·        提交读(READCOMMITTED).本事务读取到的

使用mysql innodb 使用5.7的json类型遇到的坑和解决办法

---------------------------------------------- #查询JSON的某个字段 select data -> '$.Host' from temp #创建虚拟列 ALTER TABLE temp ADD host varchar(128) GENERATED ALWAYS AS (json_extract(data,'$.Host')) VIRTUAL; #给虚拟列创建索引 ALTER TABLE temp ADD INDEX index_temp_hos

MySQL InnoDB内存压力判断以及存在的疑问

本文出处:http://www.cnblogs.com/wy123/p/7259866.html(保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错误进行修正或补充,无他) 与其他数据一样,内存对数据库的性能有着至关重要的影响,MySQL InnoDB也一样通过内存来缓存数据,在访问数据的时候通过访问内存中缓存的数据来提高数据的访问效率.MySQL中通过show variables like 'Innodb_buffer_pool%'命令或者直接

MySQL 性能比较测试:MySQL 5.6 GA -vs- MySQL 5.5

时间:2013年11月07日 ⁄ 分类: 数据库技术文档 ⁄   我要吐槽发评论 MySQL 5.6 GA 发布了,毫无疑问,这是 MySQL 最棒的一个版本. 如果你还不清楚 MySQL 5.6 版本一长串的新特性和改进内容,可以从这里获得了解. 而我这篇文章的主要目的则是性能的测试. 我使用 Sysbench workloads (Read-Only/Read-Write) 来测试.下面是我的测试环境: 硬件配置: 服务器 : 32核 bi-thread (HT) Intel 2300Mhz

MySQL Innodb表导致死锁日志情况分析与归纳

发现当备份表格的sql语句与删除该表部分数据的sql语句同时运行时,mysql会检测出死锁,并打印出日志 案例描述在定时脚本运行过程中,发现当备份表格的sql语句与删除该表部分数据的sql语句同时运行时,mysql会检测出死锁,并打印出日志.两个sql语句如下:(1)insert into backup_table select * from source_table(2)DELETE FROM source_table WHERE Id>5 AND titleWeight<32768 AND

MySQL innoDB引擎锁机制(一) —— 行锁和表锁

我们都知道,MyISAM引擎使用的是表锁,而innoDB最小粒度为行锁.但在实际使用中我们有时发现就算我们操作的是不同行的数据,还是会发生锁表.我们先来看一个例子. session1开启事务并更新id=1的数据: session2开启事务,并更新id=2的数据,但session2被阻塞了: 不是说innoDB支持行锁吗,我们这里明明更新的不是同一条数据,为什么还会被阻塞.其实这是因为MySQL innoDB给数据加锁的方式和oracle不一样.oracle是给这条数据行加锁,而innoDB是给索