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就会超时

相关推荐
山峰哥10 小时前
数据库工程与SQL调优实战:从原理到案例的深度解析
java·数据库·sql·oracle·性能优化·编辑器
OpsEye10 小时前
Redis 内存碎片的隐形消耗——如何用 memory purge 命令释放空间?
运维·网络·数据库·redis·缓存·内存·监控
朝花不迟暮10 小时前
使用Android Studio生成apk,卡在Running Gradle task ‘assembleDebug...解决方法
android·ide·android studio
yngsqq10 小时前
使用VS(.NET MAUI)开发第一个安卓APP
android·.net
施嘉伟10 小时前
一次典型的 SQL 性能问题排查:临时表导致的隐藏性能陷阱
数据库·sql
Android-Flutter10 小时前
android compose LazyVerticalGrid上下滚动的网格布局 使用
android·kotlin
Android-Flutter10 小时前
android compose LazyHorizontalGrid水平滚动的网格布局 使用
android·kotlin
IT_Octopus10 小时前
java 实体属性 Map 解决 mybatis-plus wrapper selectone 查mysql json类型为null 问题
java·mysql·mybatis
IT 乔峰10 小时前
分享一个负载均衡的NDB高可用集群架构+部署详细说明
数据库·架构·负载均衡