【MySQL#5】 事务的概念及ACID属性和使用

【MySQL#5】 事务的概念及ACID属性和使用
在这里插入图片描述

📃个人主页:island1314

⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏 💞 💞 💞

  • 生活总是不会一帆风顺,前进的道路也不会永远一马平川,如何面对挫折影响人生走向 – 《人民日报》

🔥 目录


一、什么是事务

  • 定义:由一条或者多条 sql 语句构成的 sql 集合体,这个集合体合在一起共同要完成某种任务。MySQL通过多线程实现存储工作,因此在并发访问场景中,事务确保了数据操作的一致性和可靠性。 事务还规定 不同的客户端看到的数据是不相同的
    • 事务就是要做的或所做的事情,主要用于 处理操作量大,复杂度高的数据
    • 假设一种场景:你毕业了, 学校的教务系统后台 MySQL 中,不在需要你的数据,要删除你的所有信息(一般不会:) ), 那么要删除你的 基本信息(姓名,电话,籍贯等)的同时,也删除和你有关的其他信息,比如:你的各科成绩,你在校表 现,甚至你在论坛发过的文章等。这样,就需要多条 MySQL 语句构成,那么所有这些操作合起来,就构成了一个事务。
  • 背景:在没有控制的情况下进行 CURD 操作(创建、更新、读取、删除)可能导致数据不一致的问题。例如,在火车票售票系统中,两个用户同时尝试购买最后一张票,可能造成同一张票被卖出两次的现象
image-20250210103313229

CURD满足什么属性,能解决上述问题?

买票的过程得是原子的吧买票互相应该不能影响吧买完票应该要永久有效吧买前,和买后都要是确定的状态吧

二、事务的属性及使用

2.1 事务的 ACID 属性

一个 MySQL 数据库,可不止你一个事务在运行,同一时刻,甚至有大量的请求被包装成 事务,在向 MySQL 服务器发起事务处理请求。而每条事务至少一条 SQL ,最多很多 SQL ;这样如果大 家都访问同样的表数据,在不加保护的情况,就绝对会出现问题。甚至,因为事务由多条 SQL 构成,那 么,也会存在执行到一半出错或者不想再执行的情况,那么已经执行的怎么办呢?

所有,一个完整的事务,绝对不是简单的 sql 集合,还需要满足如下四个属性:

  • 原子性(Atomicity,或称不可分割性):事务的所有操作要么全部完成,要么完全不执行,任何一部分失败都会导致整个事务的回滚。
  • 一致性(Consistency):事务前后,数据库应保持一致的状态,即事务不应破坏数据库的完整性约束。(通过原子性,隔离性,持久性 AND 用户的配合实现一致性)
    • 这表示写入的资料必须完 全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工 作。
  • 隔离性(Isolation,又称独立性):事务之间的执行是相互隔离的,一个事务的执行不会受到其他事务的影响。
    • 允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务 并发执行时由于交叉执行而导致数据的不一致。
    • 事务隔离分为不同级别,包括读未提交( Read uncommitted )、读提交(read committed)、可重复读( repeatable read )和串行化 ( Serializable
  • 持久性(Durability):一旦事务提交,其对数据库所做的改变将是永久性的,即使系统发生故障也不会丢失。

2.2 为什么存在事务

刚才说的是多个 sql交叉执行 可能会出现并发问题,进而导致数据不一致,进而导致数据完整性,这都知道。但是对事务的理解不能光站在程序员角度理解,一定要站在数据库使用者角度考虑。

  • 事务被 MySQL 编写者设计出来,但是事务并不是天然就有的,而是在用一段时间发现要有这个事务。本质是为了当应用程序访问数据库的时候,事务能够简化我们的编程模型,不需要我们去考虑各种各样的潜在错误和并发问题。
  • 只需要把告诉我你要干什么,把你的 sql 给我,我帮你封装成事务,帮你去运行。可以想一下当我们使用事务时,要么提交,要么回滚,我们不会去考虑网络异常了,服务器宕机了,同时更改一个数据怎么办对吧?这些问题统统不考虑。
  • 因此事务本质上是为了应用层服务的,是为了让上层的应用服务更好的使用数据库。而不是伴随着数据库系统天生就有的。

备注:我们后面把 MySQL 中的一行信息,称为 一行记录

总结

  • 解决并发问题:事务的设计初衷是为了应对并发操作带来的数据不一致问题,确保数据的完整性和一致性。
  • 服务应用层:事务的本质是为应用层服务的,它简化了开发者的编程模型,使开发者可以专注于业务逻辑,而不用担心底层的数据操作细节。
  • 对来的一大批 SQL–打包成事务-- 先描述再组织 管理

2.3 事务的版本支持

  • 支持引擎:在MySQL中,只有使用了 InnoDB 数据库引擎的数据库或表才 支持事务MyISAM 不支持。
  • 查看引擎
mysql>show engines \G;# ...***************************5.row***************************Engine: MyISAM Support: YES Comment: MyISAM storage engineTransactions: NO XA: NO Savepoints: NO# ...***************************7.row***************************Engine: InnoDB Support: DEFAULTComment: Supports transactions,row-level locking,andforeignkeysTransactions: YES XA: YES Savepoints: YES # ...9rowsinset(0.01 sec)

2.4 事务的提交方式

  • 自动提交:事务默认是自动提交的。
  • 手动提交

查看

show variables like'autocommit';+---------------+-------+| Variable_name |Value|+---------------+-------+| autocommit |ON|+---------------+-------+

设置

set autocommit=0;-- 设置为手动提交set autocommit=1;-- 设置为自动提交

2.5 事务的常见操作方式

linux下 mysql 是一个命令行式的客户端进程。但不仅如此 mysql 客户端还有很多其他客户端如图形化界面版的,还有其他语言版的。

  • mysql 是一套网络服务进程也就意味着除了本地主机,远端主机也可以连接 myql

换句话说 mysql 服务器可能会被多个客户端同时访问

root@VM-8-10-ubuntu:/home/lighthouse# netstat -nltp; # 需要切换成 root 用户 Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 000.0.0.0:33060.0.0.0:* LISTEN 13847/mysqld tcp 000.0.0.0:220.0.0.0:* LISTEN 823/sshd: /usr/sbin 

为了更好做事务方面演示,我们将 mysql 的默认隔离级别设置成读未提交,隔离级别后面我们专门具体说。

设置全局事务隔离级别读为提交

setglobaltransactionisolationlevelREADUNCOMMITTED;

需要重启终端,进行查看,可以看到

mysql> quit Bye # 需要重启终端,进行查看 mysql>select @@tx_isolation;# 5.0 版本 mysql>select @@transaction_isolation;# 8.0 版本+-------------------------+| @@transaction_isolation|+-------------------------+|READ-UNCOMMITTED|+-------------------------+REPEATABLE-READ# 默认是这个
  • 然后新起两个 mysql 客户端,为什么这么做呢,我们主要是为了研究事务,研究事务就要研究多个客户端并发访问的情况。
  • 其次 mysql 是有隔离性和隔离级别的,所以目前把隔离级别跳到最低,一个 mysql 做操作,另一个 mysql 就能看到。
  • 这样很能清楚看到事务交叉所带来的问题。

【案例】:

① 创建一个员工表

createtableifnotexists account( id intprimarykey, name varchar(50)notnulldefault'', blance decimal(10,2)notnulldefault0.0)ENGINE=InnoDBDEFAULTCHARSET=UTF8;

两个mysql客户端可以并发访问这张表,准备工作全部就绪,下面我们来做试验!

mysql>show processlist;+------+-----------------+-----------+--------+---------+---------+------------------------+------------------+| Id |User| Host | db | Command |Time| State | Info |+------+-----------------+-----------+--------+---------+---------+------------------------+------------------+|5| event_scheduler | localhost |NULL| Daemon |4481367| Waiting on empty queue |NULL||2385| root | localhost | learn3 | Query |0| init |show processlist ||2386| root | localhost | learn3 | Sleep |41||NULL|+------+-----------------+-----------+--------+---------+---------+------------------------+------------------+

⭕正常演示 - 事务的开始与回滚

② 启动事务

starttransaction;-- 方式一begin;-- 方式二

③ 设置保存点

savepoint s1;

④ 回滚到保存点

rollback s1;

设置和回滚使用如下

image-20250210114633672

⑤ 提交事务

  • 如果想结束这个事务,那就提交一下,相当于把这个事务提交了
commit;

⑥ 回滚事务

  • 那我们以后是不是必须要设置保持点才能回滚呢?
  • 并不是!没有保存点只是没有办法定向回滚了。但是可以直接回滚到最开始!
rollback;

直接回滚到最开始!然后数据就全没了。即使是结束事务,回归到单 sql 也是没有的。

非正常操作

下面都是默认开启自动提交的,而且 演示 1 和 演示 2 表开始的时候均无数据(empty)

非正常演示1: 证明未 commit ,客户端崩溃,MySQL自动会回滚(隔离级别设置为读未提交)

-- 终端Abegin;-- 开启事务insertinto account values(1,'张三',100);-- 插入记录 mysql>select*from account;-- 数据已经存在,但没有commit,此时同时查看终端B+----+--------+--------+| id | name | blance |+----+--------+--------+|1| 张三 |100.00|+----+--------+--------+

让 A 中止,然后再查看A终止前后 B 的数据,如下:

mysql> Aborted -- ctrl + \ 异常终止MySQL-- 终端B mysql>select*from account;-- 终端 A 崩溃前+----+--------+--------+| id | name | blance |+----+--------+--------+|1| 张三 |100.00|+----+--------+--------+1rowinset(0.00 sec) mysql>select*from account;-- 终端 A 崩溃后 Empty set(0.00 sec)

上面我们发现 数据自动进行了回滚

非正常演示2 - 证明 commit 了,客户端崩溃,MySQL数据不会在受影响,已经持久化

-- 终端 A mysql>begin;-- 开启事务 mysql>insertinto account values(1,'张三',100);-- 插入记录 Query OK,1row affected (0.00 sec) mysql>commit;-- 提交事务 Query OK,0rows affected (0.04 sec) mysql> Aborted -- ctrl + \ 异常终止MySQL-- 终端 A 终止后,切换终端 B 查看数据 mysql>select*from account;+----+--------+--------+| id | name | blance |+----+--------+--------+|1| 张三 |100.00|+----+--------+--------+1rowinset(0.00 sec)

相比于上面这里我们在异常终止终端 A 之前,已经把数据 commit 了,然后再查看 B 发现数据还在,没有回滚 – 持久化保存

  • 数据存在了,所以commit 的作用是将数据持久化到MySQL中

非正常演示3 - 对比演示 1。证明 begin 操作会自动更改提交方式,不会受 MySQL 是否自动提交影响

-- 终端 A,历史是有数据的 mysql>select*from account;+----+--------+--------+| id | name | blance |+----+--------+--------+|1| 张三 |100.00|+----+--------+--------+-- 事务 A 开始时: 是默认自动提交的查 mysql>show variables like'autocommit';+---------------+-------+| Variable_name |Value|+---------------+-------+| autocommit |ON|+---------------+-------+1rowinset(0.00 sec)-- 关闭自动提交 mysql>set autocommit=0; mysql>show variables like'autocommit';+---------------+-------+| Variable_name |Value|+---------------+-------+| autocommit |OFF|+---------------+-------+

然后再开始进行事务操作,如下:

mysql>begin;-- 开启事务 mysql>insertinto account values(2,'李四',10000);-- 插入记录-- 在终端 A 中查看插入记录 mysql>select*from account;+----+--------+----------+| id | name | blance |+----+--------+----------+|1| 张三 |100.00||2| 李四 |10000.00|+----+--------+----------+

然后再次让终端 A 异常终止,查看 A 终止前后 B 的数据,如下:

mysql> Aborted -- 再次异常终止 A -- 切换到终端 B mysql>select*from account;-- 终端A崩溃前+----+--------+----------+| id | name | blance |+----+--------+----------+|1| 张三 |100.00||2 | 李四 |10000.00|+----+--------+----------+2rowsinset(0.00 sec) mysql>select*from account;-- 终端A崩溃后,自动回滚+----+--------+--------+| id | name | blance |+----+--------+--------+|1| 张三 |100.00|+----+--------+--------+

此时我们发现,这个结果和 演示1 是类似的,可以知道 begin 与是否设置 set autocommit 无关,证明正确

④ 非正常演示4 - 证明单条 SQL 与事务的关系【autocommit】

实验一:

-- 终端 A,开始时是有数据的 mysql>select*from account;+----+--------+--------+| id | name | blance |+----+--------+--------+|1| 张三 |100.00|+----+--------+--------+ mysql>set autocommit=0;-- 关闭自动提交 mysql>insertinto account values(2,'李四',10000);-- 插入记录 Query OK,1row affected (0.00 sec) mysql>select*from account;-- 在终端 A 中查看结果,已经插入。此时可以在查看终端B+----+--------+----------+| id | name | blance |+----+--------+----------+|1| 张三 |100.00||2| 李四 |10000.00|+----+--------+----------+2rowsinset(0.00 sec) mysql>^D Bye -- ctrl + \ or ctrl + d,终止终端 -- 终端B mysql>select*from account;-- 终端A崩溃前+----+--------+----------+| id | name | blance |+----+--------+----------+|1| 张三 |100.00||2| 李四 |10000.00|+----+--------+----------+2rowsinset(0.00 sec) mysql>select*from account;-- 终端A崩溃后+----+--------+--------+| id | name | blance |+----+--------+--------+|1| 张三 |100.00|+----+--------+--------+
  • 上面数据发生了回滚

实验二:

-- 终端A mysql>show variables like'autocommit';-- 开启默认提交+---------------+-------+| Variable_name |Value|+---------------+-------+| autocommit |ON|+---------------+-------+1rowinset(0.00 sec) mysql>insertinto account values(2,'李四',10000); mysql>select*from account;-- 数据已经插入+----+--------+----------+| id | name | blance |+----+--------+----------+|1| 张三 |100.00||2| 李四 |10000.00|+----+--------+----------+2rowsinset(0.00 sec) mysql> Aborted -- 异常终止-- 终端B mysql>select*from account;-- 终端A崩溃前+----+--------+----------+| id | name | blance |+----+--------+----------+|1| 张三 |100.00||2| 李四 |10000.00|+----+--------+----------+ mysql>select*from account;-- 终端A崩溃后,并不影响,已经持久化。autocommit起作用+----+--------+----------+| id | name | blance |+----+--------+----------+|1| 张三 |100.00||2| 李四 |10000.00|+----+--------+----------+

结论

  • 未提交时客户端崩溃:事务可以手动回滚,同时操作异常时,MySQL 会自动回滚未提交的事务。
  • 已提交时客户端崩溃:只要输入begin 或者 start transaction,事务便必须要通过 commit 提交,才会持久化,与是否设置 set autocommit 无关
  • 单条SQL与事务:默认情况下,对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交。如果关闭自动提交,单条SQL语句也需要手动提交或回滚【select 有特殊情况,因为 MySQL 有 MVCC】

从上面的例子,我们能看到事务本身的原子性(回滚),持久性(commit)

注意事项

  • 回滚:如果没有设置保存点,只能回滚到事务的开始,直接使用 rollback(前提是事务 还没有提交)
  • 提交:事务提交后无法回滚。
  • 保存点:可以选择回退到哪个保存点。
  • 版本支持InnoDB 支持事务, MyISAM 不支持事务
  • 启动事务:可以使用 start transactionbegin
在这里插入图片描述

Read more

Nginx 按照流量比例反向代理配置详解

Nginx 按照流量比例反向代理配置详解

在灰度发布、A/B 测试等场景中,我们常需要将流量按一定比例分发到不同后端服务。Nginx 提供了 split_clients 模块,配合 map 和 proxy_pass,可以轻松实现基于客户端特征的流量比例分配。本文将从 Nginx 安装、Systemd 管理、完整配置到临时调试方案,提供一份可直接复制使用的完整教程。 一、Nginx 安装(Ubuntu 22.04) 说明:以下操作以 root 用户执行。 # 1. 卸载系统自带 nginxapt remove nginx apt purge nginx nginx-common -y # 2. 更新系统并安装编译依赖sudoapt update sudoaptinstall -y libpcre3 libpcre3-dev

By Ne0inhk
Oracle索引

Oracle索引

一、索引介绍 1.为什么使用索引 索引是存储引擎用于快速找到数据记录的一种数据结构,就好比一本教课书的目录部分,通过目录中找到对应文章的页码,便可快速定位到需要的文章。Oracle中也是一样的道理,进行数据查找时,首先查看查询条件是否命中某条索引,符合则通过索引查找相关数据,如果不符合则需要全表扫描,即需要一条一条地查找记录,直到找到与条件符合的记录。 如上图所示,数据库在没有索引的情况下,数据分布在硬盘的不同位置上面,读取数据时,摆臂需要前后摆动查找,这样操作非常耗时。如果数据顺序摆放,那么也需要从1到6行按顺序读取,这样就相当于进行了六次IO操作,依旧非常耗时。如果我们不借助任何索引结构帮助我们快速定位数据的话,我们查找Col2=89这条数据,就要逐行去查找、去比较。从Col2=34开始,进行比较,发现不是,继续下一行。我们当前的表只有不到10行数据,但如果表很大的话,有上千万条数据,就意味着要做很多很多次磁盘I/0才能找到。现在要查找Col2=89这条记录。CPU必须先去磁盘查找这条记录,找到之后加载到内存,再对数据进行处理。这个过程最耗时间的就是磁盘I/O(涉及到磁盘的旋

By Ne0inhk
YOLO26:面向实时目标检测的关键架构增强与性能基准测试

YOLO26:面向实时目标检测的关键架构增强与性能基准测试

YOLO26核心总结 1. 架构简化:移除分布焦点损失(DFL),简化边界框回归,提升导出兼容性。 2. 端到端推理:采用无NMS设计,直接输出检测结果,降低延迟与部署复杂度。 3. 训练增强:引入渐进损失平衡(ProgLoss)与小目标感知标签分配(STAL),提升小目标检测稳定性。 4. 优化器创新:使用MuSGD优化器,结合SGD与Muon优势,加速模型收敛。 5. 多任务支持:统一框架支持检测、实例分割、姿态估计、定向检测与分类。 6. 边缘优化:支持FP16/INT8量化,在Jetson等设备上实现低延迟实时推理。 7. 性能表现:在COCO等基准上达到高精度,CPU推理速度较前代提升最高43%。 应用场景:适用于无人机、机器人、智能制造等资源受限的边缘设备实时视觉任务。  博主简介 AI小怪兽 | 计算机视觉布道者 | 视觉检测领域创新者 深耕计算机视觉与深度学习领域,专注于视觉检测前沿技术的探索与突破。长期致力于YOLO系列算法的结构性创新、

By Ne0inhk
给数据“立规矩” —— MySQL 新手必学的表约束全指南

给数据“立规矩” —— MySQL 新手必学的表约束全指南

🔥海棠蚀omo:个人主页                 ❄️个人专栏:《初识数据结构》,《C++:从入门到实践》,《Linux:从零基础到实践》,《Linux网络:从不懂到不会》,《MySQL:新手入门指南》                 ✨追光的人,终会光芒万丈 博主简介: 目录 一.为什么要有表的约束? 二.表的约束 2.1空属性 2.2默认值 2.3列描述 2.4zerofill 2.5主键 2.5.1复合主键 2.6自增长 2.7唯一键 5.8外键 前言: 在上一篇文章中我们讲解了MySQL中的各种数据类型,那么正是因为有了各种数据类型,才会有今天我们要讲的表的约束相关知识,那么这中间到底是怎么回事呢?下面我们就一起来看看吧。 一.为什么要有表的约束? 在上一篇文章中,我们认识了很多的数据类型,并在它们的下面我们也通过例子进行了演示,

By Ne0inhk