广告
返回顶部
首页 > 资讯 > 数据库 >MySQL学习之事务详解
  • 345
分享到

MySQL学习之事务详解

MySQL事务使用MySQL事务 2022-12-08 10:12:05 345人浏览 薄情痞子
摘要

目录一. 事务的业务场景二. 事务的使用三. 事务的特性(ACID)1. 原子性(Atomicity)2. 一致性(Consistency)3. 持久性(Durability)4. 隔离性(Isolation)四. 事务

一. 事务的业务场景

数据库中 事务(transaction) 可以把多个sql给打包到一起, 即将多个SQL语句变成一个整体, 也就是说一个事务中的所有操作要么全部成功执行, 要么完全不执行.

通过实际场景来理解事务:

实际生活中我们经常涉及转帐操作, 张三给李四转账2000元, 涉及到两个操作

  • 给张三的账户余额减去2000元
  • 给李四的账户余额增加2000元

这里就要考虑到这两个操作的完整性, 也就是不能出现张三的账户余额减少了2000元, 但李四的账户余额未发生变化, 这就要求上面的两个操作要么全部执行完成功转账, 要么一个都不执行双方都没有损失, 不会出现中途发生一些问题导致数据不一致的情况.

这样的一次完整操作叫做 事务(transaction), 一个事务中的所有操作要么全部成功执行, 要么完全不执行.

二. 事务的使用

事务是如何保证操作的完整性的呢?

其实事务执行中间出错了, 只需要让事务中的这些操作恢复成之前的样子即可, 这里涉及到的一个操作, 回滚(rollback).

事务处理是一种对必须整批执行的 mysql 操作的管理机制, 在事务过程中, 除非整批操作全部正确执行, 否则中间的任何一个操作出错, 都会回滚 (rollback)到最初的安全状态以确保不会对系统数据造成错误的改动.

相关语法:

-- 开启事务
start transaction;

-- 若干条执行sql

-- 提交/回滚事务
commit/rollback;

注意:

在开启事务之后, 执行sql不会立即去执行, 只有等到commit操作后才会统一执行(保证原子性).

示例:

首先创建一个账户表并初始化数据

-- 创建一个账户表
create table account(
     id int primary key auto_increment,
     name varchar(20),
     money double(10,2)
);
-- 初始化账户信息
insert into account(name, money) values ('张三', 10000), ('李四', 10000);

首先看正常情况下的转账操作

-- 张三账户 -2000
mysql> update account set money = money - 2000 where name = '张三';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0
-- 李四账户 +2000
mysql> update account set money = money + 2000 where name = '李四';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
-- 转账成功
mysql> select * from account;
+----+--------+----------+
| id | name   | money    |
+----+--------+----------+
|  1 | 张三   |  8000.00 |
|  2 | 李四   | 12000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

如果操作中出现异常情况, 比如sql语句中所写的注释格式错误导致sql执行中断.

-- 先将张三和李四的账户余额恢复为10000元
update account set money = 10000 where name = '张三';
update account set money = 10000 where name = '李四';
-- 张三 -2000
mysql> update account set money = money - 2000 where name = '张三';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0
-- 李四 +2000
mysql> 没加--的注释
    -> update account set money = money + 2000 where name = '李四';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL Server version for the right syntax to use near '没加--的注释
update account set money = money + 2000 where name = '李四'' at line 1
-- 出现异常
mysql> select * from account;
+----+--------+----------+
| id | name   | money    |
+----+--------+----------+
|  1 | 张三   |  8000.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

观察结果发现了张三的账户少了2000元, 但李四的账户余额并没有增加, 在实际操作中这种涉及钱的操作发生这种失误可能会造成很大的损失.

为了防止这种失误的出现我们就可以使用事务来打包这些操作.

-- 先将张的账户余额恢复为10000元
update account set money = 10000 where name = '张三';
-- 开启事务
start transaction;
-- 张三 -2000
mysql> update account set money = money - 2000 where name = '张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
-- 李四 -2000
mysql> 没加--的注释
    -> update account set money = money + 2000 where name = '李四';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '没加--的注释
update account set money = money + 2000 where name = '李四'' at line 1
-- 预期结果
mysql> select * from account;
+----+--------+----------+
| id | name   | money    |
+----+--------+----------+
|  1 | 张三   |  8000.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

观察这里的结果发现在当前的数据库用户查询到的account表中的账户余额发生了变化,但开启了事务之后在commit之前只是临时的预操作并不会真的去修改表中的数据;

可以退出数据库再打开重新查询表中数据或者切换用户去查询去验证表中数据是否发生改变, 这里就不作演示了.

发现操作结果异常之后, 当前用户需要恢复到事务之前的状态, 即进行回滚操作.

-- 回滚事务
mysql> rollback;
Query OK, 0 rows affected (0.03 sec)
-- 验证回滚后的状态
mysql> select * from account;
+----+--------+----------+
| id | name   | money    |
+----+--------+----------+
|  1 | 张三   | 10000.00 |
|  2 | 李四   | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

如果开启事务之后发现预操作的结果是预期的效果, 此时我们就可以提交事务, 当我们提交完事务之后, 数据就是真的修改了, 也就是硬盘中存储的数据真的改变了.

-- 开启事务
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
-- 张三 -2000
mysql> update account set money = money - 2000 where name = '张三';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
-- 李四 +2000
mysql> update account set money = money + 2000 where name = '李四';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
-- 提交事务
mysql> commit;
Query OK, 0 rows affected (0.03 sec)

mysql> select * from account;
+----+--------+----------+
| id | name   | money    |
+----+--------+----------+
|  1 | 张三   |  8000.00 |
|  2 | 李四   | 12000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)

要注意事务也不是万能的, 不能保证你删表删库之后可以完全恢复, 只是在适量的数据和操作下使用事务可以避免一些问题.

回滚(rollback)操作, 实际上是我们把事务中的操作再进行逆操作, 前面是插入, 回滚就是删除…

这些操作是有很大开销的, 可以保存, 但不能够无限保存, 最多是将正再执行的事务保存下来, 额外的内容就不好再保存了; 数据库要是有几十亿条数据, 占据了几百G硬盘空间, 不可能去花费几个T甚至更多的空间用来记录这些数据是如何来的.

三. 事务的特性(ACID)

1. 原子性(Atomicity)

一个事务是一个不可分割的最小单位, 事务中的所有操作要么全部成功, 要么全部失败, 没有中间状态.

原子性主要是通过事务日志中的回滚日志(undo log)来实现的, 当事务对数据库进行修改时, InnoDB 会根据操作生成相反操作的 undo log, 比如说对 insert 操作, 会生成 delete 记录, 如果事务执行失败或者调用了 rollback,就会根据 undo log 的内容恢复到执行之前的状态.

事务的原子性, 也是事务的核心特性, 是事务的初心.

2. 一致性(Consistency)

事务执行之前和执行之后数据都是合法的一致性状态, 即使发生了异常, 也不会因为异常引而破坏数据库的完整性约束, 比如唯一性约束等.

事务执行前/执行后, 都得是数据合法的状态; 比如像上面的转账, 不能说转的过程出错了, 导致出现钱转丢了的情况.

3. 持久性(Durability)

事务提交之后对数据的修改是持久性的, 即使数据库宕机也不会丢失, 通过事务日志中的重做日志(redo log)来保证; 事务修改之前, 会先把变更信息预写到 redo log 中, 如果数据库宕机, 恢复后会读取 redo log 中的记录来恢复数据(回滚).

事务产生的修改, 都是会写入硬盘的, 程序重启/主机重启/掉电, 事务都可以正常工作, 保证修改是生效的.

4. 隔离性(Isolation)

这里的隔离性是指一个数据库服务器, 同时执行多个事务的时候, 事务之间的相互影响程度.

一个服务器, 可以同时给多个客户端提供服务, 这多个客户端是并发执行的关系, 多个客户端就会有多个事务, 多个事务同时去操作一个表的时候, 特别容易出现互相影响的问题.

如果隔离性越高, 就意味着事务之间的并发程度越低, 执行效率越慢, 但是数据准确性越高.

如果隔离性越低, 就意味着事务之间的并发程度越高, 执行效率越快, 但是数据准确性越低.

隔离性通过事务的隔离级别来定义, 并用机制来保证写操作的隔离性, 用 mvcC 来保证读操作的隔离性.

四. 事务并发异常

在实际生产环境下, 可能会出现大规模并发请求的情况, 如果没有妥善的设置事务的隔离级别, 就可能导致一些异常情况的出现,最常见的几种异常为脏读(Dirty Read), 幻读(Phantom Read)和不可重复读(Unrepeatable Read).

1. 脏读

一个事务读取到了另外一个事务没有提交的数据(读写的是同一份数据).

说详细点就是当一个事务正在访问数据并且对数据进行了修改, 而这种修改还没有提交到数据库中,与此同时时另外一个事务也访问这个数据, 然后使用了这个数据; 因为这个数据是还没有提交的数据, 那么另外一个事务读到的这个数据就是脏数据, 依据脏数据所做的操作可能是不正确的.

用一个场景例子来理解, 张三正在写代码, 李四趴在屏幕前看张三写代码, 等张三走掉之后, 李四就把他刚刚写的这段代码删掉了, 此时李四看到的这段代码就可能是一个错误的代码.

在这个场景下, 张三和李四就可以理解为两个事务, 这两个事务是完全并发没有任何限制的, 此时就会出现脏读问题.

解决脏读问题的办法, 就是降低并发性, 提高隔离性, 具体来说就是给这里的 “写操作” 加锁, 张三在写代码的时候, 李四不能看, 张三和李四约定张三代码写完后会提交到githup上, 李四去githup上去看.

当进行了写加锁的时候, 张三写的时候, 李四就不能同时去读了; 相当于降低了并发程度, 提高了隔离性. 降低了一定的效率, 但是提高了准确性.

2. 不可重复读

在同一事务中, 连续两次读取同一数据, 得到的结果不一致.

还是基于上面的场景进行理解, 上面已经约定了写加锁(张三写代码过程中, 李四不要读, 等到张三提交之后, 李四再去读).

此时张三在写代码, 张三和李四有约定, 所以此时李四在等张三把代码提交到githup上再去看代码.

过了一会儿, 张三写完了, 并将代码提交到了githup上, 李四开始读代码.

当李四正在读这个代码的时候, 张三觉得自己的代码还有不足, 于是张三动手修改, 重新提交了个版本; 导致李四读代码读了一半, 突然代码自动就变了.

这种情况就是不可重复读问题了, 解决办法是给读操作也加锁, 张三在读代码的时候, 李四不能修改.

此时这两个事务之间的并发程度进一步降低了, 隔离性又进一步提高了, 运行速度又进一步变慢了, 数据的准确性又进—步提高了.

3. 幻读

同一事务中, 用同样的操作读取两次, 得到的记录数不相同.

幻读是指当事务不是独立执行时发生的一种现象, 例如第一个事务对一个表中的数据进行了修改, 这种修改涉及到表中的全部数据行; 同时, 第二个事务也修改这个表中的数据, 这种修改是向表中插入一行新数据; 那么, 以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行, 就好象发生了幻觉一样.

再基于2中的场景进行理解, 当前已经约定了写加锁和读加锁, 即张三写代码的时候, 李四不能读; 李四读代码的时候, 张三也不能写.

此时李四在读代码, 张三虽然不能去修改李四现在正在读的这个文件, 但是李四又去新增/删除一个其他的文件, 此时, 虽然李四读的代码内容没变, 但他发现, 文件的数量变了; 这就是幻读问题了.

解决幻读问题的办法是 串行化, 也就是彻底的舍弃并发, 此时只要李四在读代码, 张三就不能进行任何操作.

五. MySQL的四个隔离级别

MySQL中有 4 种事务隔离级别, 由低到高依次为 读未提交 Read Uncommitted, 读已提交 Read Committed , 可重复读 Repeatable Read , 串行化 Serializable.

