首页 > 数据库 >数据库系列:MySQL InnoDB锁机制介绍

数据库系列:MySQL InnoDB锁机制介绍

时间:2023-11-30 14:33:20浏览次数:45  
标签:记录 数据库 id 索引 InnoDB MySQL 主键

数据库系列:MySQL慢查询分析和性能优化
数据库系列:MySQL索引优化总结(综合版)
数据库系列:高并发下的数据字段变更
数据库系列:覆盖索引和规避回表
数据库系列:数据库高可用及无损扩容
数据库系列:使用高区分度索引列提升性能
数据库系列:前缀索引和索引长度的取舍
数据库系列:MySQL引擎MyISAM和InnoDB的比较
数据库系列:InnoDB下实现高并发控制
数据库系列:事务的4种隔离级别
数据库系列:RR和RC下,快照读的区别

1 背景

随着互联网的发展,高并发业务的盛行,MySQL InnoDB引擎的细粒度行锁,变成很核心的特性之一。
在并发高的情况下,如果使用不当,会导致严重的性能问题。比如细粒度行锁,是实现在索引记录上的,但如果没有命中索引,就回退化成表锁,那对性能是灾难的。
下面我们从索引角度出发, 介绍下MySQL InnoDB的锁机制。

2 InnoDB的索引回顾

Innodb中有2种索引:主键索引(也叫聚集索引 Clustered Index)、辅助索引(也叫非聚集索引 Secondary Index)。
主键索引: 每个表只有一个主键索引,b+树结构,叶子节点存储主键的值以及对应整条记录的数据,非叶子节点不存储记录的数据,只存储主键的值。
当表中未指定主键时,MySQL内部会自动给每条记录添加一个隐藏的rowid字段(默认4个字节)作为主键,用rowid构建聚集索引。聚集索引在MySQL中即主键索引。
辅助索引: 每个表可以有多个辅助索引,b+树结构,非聚集索引叶子节点存储字段(索引字段)的值以及对应记录主键的值,其他节点只存储字段的值(索引字段),这就是与聚集索引不同的地方。每个表可以有多个非聚集索引。

InnoDB的每一个表都会有聚集索引:

  • 假设表定义了PK,则PK就是聚集索引
  • 如果未定义PK,则第一个非空unique列即是聚集索引
  • 如果没有PK也没有非空unique列,InnoDB会创建一个隐含的row_id作为聚集索引使用

下图更形象说明这两种索引的区别,这边假设了一个存储4行数据的表。Id为主键索引,Name作为辅助索引,图中清晰的体现了聚簇索引和非聚簇索引的差异。
表中有四条记录:

5, Gates, Microsoft
7, Bezos, Amazon
11, Jobs, Apple
14, Elison, Oracle

image

InnoDB数据检索过程
上面的表中有2个索引:id作为主键索引,name作为辅助索引。
如果需要查询id=14的数据,只需要在左边的主键索引中检索就可以了。
如果需要搜索name='Ellison'的数据,需要2步:

  1. 先在辅助索引中检索到name='Ellison'的数据,获取id为14
  2. 再到主键索引中检索id为14的记录
    辅助索引这个查询过程在mysql中叫做回表,相对于主键索引多了第二步操作。

MyISAM数据检索过程

  1. 在索引中找到对应的关键字,获取关键字对应的记录的地址
  2. 通过记录的地址查找到对应的数据记录

对比发现:Innodb中最好是采用主键索引查询,这样只需要一次索引,如果使用辅助索引检索,涉及多一步的回表操作,比主键查询要耗时一些。
所以,InnoDB的普通索引,实际上会扫描两遍:
第1遍,由普通索引找到PK:检索到name='Ellison'的数据,获取id为14
第2遍,由PK找到行记录:即到主键索引中检索id为14的记录

对索引有兴趣的,可以参考作者的这几篇文章:
MySQL全面瓦解22:索引的介绍和原理分析
MySQL全面瓦解23:MySQL索引实现和使用
MySQL全面瓦解24:构建高性能索引(策略篇)

3 InnoDB 几种常见锁

