带你了解什么是mysql事务(附代码演示)
来源:SegmentFault
时间:2023-01-11 09:53:00 276浏览 收藏
知识点掌握了,还需要不断练习才能熟练运用。下面golang学习网给大家带来一个数据库开发实战,手把手教大家学习《带你了解什么是mysql事务(附代码演示)》,在实现功能的过程中也带大家重新温习相关知识点,温故而知新,回头看看说不定又有不一样的感悟!
事务
数据准备
-- 创建数据表 CREATE TABLE account ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(10), balance DOUBLE ); -- 添加数据 INSERT INTO account (name, balance) VALUES ('张三', 1000), ('李四', 1000);
概述
在实际的开发过程中,一个业务操作如:转账,往往是要多次访问数据库才能完成的。转账是一个用户扣钱,另一个用户加钱。如果其中有一条 SQL 语句出现异常,这条 SQL 就可能执行失败。
事务执行是一个整体,所有的 SQL 语句都必须执行成功。如果其中有 1 条 SQL 语句出现异常,则所有的 SQL 语句都要回滚,整个业务执行失败。
代码演示
/* 模拟转账的操作 */ -- 张三账号-500 update account set balance = balance - 500 where name = '张三'; -- 李四账号+500 update account set balance = balance + 500 where name = '李四';
假设当张三账号上-500 元,服务器崩溃了。李四的账号并没有+500 元,数据就出现问题了。我们需要保证其中 一条 SQL 语句出现问题,整个转账就算失败。只有两条 SQL 都成功了转账才算成功。这个时候就需要用到事务.
分类
start transaction;
提交事务
commit;
回滚事务
rollback;
代码演示
-- 重置表数据 update account set balance = 1000; -- 开启事务 start transaction; -- 张三账户-500 update account set balance = balance - 500 where name = '张三'; -- 李四账号+500 update account set balance = balance + 500 where name = '李四'; -- 提交 commit; -- 查看表数据,数据已发现改变 select * from account;
-- 重置表数据 update account set balance = 1000; -- 开启事务 start transaction; -- 张三账户-500 update account set balance = balance - 500 where name = '张三'; -- 李四账号+500 update account set balance = balance + 500 where name = '李四'; -- 回滚 rollback; -- 查看表数据,数据未发现改变 select * from account;
如果事务中 SQL 语句没有问题,commit 提交事务,会对数据库数据的数据进行改变。 如果事务中 SQL 语句有问题,rollback 回滚事务,会回退到开启事务时的状态。
自动提交
MySQL 默认每一条 DML(增删改)语句都是一个单独的事务,每条语句都会自动开启一个事务,语句执行完毕 自动提交事务,MySQL 默认开始自动提交事务。
查看是否了开启自动提交事务
SELECT @@AUTOCOMMIT;-- @@表示全局变量,1 表示开启,0 表示关闭
取消自动提交事务
SET @@AUTOCOMMIT = 0;
开启自动提交事务
SET @@AUTOCOMMIT = 1;
代码演示
-- 重置表数据 update account set balance = 1000; -- 查看是否开启了自动提交事务 SELECT @@AUTOCOMMIT; -- 取消自动提交事务 SET @@AUTOCOMMIT = 0; -- 张三账户-500 update account set balance = balance - 500 where name = '张三'; -- 李四账号+500 update account set balance = balance + 500 where name = '李四'; -- 回滚 rollback; -- 查询表数据,数据未发生改变(自动提交事务已关闭,回滚,会导致前面的SQL失效) select * from account;
-- 重置表数据 update account set balance = 1000; -- 查看是否开启了自动提交事务 SELECT @@AUTOCOMMIT; -- 张三账户-500 update account set balance = balance - 500 where name = '张三'; -- 李四账号+500 update account set balance = balance + 500 where name = '李四'; -- 提交 commit; -- 查询表数据,数据已发生改变(自动提交事务已关闭,只有commit,才能提交数据) select * from account;
-- 重置表数据 update account set balance = 1000; -- 查看是否开启了自动提交事务 SELECT @@AUTOCOMMIT; -- 开启自动提交事务 SET @@AUTOCOMMIT = 1; -- 张三账户-500 update account set balance = balance - 500 where name = '张三'; -- 李四账号+500 update account set balance = balance + 500 where name = '李四'; -- 回滚 rollback; -- 查询表数据,数据已发生改变(自动提交事务已开启,回滚无效) select * from account;
事务原理
事务开启之后, 所有的操作都会临时保存到事务日志中, 事务日志只有在得到
SAVEPOINT 回滚点名字;
返回回滚点
ROLLBACK TO 回滚点名字;
代码演示
-- 重置表数据 update account set balance = 1000; -- 开启事务 start transaction; -- 李四账号-100 update account set balance = balance - 100 where name = '李四'; -- 设置回滚点 savepoint one_time; -- 李四账号再-100 update account set balance = balance - 100 where name = '李四'; -- 返回回滚点 rollback to one_time; -- 提交 commit; -- 查询表数据,发现李四账号只减少了100 select * from account;
事务特征
原子性(Atomicity)
每个事务都是一个整体,不可再拆分,事务中所有的 SQL 语句要么都执行成功, 要么都失败。
一致性(Consistency)
事务在执行前数据库的状态与执行后数据库的状态保持一致。如:转账前2个人的总金额是2000,转账后2 个人总金额也是2000
隔离性(Isolation)
事务与事务之间不应该相互影响,执行时保持隔离的状态。
持久性(Durability)
一旦事务执行成功,对数据库的修改是持久的。就算关机,也是保存下来的。
事务隔离级别
事务在操作时的理想状态是:所有的事务之间保持隔离,互不影响。
但是因为并发操作,多个用户同时访问同一个数据。就可能引发并发访问的问题,如下:
脏读
一个事务读取到了另一个事务中尚未提交的数据。
不可重复读
一个事务中两次读取的数据内容不一致(update时引发的问题)
幻读
一个事务中两次读取的数据的数量不一致(insert或delete引发的问题)
下表为
SELECT @@TX_ISOLATION;
设置事务隔离级别(需重登MySQL,才能看出隔离级别变化)
SET GLOBAL TRANSACTION ISOLATION LEVEL 级别字符串;
代码演示
/* 脏读演示 */ -- 打开 A 窗口登录 MySQL,重置表格 update account set balance = 1000; -- A 窗口设置全局的隔离级别为最低(read uncommitted——读未提交) set global transaction isolation level read uncommitted; -- 再打开 B 窗口登录MySQL,进入指定数据库,然后 A B 窗口都开启事务 start transaction; -- A 窗口更新 2 个人的账户数据,不提交 update account set balance = balance - 500 where name = '张三'; update account set balance = balance + 500 where name = '李四'; -- B 窗口查询账户, 结果读取了 A 窗口尚未提交的事务,这就叫作脏读 select * from account; -- A 窗口回滚 rollback; -- B 窗口查询,表数据又变为原来的样子 select * from account;
脏读非常危险的,比如张三向李四购买商品,张三开启事务,向李四账号转入 500 块,然后打电话给李四说钱 已经转了。李四一查询钱到账了,发货给张三。张三收到货后回滚事务,李四的再查看钱没了。
解决脏读的办法:将全局的隔离级别进行提升为
/* 解决脏读演示 */ -- 打开 A 窗口登录 MySQL,重置表格 update account set balance = 1000; -- A 窗口设置全局的隔离级别为read committed-读已提交 set global transaction isolation level read committed; -- B 窗口重登MySQL,A B窗口都开启事务 start transaction; -- A 窗口更新2人账号,不提交 update account set balance = balance - 500 where name = '张三'; update account set balance = balance + 500 where name = '李四'; -- B 窗口查询账户,并没有读取到 A 窗口尚未提交的数据(脏读已被解决) select * from account; -- A 窗口提交事务 commit; -- B 窗口查看账户,才读取到 A 窗口已经提交的数据(脏读已被解决) select * from account;隔离级别为/* 不可重复读演示 */ -- 打开 A 窗口登录 MySQL,重置表格 update account set balance = 1000; -- A 窗口设置全局的隔离级别为read committed-读已提交 set global transaction isolation level read committed; -- B 窗口重登MySQL,并开启事务 start transaction; -- B 窗口查询表数据 select * from account; -- A 窗口也开启事务 start transaction; -- A 窗口更新2人账号,并提交 update account set balance = balance - 500 where name = '张三'; update account set balance = balance + 500 where name = '李四'; commit; -- B 窗口查询,发现查询到的数据与前一次不同 select * from account;两次查询输出的结果不同,这也是一种危险情况!我们可以考虑这样一种情况,比如银行程序需要将查询结果分别输出到电脑屏幕和发短信给客户,结果在一个事务中针对不同的输出目的地进行的两次查询不一致,导致文件和屏幕中的结果不一致,这样就可能会导致账务错误。
解决不可重复读的办法:将全局的隔离级别提升为
/* 解决不可重复读演示 */ -- 打开 A 窗口登录 MySQL,重置表格 update account set balance = 1000; -- A 窗口设置全局的隔离级别为repeatable read-可重复读 set global transaction isolation level repeatable read; -- B 窗口重登MySQL,并开启事务 start transaction; -- B 窗口都查询表数据 select * from account; -- A 窗口也开启事务 -- A 窗口更新2人账号,并提交 update account set balance = balance - 500 where name = '张三'; update account set balance = balance + 500 where name = '李四'; commit; -- B 窗口查询,发现查询到的数据与前一次一致 select * from account;隔离级别为
/* 幻读演示 */ -- 打开 A 窗口登录 MySQL,重置表格 update account set balance = 1000; -- A 窗口设置全局的隔离级别为最高serializable-串行化 set global transaction isolation level serializable; -- A 窗口重登MySQL, 并开启事务 start transaction; -- A 窗口查询表记录总数 select count(*) from account; -- B 窗口登录 MySQL,并开启事务 start transaction; -- B 窗口添加一条记录(此时回车确认时,会发现操作没有继续进行,光标仍在闪烁) insert into account (name, balance) values ('Jason', 500); -- A 窗口中执行commit提交事务后,B 窗口中的insert语句就立即执行完毕 commit; -- A 窗口中查询表记录总数,发现总数未改变,B 窗口的insert没起作用 select count(*) from account; -- B 窗口中执行commit提交事务 commit; -- A 窗口查询表记录总数,发现总数已经改变,B 窗口的insert起作用了 select count(*) from account;使用默认情况下autocommit(自动提交)=1时:
执行sql修改语句(insert into,update…set,delete from)后,是立即生效的;若手动修改autocommit(自动提交)=0,即手动提交时:
执行sql修改语句(insert into,update,delete from)后,不会生效;在当前终端查看到的数据是存在缓存里的数据;
重新打开一个终端查看,会发现数据没有生效;
需要输入commit命令,提交数据到数据库,这样才会生效;
不管是autocommit=1或=0时;开启事务使用了begin命令,执行了sql修改语句后,都必须要使用commit命令提交数据
给数据库,不然不会生效;
如果使用了begin,没有使用commit,而是用begin另外开启了一个事务,之前的修改会被提交,隐式提交;
即:使用begin开启事务->后面必须使用commit/begin(其他命令不行),sql修改语句才能生效;以上要使用到commit的,在commit之前,使用命令rollback,都可以使数据回滚
事务开启到结束:begin+commit/rollback
对于set autocommit=0的设置的有效范围():
只在当前操作的mysql数据库有效,临时有效
退出当前mysql数据库(未退出终端),失效
退出当前终端,再重新使用mysql,失效
事务的开启,与是否选择了特定的数据库无关。即:在执行
use database_name命令前,就可以通过begin/start transaction来开启事务,然后在选择你需要使用的数据库,进行数据操作。
begin和start transaction等价,都是显式开启一个事务;commit和commit work等价,都是对事物进行提交;rollback和rollback work等价,都是对事物进行回滚;文中关于mysql的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《带你了解什么是mysql事务(附代码演示)》文章吧,也可关注golang学习网公众号了解相关技术文章。
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
214 收藏
-
155 收藏
-
485 收藏
-
436 收藏
-
125 收藏
-
174 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 细心的冬瓜
- 很好,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢作者分享技术文章!
- 2023-04-10 07:12:50
-
- 繁荣的故事
- 这篇技术文章真及时,太细致了,很棒,收藏了,关注师傅了!希望师傅能多写数据库相关的文章。
- 2023-04-04 20:36:57
-
- 柔弱的含羞草
- 写的不错,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢作者分享文章!
- 2023-03-06 12:17:18
-
- 苗条的金针菇
- 这篇技术文章出现的刚刚好,好细啊,受益颇多,码起来,关注老哥了!希望老哥能多写数据库相关的文章。
- 2023-02-23 18:22:44
-
- 粗犷的钢铁侠
- 这篇技术贴出现的刚刚好,太全面了,赞 👍👍,码起来,关注老哥了!希望老哥能多写数据库相关的文章。
- 2023-01-29 01:06:30