串行化的事务处理方式是最安全的, 但不能说用这个就一定好, 应该是根据实际需求去选择合适的隔离级别, 比如银行等涉及钱的场景, 就需要确保准确性, 速度慢一点也没什么; 而比如抖音,B站,快手等上面的点赞数, 收藏数就没必要那么精确了, 这个场景下速度提高一点体验会更好一些.

脏读不可重复读幻读
读未提交 read uncommited
读已提交 read commited×
可重复读 repeatable read××
串行化 serializable×××

read uncommited

不做任何限制, 事务之间都是随意并发执行的; 并发程度最高,隔离性最差.

会产生脏读 + 不可重复读 + 幻读问题.

read commited

对写操作加锁, 并发程度降低, 隔离性提高.

解决了脏读问题, 仍然存在不可重复读 + 幻读问题.

repeatable read

写加锁, 读加锁, 隔离性再次提高, 并发程度再次降低.

解决了脏读 + 不可重复读问题, 仍然存在幻读问题.

这个隔离级别也是MySQL的默认隔离级别, 如果需要改的话, 可以通过MySQL的配置文件来进行调整.

serializable

严格执行串行化, 并发程度最低, 隔离性最高, 执行速度最慢.

解决了 脏读 + 不可重复读 + 幻读问题.=

到此这篇关于MySQL学习之事务详解的文章就介绍到这了,更多相关MySQL事务内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

您可能感兴趣的文档:

--结束END--

本文标题: MySQL学习之事务详解

