MySQL 事务原理分析

news2024/11/13 15:07:58

事务

  • 前提:有并发连接。
  • 定义:事务是用户定义的一系列操作,这些操作要么都做,要么都不做,是一个不可分割的单位。
  • 目的:事务将数据库从一种一致性状态转换为另一种一致性状态,保证系统始终处于一个完整且正确的状态。
  • 组成:事务可由一条非常简单的 SQL 语句组成,也可以由一组复杂的SQL 语句组成。
  • 特征:
    • 在数据库提交事务时,可以确保要么所有修改都已经保存,要么所有修改都不保存。
    • 事务是访问并更新数据库各种数据项的一个程序执行单元。
    • 在 MySQL innodb 下,单条 SQL 语句都具备事务,可以通过 set autocommit = 0,设置当前会话手动提交事务。
  • 事务控制语句
    -- 开启事务
    START TRANSACTION | BEGIN
    -- 提交事务,并使得已对数据库做的所有修改持久化
    COMMIT
    -- 回滚事务,结束用户的事务,并撤销正在进行的所有未提交的修改
    ROLLBACK
    -- 创建一个保存点,一个事务可以有多个保存点
    SAVEPOINT identifier
    -- 删除一个保存点
    RELEASE SAVEPOINT identifier
    -- 事务回滚到保存点
    ROLLBACK TO [SAVEPOINT] identifier
    

ACID 特性

  • 原子性(A)
    • 事务操作要么都做(提交),要么都不做(回滚),事务是访问并更新数据库各种数据项的一个程序执行单元,是一个不可分割的单位。
    • 通过 undo log 来实现回滚操作,undo log 记录事务的 DML 操作,当回滚时,回放事务 DML 操作的逆运算。
    • 在 MVCC 中,undo log 记录事务 DML 操作提交后产生的行数据版本信息。
  • 一致性(C)
    • 一致性指事务将数据库从一种一致性状态转变为下一种一致性的状态,在事务执行前后,数据库完整性约束没有被破坏。
      • 例如:一个表的姓名有唯一约束,如果一个事务对姓名进行修改,但是在事务提交或事务回滚后,表中的姓名变得不唯一了,这样就破坏了一致性。
    • 逻辑上的一致性是可以被破坏的。(设置不同程度的隔离级别适当地破坏逻辑上的一致性)
    • 一致性由原子性、隔离性以及持久性共同来维护。
  • 隔离性(I)
    • 隔离性表示各个事务之间相互影响的程度。
    • 目的:防止多个并发事务交叉执行导致数据不一致。
    • 通过设置不同程度的隔离级别,适当地破环逻辑上的一致性,从而提高性能。
    • 通过 MVCC 和 锁来实现。
      • MVCC:多版本并发控制,它不使用锁来限制读操作,从而实现高效并发读性能。
      • 锁用来处理并发 DML 操作,数据库中提供粒度锁的策略,针对表(聚簇索引 B+ 树)、页(聚簇索引 B+ 树叶子节点)、行(叶子节点当中某一段记录行)三种粒度加锁。
  • 持久性(D)
    • 事务一旦完成,要将数据所做的变更记录下来,包括数据存储和多副本的网络备份。
    • 事务提交后,事务 DML 操作将会持久化(写入 redo log 磁盘文件:哪一个页、页偏移值、具体数据),即使发生宕机等故障,数据库也能将数据恢复。 redo log 记录的是物理日志,确保内存数据的安全

隔离级别

  • 目的:提升 MySQL 并发处理 SQL 语句的性能。
  • ISO 和 ANIS SQL 标准制定了四种事务隔离级别的标准,MySQL innodb 默认支持的隔离级别是 repeatable read。
  • read uncommitted(读未提交)
    • 读操作不做任何处理。
    • 写操作加 X 锁,写锁在事务提交或回滚后释放。
  • read committed(读已提交)(RC)
    • 读操作使用 MVCC,读取最新版本的行数据。
    • 写操作加 X 锁。
  • repeatable read(可重复读)(RR)
    • 读操作使用 MVCC,读取事务开始前版本的行数据。
    • 写操作加 X 锁。
  • serializable(可串行化)
    • 读操作加 S 锁,所以事务都是串行化执行,此时隔离级别最严苛。
    • 写操作加 X 锁。
-- 设置隔离级别
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 或者采用下面的方式设置隔离级别
SET @@tx_isolation = 'REPEATABLE READ';
SET @@global.tx_isolation = 'REPEATABLE READ';
-- 查看全局隔离级别
SELECT @@global.tx_isolation;
-- 查看当前会话隔离级别
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;

