首先我们有三张表t1,t2,t3,它们都是只有两个字段, int类型的id和varchar类型的name;区别是t1没有索引,t2有主键索引,t3有唯一索引。

再强调一次,在实验前必须提前关闭自动提交,set autocommit=off。然后show variables like 'autocommit'查看自动提交是否是off。

我们先假设InnoDB的锁锁住了是一行数据或者一条记录。

1.假设锁住记录

1.1 实验一:没有索引的表(t1)

这个实验操作是操作没有索引的t1,t1里面有4条数据:1、2、3、4。

现在我们在两个会话里面手工开启两个事务。在第一个事务里面,我们通过where id =1 锁住第一行数据。在第二个事务里面,我们尝试给id=3的这一行数据加锁,大家觉得能成功吗?

Transaction1 Transaction2
Begin;  
SELECT * FROM t1 where id=1 FOR UPDATE;  
  Begin;
  SELECT * FROM t1 where id=3 FOR UPDATE; // BLOCKED
  INSERT INTO t1 (id, name) VALUES (5, ‘5’); // BLOCKED

这就有点奇怪了,第一个事务锁住了id=1的这行数据,为什么我不能操作id=3的数据呢?我们再来操作一条不存在的数据,插入id=5。它也被阻塞了。实际上这里整张表都被锁住了。所以,我们的第一个猜想被推翻了,InnoDB的锁锁住的应该不是Record。

那为什么在没有索引或者没有用到索引的情况下,会锁住整张表?这个问题我们先留在这里。下面继续看第二个实验。

1.2 实验二:有主键索引的表(t2)

我们先看一下t2的表结构。字段是一样的,不同的地方是id上创建了一个主键索引。里面的数据是 1、4、7、10。

Transaction1 Transaction2
Begin;  
SELECT * FROM t2 where id=1 FOR UPDATE;  
  Begin;
  SELECT * FROM t2 where id=1 FOR UPDATE; // BLOCKED
  SELECT * FROM t2 where id=4 FOR UPDATE; // OK

第一种情况,使用相同的id值去加锁,冲突;使用不同的id加锁,可以加锁成功。

那么出现问题了,从实验一中得到锁定的不是一行数据,但是实验二操作不同记录的数据又可以成功,那有没有可能是锁住了id的这个字段呢?

2.假设锁住字段

我们看一下 t3 的表结构。字段还是一样的, id上创建了一个主键索引,name上创建了一个唯一索引。里面的数据是1、4、7、10。

在第一个事务里面,我们通过name字段去锁定值是4的这行数据。在第二个事务里面,尝试获取一样的排它锁,肯定是失败的,这个不用怀疑。在这里我们怀疑InnoDB锁住的是字段,所以这次我换一个字段,用id=4去给这行数据加锁,大家觉得能成功吗?

Transaction1 Transaction2
Begin;  
SELECT * FROM t3 where name=‘4’ FOR UPDATE;  
  Begin;
  SELECT * FROM t3 where name=‘4’ FOR UPDATE; // BLOCKED
  SELECT * FROM t3 where id=4 FOR UPDATE; // BLOCKED

很遗憾,又被阻塞了,说明锁住的是字段的这个推测也是错的,否则就不会出现第一个事务锁住了name,第二个字段锁住id失败的情况。

既然锁住的不是record,也不是column, InnoDB里面锁住的到底是什么呢?

3.其实,锁的是索引

在这三个案例里面,我们要去分析一下他们的差异在哪里,也就是这三张表的结构,是什么区别导致了加锁的行为的差异?其实答案就是索引。 InnoDB的行锁,就是通过锁住索引来实现的

那索引又是个什么东西?为什么它可以被锁住?我们在 【MySQL】详谈索引存储结构推演过程 已经分析过了。那么我们还有两个问题没有解决:

问题一:为什么表里面没有索引的时候,实验一锁住一行数据会导致锁表?或者说,如果锁住的是索引,一张表没有索引怎么办?所以,一张表有没有可能没有索引?

  1. 如果我们定义了主键(PRIMARYKEY),那么 InnoDB 会选择主键作为聚集索引
  2. 如果没有显式定义主键,则 InnoDB 会选择第一个不包含有 NULL 值的唯一索引作为主键索引
  3. 如果也没有这样的唯一索引,则 InnoDB 会选择内置 6 字节长的 ROWID 作为隐藏的聚集索引,它会随着行记录的写入而主键递增

所以,实验一为什么锁表,是因为查询没有使用索引,会进行全表扫描,然后把每一个隐藏的聚集索引都锁住了。

问题二:实验二为什么通过唯一索引给数据行加锁,主键索引也会被锁住?

在辅助索引里面, 索引存储的是二级索引和主键的值。 比如name=4,存储的是name的索引和主键id的值4。而主键索引里面除了索引之外,还存储了完整的数据。所以我们通过辅助索引锁定一行数据的时候,它跟我们检索数据的步骤是一样的,会通过主键值找到主键索引,然后也锁定。

复杂官网的一句话,A record lock is a lock on an index record,是不是只有【记录锁】是锁索引,其他锁是锁住行或表?

回复:行锁、表锁是对于锁粒度而言的,是一对最广泛的概念。表锁的实现很好想,就是需要一个标志来记录当前有没有事务已经来操做表了。而行锁的实现是锁的索引,根据锁索引的范围又可以分为记录锁、间隙锁、临键锁。 比如说我们修改一个数据库已经有的记录,那直接锁相应索引就行(记录锁);再比如我们给一张有两条数据的表(id=1,id=10)进行范围查询并加锁 where id>2 and id < 5 for update,此时命中了一个不存在数据的区间(2,5),这时该锁哪?是锁两个索引之间的整个区间(1,10),这就是间隙锁。临键锁=记录锁+间隙锁,这里就不说了,可以看我的这篇文章 https://blog.csdn.net/weixin_43935927/article/details/109410420

