MySQL 事务原理分析

事务

  • 前提:并发连接访问。
  • 定义:事务是用户定义的一系列操作,这些操作要么都做,要么都不做,是一个不可分割的单位。
  • 目的:事务将数据库从一种一致性状态转换为另一种一致性状态,保证系统始终处于一个完整且正确的状态。
  • 组成:事务可由一条非常简单的 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.mzph.cn/news/703094.shtml

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

相关文章

半导体物理基础-笔记(续)

源内容参考&#xff1a;https://www.bilibili.com/video/BV11U4y1k7zn/?spm_id_from333.337.search-card.all.click&vd_source61654d4a6e8d7941436149dd99026962 掺杂半导体的费米能级与温度及杂质浓度的关系图 在温度一定的条件下&#xff0c;施主杂质浓度越高&#xff0…

Oracle EBS GL 外币折算逻辑

背景 由于公司财务在10月份期间某汇率维护错误,导致帐套折算以后并合传送至合并帐套生成合并日记帐凭证的借贷金额特别大,但是财务核对的科目余额有没有问题,始终觉得合并日记帐生成会计分发有问题,需要我们给出外币折算逻辑。 基础设置 汇率 Path: GL->设置->币种-&…

pclpy 最小二乘法拟合平面

pclpy 最小二乘法拟合平面 一、算法原理二、代码三、结果1.左边原点云、右边最小二乘法拟合平面后点云投影 四、相关数据 一、算法原理 平面方程的一般表达式为&#xff1a; A x B y C z D 0 ( C ≠ 0 ) Ax By Cz D 0 \quad (C\neq0) AxByCzD0(C0) 即&#xff1a; …

Spring Boot对接RocketMQ示例

部署服务 参考RocketMq入门介绍 示例 引入maven依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.2</version></dependency>完整依赖如下&am…

C++ 游戏飞机大战, 字符型的

//#define _CRT_SECURE_NO_WARNINGS 1 用于禁止不安全函数的警告 #include<iostream> #include<stdlib.h> #include<string> #include<conio.h> #include<Windows.h> #include<time.h> #include <graphics.h> using namespace std;…

Rust升级慢,使用国内镜像进行加速

背景 rustup 是 Rust 官方的跨平台 Rust 安装工具&#xff0c;国内用户使用rustup update的时候&#xff0c;网速非常慢&#xff0c;可以使用国内的阿里云镜像源来进行加速 0x01 配置方法 1. Linux与Mac OS用户配置环境变量 修改~/.bash_profile文件添加如下内容&#xff1…

微信小程序 --- 小程序基础知识

小程序基础知识 1. 认识什么是小程序 什么是微信小程序 微信小程序是一种运行在微信内部的 轻量级 应用程序。 在使用小程序时 不需要下载安装&#xff0c;用户 扫一扫 或 搜一下 即可打开应用。它也体现了 “用完即走” 的理念&#xff0c;用户不用关心安装太多应用的问题…

力扣链表篇

以下刷题思路来自代码随想录以及官方题解 文章目录 203.移除链表元素707.设计链表206.反转链表24.两两交换链表中的节点19.删除链表的倒数第N个节点面试题 02.07. 链表相交142.环形链表II 203.移除链表元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链…

AutoGen Studio助力打造私人GPTs

微软最近在开源项目里的确挺能整活儿啊! 这次我介绍的是AutoGen Studio,我认为这个项目把AutoGen可用性又拔高了一个层次的项目 项目给自己的定义是交互式的多Agent workflow 项目地址:autogen/samples/apps/autogen-studio at main microsoft/autogen (github.com) 首先我…

微信小程序02: 使用微信快速验证组件code获取手机号

全文目录,一步到位 1.前言简介1.1 专栏传送门1.1.1 上文小总结1.1.2 上文传送门 2. 微信小程序获取手机号2.1 业务场景(使用与充值)2.2 准备工作2.3 具体代码使用与注释如下2.3.1 代码解释(一)[无需复制]2.3.2 代码解释(二)[无需复制] 2.4 最后一步 获取手机号信息2.4.1 两行代…

