热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据库技术:Mysql技术内幕之InnoDB锁的深入讲解

前言自7月份换工作以来,期间一直在学习mysql的相关知识,听了一些视频课,但是一直好奇那些讲师的知识是从哪里学习的。于是想着从书籍中找答案。毕竟一直看视频也不是办法,不能形成自己

前言

自7月份换工作以来,期间一直在学习mysql的相关知识,听了一些视频课,但是一直好奇那些讲师的知识是从哪里学习的。于是想着从书籍中找答案。毕竟一直

看视频也不是办法,不能形成自己的知识。于是想着看书汲取知识,看了几本mysql的相关书籍,包括《深入浅出mysql》《高性能mysql》《mysql技术内幕》,发现那些讲

师讲的内容确实都在书上有出现过,于是确信看书才是正确的汲取知识方式。本片主要记录了mysql的锁机制的学习。

1.什么是锁

锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(如cpu、ram、i/o等)的争用以外,数据也是一种供许多用户共享的资源。

如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。

相对其他数据库而言,mysql 的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制。比如,myisam和memory存储引擎采用的是表级锁(table-level

locking);bdb存储引擎采用的是页面锁(page-levellocking),但也支持表级锁;innodb存储引擎既支持行级锁(row-levellocking),也支持表级锁,但默认情况下是采用行级锁。

mysql这3种锁的特性可大致归纳如下。

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

3种锁的使用角度:

  • 表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如web应用;
  • 行级锁则更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(oltp)系统。
  • bdb的页面锁已经被innodb取代,不做讨论。

2.innodb存储引擎中的锁

2.1锁的类型

innodb存储引擎实现了如下两种标准的行级锁:

  • 共享锁(s lock),允许事务读一行数据。
  • 排他锁(x lock),允许事务删除或更新一行数据。

如果一个事务t1已经获得了行r的共享锁,那么另外的事务t2可以立即获得行r的共享锁,因为读取没有改变行r的数据,称这种情况

为锁兼容(lock compatible)。但若有其他的事务t3想获得行r的排他锁,则其必须等待事务t1、t2释放行r的共享锁——这种情况称为锁不兼容。

  x s
x 不兼容 不兼容
s 不兼容 兼容

此外,innodb存储引擎支持多粒度锁定,这种锁定允许事务在行级上锁和表锁上的锁同时存在。为了支持在不同粒度上进行加锁操作,innodb存

储引擎支持一种额外的锁方式,称之为意向锁。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度上进行加锁。 ​ innodb存

储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的主要是为了在一个事务中揭示下一行将被请求的锁类型。其支持两种意向锁:

  • 意向共享锁(is lock),事务想要获得一张表中某几行的共享锁
  • 意向排他锁(ix lock),事务想要获得一张表中某几行的排他锁

2.2 一致性非锁定读

一致性的非锁定读(consistant nonlocking read)是指innodb存储引擎通过多版本控制(multi versioning)的方法来读取当前执行时间数据库中行的

数据。如果读取的行正在执行delete或update操作,这时读取操作不会因此去等待行上锁的释放。相反地,innodb存储引擎会去读取行的一个快照

版本。如下如所示。

Mysql技术内幕之InnoDB锁的深入讲解

上图直观地展现了innodb存储引擎一致性的非锁定读。之所以称为非锁定读,因为不需要等待访问的行上x锁的释放。快照数据是指该行的之前版本

的数据,该实现是通过undo段来完成。而undo用来在事务中回滚数据,因此快照数据本身是没有额外的开销。此外,读取快照数据是不需要上锁的,

因为没有事务需要对历史的数据进行修改操作。

通过上图可以知道,快照数据其实就是当前行数据之前的历史版本,每行记录可能有多个版本,一般称这种技术为行多版本技术。由此带来的并发控制,

称之为多版本并发控制(multi version concurrency control, mvcc)。

在事务隔离级别read committed和repeatable read下,innodb存储引擎使用非锁定的一致性读。然而,对于快照数据的定义却不相同。在read

committed事务隔离级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在repeatable read事务隔离级别下,对于快照

数据,非一致性读总是读取事务开始时的行数据版本。如下表所示示例:

时间 会话a 会话b
1 begin  
2 select * from t_user where id = 1;  
3   begin
4   update t_user set id = 10 where id = 1;
5 select * from t_user where id = 1;  
6   commit;
7 select * from t_user where id = 1;  
8 commit;  

假设原本id = 1的记录是存在的,大家可以按上表时间顺序执行对应的会话,比较及验证2者的不同。

2.3 一致性锁定读

在默认配置下,在事务的隔离级别为repeatable read模式下,innodb存储引擎的select操作使用一致性非锁定读。但是在某些情况下,用户需要显示地

对数据库读取操作进行加锁以保证数据逻辑的一致性。而这要求数据库支持加锁语句,即使时对于select的只读操作。innodb存储引擎对于select语句支持两

种一致性的锁定读(locking read)操作:

  • select ··· for update
  • select ··· lock in share mode

select ··· for update对读取的行记录加一个x锁,其他事务不能对已锁定的行加上任何锁。select ··· lock in share mode对读取的行记录加一个s锁,其他事务可

以向被锁定的行加s锁,但是如果加x锁,则会被阻塞。

对于一致性非锁定读,即使读取的行已被执行了select ··· for update,也是可以进行读取的。此外,select ··· for update或者select ··· lock in share mode必须在

一个事务中,当事务提交了,锁也就释放了。因此在使用上述两种select锁定语句时,务必加上begin,start transaction或者set autocommit=0。