本文链接: https://www.lsjlt.com/news/174173.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • MySQL学习之事务详解
    目录一. 事务的业务场景二. 事务的使用三. 事务的特性(ACID)1. 原子性(Atomicity)2. 一致性(Consistency)3. 持久性(Durability)4. 隔离性(Isolation)四. 事务...
    99+
    2022-12-08
    MySQL事务使用 MySQL事务
  • MySql学习笔记之事务隔离级别详解
    背景 说的事务,大家应该都不陌生,开发用到 MySql 数据库的时候,通常会用到事务。其中比较经典的例子就是转账,比如你要给小明转 50 块钱,而此时你的银行卡也就只有 50 块钱...
    99+
    2022-11-12
  • Java事务管理学习之JDBC详解
    什么是Java事务通常的观念认为,事务仅与数据库相关。事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)的缩...
    99+
    2023-05-31
    java jdbc 事务管理
  • Vue3学习之事件处理详解
    目录1.基本使用2.事件修饰符3.按键修饰符1.基本使用 我们可以使用 v-on 指令来监听 DOM 事件,从而执行 JavaScript 代码。 v-on 指令可以缩写为 @ 符号...
    99+
    2022-12-08
    Vue3事件处理 Vue3事件 Vue 事件处理
  • 详解Spring学习之编程式事务管理
    前言在前面的内容,基本已经学习了事务的基本概念以及事务隔离级别等,接下来的几个小节,将学习怎么使用Spring进行事务管理,在Spring中,对事务进行管理有多种方法,主要分别编程式和声明式,本小节主要学习编程式事务管理,后面讲学习Spri...
    99+
    2023-05-31
    spring 编程式 事务
  • VUE3基础学习之click事件详解
    目录1. 概述2. click 事件2.1 实现数字递减2.2 事件方法中获取 event 对象2.3 事件方法中增加参数2.4 有参事件方法中获取 event 对象2.5 点击按钮...
    99+
    2022-11-12
  • MySQL事务学习
    MySQL事务: 1、事务特性:原子性,一致性,隔离性,持久性 原子性:对一些操作,要么同时成功,要么同时失败。 一致性:对一些操作,处理结果必须一致的,比如转账:A转给B,那么A账户减少100元,则B账户必须增加100元。 隔离性:多个...
    99+
    2019-11-20
    MySQL事务学习
  • MySQL学习之完整性约束详解
    数据完整性指的是数据的一致性和正确性。完整性约束是指数据库的内容必须随时遵守的规则。若定义了数据完整性约束,mysql会负责数据的完整性,每次更新数据时,MySQL都会测试新的数据内容是否符合相关的完整性约束条件,只有符...
    99+
    2022-08-11
    MySQL 完整性约束 MySQL 约束
  • MySQL学习之数据库备份详解
    目录1.DB,DBMS,SQL2.数据库的特点3.SQL分类4.mysql两种启动关闭方式5.mysql的登录方式()6.SQL语言规范7.navicat常用快捷键8.数据库的备份和...
    99+
    2022-11-12
  • MySQL学习之数据更新操作详解
    目录UPDATE 语句UPDATE 语句练习①UPDATE 语句练习②UPDATE 语句练习③UPDATE 语句的表连接UPDATE 语句练习④UPDATE 语句 UPDATE 语句用于修改表的记录,语法如下 UPDAT...
    99+
    2022-08-08
    MySQL数据更新操作 MySQL数据更新
  • C++学习之线程详解
    目录开篇线程的状态多线程的构建计算时间一、程序运行时间二、chrono共享资源和互斥锁condition_variable线程池总结开篇 多线程是开发中必不可少的,往往我们需要多个任...
    99+
    2022-11-12
  • Java Spring5学习之JdbcTemplate详解
    目录一、JdbcTemplate二、实战2.1 引入依赖2.2 配置连接池2.3 配置JdbcTemplate 对象,注入 DataSource2.4 扫描注解2.5 创建 serv...
    99+
    2022-11-12
  • 深度学习详解之初试机器学习
    机器学习可应用在各个方面,本篇将在系统性进入机器学习方向前,初步认识机器学习,利用线性回归预测波士顿房价; 原理简介 利用线性回归最简单的形式预测房价,只需要把它当做是一次线性函数y...
    99+
    2022-11-12
  • MySQL数据库学习之查询操作详解
    目录1.示例表内容2.简单查询3.给列起别名4.列运算5.条件查询1.示例表内容 dept表: +--------+------------+----------+ | DEPTNO | DNAME | LOC...
    99+
    2022-07-24
    MySQL数据库查询 MySQL查询
  • MySQL数据库学习之分组函数详解
    目录1.分组函数极值求和平均值列数和2.分组查询3.小练习4.大BOSS1.分组函数 极值 示例表内容见此篇文章 找出最高工资: mysql> select max(sal) from emp; +---...
    99+
    2022-07-24
    MySQL数据库分组函数 MySQL分组函数
  • MySQL学习之分组查询的用法详解
    目录为什么要分组逐级分组逐级分组对 SELECT 子句的要求对分组结果集再次做汇总计算GROUP_CONCAT 函数GROUP BY 子句的执行顺序该章节来开始学习分组查询,上一章节我们学习了聚合函数,默认统计的...
    99+
    2022-07-28
    MySQL分组查询 MySQL 查询
  • MySQL学习之日期函数的用法详解
    目录获取 系统时间 函数日期格式化 函数日期函数练习①日期函数练习②日期计算的注意事项日期偏移计算计算日期之间相隔的天数获取 系统时间 函数 “NOW()” 函数 能够获得当前系统日期和时间,格式...
    99+
    2022-08-12
    MySQL日期函数用法 MySQL日期函数
  • MySQL学习之三大范式详解小白篇
    目录1.范式基础1.1范式的概念2.三大范式2.1三大范式概念2.2三大范式举例1.进行第一范式的改造2.进行第二范式的改造3.进行第三范式的改造1.范式基础 1.1范式的概念 设计...
    99+
    2022-11-12
  • Android学习之SharedPerference存储详解
    SharedPerference不同同于文件存储,它是使用键值的方式来存储数据,对于保存的每一条数据都会给一个键值,这样在读取数据时直接通过键值取出相应数据。amdroid提供了三个方法来获取实例:1.Context类中的getShareP...
    99+
    2023-05-30
    android sharedperference 存储
  • QT学习之[QTreeWidgetItem]详解,示例
    QTreeWidgetItem 树形控件,当选中顶层树形节点时,子节点全部被选中,当取消选择顶层数节点时,子节点全部被取消,当选中子节点时候,父节点显示被选中,示例如下: 用法以及API详解如图: 图...
    99+
    2023-09-21
    qt 学习 ui c++
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作