MySQL事务

MySQL事务

事务的概念

事务就是将一组SQL语句打包成一个整体,这组SQL要么全部执行,要么都不执行,不会存在执行一半的问题

例如:此时有张三、李四两个用户,并且账余额都为1000,现在让张三给李四转100块

sql 复制代码
create table bank_account(
			name varchar(20),
			balance double );
			
insert into bank_account(name,account) values ('张三',1000),('李四',1000);
select name,account from bank_account;

update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四'; 

转账前

转账后

此时这里张三账户减少100变成了900,李四增加100变成1100

这里转账过程中必须得到保证,其余额不会受到其他的干扰,也就是事务的ACID特性
Atomicity (原⼦性) :一个事务中的所有操作,要么全部成功,要么全部失败,不会出现只执行了一半的情况,如果过程中发生错误,会回滚Rollback到事务执行前的状态
Consistency (⼀致性) :事务执行前和执行后都是合理的,符合预期结果,执行过程中不可以破坏数据库的完整性
Isolation (隔离性) :数据库是允许多个客户端并发事务同时对同一个数据进行操作,隔离性是防止这些事务之间相互影响,因此可以指定不同隔离级别平衡性能和安全
Isolation (隔离性):事务对数据库的修改都是持久的,保存在硬盘上,即使系统故障、重启服务器、重启数据库,都不会使数据丢失

如何使用事务

使用事务就要选择支持事务的引擎,MySQL中只有InnoDB支持事务Transactions,并且MySQL中默认的引擎也是InnoDB

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

START TRANSACTION 和 BEGIN都是表示开启一个事务

COMMIT提交事务,执行事务提交事务之后,这个事务才会生效

ROLLBACK回滚当前事务,将数据恢复到事务前的样子

这里提交和回滚都会使 事务关闭

使用事务

sql 复制代码
create table bank_account(
			name varchar(20),
			balance double );
			
insert into bank_account(name,balance) values ('张三',1000),('李四',1000);

原始数据

现在开始一个事务,让张三给李四转账100

sql 复制代码
start TRANSACTION;# 开启事务

update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四'; 

COMMIT;#提交事务
select name,balance from bank_account;

此时这里数据被正常修改了

回滚事务

sql 复制代码
start TRANSACTION;

update bank_account set balance = balance - 100 where name = '张三';
update bank_account set balance = balance + 100 where name = '李四'; 
# 回滚前
select name,balance from bank_account;
rollback;
# 回滚后
select name,balance from bank_account;

第一个查询回滚前的查询

第二个回滚后的查询又回到了执行事务前的数据

设置保存点,让其回滚到这个保存点的位置

sql 复制代码
start TRANSACTION;#开启事务

update bank_account set balance = balance - 100 where name = '张三';

SAVEPOINT point1;# 设置保存点
update bank_account set balance = balance + 100 where name = '李四'; 
# 回滚前
select name,balance from bank_account;
rollback to point1;# 回滚到保存点位置
# 回滚后
select name,balance from bank_account;

回滚前数据

这里回滚到修改李四数据前面,所以此时张三余额-100,因为回滚此时李四余额没变

自动/手动提交事务

sql 复制代码
show variables like 'autocommit';

这里默认是自动提交 比如插入、删除和修改其都会自动开启一个事务完成后自动提交,异常就进行回滚

sql 复制代码
# 设置自动提交
set autocommit = 1;
set autocommit = ON;
# 设置手动提交
set autocommit = 0;
set autocommit = OFF;

但是这里我们START TRANSACTION 或 BEGIN 开启事务 ,就必须使用commit提交或者rollback回滚才会持久,和这里set autocommit设置自动手动提交并没有关系

事务隔离级别

事务是具有隔离性的,并且事务间有不同程度的隔离,也就是事务的隔离级别 ,不同隔离级别在性能和安全方面有取舍,需要根据事务的需要,有的是注重安全,有的注重效率

READ UNCOMMITTED 读未提交

READ COMMITTED 读已提交

REPEATABLE READ 不可重复读(默认)

SERIALIZABLE 串行化

sql 复制代码
# 查询全局
SELECT @@GLOBAL.transaction_isolation;
#会话作用域
SELECT @@SESSION.transaction_isolation;


sql 复制代码
#修改隔离级别
# 方式一
SET GLOBAL transaction_isolation = 'SERIALIZABLE';
SET SESSION transaction_isolation = 'REPEATABLE-READ'; 

#方式二
SET @@GLOBAL.transaction_isolation='SERIALIZABLE';
SET @@SESSION.transaction_isolation='REPEATABLE-READ';

READ UNCOMMITTED

这个隔离级别下,对读取数据时不做任何限制,所以并发性高,效率高,但是安全性较低,出现数据安全问题
脏读问题

例如:事务1执行一条修改UPDATE操作,但是没有执行COMMIT提交
事务2进行读取这条数据 ,但是此时事务1将这条数据进行回滚 ,此时事务2读取的数据就是错误的,这就是"脏读 ",此时事务2可能就会那这个错误的数据进行操作,都是错误的

这里使用Navicat建立两个连接,两个连接分别创建一个查询来表示两个事务

sql 复制代码
# 开启事务
create table bank_account(
			 id int primary key auto_increment,
			 name varchar(20),
			 balance int);

SELECT @@GLOBAL.transaction_isolation;

SET GLOBAL transaction_isolation = 'READ-UNCOMMITTED';

insert into bank_account(id,name,balance) values(null,'张三',1000),(null,'李四',2000);
start TRANSACTION;

update bank_account set balance = 10000 where name = '张三';

select id,name,balance from bank_account;

rollback;
select id,name,balance from bank_account;
sql 复制代码
# 开启事务
start TRANSACTION;

select id,name,balance from bank_account;

此时这里将张三余额设置成10000,事务2读取到了这个修改,但是事务1将其回滚,此时事务2那10000这条数据进行后面操作就是错误的

此时这里问题是事务1并没有提交事务,事务2就读取到了,因此需要提高隔离级别

READ COMMITTED

此时这里读取到的数据就会是事务提交之后的数据

不可以重复读 :这样解决了"脏读 "问题,但是有不可以重复读 问题,此时事务1进行了修改某个数据,但是未提交,事务2此时读取数据是开启事务前的,但是如果事务1提交了这个事务,此时事务2中读取到这条数据又是修改后的数据,此时一个事务2中读取一条数据记录,但是读取的结果却不一样

此时这个问题就需要不可重复读来解决这个问题

REPEATABLE READ



幻读问题 :一个事务内读取到的结果集不同

因为这里同一个事务读取到的结果集是相同的,因此当事务1提交了一个事务事务2进行读取整个数据 ,此时事务2中都是这个数据,但是如果此时事务3对其这个数据进行插入一条数据 ,但是此时无法得到这个新插入数据导致结果集不同 幻读问题

SERIALIZABLE

根本上解决幻读问题,一个事务执行完,才可以执行下一个事务

上面这样,只有事务2提交,此时事务3才可以执行,事务2中不存在幻读问题了

此时事务3就会超时

相关推荐
NineData19 分钟前
NineData 迁移评估功能正式上线
数据库·dba
火柴就是我5 小时前
让我们实现一个更好看的内部阴影按钮
android·flutter
NineData6 小时前
数据库迁移总踩坑?用 NineData 迁移评估,提前识别所有兼容性风险
数据库·程序员·云计算
赵渝强老师8 小时前
【赵渝强老师】PostgreSQL中表的碎片
数据库·postgresql
砖厂小工12 小时前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
全栈老石12 小时前
拆解低代码引擎核心:元数据驱动的"万能表"架构
数据库·低代码
张拭心12 小时前
春节后,有些公司明确要求 AI 经验了
android·前端·人工智能
张拭心13 小时前
Android 17 来了!新特性介绍与适配建议
android·前端
Kapaseker15 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴15 小时前
Android17 为什么重写 MessageQueue
android