★InnoDB默认的事务隔离级别为可重复读(Repeated Read, RR),我们当下的所有介绍都是基于这个隔离级别为前提的。

  • 记录锁(Record Locks):锁定单一行记录,InnoDB 使用记录锁来实现行级锁,这样允许多个事务并发访问不同的行。
  • 间隙锁(Gap Locks):InnoDB 的特性,用于锁定一个范围,但不包括实际的记录。这主要用于防止幻读(Phantom Reads)。
  • 临键锁(Next-Key Locks):InnoDB 存储引擎的一种锁定机制,在执行查询语句时,根据查询条件所锁定的一个范围。这个范围中包含有间隙锁和记录锁。它的设计目的是为了解决幻读(Phantom Reads)。

3.1 记录锁(Record Locks)

记录锁,它封锁索引记录,例如:

select * from table where id=5 for update;

它会在id=1的索引记录上加锁,以阻止其他事务插入,更新,删除id=1的这一行。

需要说明的是:

select * from table where id=5;

则是快照读(SnapShot Read),它并不加锁,快照读可以参考作者这篇文章:数据库系列:RR和RC下,快照读的区别

3.2 间隙锁(Gap Locks)

间隙锁,它封锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一条索引记录之后的范围。
延续上面的那个例子继续演示:

# 表结构
table (Id PK, Name , Company);

# 表中包含四条记录
5, Gates, Microsoft
7, Bezos, Amazon
11, Jobs, Apple
14, Elison, Oracle

执行SQL语句如下:

select * from table
    where id between 7 and 13 
    for update;

这样的话,会封锁数据的区间,以防止其他事务插入id=8的记录。
假设没有间隙锁,则可能够插入成功,而之前的select事务,会发现检索的结果集莫名多了一条记录,即幻影数据。
所以间隙锁主要目的用于防止幻读(Phantom Reads),避免其他事务在间隔中插入数据,导致 『不可重复读』。
如果把事务的隔离级别降级为读提交(Read Committed, RC),对,就是互联网最常用的隔离级别,间隙锁则会自动失效。

3.3 临键锁(Next-Key Locks)

临键锁(Next-Key Locks)是数据库管理系统InnoDB中的一种重要锁定机制。这种锁是查询时根据查询条件锁定的一个范围,这个范围包括间隙锁和记录锁,左开右闭,即不锁住左边界,但会锁住右边界。临键锁的主要设计目的是为了解决所谓的“幻读”问题。

# 左开右闭 示例
(-infinity, 1]
(1, 7]
(7, 9]
(9, +infinity]

依然沿用上面的例子,InnoDB引擎,RR隔离级别:

-- 创建一个示例表  
CREATE TABLE users (  
    Id INT PRIMARY KEY,  
    Name VARCHAR(255) NOT NULL,  
    Company VARCHAR(255) NOT NULL,  
);  
  
-- 插入一些示例数据  
INSERT INTO users (id, name, company) VALUES (1, 'Alice', 'ali');
INSERT INTO users (id, name, company) VALUES (2, 'Brand', 'tencent');
INSERT INTO users (id, name, company) VALUES (3, 'Charlie', 'baidu');
  
-- 开始一个事务,并使用临键锁查询数据  
START TRANSACTION;  
SELECT * FROM users WHERE id > 1 FOR UPDATE;  
  
-- 在另一个事务中尝试插入新数据,将会被阻塞直到第一个事务释放锁 
START TRANSACTION;  
INSERT INTO users (id, name, age) VALUES (4, 'David', 30);  
COMMIT;  
  
-- 第一个事务提交后,第二个事务可以继续执行插入操作  
COMMIT;

临键锁的主要目的,也是为了避免幻读(Phantom Read),在事务隔离级别为可重复读的情况下,InnoDB存储引擎默认使用临键锁。这种锁提供了一种有效的机制来保证在并发环境中数据的完整性和一致性。
如果把事务的隔离级别降级为RC,临键锁则也会失效。

4 总结

  • InnoDB的索引与行记录存储在一起,MyISAM则是通过索引的地址查找到对应的数据记录,效率低一些
  • InnoDB的聚集索引存储行记录,普通索引存储PK,所以普通索引要查询两次
  • 记录锁锁定索引关联的具体记录
  • 间隙锁锁定间隔,防止间隔中被其他事务插入
  • 临键锁锁定索引记录+间隔,防止幻读
  • elect...for update加锁的几种情况:
    • 主键字段:加行锁。
    • 唯一索引字段:加行锁。
    • 普通索引字段:加行锁。
    • 主键范围:加多个行锁。
    • 普通字段:加表锁。
    • 查询空数据:不加锁。
  • 行锁与表锁的区别
    • 如果事务1加了行锁,一直未释放锁,事务2操作相同记录,会一直等待直至超时。
    • 如果事务1加了表锁,一直未释放锁,事务2无论操作哪一行记录,都会一直等待直到超时