-- 手动给读操作加 S 锁
SELECT ... LOCK IN SHARE MODE;
-- 手动给读操作加 X 锁
SELECT ... FOR UPDATE;
-- 查看当前锁信息
SELECT * FROM information_schema.innodb_locks;

不同隔离级别并发异常

  • 脏读
    • 事务 A 读到事务 B 未提交的数据,也就是事务 A 读到脏数据。
seqsession Asession B
1SET @@tx_isolation=‘READ UNCOMMITTED’;SET @@tx_isolation=‘READ UNCOMMITTED’;
2BEGIN;
3             UPDATE account_t SET money = money - 100 WHERE name = ‘A’;
4BEGIN;
5SELECT money FROM account_t WHERE name = ‘A’;
6COMMITCOMMIT
  • 不可重复读
    • 一个事务内两次读取同一个数据不一样
    • 一般而言,不可重复读的问题是可以接受的,因为读到已经提交的数据,一般不会带来很大的问题,所以很多厂商(如 Oracle、SQL Server)默认隔离级别就是 read committed。
seqsession Asession B
1SET @@tx_isolation=‘READ COMMITTED’;SET @@tx_isolation=‘READ COMMITTED’;
2BEGIN;BEGIN;
3SELECT money FROM account_t WHERE name = ‘A’;
4                UPDATE account_t SET money = money - 100 WHERE name = ‘A’;
5COMMIT;
6SELECT money FROM account_t WHERE name = ‘A’;
6COMMIT;
  • 幻读
    • 一个事务内两次读取同一个范围内的记录得到的结果集不一样快照读和当前读不一致
    • 在 repeatable read 隔离级别下通过读加锁解决。
seqsession Asession B
1SET @@tx_isolation=‘REPEATABLE READ’;SET @@tx_isolation=‘REPEATABLE READ’;
2BEGIN;BEGIN;
3SELECT * FROM account_t WHERE id >= 2;                
4INSERT INTO account_t(id,name,money) VALUES (4,‘D’,1000);
5COMMIT;
6INSERT INTO account_t(id,name,money) VALUES (4,‘D’,1000); # 报错,因为幻读
seqsession Asession B
1SET @@tx_isolation=‘REPEATABLE READ’;SET @@tx_isolation=‘REPEATABLE READ’;
2BEGIN;BEGIN;
3SELECT * FROM account_t WHERE id >= 2 lock in share mode;
4                INSERT INTO account_t(id,name,money) VALUES (4,‘D’,1000); # 等待执行
5INSERT INTO account_t(id,name,money) VALUES (4,‘D’,1000);# 等待执行
6COMMIT;# 报错,因为破坏了数据库完整性约束

MVCC

  • MVCC 是一致性非锁定读,也就是读不加锁。
  • 每一次开启事务的时候,MySQL 都会为其创建一个唯一的事务 id(长度为 64 位,并且一直递增)。
  • read view
    • m_ids:创建 read view 时,已启动但未提交的事务 id 列表。
    • min_trx_id:创建 read view 时,已启动但未提交的最小事务 id。
    • max_trx_id:创建 read view 时,预分配给下一个未开启事务的 id。
    • creator_trx_id:创建该 read view 的事务 id。
  • 聚簇索引记录的隐藏列
    • trx_id
      • 事务修改记录时,trx_id 记录该修改事务 id。
    • roll_pointer
      • 事务修改记录时,将旧记录写入 undo log,该指针指向旧版本记录。

在这里插入图片描述

  • 事务可见性问题
    • 事务可以看到事务本身的修改。
    • 事务间的可见性
      • trx_id < min_trx_id:已提交,可见。
      • trx_id >= max_trx_id:未启动,不可见。
      • min_trx_id <= trx_id < max_trx_id
        • trx_id in m_ids:已启动但未提交,不可见。
        • trx_id not in m_ids:已提交,可见。
  • 读已提交
    • 每次读取数据时,生成新的 read view。
  • 可重复读
    • 启动事务时,生成新的 read view,一直使用直到事务提交。
  • 快照读
    select * from table where
  • 当前读
    select * from table where ? lock in share mode # S 锁(读锁)
    select * from table where ? for update         # X 锁(写锁)
    
    insert into table values(...)
    update table set ? where ?
    delete from table where ?
    

  • 全局锁
    • 用于全库备份。
