MySQL中的锁

2023-11-08

数据库中的锁

锁分类

按锁的粒度划分:表级锁、行级锁、页级锁

按锁级别划分:共享锁、排它锁、意向锁

按加锁方式划分:自动锁、显示锁

按使用方式划分:乐观锁、悲观锁

MySQL中的行级锁、表级锁和页级锁

行级锁:行级锁分为共享锁和排他锁。行级锁是MySQL中锁定粒度最细的锁。InnoDB引擎支持行级锁和表级锁,只有在通过索引条件检索数据的时候,才使用行级锁,否就使用表级锁。行级锁开销大,加锁慢,锁定粒度最小,发生锁冲突的概率最低,并发度最高。

表级锁:表级锁分为表共享锁和表独占锁。表级锁开销小,加锁快,锁定粒度大,发生锁冲突最高,并发度最低

页级锁:页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折中的页级,一次锁定相邻的一组记录。BDB支持页级锁。开销和加锁时间界于表锁和行锁之间;会出现死锁。锁定粒度界于表锁和行锁之间,并发度一般。

MySQL中排它锁和共享锁

排它锁(exclusive lock)

排他锁又叫写锁,如果事务T对A加上排它锁,则其他事务都不能对A加任何类型的锁。获准排它锁的事务既能读数据,又能写数据

共享锁(share lock)

共享锁又叫读锁,如果事务T对A加上共享锁,则其他事务只能对A再加共享锁,不能加其他锁。共享锁的事务只能读数据,不能写数据。

意向锁

其实有排它锁和共享锁就足够了为什么还需要有意向锁,这里举一个比较形象的例子:

在mysql中有表锁,读锁锁表,会阻塞其他事务修改表数据。写锁锁表,会阻塞其他事务读和写。

  1. Innodb引擎又支持行锁,行锁分为共享锁,一个事务对一行的共享只读锁。排它锁,一个事务对一行的排他读写锁。

  2. 这两中类型的锁共存的问题考虑这个例子:事务A锁住了表中的一行,让这一行只能读,不能写。之后,事务B申请整个表的写锁。如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。

 

数据库要怎么判断这个冲突呢?

  • step1:判断表是否已被其他事务用表锁锁表

  • step2:判断表中的每一行是否已被行锁锁住。

注意step2,这样的判断方法效率实在不高,因为需要遍历整个表。于是就有了意向锁。在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。

在意向锁存在的情况下,上面的判断可以改成

  • step1:不变

  • step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。

注意:申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。

 

 

InnoDB的锁定模式实际上可以分为四种:共享锁(S)、排它锁(X)、意向共享锁(IS)和意向排它锁(IX),我们可以通过以下表来总结上面四种锁的共存逻辑关系:

如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。
意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;

数据库隔离级别

Read Uncommitted,读写均不使用锁,数据的一致性最差,也会出现许多逻辑错误。

Read committed,使用写锁,但是读会出现不一致,不可重复度

Repeatable Read,使用读锁和写锁,解决不可重复读的问题,但会有幻读

Serializable,使用事务串行化调度,避免出现因为插入数据没法加锁导致的不一致的情况

 

读不提交,造成脏读(Read Uncommitted)

一个事务中的读操作可能读到另一个事务中未提交修改的数据,如果事务发生回滚就可能造成错误。

例子:A打100块给B,B看账户,这是两个操作,针对同一个数据库,两个事物,如果B读到了A事务中的100块,认为钱打过来了,但是A的事务最后回滚了,造成损失。

避免这些事情的发生就需要我们在写操作的时候加锁,使读写分离,保证读数据的时候,数据不被修改,写数据的时候,数据不被读取。从而保证写的同时不能被另个事务写和读。

读提交(Read Committed)

我们加了写锁,就可以保证不出现脏读,也就是保证读的都是提交之后的数据,但是会造成不可重读,即读的时候不加锁,一个读的事务过程中,如果读取数据两次,在两次之间有写事务修改了数据,将会导致两次读取的结果不一致,从而导致逻辑错误。

可重复度(Repeatable Read)