标签:记录,数据库,id,索引,InnoDB,MySQL,主键
From: https://www.cnblogs.com/wzh2010/p/17855987.html

相关文章

  • 达梦数据库,使用存储过程切分','分隔字段,生成多条数据
      CREATEORREPLACEPROCEDUREsplit_listASv_nameVARCHAR2(255);v_listVARCHAR2(255);v_valueVARCHAR2(255);v_posNUMBER;v_lengthNUMBER;BEGINFORrecIN(SELECTZLY_NAME,LIST_IDFROMyour_table)LOOPv_name:=re......
  • MySQL8.0存储引擎对比
    MySQL8.0存储引擎对比MySQL8默认支持的存储引擎有MEMORY,PERFORMANCE_SCHEMA,MyISAM,MRG_MYISAM,BLACKHOLE,CSV,ARCHIVE和InnoDB共8种存储引擎。其中InnoDB为MySQL8的默认存储引擎。存储引擎信息在数据库中的存储位置为:INFORMATION_SCHEMA下的ENGINES表。ENGINESUPPORTCOMMENT......
  • 【必读】开发者必备!轻松学习MySQL事务的使用方法
    在日常开发中我们经常会遇到需要同时处理多个操作的情况,比如在购物时,我们需要同时完成支付和更新库存两个操作。这时,如果其中一个操作失败了,我们就需要进行回滚,以保证数据的一致性。那么,如何在MySQL中实现这样的功能呢?答案就是——事务。下面我们就来介绍一下MySQL事务是什么?它......
  • 翻译:MySQL InnoDB Cluster - Navigating the Cluster
    本文是对这篇文章MySQLInnoDBCluster-NavigatingtheCluster[1]的翻译,翻译如有不当的地方,敬请谅解,请尊重原创和翻译劳动成果,转载的时候请注明出处。谢谢!当我们管理InnoDBCluster时,一件非常重要的事情就是了解集群处于什么样的状态,特别是要了解如何解释集群状态的报告,以及如何......
  • 部分MySQL的SQL信息整理
    模块补丁信息查看selectsuas补丁模块,count(1)as数量fromgsppatchlogwhereTO_DAYS(NOW())-TO_DAYS(deployedtime)<=300groupbysuorderby2descselectpatchcodefromgsppatchlogwhereTO_DAYS(NOW())-TO_DAYS(deployedtime)<=1orderby1......
  • CTP行情实时写入MySQL数据库
    CTP行情实时写入MySQL数据库 CTP即中国期货市场的交易系统,是一种可以实时获取行情数据的开放式软件平台。在这个平台上,我们可以订阅多种不同的市场行情数据,例如期货、股票、基金等等。而将这些大量的数据存入数据库,可以为量化交易策略提供必要的支持。 在这篇文章中,我们......
  • 不同数据库创建用户,数据库的SQL语句整理
    不同数据库创建用户,数据库的SQL语句整理MySQLmysql-uroot-p#输入密码登录数据库CREATEDATABASEIFNOTEXISTSxxxdata_someinfoDEFAULTCHARSETutf8mb4;createuser'xxx_someinfo'@'%'identifiedby'Testsomepassword';grantallprivilegesonxxxdata_......
  • 数据库简介
    数据库简介一、数据库的产生数据库技术是计算机科学中的一个重要分支,其产生源于数据管理的需求。随着计算机技术的不断发展,人们开始意识到需要一种高效、可靠的数据存储和管理方式来满足日益增长的数据处理需求。因此,数据库技术应运而生,并随着应用的不断扩展和深化,逐渐发展成为......
  • 数据库表的创建、数据类型、约束条件、增删改数据
    MYSQL数据库、表基本操作一、常用数据类型1.1、整型类型数据类型所占字节存储范围(有符号)存储范围(无符号)TINYINT1字节-128~1270~255SMALLINT2字节-32768~327670~65535MEDIUMINT3字节-8388608~83886070~16777215INT4字节-2147483648~21474836470~4294967......
  • basis of PHP DBMA about mysql
    php连接数据库header("Content-type:text/html;charset=utf8");$servername="localhost";$username="root";$password="root";$dbTable="dvwa";$conn=mysqli_connect($servername,$username,$password,$......