flush tables with read lock  # 整个数据库处于只读状态
unlock tables 
  • 表级锁
    • 表锁
      lock tables 'table' [read/write]
      unlock tables
      
    • 元数据锁
      crud
      alter
      
    • 意向锁
      • 目的:告诉其他事务,此时该表正在被一个事务访问。
      • 作用:阻塞表级读写锁(全面扫描加锁),由于 innodb 支持的是行级别的锁,意向锁并不会阻塞除了全表扫描以外的任何请求。
      • 意向锁存储在表结构中。
      • 意向锁之间是互相兼容的,并且由数据库自动添加。
      • 当事务试图读或写某一条记录时,会先在表上加上意向锁,然后才在要操作的记录上加上读锁或写锁。这样判断表中是否有记录加锁就很简单了,只要看下表上是否有意向锁就可以了。
      • 分类
        • 意向共享锁(IS):对一张表中某几行加的共享锁。
        • 意向排他锁(IX):对一张表中某几行加的排他锁。
    • auto-inc 锁
      • 特殊表锁,实现自增约束,语句结束后释放锁(而非在事务结束时释放)。
  • 行级锁
    • 记录锁(record lock)
      • 共享锁(S)
        • 事务读操作加的锁,对某一行加锁。
        • 在 serializable 隔离级别下,默认给读操作加共享锁。
        • 在 RR 隔离级别下,需手动加共享锁,可解决幻读问题。
        • 在 RC 隔离级别下,没必要加共享锁,采用的是 MVCC。
        • 在 read uncommitted 隔离级别下,既没有加锁也没有使用 MVCC。
      • 排他锁(X)
        • 事务删除或更新加的锁,对某一行加锁。
        • 在 4 种隔离级别下,都添加了排他锁,事务提交或事务回滚后释放锁。
    • 间隙锁(gap lock)
      • RR 隔离级别下,where 条件语句未命中时会自动添加间隙锁。
      • 防止其他事务在记录间隙插入新的记录,从而避免幻读现象。
      • 间隙锁会锁定一个范围,加锁区间为 (row1, row2)。
    • 临键锁(next-key lock)
      • 记录锁 + 间隙锁。
      • 加锁区间为 (row1, row2]。
  • 查询
    • MVCC:undo log 实现历史版本记录。
    • S 锁:lock in share mode
    • X 锁:for update
    • 不做任何处理:read uncommitted 使用的策略。
  • 删除、更新
    • 自动添加 X 锁。
  • 插入
    • 使用插入意向锁(特殊的 gap 锁)和 X 锁。
      • 在多事务同时写入不同数据至同一索引间隙的时候,并不需要等待其他事务完成,不会发生锁等待。
    • auto-inc lock:特殊表锁实现。

锁兼容

GAP(持有)Insert Intention(持有)Record(持有)Next-key(持有)
GAP(请求)兼容兼容兼容兼容
Insert Intention(请求)冲突兼容兼容冲突
Record(请求)兼容兼容冲突冲突
Next-key(请求)兼容兼容冲突冲突
  • 横向:表示已经持有的锁。
  • 纵向:表示正在请求的锁。
  • 一个事务已经获取了插入意向锁,对其他事务是没有任何影响的。
  • 一个事务想要获取插入意向锁,如果有其他事务已经加了 gap lock 或 next-key lock 则会阻塞,这个是重点,死锁之源。

锁的对象

  • 行级锁是针对表的索引加锁,索引包括聚簇索引和辅助索引。
  • 表级锁是针对页或表进行加锁;
  • 重点考虑 innodb 在 read committed 和 repeatable read 隔离级别下锁的情况。
  • 聚簇索引,查询命中:
    UPDATE students SET score = 100 WHERE id = 15;
    

在这里插入图片描述

  • 聚簇索引,查询未命中:
     UPDATE students SET score = 100 WHERE id = 16;
    

在这里插入图片描述

  • 辅助唯一索引,查询命中:
    UPDATE students SET score = 100 WHERE no = 'S0003';
    

在这里插入图片描述

  • 辅助唯一索引,查询未命中:
     UPDATE students SET score = 100 WHERE no = 'S0008';
    

在这里插入图片描述

  • 辅助非唯一索引,查询命中:
    UPDATE students SET score = 100 WHERE name = 'Tom';
    

在这里插入图片描述

  • 辅助非唯一索引,查询未命中:
     UPDATE students SET score = 100 WHERE name = 'John';
    

在这里插入图片描述

  • 无索引:
    UPDATE students SET score = 100 WHERE score = 22;
    

在这里插入图片描述

  • 聚簇索引,范围查询:
    UPDATE students SET score = 100 WHERE id <= 20;
    

在这里插入图片描述

  • 辅助索引,范围查询:
    UPDATE students SET score = 100 WHERE age <= 23;
    

在这里插入图片描述

  • 修改索引值:
    UPDATE students SET name = 'John' WHERE id = 15;
    

在这里插入图片描述