解决不可重复读问题,一个事务中如果有多次读取操作,读取结果需要一致(指的是固定一条数据的一致,幻读指的是查询出的数量不一致)。 这就牵涉到事务中是否加读锁,并且读操作加锁后是否在事务commit之前持有锁的问题,如果不加读锁,必然出现不可重复读,如果加锁读完立即释放,不持有,那么就可能在其他事务中被修改,若其他事务已经执行完成,此时该事务中再次读取就会出现不可重复读,

可串行话(Serializable)

解决幻读问题,在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。幻读是由于并发事务增加记录导致的,这个不能像不可重复读通过记录加锁解决,因为对于新增的记录根本无法加锁。需要将事务串行化,才能避免幻读。

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争

 

MySQL默认的事务隔离级别为可重复读

间隙锁(Next-Key锁)

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的 索引项加锁;对于键值在条件范围内但并不存在的记录,叫做"间隙(GAP)",InnoDB也会对这个"间隙"加锁,这种锁机制就是所谓的间隙锁 (Next-Key锁)。 
举例来说,假如emp表中只有101条记录,其empid的值分别是 1,2,…,100,101,下面的SQL:

Select * from emp where empid > 100 for update;

是一个范围条件的检索,InnoDB不仅会对符合条件的empid值为101的记录加锁,也会对empid大于101(这些记录并不存在)的"间隙"加锁。

InnoDB使用间隙锁的目的,一方面是为了防止幻读,以满足相关隔离级别的要求,对于上面的例子,要是不使 用间隙锁,如果其他事务插入了empid大于100的任何记录,那么本事务如果再次执行上述语句,就会发生幻读;另外一方面,是为了满足其恢复和复制的需要。

很显然,在使用范围条件检索并锁定记录时,InnoDB这种加锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的锁等待。因此,在实际应用开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。

还要特别说明的是,InnoDB除了通过范围条件加锁时使用间隙锁外,如果使用相等条件请求给一个不存在的记录加锁,InnoDB也会使用间隙锁!下面这个例子假设emp表中只有101条记录,其empid的值分别是1,2,……,100,101。 

InnoDB存储引擎的间隙锁阻塞例子 

MVCC

MVCC(Multi-Version Concurrency Control)即多版本并发控制

MySQL的大多数事务型(如InnoDB,Falcon等)存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,他们一般都同时实现了MVCC。当前不仅仅是MySQL,其它数据库系统(如Oracle,PostgreSQL)也都实现了MVCC。MVCC并没有一个统一的实现标准,所以不同的数据库,不同的存储引擎的实现都不尽相同。

MVCC是通过在每行记录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。

下面看一下在REPEATABLE READ隔离级别下,MVCC具体是如何操作的。

SELECT

InnoDB会根据以下两个条件检查每行记录:

  1. InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。
  2. 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。

只有符合上述两个条件的记录,才能返回作为查询结果

INSERT

InnoDB为新插入的每一行保存当前系统版本号作为行版本号。

DELETE

InnoDB为删除的每一行保存当前系统版本号作为行删除标识。

UPDATE

InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。
保存这两个额外系统版本号,使大多数读操作都可以不用加锁。这样设计使得读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作,以及一些额外的维护工作

举例说明

transaction 1:

假设系统初始事务ID为1;

transaction 2:

SELECT

假设当执行事务2的过程中,准备执行语句(2)时,开始执行事务3:

transaction 3:

事务3执行完毕,开始执行事务2 语句2,由于事务2只能查询创建时间小于等于2的,所以事务3新增的记录在事务2中是查不出来的,这就通过乐观锁的方式避免了幻读的产生

UPDATE

假设当执行事务2的过程中,准备执行语句(2)时,开始执行事务4:

transaction session 4:

InnoDB执行UPDATE,实际上是新插入了一行记录,并保存其创建时间为当前事务的ID,同时保存当前事务ID到要UPDATE的行的删除时间

事务4执行完毕,开始执行事务2 语句2,由于事务2只能查询创建时间小于等于2的,所以事务修改的记录在事务2中是查不出来的,这样就保证了事务在两次读取时读取到的数据的状态是一致的

DELETE

假设当执行事务2的过程中,准备执行语句(2)时,开始执行事务5:

transaction session 5:

事务5执行完毕,开始执行事务2 语句2,由于事务2只能查询创建时间小于等于2、并且过期时间大于等于2,所以id=2的记录在事务2 语句2中,也是可以查出来的,这样就保证了事务在两次读取时读取到的数据的状态是一致的

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL中的锁 的相关文章

  • 绕过外键约束强制删除mysql

    我试图从数据库中删除除一个表之外的所有表 最终出现以下错误 无法删除或更新父行 外键约束失败 当然 我可以反复试验来看看这些关键约束是什么 并最终删除所有表 但我想知道是否有一种快速方法来强制删除所有表 因为我将能够重新插入那些我想要的表
  • 在 MySQL 中查找 varchar 是否包含百分号

    找不到这个问题的答案 在 MySQL 中 如何选择特定列 varchar 包含百分号 的行 where col like escape
  • MySQL 中电话号码的最佳数据类型是什么?它的 Java 类型映射应该是什么?

    我正在将 MySQL 与 Spring JDBC 模板一起用于我的 Web 应用程序 我需要存储仅包含数字的电话号码 10 我对使用数据类型的数据类型有点困惑 MySQL 中最适合它的数据类型是什么 为此 Bean POJO 类中的 Jav
  • MySQL:主键的所有部分都必须为 NOT NULL;如果您需要在键中使用 NULL,请使用 UNIQUE 代替

    我的 MySQL 有问题 我创建了名为 BucketList 的数据库 然后尝试创建名为 tbl user 的表 它看起来像这样 CREATE TABLE BucketList tbl user user id BIGINT NULL AU
  • MySQL 一起使用 LIKE、AND、OR

    我正在创建一个搜索功能来搜索一些图片 每张图片都有一个状态 表示它是被批准还是被拒绝 mysql 在返回之前检查状态 但是它仍然返回不应该返回的图像 这是我的查询 SELECT FROM Pictures WHERE ImageTitle
  • MySQL CREATE TABLE 语句上的外键错误(错误:150)

    我觉得我已经在一对非常简单的创建表语句上尝试了一切可能的方法 类型匹配 我尝试使用 ENGINE InnoDB 等 但很困惑为什么我收到外键错误 我已经离开 SQL 一段时间了 所以这可能是一个简单的问题 mysql gt CREATE T
  • 如何在 MySQL 中存储工作日列表?

    我正在使用编写一个应用程序PHP我需要存储一个独特的工作日列表MySQL 在应用程序中 我有一个数组来存储工作日 如下所示 days Wed Thu Sat 我知道我可以使用SET列 但我不想使用这种类型 因为它与我正在使用的框架 Lara
  • 如何向 MySQL 中的 ENUM 类型列添加更多成员?

    MySQL 参考手册没有提供关于如何执行此操作的明确示例 我有一个 ENUM 类型的国家 地区名称列 我需要向其中添加更多国家 地区 实现此目的的正确 MySQL 语法是什么 这是我的尝试 ALTER TABLE carmake CHANG
  • FIND_IN_SET 具有多个值[重复]

    这个问题在这里已经有答案了 我想从数据库字段搜索多个值 以下是我的查询 SELECT FROM tablename WHERE FIND IN SET 12 13 15 15 category id 我如何搜索它对我不起作用 FIND IN
  • PDO 如何在执行 rollBack() 函数之前回滚查询?

    这是我的脚本 try dbh con gt beginTransaction stmt1 dbh conn gt prepare UPDATE activate account num SET num num 1 stmt1 gt exec
  • MySQL中Join同表临时表

    我喜欢在 MySQL 中加入一个失败的临时表 这个想法很简单 CREATE TEMPORARY TABLE temp table LIKE any other table srsly it does not matter which tab
  • 使用python中的mysql连接器正确从mysql数据库获取blob

    当执行以下代码时 import mysql connector connection mysql connector connect connection params here cursor connection cursor curso
  • 按组内顺序排序

    order by 在第二个查询中不起作用 我需要先按 DNAID 订购 然后按 DNBID 订购 首先查询其顺序为 111221 第二个查询的顺序为 112112 有关我想要完成的任务的更多信息和细节https stackoverflow
  • Mysql:my.cnf中的修改不生效

    我已经更新了my cnf我的数据库文件包含以下行 max connections 200 之后我停止并启动 mysql 服务以使更改生效 但由于某种原因 此更改不会影响数据库 因为如果我运行 mysql gt select max conn
  • 我应该将标签存储在文本字段还是单独的表中?

    我有一个表 其中的行如下所示 id path tags 1 pictures pic1 jpg car bmw 3 pictures pic2 jpg cat animal pussy 4 pictures pic3 png gun 基本上
  • 如何将Hive数据表迁移到MySql?

    我想知道如何将日期从 Hive 转移到 MySQL 我看过有关如何将 Hive 数据移动到 Amazon DynamoDB 的示例 但没有看到有关如何将 Hive 数据移动到 MySQL 等 RDBMS 的示例 这是我在 DynamoDB
  • varchar(20) 和 varchar(50) 相同吗?

    我看到评论 如果 varchar 20 列中有 5000 万个 10 到 15 个字符之间的值 而 varchar 50 列中有同样的 5000 万个值 它们将占用完全相同的空间 这就是重点varchar 而不是 char 有人可以告诉我原
  • 创建用于存储高尔夫球成绩的可扩展数据库架构

    我正在尝试设计一个数据库来存储我所有的朋友和我的高尔夫球成绩 您可能知道 高尔夫得分由 18 洞的个人得分组成 我可以想到两种设计模式的方法 创建一个表 每个洞有一列 例如 h1 到 h18 该表具有引用其他表的 FK player id
  • 编写多个mysql脚本

    是否可以在复合脚本中包含其他 mysql 脚本 理想情况下 我不想为包含的脚本创建存储过程 对于较大的项目 我想分层维护几个较小的脚本 然后根据需要组合它们 但现在 我很乐意学习如何包含其他脚本 source是一个内置命令 您可以在 MyS
  • 使用外部硬盘写入和存储 mysql 数据库

    我已经设置了 mysql 数据库在我的 Mac 上使用 java 和 eclipse 运行 它运行得很好 但现在我将生成大约 43 亿行数据 这将占用大约 64GB 的数据 我存储了大量的密钥和加密值 我有一个 1TB 外部我想用作存储位置

