当前位置: 首页 > news >正文

MySQL篇(事务 - 基础)

目录

一、简介

二、事务操作

1. 数据准备

2. 未控制事务

2.1. 测试正常情况

2.2. 测试异常情况

3. 控制事务

3.1. 控制事务一

查看/设置事务提交方式

提交事务

回滚事务

3.2. 控制事务二

开启事务

提交事务

回滚事务

3.3. 转账案例

四、事务的好处

五、事务四大特性(ACID)

五、事务能干什么用

六、事务控制

1. 查看事务参数状态

2. 查看事务提交模式

3. 修改事务提交模式:手动提交模式

4. 恢复自动提交模式

5. 开启事务

6. 提交事务

7. 回滚事务

8. 自动提交模式下开启事务

七、并发事务问题

1. 脏读

模板一

模板二

2. 不可重复读

模版一

模板二

3. 虚读/幻读

模板一

模板二

4. 第一类更新

5. 第二类更新

八、事务隔离级别

1. 简介

2. Oracle 支持 2 种事务隔离级别

3. MySQL支持 4 种事务隔离级别

4. 隔离级别操作

查看当前会话隔离级别

查看系统隔离级别

设置当前会话隔离级别

设置系统隔离级别

查看锁资源使用情况

查看锁等待

查看事务


一、简介

事务就是一系列sql语句的组合,是一个整体,在执行的过程中要么都一起成功,要么一起失败

在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态。

为确保数据库中数据的一致性,数据的操纵应当是离散的成0组的逻辑单元,当它全部完成时,数据的一致性可以保持,

而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。

DML 语句需要事务,DDL、DQL 可不需要事务。

简单来说:

事务 是一组操作的集合,它是一个不可分割的工作单位,

事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

就比如:

张三给李四转账1000块钱,张三银行账户的钱减少1000,而李四银行账户的钱要增加1000。

这一组操作就必须在一个事务的范围内,要么都成功,要么都失败。

正常情况:

转账这个操作, 需要分为以下这么三步来完成 , 三步完成之后, 张三减少1000, 而李四增加1000, 转账成功 :

异常情况:

转账这个操作, 也是分为以下这么三步来完成 , 在执行第三步是报错了,

这样就导致张三减少1000块钱, 而李四的金额没变, 这样就造成了数据的不一致, 就出现问题了。

为了解决上述的问题,就需要通过数据的事务来完成,我们只需要在业务逻辑执行之前开启事务,执行完毕后

提交事务。

如果执行过程中报错,则回滚事务,把数据恢复到事务开始之前的状态。

注意:

默认MySQL的事务是自动提交的,也就是说,当执行完一条DML语句时,MySQL会立即隐式的提交事务。

二、事务操作

1. 数据准备

drop table if exists account;-- 数据准备
create table account(id int auto_increment primary key comment '主键ID',name varchar(10) comment '姓名',money int comment '余额'
) comment '账户表';insert into account(id, name, money) VALUES (null,'张三',2000),(null,'李四',2000);

2. 未控制事务

2.1. 测试正常情况

-- 1. 查询张三余额
select * from account where name = '张三';-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';

测试完毕之后检查数据的状态, 可以看到数据操作前后是一致的。

2.2. 测试异常情况

-- 1. 查询张三余额
select * from account where name = '张三';-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';出错了....-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';

我们把数据都恢复到2000, 然后再次一次性执行上述的SQL语句

(出错了.... 这句话不符合SQL语法,执行就会报错),

检查最终的数据情况, 发现数据在操作前后不一致了。

3. 控制事务

3.1. 控制事务一

查看/设置事务提交方式
SELECT @@autocommit;SET @@autocommit = 0;
提交事务
COMMIT;
回滚事务
ROLLBACK;

注意:

上述的这种方式,我们是修改了事务的自动提交行为, 把默认的自动提交修改为了手动提交,

此时我们执行的DML语句都不会提交, 需要手动的执行commit进行提交。

3.2. 控制事务二

开启事务
START TRANSACTION 或 BEGIN ;
提交事务
COMMIT;
回滚事务
ROLLBACK;

3.3. 转账案例

-- 开启事务start transaction -- 1. 查询张三余额select * from account where name = '张三';-- 2. 张三的余额减少1000update account set money = money - 1000 where name = '张三';-- 3. 李四的余额增加1000update account set money = money + 1000 where name = '李四';-- 如果正常执行完毕, 则提交事务commit;-- 如果执行过程中报错, 则回滚事务-- rollback;