死锁

  • 死锁原因:并发事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。
  • 相反加锁顺序死锁
    • 不同表加锁顺序相反。
    • 相同表不同行加锁顺序相反。
      • 给辅助索引行加锁的时候,同时会给聚簇索引行加锁。
      • 使用外键索引时,给父表加锁,同时隐含给子表加锁。
    • 解决:调整加锁顺序。
  • 锁冲突死锁
    • RR 隔离级别下,插入意向锁与 gap 锁冲突死锁。一个事务想要获取插入意向锁,如
      果有其他事务已经加了 gap lock 或 next-key lock 则会阻塞。
    • 解决:降低隔离级别至 RC。
  • 如何避免死锁
    • 尽可能以相同顺序来访问索引记录和表。
    • 如果能确定幻读和不可重复读对应用影响不大,考虑将隔离级别降低为 RC。
    • 添加合理的索引,不走索引将会为每一行记录加锁,死锁概率非常大。
    • 尽量在一个事务中锁定所需要的所有资源,减小死锁概率。
    • 避免大事务,将大事务分拆成多个小事务,大事务占用资源多,耗时长,冲突概率变高。
    • 避免同一时间点运行多个对同一表进行读写的概率。
  • 查看死锁
    • 系统表
      -- 开启标准监控
      CREATE TABLE innodb_monitor (a INT)
      ENGINE=INNODB;
      -- 关闭标准监控
      DROP TABLE innodb_monitor;
      -- 开启锁监控
      CREATE TABLE innodb_lock_monitor (a INT)
      ENGINE=INNODB;
      -- 关闭锁监控
      DROP TABLE innodb_lock_monitor
      
    • 系统参数
      -- 开启标准监控
      set GLOBAL innodb_status_output=ON;
      -- 关闭标准监控
      set GLOBAL innodb_status_output=OFF;
      -- 开启锁监控
      set GLOBAL innodb_status_output_locks=ON;
      -- 关闭锁监控
      set GLOBAL innodb_status_output_locks=OFF;
      -- 将死锁信息记录在错误日志中
      set GLOBAL innodb_print_all_deadlocks=ON;
      
    • 命令
      -- 查看事务
      select * from information_schema.INNODB_TRX;
      -- 查看锁
      select * from information_schema.INNODB_LOCKS;
      -- 查看锁等待
      select * from information_schema.INNODB_LOCK_WAITS;
      

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

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

相关文章

Vue2:路由的两种模式history模式和hash模式

一、情景说明 之前我们写的项目启动后&#xff0c;浏览器访问时&#xff0c;路径中会有个#/&#xff0c;会导致不够美观 因为一般的访问地址都是http://123.123.123.123/aaa/bbb这种形式 这一篇&#xff0c;就来解决这个问题 二、案例 1、hash模式 特点&#xff1a;#/后的…

QMessageBox用法及技巧

一&#xff0e;QMessageBox是什么&#xff1f; QMessageBox类为用户提供了主要的警告信息&#xff0c;用户可以根据需求选择需要的响应&#xff1b; QMessageBox 还提供了一些常用的按钮&#xff0c;例如"确定"、"取消"、"是"、"否"…

redis 面试题

1 redis 如何扩容 Redis 的扩容主要分为两种场景&#xff0c;一种是单实例的内存扩容&#xff08;垂直扩容&#xff09;&#xff0c;另一种是Redis集群的扩容&#xff08;水平扩容&#xff09;。 单实例Redis内存扩容&#xff08;垂直扩容&#xff09; 硬件升级&#xff1a; 垂…

Spring MVC 和 Spring Cloud Gateway不兼容性问题

当启动SpringCloudGateway网关服务的时候&#xff0c;没注意好依赖问题&#xff0c;出现了这个问题&#xff1a; Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway. 解决办法就是&#xff1a;删除SpringMVC的依赖&#xff0c;即下列依赖。 &…

年轻人怎么搞钱?

年轻人想要搞钱&#xff0c;可以考虑以下几个方面&#xff1a; 1. 创业&#xff1a;年轻人可以通过自己的创意&#xff0c;找到一个市场的空缺&#xff0c;开创自己的业务。可以从比较小的项目开始&#xff0c;逐渐扩大范围&#xff0c;积累经验和财富。 2. 投资&#xff1a;…

【C++从0到王者】第四十八站:最短路径

文章目录 一、最短路径二、单源最短路径 -- Dijkstra算法1.单源最短路径问题2.算法思想3.代码实现4.负权值带来的问题 三、单源最短路径 -- Bellman-Ford算法1.算法思想2.算法实现3.SPFA优化4.负权回路 四、多源最短路径 -- Floyd-Warshall算法1.算法思想2.算法实现 一、最短路…

