数据库事务

1.事务:

事务把一组SQL语句打包成一个整体,在这组SQL执行过程中,要么全部成功,要么全部失败,这组SQL语句可以是一条,也可以是多条。单个SQL自己本身就是一个事务!

这里举出一个例子:张三转账给李四100元;

sql 复制代码
create table bank_account(id bigint primary key auto_increment,name varchar(20),balance decimal(10,2) not null);
insert into bank_account(name,balance) values("张三",1000);
insert into bank_account(name,balance) values("李四",1000);
select * from bank_account;
update bank_account set balance=balance-100 where name="张三";
update bank_account set balance=balance+100 where name="李四";
  1. 张三的账⼾余额减少 100 ,变成 900 ,李四的账⼾余额增加了 100 ,变成 1100 ,不能出现张
    三的余额减少⽽李四的余额没有增加的情况;
  2. 张三和李四在发⽣转账前后的总额不变,也就是说转账前张三和李四的余额总数为
    1000+1000=2000 ,转账后他们的余额总数为 900+1100=2000 ;
  3. 转账后的余额结果应当保存到存储介质中,以便以后读取;
  4. 还有⼀点需要要注意,在转账的处理过程中张三和李四的余额不能因其他的转账事件⽽受到⼲扰;
    以上这四点在事务的整个执⾏过程中必须要得到保证,这也就是事务的 ACID 特性

2.事物的ACID特性:

2.1.原子性:

⼀个事务中的所有操作,要么全部成功,要么全部失败,不会出现只执 ⾏了⼀半的情况,如果事务在执⾏过程中发⽣错误,会回滚( Rollback )到事务开始前的状 态,就像这个事务从来没有执⾏过⼀样;

2.2.一致性:

在事务开始之前和事务结束以后,数据库的完整性不会被破坏。这表 ⽰写⼊的数据必须完全符合所有的预设规则,包括数据的精度、关联性以及关于事务执⾏过程中服 务器崩溃后如何恢复;也就是说:如果SQL执行成功,那么得到的就是一致的结果,如果SQL执行出错,那么通过回滚(rollback)回到最初的状态,结果也是一致的,重要的就是避免卡在中间这种 "不上不下" 的状态。

2.3.持久性:

事务处理结束后,对数据的修改将永久的写⼊存储介质(硬盘),即便系统故障 也不会丢失。

2.4.隔离性:

数据库允许多个并发事务同时对数据进⾏读写和修改,隔离性可以防⽌多 个事务并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务可以指定不同的隔离级别,以权衡在不 同的应⽤场景下数据库性能和安全。

2.4.1:脏读问题:

2.4.1.1【简介】:

简单来说就是事务一对某个数据进行了修改,但是在修改过程中,事务并没有提交;事务二对这个数据进行读取读取到的数据可能是已经被事务一修改的数据,事务二读到的这个数据很可能是脏数据,也就是临时的数据(只是中间结果,不是最终状态);

这时如果事务一进行了回滚或者进行了其他的数据修改,那么事务二读到的数据就是错误的。

2.4.1.2【解决问题的方案】:

就是数据库规定,只有在事务一把事务提交了之后,才允许事务二读取到修改的数据。

下面给出一副示意图进行解释:

2.4.2.不可重复读问题:

2.4.2.1【简介】:

同一事务里,两次读取同一数据,结果不一样,因为中间被其他事务修改提交了。

2.4.2.2【解决问题的方案】:

就是给读操作加锁,也就是说有事务在进行读操作的时候,其他事务不能进行修改;虽然加了锁之后可以保证不出现重复读的问题,但由于事务之间无法同时进行,会导致并发程度降低,但隔离性提高,效率降低,数据的准确性提高。

2.4.3:幻读问题

2.4.3.1【简介】:

同一个事务中,针对某个查询,两次读取到的结果集(查询操作得到的临时表)不同。

例如:如图所示,第一次读取数据得到三条数据,而第二次读取数据后却得到四条数据,在中间的过程有其他事务参与并添加了其他数据,这就是幻读问题,也可以把它看作不可重复读的更进一步。

2.4.3.2.【解决方案】:

需要进行 "串行化" 彻底让服务器 "串行的" 处理事务,一条一条的处理,彻底避免幻读问题;这也会造成一些影响,并发程度降低,隔离性提高,执行效率进一步降低,数据准确性进一步提高!

总结一下,从上面这些三种问题我们也可以看出并发性与准确性无法同时兼得,mysql的隔离性给程序员提供了几个隔离级别,让程序员自主选择。

3.事务的使用:

3.1:查看支持事务的存储引擎:

要使⽤事务那么数据库就要⽀持事务,在MySQL中⽀持事务的存储引擎是InnoDB,可以通过
show engines; 语句查看:

3.2:语法:

sql 复制代码
# 开始⼀个新的事务
START TRANSACTION;
# 或
BEGIN;
# 提交当前事务,并对更改持久化保存
COMMIT;
# 回滚当前事务,取消其更改
ROLLBACK;

START TRANSACTION 或 BEGIN 开始⼀个新的事务;

COMMIT 提交当前事务,并对更改持久化保存;

ROLLBACK 回滚当前事务,取消其更改;

⽆论提交还是回滚,事务都会关闭。

3.3:保存点:

保存点是事务里的 "临时存档点",可以回滚到某个存档点,不用全部撤销。

sql 复制代码
START TRANSACTION;

-- 操作1...
SAVEPOINT sp1;  -- 设置保存点 sp1

-- 操作2...
SAVEPOINT sp2;  -- 设置保存点 sp2

