InnoDB行锁有3种实现
1.record lock,锁住单行记录
2.gap lock,我们熟知的间隙锁,会锁住一个范围,但不包含记录本身
3.next -key lock,前两种锁的联合使用,不仅锁定范围,也锁定记录本身。
InnoDB默认的事务隔离级别是repeatable read,在该隔离级别下,采用第三种方式来加锁;事务级别在read committed下,采用第一种加锁方式。
下面简要说说锁的问题,做一下铺垫
1.第一个问题,脏读:简单来说,就是在不同的事务下,当前事务可以读到另外事务未提交的数据,这就是脏数据。脏读不怎么会发生,因为它发生的条件是事务隔离级别要为read uncommitted,而大部分数据库现在都是read committed;但是偶尔也会在mysq 的master-slave中发生
2.第二个问题,不可重复读:就是一个事务内两次读到不一样的数据,但是第二次读到的数据却是已经commit过的,所以这其实也算正常的情况,这种情况下事务隔离级别是read committed。这里引出一个比较重要的问题,在InnoDB中,就是通过next -key lock(上边的第三种锁)来避免不可重复读,也就是phantom problem(幻读)。在next -key lock下,锁住的不仅是索引,还会锁住索引覆盖的范围,所以就不会出现当前事务执行过程中,所要查询的数据别另外一个事务更改并提交结果。nnoDB默认的事务隔离级别是repeatable read,使用next-key lock,就避免了幻读问题。
3.第三个问题,丢失更新:就是一个事务的更新操作会被另外一个事务的更新操作覆盖掉,这种情况理论上现在不会发生,因为即使是隔离级别最弱的read uncommitted也会在对数据进行更新数加上锁,所以数据库层面不会发生这种问题,但是在业务层却会发生这种问题,如银行转账,要避免这种问题,最好是让事务串行化执行。其实我们日常使用的数据库操作语言,select 、update、delete我们都不是很深入去了解就开始使用了,所以很大程度上,数据库保证了数据的安全性,但程序员却会犯很多错误,所以我的感触是,数据库真博大精深啊,看似简单的东西,如select语句,都有着深入的内涵,不了解其原理,发生问题了,都一脸懵逼。
说说关于死锁和锁升级
InnoDB处理死锁有两种方式,一种是超时,一种是超时图;前一种是被动检测死锁,后一种是主动检测,InnoDB对于一般的异常不会回滚,但会对死锁进行回滚,如果发生死锁的时候,或者检测到死锁即将发生时(超时图),会主动回滚一个事务,从而让其他事物执行下去。
锁升级就是将当前锁粒度变粗,如果认为锁是一种稀有资源(ms SqlServer,为每条记录产生锁对象),按照这种思路设计数据库时,可能就会发生将多个行锁升级为页锁或者表锁的情况,这种情况下,保护了系统资源,避免过多的系统资源别用于维护锁,这会提高效率。
而InnoDB不存在锁升级问题,因为它不是根据每条记录产生行锁的,而是根据事务访问的每个页对锁进行管理的,采用的是位图的方式,因此不管一个事物锁住的是页中一个记录还是多个记录,开销是一样的。
想想如果有1000行数据,SqlServer会对每一行产生锁对象,而InnoDB只会对数据页产生锁对象(一个数据页肯定存放好多个行记录),这种情况下,InnoDB实现锁的资源开销将是相当小的,所以SqlServer就会有锁升级的概念。
最后再多说一句,之前说的,SqlServer对于外键要手动加索引,而InnoDB会主动加索引,而且删除这个索引是不被允许的,这样也会大大减少InnoDB死锁的几率。
其实数据库上层因为标准一样,所以我们使用不同数据库的时候,如oracle、mysql、SqlServer时,感觉好像都一样,但只有深入实现中,才会发现各个厂家的思路真是不一样,所以产品在各种情况下的性能也就会有各种差异。
好啦,关于InnoDB锁和死锁这一块真的是博大精深,我也只是学习了一下皮毛,还有待深入学习,这是一个学习笔记,不喜勿喷,欢迎指正,共同进步!