四、事务的好处

通过以上案例发现事务的好处如下:

① 保证数据一致性,修改过的数据在没有提交之前是不能被其他用户看到的;

② 在数据永久性生效前可以撤回原来的修改操作;

③ 将相关操作组织在一起,一个事务中相关的数据改变或者都成功,或者都失败。

五、事务四大特性(ACID)

  1. 原子性(atomicity)一个事务必须被视为一个不可分割的工作单元,整个事务中的所有操作要么全部提交

成功,要么全部失败回滚。对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。

  1. 一致性(consistency)数据库总是从一个一致性状态转换到下一个一致性状态。在前面的例子中,一致性

确保了,即使在执行第3、4条语句之间时系统崩溃,支票账户中也不会损失200美元。如果事务最终没有

提交,该事务所做的任何修改都不会被保存到数据库中。

  1. 隔离性(isolation)通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的,这就是隔

离性带来的结果。在前面的例子中,当执行完第3条语句、第4条语句还未开始时,此时有另外一个账户汇

总程序开始运行,其看到的支票账户的余额并没有被减去200美元。后面我们讨论隔离级别(isolation

level)的时候,会发现为什么我们要说“通常来说”是不可见的。

  1. 持久性(durability)一旦提交,事务所做的修改就会被永久保存到数据库中。此时即使系统崩溃,数据也

不会丢失。持久性是一个有点模糊的概念,实际上持久性也分很多不同的级别。有些持久性策略能够提供

非常强的安全保障,而有些则未必。而且不可能有100%的持久性保障(如果数据库本身就能做到真正的

持久性,那么备份又怎么能增加持久性呢?)。

五、事务能干什么用

事务是访问和更新数据库的程序执行单元,事务中包含一个或者多个SQL语句,这些语句要么都执行,要么就

都不执行,也就是要么都成功,要么都不成功。

介绍四大特性之前先来说下MySQL的架构:

第一层:客户端层,实现链接处理,身份验证,确保安全性等。

第二层:服务器层,大多数MySQL的核心功能都在这一层,包括查询解析、分析、优化、一级所有的内置

函数(日期,时间、数学、加密函数),以及跨存储引擎的功能(存储过程,触发器,视图等)。

第三层:存储引擎层,负责数据的存储和提取。事务的实现也是在存储引擎中实现的。

MySQL事务的提交与回滚:

- start transaction(开启事务)

- 要执行的SQL语句

- commit/rollback (提交/回滚)

MySQL默认自动提交事务,手动提交事务设置:

set autocommit = 0

上面介绍完相关的理论,记下来我们来详细介绍下到底什么是ACID。

上面介绍完相关的理论,记下来我们来详细介绍下到底什么是ACID。

原子性:

原子性是指一个事务是一个不可分割的单位,是一个最小的操作单元,事务中的操作,要么全成功,要

么全不成功。如果事物中的一个sql语句执行失败了,那么已经执行的SQL语句,要执行回滚动作,回滚到这个

事务执行之前的状态。它的实现原理,主要是通过undo log,它是一个日志,innodb存储引擎提供了两种事

务的日志,一个是redo log,还有一个undo log。原子性的体现,就是在回滚上面,如果SQL报错的话,会

执行回滚,回滚到执行之前的状态。那么怎么回到执行之前的状态呢,这就需要把执行之前的状态记录下

来,一旦这个SQL语句发生错误之后,我们就可以回到我们之前的状态,举个例子:在我们程序上线的过程

中,如果程序上线失败,或者刚上线的时候遇到一些不能及时解决的bug,这个时候我们要把当前的错误版本

回滚到上一个正确的版本,它们之间的原理是相通的,就是我需要要记住上一个正确的版本是什么,当这个事

务对数据库进行修改的时候,innodb就会生成这个对应的undo log,记录这个sql执行的相关信息,如果语句

执行失败,发生回滚,innodb就会根据这个undo log内容去做相反的工作,比如执行了一个insert的操作,

那么回滚的时候,就会执行一个相反的操作,就是delete,我之前执行的是一个delete操作呢,那么回滚的时

候,就会执行一个相反的insert,对于update,回滚的时候会执行一个相反的update,把数据再改回去。