-- 操作3...

-- 回滚到 sp2,操作3 撤销,操作1、2 保留
ROLLBACK TO SAVEPOINT sp2;

-- 回滚到 sp1,操作2、3 撤销
ROLLBACK TO SAVEPOINT sp1;

-- 最终提交
COMMIT;

【举例】:

sql 复制代码
BEGIN;
INSERT INTO t VALUES(1);
SAVEPOINT a;

INSERT INTO t VALUES(2);
SAVEPOINT b;

INSERT INTO t VALUES(3);

ROLLBACK TO b; -- 删掉第三条
COMMIT;       -- 最终只提交 1、2

3.4:自动/手动提交事务

  • 自动提交:执行一条就提交一条,简单但不能回滚。
  • 手动提交:自己 begin → 执行 → commit/rollback,保证多条操作要么全成要么全败。

4.隔离性详解:

4.1.隔离级别:

(1).读未提交(READ UNCOMMITTED):

隔离性最弱,并发性最强 ==>最不准确,效率最高, ==> 脏读,不可重复读,幻读。

(2).读已提交(READ COMMITTED):

隔离性提高,并发性降低 ==>准确性提高了,效率降低 ==>脏读解决了,不可重复读,幻读仍然存在。

(3).可重复读(REPAEATABLE READ):MySQL默认的隔离级别

隔离性再次提高,并发性再次降低 ==>准确性再次提高,效率再次降低 ==>脏读,不可重复读解决,幻读仍然存在。

(4).串行化(SERIALIZABLE):

隔离性最高,并发性最低 ==>准确性最高,效率最低 ==>解决所有并发处理所产生的问题。

4.2查看和设置隔离级别:

4.2.1:查看隔离级别:

如果这里只有一个@,则代表用户自定义会话变量,不代表MySQL的系统变量(通常依靠@@来访问)

sql 复制代码
# 全局作⽤域
SELECT @@GLOBAL.transaction_isolation;
# 会话作⽤域
SELECT @@SESSION.transaction_isolation;
# 可以看到默认的事务隔离级别是REPEATABLE-READ(可重复读)

4.2.2:设置隔离级别:

1).通过sql语句修改:

sql 复制代码
# 通过GLOBAL|SESSION分别指定不同作⽤域的事务隔离级别
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level|access_mode;
# 隔离级别
level: {
 REPEATABLE READ # 可重复读
 | READ COMMITTED # 读已提交
 | READ UNCOMMITTED # 读未提交
 | SERIALIZABLE # 串⾏化
}
# 访问模式
access_mode: {
 READ WRITE # 表⽰事务可以对数据进⾏读写
 | READ ONLY # 表⽰事务是只读,不能对数据进⾏读写
}

a.GLOBAL:设置了之后,后续连上服务器的客户端都能生效;

b.SESSION:只针对当前连接生效;

c.**注意:**不管你用 SESSION 还是 GLOBAL 去改隔离级别,只要 MySQL 服务器重启,全部恢复成默认值(可重复读 RR)。

原因:SESSION只对当前连接有效,GLOBAL只修改当前内存里的配置,二者都不会写入配置文件;如果MySQL重启,内存就会清空,一切都会回到默认配置。

【示例】:

sql 复制代码
-- 设置全局事务隔离级别为串行化,后续所有事务生效,不影响当前事务
set global TRANSACTION ISOLATION level serializable;
-- 设置会话事务隔离级别为串行化当前会话后续所有事物生效,不影响当前事务,可以在任何时候执行
set session transaction isolation level serializable;
-- 如果不指定任何作用域,设置只针对下一个事务,随后的事务恢复之前的级别
set transaction isolation level serializable;

2).配置文件:通过打开文件位置找到MySQL command line...而后通过配置属性,如图所示界面的 "目标" 处进行权限修改!

4.3隔离级别各种问题案例:

4.3.1:脏读问题:

这里的场景是隔离级别为读未提交,所展示出来的问题是脏读问题:

这里我们修改隔离级别未read commited,并将account修改为3000(4000),会发现这里并没有生效,事务二(右图)中仍未3000,这时我们需要将数据库java118重启一下;

重启操作如下:关闭后会发现数据库图标会变灰,随后单击右键再打开数据库即可;

重启数据库之后,我们就可以解决脏读这个问题了,这里事务一中(右图)已修改为4000,但事务二中(左图)仍未3000。

4.3.2:不可重复读问题:

操作流程同上

4.3.3:幻读问题:

总结一下:

事务完。。。。。。

相关推荐
会飞的大可2 小时前
Redis 竞品与替代方案选型可行性分析报告
数据库·redis·缓存
昨夜见军贴06162 小时前
AI报告文档审核助力本地化升级:IACheck如何支撑食品加工行业数据安全与质量协同发展
大数据·人工智能
周杰伦的稻香2 小时前
PostgreSQL基础命令
数据库·postgresql
Mem0rin2 小时前
[Java/数据结构]线性表之链表
java·数据结构·链表
先做个垃圾出来………3 小时前
JSON序列化问题
数据库·json
财经资讯数据_灵砚智能3 小时前
全球财经资讯日报(日间)2026年4月2日
大数据·人工智能·python·语言模型·ai编程
我科绝伦(Huanhuan Zhou)3 小时前
InnoDB Undo Log 深度解析:从原理到实现(基于 MySQL 8.0)
数据库·mysql
鱼骨不是鱼翅3 小时前
数据处理与统计分析----沙箱
大数据
香香甜甜的辣椒炒肉3 小时前
Spring(1)基本概念+开发的基本步骤
java·后端·spring