为什么开发人员必须要了解数据库锁?

作者:微信小助手

发布时间: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 中常见的锁类型有哪些:


S or X


在 InnoDB 中实现了两个标准的行级锁,可以简单的看为两个读写锁:

  • S 共享锁:又叫读锁,其他事务可以继续加共享锁,但是不能继续加排他锁。

  • X 排他锁:又叫写锁,一旦加了写锁之后,其他事务就不能加锁了。

兼容性:是指事务 A 获得一个某行某种锁之后,事务 B 同样的在这个行上尝试获取某种锁,如果能立即获取,则称锁兼容,反之叫冲突。


纵轴是代表已有的锁,横轴是代表尝试获取的锁。


意向锁


意向锁在 InnoDB 中是表级锁,和它的名字一样它是用来表达一个事务想要获取什么。


意向锁分为: