作者:微信小助手
发布时间:2018-09-21T21:16:21
何为锁?锁在现实中的意义为:封闭的器物,以钥匙或暗码开启。在计算机中的锁一般用来管理对共享资源的并发访问,比如我们 Java 同学熟悉的 Lock,synchronized 等都是我们常见的锁。
为什么要懂数据库锁?
通常来说对于一般的开发人员,在使用数据库的时候一般懂点 DQL(select),DML(insert,update,delete)就够了。
小明是一个刚刚毕业在互联网公司工作的 Java 开发工程师,平常的工作就是完成 PM 的需求。
当然在完成需求的同时肯定逃脱不了 Spring,Spring MVC,Mybatis 的那一套框架,所以一般来说 SQL 还是自己手写,遇到比较复杂的 SQL 会从网上去百度一下。
对于一些比较重要操作,比如交易啊这些,小明会用 Spring 的事务来对数据库的事务进行管理,由于数据量比较小目前还涉及不到分布式事务。
前几个月小明过得都还风调雨顺,直到有一天,小明接了一个需求,商家有个配置项,叫优惠配置项,可以配置买一送一,买一送二等等规则。
当然这些配置是批量传输给后端的,这样就有个问题每个规则都得去匹配,他到底是删除还是添加还是修改,这样后端逻辑就比较麻烦。
聪明的小明想到了一个办法,直接删除这个商家的配置,然后全部添加进去。小明马上开发完毕,成功上线。
开始上线没什么毛病,但是日志经常会出现一些 mysql-insert-deadlock 异常。
由于小明经验比较浅,对于这类型的问题第一次遇见,于是去问了他们组的老司机大红。
大红一看见这个问题,然后看了他的代码之后,输出了几个命令看了几个日志,马上定位了问题,告诉了小明:这是因为 delete 的时候会加间隙锁。
但是间隙锁之间却可以兼容,但是插入新的数据的时候就会因为插入意向锁会被间隙锁阻塞,导致双方资源被互占,导致死锁。
小明听了之后似懂非懂,由于大红的事情比较多,不方便一直麻烦大红,所以决定自己下来自己想。
下班过后,小明回想大红说的话,什么是间隙锁,什么是插入意向锁,看来作为开发者对数据库不应该只会写 SQL 啊,不然遇到一些疑难杂症完全没法解决啊。想完,于是小明就踏上了学习 MySQL 锁这条不归之路。
什么是 InnoDB?
MySQL 体系架构
小明没有着急去了解锁这方面的知识,他首先先了解了下 MySQL 体系架构:
可以发现 MySQL 由连接池组件、管理服务和工具组件、SQL 接口组件、查询分析器组件、优化器组件、 缓冲组件、插件式存储引擎、物理文件组成。
小明发现在 MySQL 中存储引擎是以插件的方式提供的,在 MySQL 中有多种存储引擎,每个存储引擎都有自己的特点。
随后小明在命令行中打出了:
show engines \G;
一看原来有这么多种引擎。又打出了下面的命令,查看当前数据库默认的引擎:
show variables like '%storage_engine%';
小明恍然大悟:原来自己的数据库是使用的 InnoDB,依稀记得自己在上学的时候好像听说过有个引擎叫 MyIsAM,小明想这两个有啥不同呢?
马上查找了一下资料:
小明大概了解了一下 InnoDB 和 MyIsAM 的区别,由于使用的是 InnoDB,小明就没有过多的纠结这一块。
事务的隔离性
小明在研究锁之前,又回想到之前上学的时候教过的数据库事务隔离性,其实锁在数据库中其功能之一也是用来实现事务隔离性。而事务的隔离性其实是用来解决脏读,不可重复读,幻读几类问题。
一个事务读取到另一个事务未提交的更新数据。什么意思呢?
在事务 A,B 中,事务 A 在时间点 2,4 分别对 user 表中 id = 1 的数据进行了查询。
但是事务 B 在时间点 3 进行了修改,导致了事务 A 在 4 中的查询出的结果其实是事务 B 修改后的。这样就破坏了数据库中的隔离性。
在同一个事务中,多次读取同一数据返回的结果不同,不可重复读和脏读不同的是这里读取的是已经提交过后的数据。
在事务 B 中提交的操作在事务 A 第二次查询之前,但是依然读到了事务 B 的更新结果,也破坏了事务的隔离性。
一个事务读到另一个事务已提交的 insert 数据。
在事务 A 中查询了两次 id 大于 1 的,在第一次 id 大于 1 查询结果中没有数据,但是由于事务 B 插入了一条 id = 2 的数据,导致事务 A 第二次查询时能查到事务 B 中插入的数据。
事务中的隔离性:
小明注意到在收集资料的过程中,有资料写到 InnoDB 和其他数据库有点不同,InnoDB 的可重复读其实就能解决幻读了,小明心想:这 InnoDB 还挺牛逼的,我得好好看看到底是怎么个原理。
InnoDB 锁类型
小明首先了解了 MySQL 中常见的锁类型有哪些:
在 InnoDB 中实现了两个标准的行级锁,可以简单的看为两个读写锁:
S 共享锁:又叫读锁,其他事务可以继续加共享锁,但是不能继续加排他锁。
X 排他锁:又叫写锁,一旦加了写锁之后,其他事务就不能加锁了。
兼容性:是指事务 A 获得一个某行某种锁之后,事务 B 同样的在这个行上尝试获取某种锁,如果能立即获取,则称锁兼容,反之叫冲突。
纵轴是代表已有的锁,横轴是代表尝试获取的锁。
意向锁在 InnoDB 中是表级锁,和它的名字一样它是用来表达一个事务想要获取什么。
意向锁分为: