Mysql-锁机制

news2024/11/25 0:33:09

Mysql的锁机制

一、简介

锁是为了保证数据库中数据的一致性,使各种【共享资源】在被访问时变得【有序】而设计的一种规则。

tip MysQL中不同的存储引擎支持不同的锁机制:

  1. InoDB支持【行锁】,有时也会升级为表锁。
  2. MyIsam只支持表锁。

【表锁】:

  1. 特点:就是开销小、加锁快,不会出现死锁。
  2. 缺点:锁粒度大,发生锁冲突的概率小,并发度相对低。

【行锁】:

  1. 特点:就是开销大、加锁慢,会出现死锁。
  2. 锁粒度小,发生锁冲突的概率高,并发度高。

数据演示:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
  `age` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

INSERT INTO `user` VALUES (1, 'jack', 99);
INSERT INTO `user` VALUES (2, 'rose', 19);
INSERT INTO `user` VALUES (3, 'mike', 24);
INSERT INTO `user` VALUES (4, 'smith', 22);
INSERT INTO `user` VALUES (7, 'james', 16);
INSERT INTO `user` VALUES (8, 'haha', 22);
INSERT INTO `user` VALUES (9, 'zara', 21);


SET FOREIGN_KEY_CHECKS = 1;

二、InnoDB的锁类型

InnoDB的锁类型主要有读锁(共享锁)、写锁(排他锁)、意向锁和MDL锁。

1、s锁

1.1 概述

读锁(共享锁,shared lock)简称S锁。

一个事务获取了一个数据行的读锁,其他事务也能获得该行对应的读锁,但不能获得写锁,即一个事务在读取一个数据行时,其他事务也可以读,但不能对该数行增删改的操作。

简而言之:就是可以多个事务读,但只能一个事务写。

tip

读锁是共享锁,多个事务可以同时持有,当有一个或多个事务持有共享锁时,被锁的数据就不能修改。

1.2 示例

读锁是通过【select… lock in share mode】语句给被读取的行记录或行记录的范围上加一个读锁,让其他事务可以读,但是要想申请加写锁,那就会被阻塞。

事务一:

begin;
select * from `user` where id = 1 lock in share mode;

事务二:

begin;
update `user` set name = 'jack&rose' where id = 1;

卡住了,说明程序被阻塞,确实加了锁。

在这里插入图片描述

s锁是可以被多个事务同时获取的,我们在两个不同的事务中分别对同一行数据加上s锁,结果都可以成功,如下图:

在这里插入图片描述

2、x锁

2.1 概述

写锁,也叫排他锁,或者叫独占所,简称x锁(exclusive)。

一个事务获取了一个数据行的写锁,既可以读该行的记录,也可以修改该行的记录。但其他事务就不能再获取该行的其他任何的锁,包括s锁,直到当前事务将锁释放。【这保证了其他事务在当前事务释放锁之前不能再修改数据】。

简而言之:就是只能自己操作该行数据,其它事务都不行。

tip:

写锁是独占锁,只有一个事务可以持有,当这个事务持有写锁时,被锁的数据就不能被其他事务修改。

2.2 示例

(1)一些DML语句的操作都会对行记录加写锁。

事务一:

begin;
update `user` set name = 'jack&rose' where id = 2;

事务二:

begin;
update `user` set name = 'jack&rose' where id = 2;

卡住了,说明程序被阻塞,确实加了锁。但是,我们发现其他事务还能读,有点不符合逻辑,这是应为mysql实现了MVCC模型,后边会详细介绍。

在这里插入图片描述

(2)比较特殊的就是select for update,它会对读取的行记录上加一个写锁,那么其他任何事务不能对被锁定的行上加任何锁了,要不然会被阻塞。

事务一:

begin;
select * from `user` where id = 1 for update;

事务二:

begin;
update `user` set name = 'name&jack' where id = 1;

卡住了,说明加了锁了。

在这里插入图片描述

3)x锁是只能被一个事务获取,我们在两个不同的事务中分别对同一行数据加上x锁,发现后者会被阻塞,如下图:

在这里插入图片描述

3、记录锁(Record Lock)

记录锁就是我们常说的行锁,只有innodb才支持,我们使用以下四个案例来验证记录锁的存在:

1、两个事务修改【同一行】记录,该场景下,where条件中的列不加索引

事务一:

begin;
update `user` set age = 88 where name = 'jack';

事务二:

begin;
update `user` set age = 99  where name = 'jack';

发现事务二卡住了,只有事务一提交了,事务二才能继续执行,很明显,这一行数据被【锁】住了。

在这里插入图片描述

2、两个事务修改同表【不同行】记录,此时where条件也不加索引。

事务一:

begin;
update `user` set age = 99  where name = 'jack';

事务二:

begin;
update `user` set age = 99  where name = 'rose';

现事务二卡住了,只有事务一提交了,事务二才能继续执行,很明显,表被【锁】住了。

在这里插入图片描述

3、两个事务修改【同一行】记录,where条件加索引

事务一:

begin;
update `user` set age = 99  where id = 1;

事务二:

begin;
update `user` set age = 100  where id = 1;

现事务二卡住了,只有事务一提交了,事务二才能继续执行,很明显,这一行数据被【锁】住了。

在这里插入图片描述

4、两个事务修改同表【不同行】记录,此时where条件加索引。

事务一:

begin;
update `user` set age = 99  where id = 1;

事务二:

begin;
update `user` set age = 99  where id = 2;

发现都可以顺利修改,说明锁的的确是行。

在这里插入图片描述

证明:行锁是加在索引上的,这是标准的行级锁。

总结:只有在修改时候的条件加上了索引才会锁住改行数据-行锁,不然就是所著整张表-表锁

4、间隙锁(GAP Lock)

间隙锁帮我们解决了mysql在rr级别下的一部分幻读问题。

间隙锁是按照索引值的范围进行加锁,而不是按照行进行加锁,这意味着即时数据行在锁范围外,如果它们的索引值在锁范围内,它们也会被锁住。

间隙锁生成的条件:

1、A事务使用where进行范围检索时未提交事务,此时B事务向A满足检索条件的范围内插入数据。

2、where条件必须有索引。

事务一:

begin;
select * from `user` where id between 3 and 7 lock in share mode;

事务二:

begin;
insert into `user` value(5,'jacky',66);

发现卡住了,第一个事务会将id在3到7之间的数据全部锁定,不允许在缝隙间插入。

在这里插入图片描述

事务三:

begin;
insert into `user` values (10,'jacky',66);

插入一个id为10的数据,竟然成功了,因为10不在事务一的检索的范围。

在这里插入图片描述

5、记录锁和间隙锁的组合(next-key lock)

临键锁,是记录锁与间隙锁的组合,它的封锁范围,既包含【索引记录】,又包含【索引区间】。

注:临键锁的主要目的,也是为了避免幻读(Phantom Read)。如果把事务的隔离级别降级为RC,临键锁则也会失效。

6、MDL锁

MySQL 5.5引入了meta data lock,简称MDL锁,用于保证表中元数据的信息。在会话A中,表开启了查询事务后,会自动获得一个MDL锁,会话B就不可以执行任何DDL语句,不能执行为表中添加字段的操作,会用MDL锁来保证数据之间的一致性。

元数据就是描述数据的数据,也就是你的表结构。意识是在你开启了事务之后获得了意向锁,其他事务就不能更改你的表结构。MDL锁都是为了防止在事务进行中,执行DDL语句导致数据不一致。

7、死锁问题

发生死锁的必要条件有4个,分别为互斥条件、不可剥夺条件、请求与保持条件和循环等待条件:

  • 互斥条件,在一段时间内,计算机中的某个资源只能被一个进程占用。此时,如果其他进程请求该资源,则只能等待。
  • 不可剥夺条件,某个进程获得的资源在使用完毕之前,不能被其他进程强行夺走,只能由获得资源的进程主动释放。
  • 请求与保持条件,进程已经获得了至少一个资源,又要请求其他资源,但请求的资源已经被其他进程占有,此时请求的进程就会被阻塞,并且不会释放自己已获得的资源。
  • 循环等待条件,系统中的进程之间相互等待,同时各自占用的资源又会被下一个进程所请求。例如有进程A、进程B和进程C三个进程,进程A请求的资源被进程B占用,进程B请求的资源被进程C占用,进程C请求的资源被进程A占用,于是形成了循环等待条件,如图1-7所示。

如下:

在这里插入图片描述


InnoDB使用的是行级锁,在某种情况下会产生死锁问题,所以InnoDB存储引擎采用了一种叫作等待图(wait-for graph)的方法来自动检测死锁,如果发现死锁,就会自动回滚一个事务。

在这里插入图片描述

tip 避免死锁:在MySQL中,通常通过以下几种方式来减少死锁的概率和影响:

减少概率:

  1. 尽量控制事务的大小,减少事务持有锁的时间,尽早提交事务或尽快释放锁
  2. 合理设计索引,尽量缩小锁的范围。
  3. 尽量减少查询条件的范围,尽量避免间隙锁或缩小间隙锁的范围。
  4. 使用较短的事务和较小的锁
    在事务中按照固定的顺序获取锁
  5. 如果一条SQL语句涉及事务加锁操作,则尽量将其放在整个事务的最后执行。

避免范围扩大:

  • 尽量让数据表中的数据检索都通过索引来完成,避免无效索引导致行锁升级为表锁

减小影响:

  • 使用超时机制:在事务发送死锁时,使用超时机制自动回滚并释放锁

三、表锁

1、对于InnoDB表,在绝大部分情况下都应该使用【行级锁】,因为事务和行锁往往是我们之所以选择InnoDB表的理由。但在个另特殊事务中,也可以考虑使用表级锁。

  • 第一种情况是:事务需要更新【大部分或全部数据】,表又比较大,如果使用默认的行锁,不仅这个事务执行效率低,而且可能造成其他事务长时间锁等待和锁冲突,这种情况下可以考虑使用表锁来提高该事务的执行速度。

  • 第二种情况是:事务涉及多个表,比较复杂,很可能引起死锁,造成大量事务回滚。这种情况也可以考虑一次性锁定事务涉及的表,从而避免死锁、减少数据库因事务回滚带来的开销。

2、在InnoDB下 ,主动上表锁的方式如下:

lock tables `user` write,`user` read;
select * from `user`;
commit;
unlock tables;

使用时有几点需要额外注意:

  • 使用【LOCK TALBES】虽然可以给InnoDB加表级锁,但必须说明的是,表锁不是由InnoDB存储引擎层管理的,而是由其上一层MySQL Server负责的,仅当autocommit=0innodb_table_lock=1(默认设置)时,InnoDB层才能感知MySQL加的表锁,MySQL Server才能感知InnoDB加的行锁,这种情况下,InnoDB才能自动识别涉及表级锁的死锁;否则,InnoDB将无法自动检测并处理这种死锁。
  • 在用LOCAK TABLES对InnoDB加锁时要注意,事务结束前,不要用UNLOCAK TABLES释放表锁,因为UNLOCK TABLES会隐含地提交事务;COMMIT或ROLLBACK不能释放用LOCAK TABLES加的表级锁,必须用UNLOCK TABLES释放表锁,正确的方式见如下语句。
  • 表锁的力度很大,慎用。
  • 总结:
    1. 【lock tables】加锁:不是由InnoDB存储引擎层管理的,而是由其上一层MySQL Server负责的
    2. 【unlock tables】释放锁:commit和rollback不会释放锁,只有unlock tables才会释放锁,且会自动提交事务

四、从另一个角度区分锁的分类

1、乐观锁

概述:

乐观锁是一种乐观思想的锁,它假设数据不会同时被多个事务修改,因此不会像悲观锁那样直接阻塞等待锁的释放。

相反,乐观锁允许多个事务同时读取和修改相同的数据,但在提交更新操作时,会检查数据是否已被其他事务修改。如果数据已被修改,更新操作将被回滚。

乐观锁的实现原理:

通常是通过在数据表中添加一个版本号或时间戳字段,每次更新数据时,都会将版本号或时间戳字段的值加1。当其他事务尝试修改该数据时,会检查版本号或时间戳字段的值是否与当前数据相同,如果不同则更新操作将失败。

基于版本号的乐观锁的示例:

假设有一个订单表order,包含订单号、商品编号和商品数量三个字段,其中订单号为主键,版本号为字段version。

现在有两个事务T1和T2,分别想要修改订单号为001的商品数量,T1修改后的数量为10,T2修改后的数量为20。

初始状态下,订单号为001的商品数量为5,版本号为1。

T1读取订单号为001的商品数量和版本号,并将数量更新为10,版本号加1。

T2读取订单号为001的商品数量和版本号,发现版本号仍为1,因此将数量更新为20,版本号加1。

T1提交更新操作时,发现版本号已经不为1,而是2,说明订单已被T2修改过了,因此T1的更新操作会失败并回滚。

适用场景:

​ 乐观锁适用于并发读写较多的场景,可以有效地提高并发度和吞吐量,但也有一定的缺点,即可能存在更新冲突的问题。因此在实际应用中,需要根据具体情况选择适当的锁机制来保证数据的一致性和可靠性。

乐观锁:

乐观锁大多是基于数据【版本记录机制】实现,一般是给数据库表增加一个 “version” 字段。

读取数据时,将此版本号一同读出,

更新时,对此版本号加一。此时将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据

事务一:

select * from `user` where id = 1;

事务二:

select * from `user` where id = 1;
update `user` set score = 99,version = version + 1 where id = 1 and version = 1;
commit;

事务一:

update `user` set score = 100,version = version + 1 where id = 1 and version = 1;
commit;

发现更新失败,应为版本号被事务二、提前修改了,这使用了不加锁的方式,实现了一个事务修改期间,禁止其他事务修改的能力。

2、悲观锁

总结一句话:总有刁民想害朕

悲观锁依靠数据库提供的锁机制实现。

MySQL中的共享锁和排它锁都是悲观锁。数据库的增删改操作默认都会加排他锁,而查询不会加任何锁。此处不赘述。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/417784.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

实验6 TensorFlow基础

1. 实验目的 掌握TensorFlow低阶API,能够运用TensorFlow处理数据以及对数据进行运算。 2.实验内容 ①实现张量维度变换,部分采样等; ②实现张量加减乘除、幂指对数运算; ③利用TensorFlow对数据集进行处理。 3.实验过程 题目…

UE4 几种常见的项目优化方式

1.灯光范围优化 当屏幕某一块像素被多盏灯光所影响,那么也会拖慢帧率,可以打开灯光复杂度视图进行查看,屏幕上越红的地方灯光复杂度越高,尝试降低灯光半径可以解决: 2.材质纹素优化 有时候我们并不知道目标模型的…

Vue:自定义组件事件

一、直接在组件标签上绑定事件 1、关于组件的自定义事件,实现步骤: ①提供事件源(这个事件源是一个组件)②给组件绑定事件:v-on:事件名 或者 事件名 ③编写回调函数,将回调函数和事件进行绑定。 ④等…

NeRF:今年最火的AI技术

转载: https://mp.weixin.qq.com/s?__bizMzIxOTczOTM4NA&mid2247549727&idx2&snc59e18cb2bd8dfe34eafdc6e7cf9f4fd&chksm97d4e688a0a36f9ea0ed4c5a988a3802b4e9265655c5c0a6f208a396860f3842abae9be3a9a3&scene27 什么是NeRF? Ne…

springboot解决ajax跨域问题

方式1:使用CrossOrigin注解方式 CrossOrigin(origins "*") //解决跨域的关键注解,该注解可以标注在类上也可以标注在方法上域A&#xff1a;http://localhost/across_test/test.html 域A下的test.html <!DOCTYPE html> <html lang"en"> <h…

Three——一、详解基础场景搭建(结尾含完整代码)

速成Three.js——一、详解基础场景搭建(结尾含源码) 给模型添加标签时需要准标签部分 从本章开始会从最初的搭建场景模型开始到插入精灵图部分结尾&#xff0c;便于刚入门three而不知如何去学起的前端工程师去学习&#xff0c;这里可以学到场景搭建的基础知识&#xff0c;引入…

【微服务笔记12】微服务组件之Hystrix和OpenFeign结合使用

这篇文章&#xff0c;主要介绍微服务组件之Hystrix和OpenFeign结合使用。 目录 一、Hystrix和OpenFeign结合使用 1.1、引入依赖 1.2、openfeign启用hystrix 1.3、编写FeignClient接口 1.4、编写FeignClient实现类 1.5、编写Controller控制器 1.6、启动类启动OpenFeign …

Apache网页的优化,安全与防盗链

在企业中&#xff0c;部署Apache后只采用默认的配置参数&#xff0c;会引发网站很多问题&#xff0c;换言之默认配置是针对以前较低的服务器配置的&#xff0c;以前的配置已经不适用当今互联网时代。 为了适应企业需求&#xff0c;就需要考虑如何提升Apache的性能与稳定性&…

智慧水务系统-全域孪生驾驶舱-三维实景模型

平台概述 柳林智慧水务系统平台是以物联感知技术、大数据、智能控制、云计算、人工智能、数字孪生、AI算法、虚拟现实技术为核心&#xff0c;以监测仪表、通讯网络、数据库系统、数据中台、模型软件、前台展示、智慧运维等产品体系为支撑&#xff0c;以城市水资源、水生态、水…

LeetCode-152. 乘积最大子数组

目录思路动态规划题目来源 152. 乘积最大子数组 思路 这题跟LeetCode-53. 最大子数组和很像 最后把整个 dp 数组看一遍求最大值即可。因此状态转移方程可能是&#xff1a; dp[i] Math.max(dp[i-1]nums[i],nums[i]);说明&#xff1a;牢记状态的定义&#xff0c;一定以下标 i…

Android13 PMS是如何启动的?

作者&#xff1a;Arthas0v0 平常使用安卓实际就是在使用各种app&#xff0c;而下载的app实际是一个apk文件。这个apk文件的安装就交给了PackageManagerService来实现。PackageManagerService的启动也是在SystemServer中。这个过程比较长需要长一点的时间来理。 SystemServer.s…

2023-04-09 有向图及相关算法

有向图及相关算法 1 有向图的实现 有向图的的应用场景 社交网络中的关注互联网连接程序模块的引用任务调度学习计划食物链论文引用无向图是特殊的有向图&#xff0c;即每条边都是双向的 改进Graph和WeightedGraph类使之支持有向图 Graph类的改动WeightedGraph类的改动 2 …

肖 sir_就业课__004项目流程(H模型)

项目流程&#xff1a; 一、面试提问&#xff08;h模型&#xff09; 1、你说下你们公司测试流程&#xff1f; 2、给你一个需求你会怎么做? 3、你讲下你的工作&#xff1f; 4、谈谈你是如何去测试&#xff1f; 答案&#xff1a;h模型 要求第一人称来写 讲解简化文字流程&#x…

基于Python实现的深度学习技术在水文水质领域应用

目录 一、深度学习的基本概念和发展现状 二、Python系列开源软件包基本用法 三、前馈神经网络模型、原理和代码实现 四、前馈神经网络模型在水质、水位预测中的应用 五、卷积神经网络模原理、结构和应用&#xff0c;与支持向量机进行比较 六、循环神经网络原理与结构 七…

web 页面在浏览器运行eval性能分析和优化

公司有个低代码老项目&#xff0c;里面有一些自定义脚本运行使用了大量的eval 动态运行。 分析 网上对eval 的争论也非常激烈&#xff0c;大部分不建议使用&#xff0c;一些人观点是用不好才导致问题。 eval 是否真的存在效率问题&#xff1f; eval is evil 我们知道new Fun…

定时任务-常用的cron表达式

常用cron表达式例子&#xff1a; &#xff08;1&#xff09;0/2 * * * * ? 表示每2秒 执行任务 &#xff08;1&#xff09;0 0/2 * * * ? 表示每2分钟 执行任务 &#xff08;1&#xff09;0 0 2 1 * ? 表示在每月的1日的凌晨2点调整任务 &#xff08;2&#xff09;0 15 1…

宝塔设置PHP定时任务实战记录(定时任务、ajax异步刷新API、shell脚本、访问url)

文章目录项目需求生产环境一、php定时任务二、实战开发1.创建mysql数据表2.入库封装函数&#xff08;1&#xff09;封装入库&#xff08;2&#xff09;入库操作3.定时任务的实现&#xff08;1&#xff09;$ajax异步调用法&#xff08;2&#xff09;宝塔定时访问url&#xff08;…

http请求头部(header)详解

通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息。这两种类型的消息由一个起始行&#xff0c;一个或者多个头域&#xff0c;一个只是头域结束的空行和可 选的消息体组成。HTTP的头域包括通用头&#xff0c;请求头&#xff0c;响应头和实体头四个部分。每个…

OpenShift 4 - 通过 SSH 远程访问 OpenShift Virtualization 的虚拟机

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在支持 OpenShift 4.12 的 OpenShift 环境中验证 在《OpenShift 4 - 用 OpenShift Virtualization 运行容器化虚拟机 &#xff08;视频&#xff09;》一文中使用了 OpenShift 控制台直接访问运行在 OpenSh…

【Python实战】从架构设计到实现:一个Powerful的图书管理系统

&#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是Zeeland&#xff0c;全栈领域优质创作者。&#x1f4dd; CSDN主页&#xff1a;Zeeland&#x1f525;&#x1f4e3; 我的博客&#xff1a;Zeeland&#x1f4da; Github主页: Undertone0809 (Zeeland) (github.com)&…