python笔记_数据类型

定义&#xff1a;python的变量在使用前必须赋值&#xff0c;数据类型指的是变量指定的内存数据的类型 string字符串类型使用引号int整型整数float浮点型小数bool布尔值(逻辑)输出true/false A&#xff0c;整数类型 整型字节 1,python的整数有十六进制&#xff0c;十进制&#…

HDSRNet | 入局图像超分,异构动态卷积玩出新花样~

首发AIWalker&#xff0c;欢迎关注~ https://arxiv.org/abs/2402.15704 https://github.com/hellloxiaotian/HDSRNet 卷积神经网络可以通过深度网络架构和给定的输入样本自动学习特征。然而&#xff0c;所获得的模型的鲁棒性在不同的场景中可能具有挑战性。网络架构的差异越大…

基于C#开发OPC DA客户端——基于OPCDAAuto

OPC DA OPC DA(OPC Data Access)&#xff0c;即OPC数据访问接口&#xff0c;定义了数据交换的规范&#xff0c;包括&#xff1a;过程值、更新时间、数据品质等信息。 自动化接口中共定义了6类对象&#xff1a;OPCServer对象、OPCBrowser对象、OPCGroups对象、OPCGroup对象、O…

《最新出炉》系列初窥篇-Python+Playwright自动化测试-34-处理https 安全问题或者非信任站点-下篇

1.简介 这一篇宏哥主要介绍playwright如何在IE、Chrome和Firefox三个浏览器上处理不信任证书的情况&#xff0c;我们知道&#xff0c;有些网站打开是弹窗&#xff0c;SSL证书不可信任&#xff0c;但是你可以点击高级选项&#xff0c;继续打开不安全的链接。举例来说&#xff0c…

运用JProfiler分析Java程序中的OOM问题

前言 在Java开发过程中&#xff0c;内存管理是一项至关重要的任务。作为开发者&#xff0c;我们时常会遇到一个让人头疼的问题——Java堆空间溢出&#xff08;OutOfMemoryError&#xff0c;简称OOM&#xff09;。当程序试图分配超出Java虚拟机&#xff08;JVM&#xff09;堆大…

解决error: the following arguments are required问题

今天在运行代码的时候&#xff0c;文件报错如下&#xff1a; mcts.py: error: the following arguments are required: --num_sims, --levels 根据报错信息可以看出这应该是说--num_sims和--levels两个属性并没有定义&#xff0c;但在代码中找了许久&#xff0c;不知道要在哪里…

LeetCode238题:除自身以外数组的乘积(python3)

代码思路&#xff1a; 当前位置的结果就是它左部分的乘积再乘以它右部分的乘积&#xff0c;因此需要进行两次遍历&#xff0c;第一次遍历求左部分乘积&#xff0c;第二次遍历求右部分的乘积&#xff0c;再将最后的计算结果一起求出来。 class Solution:def productExceptSelf(…

外包干了7个月,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入北京某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

上位机图像处理和嵌入式模块部署(当前机器视觉新形态)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 过去的机器视觉处理&#xff0c;大部分都是集中在上位机、或者是服务器领域&#xff0c;这种形式维持了很长的时间。这种业务形态下&#xff0c;无…

如何将一台电脑主机分裂成两台、三台?

有用户问&#xff1a;如何将一台电脑主机拆分成两台、三台甚至更多台使用&#xff1f; 这是什么意思&#xff1f; 简单解释一下&#xff1a;在一台计算机主机上&#xff0c;连接两台、三台或者更多台显示器&#xff0c;然后将这台主机的硬件资源分配给这些显示器&#xff0c;然…

Tomcat -2

1. 动静分离 ① 单机反向代理 7-2 代理服务器 7-5 tomcat 设置 7-3 测试&#xff1a; 代理服务器那里写什么就显示什么

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——02.数据格式整理(2)

内容如下&#xff1a; 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的…

【论文笔记】Improving Language Understanding by Generative Pre-Training

Improving Language Understanding by Generative Pre-Training 文章目录 Improving Language Understanding by Generative Pre-TrainingAbstract1 Introduction2 Related WorkSemi-supervised learning for NLPUnsupervised pre-trainingAuxiliary training objectives 3 Fra…

AttributeError: ‘list‘ object has no attribute ‘view‘

问题描述 训练yolov9的时候遇到了下面的问题。 In loss_tal.py: pred_distri, pred_scores torch.cat([xi.view(feats[0].shape[0], self.no, -1) for xi in feats], 2).split( (self.reg_max * 4, self.nc), 1) The error is as follows&#xff1a; AttributeError: list …