3 锁的算法

3.1行锁的3中算法

innodb存储引擎有3种行锁的算法,其分别是:

  • record lock:单个行记录上的锁
  • gap lock:间隙锁,锁定一个范围,但不包含记录本身
  • next-key lock:gap lock + record lock,锁定一个范围,并且锁定记录本身

  record lock总是会去锁住主键索引记录,如果innodb存储引擎表在建立的时候没有设置任何一个主键或唯一非空索引,那么这时innodb存储引擎会使用隐式的

主键来进行锁定。

next-key lock是结合了gap lock+record lock的一种锁定算法,在next-key lock算法下,innodb对于行的查询都是采用这种锁定算法。假如一个索引有10,11

,13和20这4个值,那么该索引可能被next-key locking的区间为:

(-无穷,10] ,(10,11], (11,13], (13,20], (20,+无穷)

  采用next-key lock的锁定技术称为next-key locking。其设计的目的是为了解决幻读问题。而利用这种锁定技术,锁定的不是单个值,而是一个范围。 ​ 然而,

当查询的索引含有唯一属性时,innodb存储引擎会对next-key lock进行优化将其降级为record lock,即仅锁住索引本身,而不是范围。下面演示一个例子。

  mysql> create table t (a int primary key);  query ok, 0 rows affected (0.01 sec)  ​  mysql> insert into t select 1;  query ok, 1 row affected (0.00 sec)  records: 1 duplicates: 0 warnings: 0  ​  mysql> insert into t select 2;  query ok, 1 row affected (0.00 sec)  records: 1 duplicates: 0 warnings: 0  ​  mysql> insert into t select 5;  query ok, 1 row affected (0.01 sec)  records: 1 duplicates: 0 warnings: 0  

接着按下表时间顺序执行操作。

时间 会话a 会话b
begin;  
select * from t where a = 5 for update;  
  begin;
  insert into t select 4;
  commit; #成功,不需要等待
commit;  

表t共有1,2,5三个值。在上面的例子中,在会话a中首先对a=5进行x锁定。而由于a是主键且唯一,因此锁定的仅是5这个值,而不是(2,5)这个范围,这样在会话

b中插入值4而不会阻塞,可以立即插入并返回。即锁定由next-key lock算法降级为了record lock,从而提高应用的并发性。

如上,next-key lock降级为record lock仅在查询的列是唯一索引的情况下。若是辅助索引,则情况会完全不同。同样,首先创建测试表z进行测试:

  mysql> create table z (a int ,b int ,primary key(a), key(b));  mysql> insert into z select 1,1;  mysql> insert into z select 3,1;  mysql> insert into z select 5,3;  mysql> insert into z select 7,6;  mysql> insert into z select 10,8;  

表z的列b是辅助索引,若在会话a中执行下面的sql语句:

  mysql> select * from z where b = 3 for update;  

很明显,这时sql语句通过索引列b进行查询,因此其使用传统的next-key locking技术加锁,并且由于有两个索引,其需要分别进行锁定。对于聚集索引,其仅对列

a等于5的索引加上record lock。而对于辅助索引,其加上的是next-key lock,锁定的范围是(1,3),特别需要注意的是,innodb存储引擎还会对辅助索引下一个

键值加上gap lock,即还有一个辅助索引范围为(3,6)的锁。因此,若在新会话b中运行下面的sql语句,都会被阻塞:

  mysql> select * from z where a = 5 lock in share mode;  mysql> insert into z select 4,2;  mysql> insert into z select 6,5;  

第一个sql语句不能执行,因为在会话a中执行的sql语句已经对聚集索引中列a=5的值加上x锁,因此执行会被阻塞。第二个sql语句,主键插入4,没有问题,但是插入

的辅助索引值2在锁定的范围(1,3)中,因此执行同样会被阻塞。第三个sql语句,插入的主键6没有被锁定,5也不在范围(1,3)之间。但插入的值5在另一个锁定的

范围(3,6)中,故同样需要等待。而下面的sql语句,不会被阻塞,可以立即执行:

  mysql> insert into z select 8,6;  mysql> insert into z select 2,0;  mysql> insert into z select 6,7;  

从上面的例子可以看到,gap lock的作用是为了阻止多个事务将记录插入到同一个范围内,而这会导致幻读问题的产生。假如在上面的例子中,会话a中用户已经锁定了

b=3的记录。若此时没有gap lock锁定(3,6),那么用户可以插入索引b列为3的记录,这会导致会话a中的用户再次执行同样查询时会返回不同的记录,即幻读。

这里主要探究的是innodb存储引擎锁表的机制,至少自己明白了mysql的行锁机制,不知道读者是否有疑问,欢迎留言。下次会记录关于mysql事务特性及其内部的实现机制,

包括mysql的内部架构,innodb buffer pool,redo log, undo log等具体的详解,目前只是对知识过了一遍,但还未总结。

总结

到此这篇关于mysql技术内幕之innodb锁的文章就介绍到这了,更多相关mysql innodb锁内容请搜索<编程笔记>以前的文章或继续浏览下面的相关文章希望大家以后多多支持<编程笔记>!

需要了解更多数据库技术:Mysql技术内幕之InnoDB锁的深入讲解,都可以关注数据库技术分享栏目—编程笔记


推荐阅读
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • 本文介绍了解决Facebook脸书面试题中插入区间的方法,通过模拟遍历的方式判断当前元素与要插入元素的关系,找到插入点并将新区间插入。同时对算法的时间复杂度和空间复杂度进行了分析。 ... [详细]
author-avatar
fan9210729
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有