码迷,mamicode.com
首页 > 数据库 > 详细

MySQL-InnoDB锁(一)

时间:2019-08-24 18:37:51      阅读:137      评论:0      收藏:0      [点我收藏+]

标签:升级   alt   sha   可用性   完整   关闭   let   通过   adb   

本文主要记录InnoDB存储引擎中锁的关键点,下篇文章通过实例确认加锁的范围。

InnoDB中的锁

1. 锁提供数据完整性和一致性

2. InnoDB行级锁:共享锁(S)和排他锁(X)。

  为了支持多粒度锁定,InnoDB支持意向锁,该锁允许事务在行锁和表锁同时存在。包括意向共享锁(IS)和意向排他锁(IX)。

  意向锁将锁定的对象分为多个层次,意味着事务希望在更细粒度上进行加锁,如需要对页上的记录r加X锁,分别需要对数据库、表、页加意向锁IX,最后对记录r加X锁,其中任何一部分导致等待,该操作需要等待粗粒度锁的完成。

技术图片

3. 锁的查看方式

  通过show engine innodb status来查看,其中的transactions片段可以看到事务,其中包括锁等待。

  在information_schema架构下,有3个表记录了事务和锁相关的信息。分别是INNODB_TRX,INNODB_LOCKS,INNODB_LOCK_WAITS。(具体看书或博客)

4. 一致性非锁读

  非锁定读机制,是InnoDB存储引擎的默认设置,默认读取不会占用和等待表上的锁

  InnoDB存储引擎利用行多版本控制实现一致性非锁读,

  当读取的行正在加X锁DELETE或UPDATE时,读操作不会等待锁释放,会读取行的一个快照数据

  快照数据是指该行之前版本的数据,其通过undo段完成的,因为undo用于事务中回滚数据,因此快照数据没有额外的开销,并且读快照也不需要加锁

  每行记录可能有多个版本的快照,由此带来的并发控制,称为多版本并发控制(Multi Version Concurrency Control,MVCC)

  在READ COMMIT事务隔离级别下,非锁一致性读总是读取被锁定行的最新一份快照数据,故可能会读到其他事务的提交,违反了ACID的隔离性

  在REPEATALE READ事务隔离级别下,非锁一致性读总是读取事务开始时的行数据版本

5. 一致性锁定读

  除了默认非锁定一致性读,还可以显式加锁读,有2种一致性锁定读:

    SELECT .... FOR UPDATE    行记录加X锁

    SELECT .... LOCK INSHARE MODE     行记录加S锁

6. 自增长与锁

  对含有自增长值的表都有一个自增长计数器,插入时根据该自增长计数器加1赋予该增长列,通过AUTO-INC Locking实现,其为特殊的表锁机制

  并发插入时,该方式性能较差,一个事务需要等待另一个事务而阻塞

  5.1.22版本后,InnoDB提供了一种轻量级互斥量的自增长实现机制,提高插入性能

  InnoDB通过innodb_autoinc_lock_mode参数控制自增长模式,值有0\1\2

  自增长值的列必须是索引,同时必须是索引的第一个列

7. 外键和锁

  对一个外键列,如果没有显式对该列加索引,InnoDB会自动为其加一个索引,可以避免表锁(锁是对索引进行锁定的)

  对于外键值的插入或更新是首先需要查询父表中的记录,即SELECT 操作,会为父表的记录使用SELECT....LOCK IN SHARE MODE加一个S锁。而不是使用一致性非锁定读的方式,因为该方式在读取时可能读到的是行记录的快照,如果父表同时更改该记录,可能会发生数据不一致的问题

  如事务A中父表删除记录r(加X锁,未提交),事务B插入引用父表记录r的行

    若在父表加S锁,碰到父表已经加了X锁,则事务B阻塞等待;

    若使用一致性非锁定读,并在REPEATABLE READ事务隔离级别下,可以读到父表中的r记录,事务B插入成功。

    等到事务A提交后,就会出现父、子表数据不一致的情况。

锁的算法

1. InnoDB支持行级锁,还支持范围锁。有3种行锁实现:

  Record Lock:单行锁,锁住索引记录,如果表没有建立索引,会使用隐式的主键进行锁定

  Gap Lock:间隙锁,锁定一个范围,不包括记录本身

  Next-Key Lock: Record Lock + Gap Lock,锁定一个范围,包括记录本身。可以解决Phantom Problem,在默认的事务隔离级别下,REPEATABLE READ 采用该锁技术。

2. Next-Key Lock 的锁机制

(1)有索引情况

  当索引具有唯一属性时

    ① 查具体行时,Next-Key Lock会降级为Record Lock,即仅锁住索引本身,而不是范围;

    ② 查小于某值时,Next-Key Lock会锁定小于该值的所有行,并锁定到下一个键值的范围;

    ③ 查大于某值时,Next-Key Lock会锁定大于该值的所有行,并锁定到前一个键值的范围;

  当索引列为辅助索引,Next-Key Lock会为聚集索引和辅助索引分别加锁,聚集索引相等时在该索引上只加Record Lock,辅助索引正常加,聚集索引也是范围查找时,应该会加GAP锁。

 

  两种显式关闭Next-Key Lock的方式:事务隔离级别改为READ COMMITTED;设置innodb_locks_unsafe_for_binlog为1

(2)没有索引情况

  因为没有索引键值的时候,自动隐式创建索引会锁定整个区间。但依然是行锁而不是表锁,只是等价于表锁。

锁的问题

1. 脏读

  脏读,是事务读取到脏数据。脏数据是事务对缓冲池中行记录的修改,还没有被提交。如READ UNCOMMIT事务隔离级别,违反了事务隔离性

  脏页,是指事务提交后在缓冲池中已被修改的页,但还没有刷新到磁盘中,即数据库实例内存中的页与磁盘中的页数据不一致,当然当数据刷新到磁盘之前,日志都已经被写到重做日志文件中。

  对脏页的读取是正常的,因为脏页的刷新是异步的,内存中和磁盘最终会达到一致性。这种方式不影响数据的可用性,还可以带来性能的提高。

2. 不可重复读(幻读)

  在一个事务内两次查询得到的数据不一致的情况,为不可重复读。如READ COMMITTED事务隔离级别,违反事务一致性

  在默认的事务隔离级别下,REPEATABLE READ 采用Next-Key Lock锁技术,锁定索引范围,可避免该现象。

3. 丢失更新

  在READ UNCOMMIT事务隔离级别下,并发事务可以相互覆盖其他事务的更改,导致丢失更新。

死锁

  死锁是只两个或两个以上的事务在执行过程中,因争夺资源而造成的一种互相等待的现象

  解决死锁方式:

    ① 最简单的方式是超时,当事务等待时间超过时间阈值,进行回滚。根据FIFO的顺序选择回滚对象时,若超时的事务所占权重比较大,即更新操作很多行,占用较多的undo log,回滚该事务比回滚其他事务占用时间长,该方式就不合适。

    ② 等待图(wait-for graph),一种主动的死锁检测方式,要求数据库存储两种信息:锁的信息链表和事务的等待链表,前者记录当前事务加锁信息,后者记录事务的等待情况。

    通过这两张表可以构建一张事务等待图,当出现回路,表示存在死锁。此时,InnoDB选择回滚undo量最小的事务。 

锁升级

  InnoDB存储引擎不存在锁升级的问题。

  其不是根据每个记录来产生行锁的,而是根据每个事务访问的每个页对锁进行管理的(通过意向锁为行记录加锁),故事务锁住页中一个记录或多个记录,锁开销是一样的。

  对锁的管理是采用位图的方式(待研究),假如一张表有3 000 000(3M)个数据页,每页大约100条记录,共300 000 000条记录,若、假如一个事务对全表加X锁,若行锁根据每行记录进行锁定,每个锁占用10字节,则锁的管理就需要差不多3G内存。而InnoDB根据页加锁,采用位图的方式,假如每个页存储的锁信息占用30个字节,则所对象仅需30M的内存。

参考

《MySQL技术内幕-InnoDB存储引擎》

详细介绍MySQL/MariaDB的锁

 

MySQL-InnoDB锁(一)

标签:升级   alt   sha   可用性   完整   关闭   let   通过   adb   

原文地址:https://www.cnblogs.com/shuimuzhushui/p/11392245.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!