随机推荐

  • C++继承内存对象模型

    最近研究了一下 C 继承的内存对象模型 主要是读了读http blog csdn net haoel article details 3081328 C 对象的内存布局 很推荐这篇文章 对这篇文章做了做总结 本文的大部分内容来自于这篇文章中
  • oracle归档日志的概念,浅谈Oracle归档日志

    什么是归档日志 归档日志 Archive Log 是非活动的重做日志备份 通过使用归档日志 可以保留所有重做历史记录 当数据库处于ARCHIVELOG模式并进行日志切换式 后台进程ARCH会将重做日志的内容保存到归档日志中 当数据库出现介质
  • 使用jstl时出错

    报错描述 在maven项目中配置了jstl工具后 并在启动项目时出现如下问题 报错原因 第一次报错原因 pom xml文件中对jstl引用错误 第二次报错原因 没有加入standard jar 解决办法 在maven的依赖中写成如下配置
  • ts中lambda表达式_'Lambda表达式在Javac中的翻译'规范

    ts中lambda表达式 现在可以使用 javac中的Lambda表达式的翻译 规范的初稿 该规范旨在提出一种策略 用于将 Lambda Strawman 提议的各个部分从Java源代码转换为字节码 方法引用的伪语法将用 于转换为采用 方法
  • 深圳铨顺宏圆满落幕IOTE 2022第十八届国际物联网展

    11月15日 期待已久的IOTE 2022 第十八届国际物联网展在深圳会展中心 宝安新馆 正式拉开序幕 展商云集 盛大开幕 作为物联网行业一年一度的重大盛会 本届展会聚集了全国行业精英 涵盖了全球范围智慧仓储物流 档案信息化 智慧新零售 智
  • vue 解决Invalid Host header

    vue 解决Invalid Host header 在build webpack dev conf js文件中 加上disableHostCheck true 如下图
  • 路由器抓包工具TCPDUMP使用方式

    一 概述 tcpdump 用简单的语言概括就是dump the traffic on a network 是一个运行在linux平台可以根据使用者需求对网络上传输的数据包进行捕获的抓包工具 windows平台有sniffer等工具 tcpd
  • 关于如何提高自己的写程序的思维!

    工作有两种 一种是作法与程序已经确定 执行者只要依样画葫芦 就可完成工作 这是所谓的 标准操作 另一种则必须加进执行者的创意功夫 最常见的例子是 开发新产品如果没有创意 就不可能与众不同 如果你希望自己年薪百万以上 唯一方法就是养成工作时加
  • 为取经而来_唐僧在取经路上犯的哪个大错?原本两三年的路他却走了14年

    三世诸佛 十二部经 在人性中本自具有 不能自悟 需求善知识指示方见 唐朝盛年 为了取得经书 感化众人 唐僧师徒踏上了西天取经之路 路途遥远 责任重大 唐僧师徒在路上马不停蹄 从未有片刻休息 但是 唐僧犯了一个致命的错误 导致原本两三年的路他
  • Python之枚举类Enum定义错误码

    在 web 项目中 我们经常使用自定义状态码来告知请求方请求结果以及请求状态 在 Python 中该如何设计自定义的状态码信息呢 1 普通类 字典设计状态码 class RETCODE OK 0 ERROR 1 IMAGECODEERR 4
  • 循序渐进看Java web日志跟踪(1)-Tomcat 日志追踪与配置

    日志 是软件运行过程中 对各类操作中重要信息的记录 日志跟踪 不管对于怎么样的项目来说 都是非常重要的一部分 它关系到项目后期的维护和排错 起着举足轻重的作用 项目开发过程中 对日志的记录规则 也将影响到改项目后期维护的难度 在开发过程中
  • git常用操作及常见问题解决

    一 创建一个版本库 选择一个合适的地方 创建一个空目录 mkdir learngit cd learngit pwd root test learngit 通过git init 命令将这个目录变成git可以管理的仓库 root zrjdeb
  • java应用系统运行速度慢的解决方法

    场景 我们在部署了TOMCAT应用 刚刚开始启动的一个段时间内 访问系统的速度比较快 但是过了一段时间 应用系统就慢慢的变慢起来了 服务的访问加载时间慢慢变长 问题解决思路 1 查看部署应用系统的系统资源使用情况 CPU 内存 IO这几个方
  • jstack 命令

    NAME jstack Prints Java thread stack traces for a Java process core file or remote debug server SYNOPSIS jstack options
  • Python数据挖掘:利用聚类算法进行航空公司客户价值分析

    无小意丶 个人博客地址 无小意 知乎主页 无小意丶 公众号 数据路 shuju lu 刚刚开始写博客 希望能保持关注 会继续努力 以数据相关为主 互联网为辅进行文章发布 本文是 Python数据分析与挖掘实战 一书的实战部分 在整理分析后的
  • python : Numpy: numpy.c_

    numpy c numpy c
  • BigDecimal 除法运算提示:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact repres

    业务场景 今天在计算重点工程总数占比工程总数 百分比 的时候 遇到一个错误 java lang ArithmeticException Non terminating decimal expansion no exact repres 异常
  • lambda函数

    文章目录 一 定义 二 格式 一 定义 lambda函数是一种匿名函数 是一种通过单个语句生成函数的方式 其结果是返回值 使用lambda关键字定义 该关键字仅表达 我们声明一个匿名函数 的意思 二 格式 冒号前是参数 可以有多个 用逗号隔
  • flex-wrap 后内容高度被撑开

    问题 布局时出现换行后 高度异常 box height calc 100vh 100px background fff overflow auto padding 76px 0 0 17px display flex flex wrap w
  • MySQL中的锁

    数据库中的锁 锁分类 按锁的粒度划分 表级锁 行级锁 页级锁 按锁级别划分 共享锁 排它锁 意向锁 按加锁方式划分 自动锁 显示锁 按使用方式划分 乐观锁 悲观锁 MySQL中的行级锁 表级锁和页级锁 行级锁 行级锁分为共享锁和排他锁 行级