MYSQL--(1.存储引擎 *2.事务*)

一 存储引擎: 1.介绍 1>在数据库管理系统当中通过使用数据引擎来实现数据的增删改,查询 2>不同的存储引擎提供的有不同的存储机制,索引技巧等功能 MYSQL的核心,就是存储引擎 3>同样的,用户也可以根据自己的需要进行选择,更改自己需要…

【Linux】部署前后端分离项目---(Nginx自启,负载均衡)

目录 前言 一 Nginx&#xff08;自启动&#xff09; 2.1 Nginx的安装 2.2 设置自启动Nginx 二 Nginx负载均衡tomcat 2.1 准备两个tomcat 2.1.1 复制tomcat 2.1.2 修改server.xml文件 2.1.3 开放端口 2.2 Nginx配置 2.2.1 修改nginx.conf文件 2.2.2 重启Nginx服务 2…

力扣● 343. 整数拆分 ● 96.不同的二叉搜索树

● 343. 整数拆分 想不到&#xff0c;要勇于看题解。 关键在于理解递推公式。 1、DP数组及其下标的含义&#xff1a;dp[i]是分解i这个数得到的最大的乘积。 2、DP数组如何初始化&#xff1a;dp[0]和dp[1]都没意义&#xff0c;所以直接不赋值&#xff0c;初始化dp[2]1即可。…

WordPres Bricks Builder 前台RCE漏洞复现(CVE-2024-25600)

0x01 产品简介 Bricks Builder是一款用于WordPress的开发主题,提供直观的拖放界面,用于设计和构建WordPress网站。它使用户能够轻松创建自定义的网页布局和设计,无需编写或了解复杂的代码。Bricks Builder具有用户友好的界面和强大的功能,使用户可以通过简单的拖放操作添加…

计算机设计大赛 深度学习图像风格迁移 - opencv python

文章目录 0 前言1 VGG网络2 风格迁移3 内容损失4 风格损失5 主代码实现6 迁移模型实现7 效果展示8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习图像风格迁移 - opencv python 该项目较为新颖&#xff0c;适合作为竞赛课题…

istio系列教程

istio学习记录——安装https://suxueit.com/article_detail/otVbfI0BWZdDRfKqvP3Gistio学习记录——体验bookinfo及可视化观测https://suxueit.com/article_detail/o9VdfI0BWZdDRfKqlv0r istio学习记录——kiali介绍https://suxueit.com/article_detail/pNVbfY0BWZdDRfKqX_0K …

【Java EE初阶二十三】servlet的简单理解

1. 初识servlet Servlet 是一个比较古老的编写网站的方式&#xff0c;早起Java 编写网站,主要使用 Servlet 的方式&#xff0c;后来 Java 中产生了一个Spring(一套框架)&#xff0c;Spring 又是针对 Servlet 进行了进一步封装,从而让我们编写网站变的更简单了&#xff1b;Sprin…

【卷积神经网络中用1*1 卷积有什么作用或者好处呢?】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;深度学习 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 1*1 卷积有什么作用或者好处呢 作用降维和增加非线性特征组合和交互网络的宽度和深度调整全连接替代增强…

高频面试题整理(一)

文章目录 平台无关性如何实现&#xff1f;JVM如何加载 .class文件&#xff1f;什么是反射?谈谈ClassLoader谈谈类的双亲委派机制类的加载方式Java的内存模型?JVM内存模型-jdk8程序计数器&#xff1a;Java虚拟机栈局部变量表和操作数栈&#xff1a; Java内存模型中堆和栈的区别…

Android Gradle开发与应用 (二) : Groovy基础语法

1. Groovy是什么 Groovy是基于JVM虚拟机的一种动态语言&#xff0c;语法和Java非常相似&#xff0c;并能够无缝地与Java代码集成和互操作&#xff0c;增加了很多动态类型和灵活的特性。(闭包、DSL) 语法和Java非常相似这个特点&#xff0c;意味着&#xff0c;如果我们完全不懂…