持久性:

持久性的原理,也是通过log,叫redo log,持久性是指这个事物一旦提交,它对数据库的改变,就是永

久性的。首先说下它存在的背景,MySQL的数据,是存在磁盘中的,但是如果每次去读数据都需要经过这个磁

盘IO,那么它的效率就会很低,所以innodb提供了一个缓存buffer,这个buffer中包含了磁盘中的部分数据页

的一个映射,作为访问数据库的一个缓冲,当从数据库读取数据的时候,就会先从这个buffer中取,如果

buffer没有,再去磁盘中读取,读取完之后再放到buffer缓存中。当向数据库写入数据的时候,也会首先向

buffer中写入数据,然后定期将buffer的数据刷新到磁盘上,来完成持久化的操作。这个时候就存在一个问

题,虽然读写效率提升了,但是它也增加了数据丢失的风险,如果buffer中的数据还没有来得及同步到这个磁

盘上,这个时候MySQL宕机了,那么buffer中的数据就会丢失,进而造成数据的丢失。基于这个背景,所以

read log就被引入进来解决这个问题。改进之后的流程是这样的,当数据库的数据要进行新增和修改的时候,

除了修改这个buffer的数据,还要把这次的操作记录到read log日志里面,那么大家可以想一下,如果这个

MySQL宕机了,那么还有这个read log可以去恢复数据,read log是这个预写式日志,也就是说它会将所有

的修改先写入到日志里面,然后再更新到buffer里面,保证了数据不会丢失。既然read log也需要把事务提交

的日志写入到磁盘,那它为什么比直接将buffer中的数据写入到磁盘中要快呢?主要有两个原因,一个是

buffer中的数据持久化是随机写的,IO每次修改的数据位置,都是随机的,但是read log是追加模式的,它是

在文件的尾部去追加,属于一种顺序IO的操作,这种方式就很快,第二个就是buffer持久化数据是以数据页配

置为单位的,MySQL默认的配置页大小是16k,一个数据页上一个小小的修改都要把整个页的数据写入。而

read log只需要写入真正需要的部分就可以了,这样无效的IO就大大的减少了。所以read log要比buffer同步

数据要快的很多很多,那么read log是什么时候会同步到磁盘里去呢?read log在没有同步到磁盘之前,是在

缓冲区中的,叫做read log缓冲区,这个时候就算这个宕机了,也没有关系,因为事务没有执行完,没有去提

交,这样在恢复好数据库之后,还可以走回滚的操作,可以通过这个undo log日志去做回滚。read log的三种

持久化的机制(innodb_flush_log_at_trx_commit ):

0:表示当提交事务时,并不将缓冲区的redo日志写入磁盘的日志文件,而是等待主线程每秒刷新;

1:在事务提交时将redo日志同步写入磁盘,保证一定会写入成功(推荐的方式);

2:在事务提交时缓冲区的redo日志异步写入到磁盘,不能保证在commit时肯定会写入redo日志,只是

会有这个动作。

隔离性:

隔离性呢,又分为两种情况,一个是写写操作,还有一个是写读操作。写写操作,是通过锁去实现隔离

性,写读操作,是通过mvcc来实现。关于读的话大家应该知道,它会存在脏读,不可重复读,以及幻读的情

况,这三种情况是基于mvcc去解决的。隔离性的含义,是指不同的事物之间,它要相互不能影响,就像线程一

样,线程与线程之间是不能互相影响对方的。如果有两个事物,同时要对一行数据进行写操作,那么这时候只

有一个事务能对这个数据进行操作,这就需要锁来保证同一时刻只有一个人在操作这个数据,在这个事务修改

数据之前,要获取相应的锁,获取到锁之后,然后才可以修改这个数据,如果其他事务想操作这个数据,必须

等待当前的事务提交或者回滚之后,释放了这个锁,下一个事务才能继续来抢这个锁,去执行它的事务。关于

锁和mvcc的知识,内容比较多,后面单独一篇文章来讲。

一致性:

一致性是指这个事务执行之后,数据库的完整性约束没有被破坏,事务执行前后都是合法的一个数据状

态,比如这个数据库的主键要唯一,字段类型大小,长度要符合这要求,还有外界的约束要符合要求。这个一

致性,是事务追求的最终目标,前面所提到的原子性、持久性、隔离性,都是为了保证数据库最终状态的一致

性。

六、事务控制

1. 查看事务参数状态

show variables like '%commint%'

2. 查看事务提交模式

SELECT @@autocommit;

3. 修改事务提交模式:手动提交模式

set autocommit = false;  或  set autocommit = 0;

上面语句执行之后,它之后的所有sql,都需要手动提交才能生效,直到恢复自动提交模式。

4. 恢复自动提交模式

set autocommit = true; 或 set autocommit = 1;

5. 开启事务

START TRANSACTION 或 BEGIN;

6. 提交事务

COMMIT;

7. 回滚事务

ROLLBACK;

8. 自动提交模式下开启事务

start transaction;(1)....(3)commit; 或 rollback;   此时,在(1)和(3)之间的语句是属于手动提交模式,其他的仍然是自动提交模式。

注意:

MySQL的事务默认是自动提交的,也就是说,当执行完一条DML语句时,MySQL会立即隐式的提交事务。

七、并发事务问题

1. 脏读

脏读即一个事务读到另外一个事务还没有提交的数据

模板一

比如B读取到了A未提交的数据。

模板二

2. 不可重复读

不可重复读即一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。

模版一

事务A两次读取同一条记录,但是读取到的数据却是不一样的。

模板二

3. 虚读/幻读

虚读/幻读即一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存

在,好像出现了 "幻影"。

模板一

模板二

4. 第一类更新

一个事务覆撤销时,把已提交的另一个事务的更新数据覆盖看(严重)

5. 第二类更新

一个事务覆盖另外一个事务已经提交的数据,造成另外一个事务所做的操作丢失

八、事务隔离级别

1. 简介

为了解决并发事务所引发的问题,在数据库中引入了事务隔离级别。

在标准 SQL 规范中定义了四种隔离级别:

READ UNCOMMITED < READ COMMITED < REPEATABLE READ < SERIALIZABLE

其中√表示可能出现的情况

2. Oracle 支持 2 种事务隔离级别

① READ-COMMITED(Oracle默认事务隔离级别)

② SERIALIZABLE

Oracle 支持 READ COMMITED(缺省)和 SERIALIZABLE

3. MySQL支持 4 种事务隔离级别

① read-uncommitted

② read-committed

③ repeatable-read(MySQL默认事务隔离级别)

④ serializable

MySQL 支持 四种隔离级别,缺省为 REPEATABLE READ,

默认情况下也不会出现幻读和第一类丢失的问题,所以只需处理第二类丢失更新的问题。

4. 隔离级别操作

查看当前会话隔离级别

SELECT @@tx_isolation;

查看系统隔离级别

select @@global.tx_isolation;

设置当前会话隔离级别

set session transaction isolation level repeatable read;

设置系统隔离级别

set global transaction isolation level repeatable read;

查看锁资源使用情况

select * from information_schema.INNODB_LOCKS;

查看锁等待

select * from information_schema.INNODB_LOCK_WAITS;

查看事务

select * from information_schema.INNODB_TRX;


http://www.mrgr.cn/news/33285.html

相关文章:

  • 【LLM】Ollama:本地大模型使用
  • 从编辑器到自动化脚本,提高编程效率的必备工具秘籍
  • 【C语言】const char*强制类型转换 (type cast)的告警问题
  • Qt 每日面试题 -2
  • react:React Hook函数
  • 华为OD机试真题-IPv4地址转换成整数-2024年OD统一考试(E卷)
  • 打开C嘎嘎的大门:你好,C嘎嘎!(2)
  • 语言RPA流程组件介绍--获取网页信息
  • 上位机图像处理和嵌入式模块部署(linux小系统开发)
  • 字符串 下【KMP再续】
  • GitHub每日最火火火项目(9.22)
  • 【Elasticsearch系列十八】Ik 分词器
  • 解锁电商新视野:京东商品详情API——您的精准商品信息探索利器
  • Java后端中的延迟队列实现:使用Redis与RabbitMQ的不同策略
  • AI学习指南深度学习篇-Adadelta简介
  • JavaScript(二)
  • 【Linux 从基础到进阶】 AWS云服务在Linux上的应用
  • C\C++内存管理详解
  • PHP在将数据存储到数据库之前如何转义数据
  • Java项目实战II基于Java+Spring Boot+MySQL的植物健康系统(开发文档+源码+数据库)