【转】【MySQL】事务与锁(四):行锁到底锁住的是什么?记录?字段?索引?相关推荐

  1. MySql事务4种隔离级别以及悲观锁和乐观锁

    前言:在那鬼公司呆着发现自己居然把事务给搞明白了. 缘由:公司做的一个项目在进行首页内容显示的时候发现查询结果特别慢,有时候需要一到五分钟才能显示出结果.于是乎,我就顺着SQL语句查询慢的原因找了下去 ...

  2. MySQL 事务 | ACID、四种隔离级别、并发带来的隔离问题、事务的使用与实现

    文章目录 事务 ACID 并发带来的隔离问题 幻读(虚读) 不可重复读 脏读 丢失更新 隔离级别 Read Uncommitted (读未提交) Read Committed (读已提交) Repea ...

  3. mysql事务特性及四种隔离级别

    事务的特性:ACID 我刚才提到了事务的特性:要么完全执行,要么都不执行.不过要对事务进行更深一步的理解,还要从事务的 4 个特性说起,这 4 个特性用英文字母来表达就是 ACID. A,也就是原子性 ...

  4. MySQL事务的可串行化

    可串行化--SERIALIZABLE 事务的最高级别,在每个读的数据行上,加上锁,使之不可能相互冲突,因此,会导致大量的超时现象 设置b账户,事务的隔离级别 B账户,首先,将b账户的隔离级别设置为SE ...

  5. oracle查看被锁的行,查找被锁的表到底是哪一行被锁定了

    有一个人问,所以总结了一下. http://www.itpub.net/528153.html 简单的总结一下. 首先v$session中的这几个字段的含义分别为 row_wait_row#---被锁 ...

  6. mysql的锁机制(读锁,写锁,表锁,行锁,悲观锁,乐观锁,间隙锁)

    读锁和写锁 介绍 MyISAM表锁中的读锁和写锁 读锁(共享锁S): 对同一个数据,多个读操作可以同时进行,互不干扰.加锁的会话只能对此表进行读操作,其他会话也只能进行读操作.MyISAM的读默认是加 ...

  7. mysql锁(全局锁、表锁、行锁、页锁、排他锁、共享锁)

    mysql锁 简介 数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则. MySQL数据库由于其自身架构的特点,存在多种数据存储引擎,每种存储 ...

  8. MySQL锁机制详解-表锁与行锁

    文章目录 1. 数据库锁理论 2. 锁的分类 2.1 按数据操作的类型分类 2.2 按数据操作的颗粒度分类 3. 表锁的应用 3.1 表锁相关命令 3.2 给表加表共享读锁 3.3 给表加表独占写锁 ...

  9. [精选]MySQL的各种锁(表锁,行锁,悲观锁,乐观锁,间隙锁,死锁)

    不少人在开发的时候,应该很少会注意到这些锁的问题,也很少会给程序加锁(除了库存这些对数量准确性要求极高的情况下),即使我们不会这些锁知识,我们的程序在一般情况下还是可以跑得好好的.因为数据库隐式帮我们 ...

  10. MySQL中的锁(表锁、行锁)

    锁是计算机协调多个进程或纯线程并发访问某一资源的机制.在数据库中,除传统的计算资源(CPU.RAM.I/O)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所在有数 ...

最新文章

  1. layui表单提交使用form.on(‘submit(sub)‘,function (){}) 使用ajax请求时回调不执行的原因及解决方法
  2. 监控录像帮忙找回医院被偷的女婴
  3. mybatis08--关联查询多对一
  4. boost::math::tools::polynomial用法的测试程序
  5. html网页效果分析,熟手的html编写风格与原因分析_HTML/Xhtml_网页制作
  6. 配置gitlab通过smtp发送邮件
  7. session丢失php,PHP Session丢失无效问题总结
  8. PG中的几种数据类型转换方式
  9. 校友会2019中国大学计算机,校友会2019中国大学一流专业排名800强出炉,北大清华复旦前三...
  10. jinja Import
  11. android 访问web.py,Appium 测试 Android 时,python 用例调用 Webdriver.remote 后无回应
  12. wxPython--学习笔记
  13. apache 2.4.4 自动分割日志,按年月日生成
  14. roller java,月光软件站 - 编程文档 - Java - 修改ReadMorePlugin.java,使其支持中文标题(roller webblog)...
  15. 如何在WPS文字中插入打勾(叉)方框
  16. mysql金额分隔符_mysql分隔符
  17. 【C语言】一文带你简单了解C语言
  18. execution(表达式)总结 AOP
  19. 东方通应用服务器TongWeb的安装,使用,排错(不定时更新)
  20. three.js加载obj模型 键盘控制模型局部动作

热门文章

  1. 四维空间和五维空间N维空间遐想
  2. GAN 的推导、证明与实现。
  3. IOS 修改UIAlertController的按钮标题的字体颜色,字号,内容
  4. Spring的常见问题及答案
  5. 判断一个数是不是整数
  6. 国外好的软件测试网站
  7. 1-4 鸡兔同笼(算法竞赛经典入门)
  8. 高仿人人android梦想版终极源码发送,人人Android客户端梦想版发布
  9. java多属性的map_java - 具有多个参数的MapStruct QualifiedByName - 堆栈内存溢出
  10. l启动进程 linux,《日子》. linux 查看进程启动路径