MySQL 事务

文章目录

为什么要用到事务

我们所有的程序都是按顺序来执行代码,但是就会存在中途出现异常,甚至代码挂掉无法执行后续的代码,这是很常见的。可是有的情况下我们容不得程序中途挂掉,就比如银行汇款,如果a账户给b账户汇款,a中余额1000元,b中余额1000元,当a给b汇款500元时,首先a要从余额中扣除500元,b要从余额中加上500元,在不出错的情况下,程序执行结束,a余额500元,b余额1500元。但是,当中途程序出错,数据库中途挂掉,a扣了500之后,b的500并没有加上去吗,这个时候就很尴尬了...我们宁愿程序出错a的余额没有扣除都是好的,最起码没有金钱的损失。为了解决以上问题,我们就会用到事务,就是为了解决SQL语句要么全部执行成功,要么全部执行失败。

事物的概念

事物指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。在不同的环境中,都可以有事务。对应在数据库中,就是数据库事务。

简单来说,就是把若干个SQL语句打包成一个"整体",要么都不执行,要么都执行完毕。但是这里的"都不执行"不是这些SQL真的没有执行,而是执行一半,发现出错的时候,数据库会自动进行"还原操作",相当于把之前执行过的SQL给撤销,最终看起来像是一个都没有执行的效果。这样的机制我们称为"回滚(rollback),同时也把事务支持的上述"特性"称为"原子性"。

日志体系

数据库是怎么进行回滚的?如何知道前面的SQL做出了啥样的修改呢?

数据库中存在一系列的"日志体系 ",当开启事务的时候,此时每一步执行的SQL都对数据进行了哪些修改,这些信息就会记录在案。后续如果需要回滚,就可以参考之前记录的内容,进行还原。并且以文件的方式记录和存储,这样即可以应对"程序崩溃"也能应对"主机掉电"的情况。

事务的使用

(1)开启事务
start transaction

(2)执行多条语句

(3)回滚或提交
rollback/commit

说明:rollback是全部失败,commit是全部成功

例如:

sql 复制代码
-- salary_id 为1的 给 salary_id 为2的转100
start transaction;
update salary set salary = salary - 100 where salary_id = 1;
update salary set salary = salary + 100 where salary_id = 2;
commit;

程序运行之前

程序运行之后

事务的基本特性(重点掌握)

事务涉及到的四个核心特性:
1.原子性(最重要的特性):就是上文的回滚机制
2.一致性:描述的是,事务执行前和执行后,数据库中的数据,都是"合法状态",不会出现非法的零时结果的状态。
3.持久性:事务执行完毕之后,就会修改硬盘上的数据,事务都会持久生效。
4.隔离性(非常复杂的一个性质):描述了多个事务并发执行的时候,互相之间产生的影响是怎样的。

隔离性

1)脏读

2)不可重复读

3)幻读

  • 脏读
    两个事务A和B并发进行,其中事务A在针对某个表的数据进行修改,A执行过程中,B也去读取数据,当B读完之后,由于A的修改操作有误,回滚至修改之前,导致B读到的数据不是"正确"的数据,而是读到了零时的"脏数据"
时间顺序 转账事务 取款事务
1 开始事务
2 开始事务
3 查询账户余额为2000元
4 取款1000元,余额被更改为1000元
5 查询余额为1000元(产生脏读数据)
6 取款操作发生错误,事务回滚,余额变为2000元
7 转入2000元,余额更改为3000元(脏读的1000+2000)
8 提交事务
备注 按照正确的逻辑,此时账户余额应为4000元

结论:读取了为提交的数据!

解决方法:修改时,不能读(给写操作加锁!

  • 不可重复读
    不可重复读指在数据库访问时,一个事务在前后两次相同的访问中却读到了不同的数据内容。
    比如说事务A的执行周期较长,事务A在第一次读取某个数据时,此数据的值为100,读取完成后,事务A又去执行其他的事情,在这个过程中,事务B将这个数据的值修改为200,然后事务A做完其他的事情后,又来读取这个数据的值,发现这个值和第一次所读取的值不相同,这种现象称为不可重复读。
时间顺序 事务A 事务B
1 开始事务
2 第一次查询小明年龄为21岁
3 开始事务
4 其他操作
5 更改小明年龄为22岁
6 提交事务
7 第二次查询小明年龄为22岁
备注 按照正确的逻辑,事务A前后两次读的数据应该保持一致

结论:前后多次读取,数据内容不一致!

解决方法:读取时,其他事物不能修改(给读操作加锁!

  • 幻读
    幻读指的是事务A在查询完记录总数之后,事务B执行了新增操作,事务A再次查询记录总数,发现两次查询的结果不一致,平白无故的多出了几行数据。
时间顺序 事务A 事务B
1 开始事务
2 第一次查询,数据总量100条
3 开始事务
4 其他操作
5 新增100条数据
6 提交事务
7 第二次查询,数据总量为200条
备注 按照正确的逻辑,事务A前后两次读的数据总量应该保持一致

解决方法:进行"串行化",一次只执行一个事务。

那么脏读、不可重复读和幻读又和隔离性有什么关系呢?

在MySQL中提供了四个隔离级别,可以通过配置文件来设置当前的服务器隔离级别时哪个级别。
1)read uncommitted 读未提交

这种情况下,一个事务可以读取另一个事务未提交的数据,此时就可能会产生脏读、不可重复读和幻读三种问题,但是此时多个事务并发程度是最高的,执行速度也是最快的。
2)read committed 读已提交

这种情况下,一个事务只能读取另一个事务提交之后的数据(给写操作加了锁)此时,可能会产生不可重复读和幻读问题(脏读问题解决了)此时并发成都降低,执行速度会变慢,同时事务之间的隔离性提高了,事物之间的相互影响变小了,得到的数据更准确了。
3)repeatable read 可重复读

这是MySQL默认的隔离级别,这个情况下,相当于是给写操作和读操作都加了锁,此时可能产生幻读问题,解决了脏读和不可重复读问题,并发程度进一步降低,执行速度进一步变慢,事务直接按的隔离性进一步提高了。
4)serializable 串行化

此时,所有的事务都是在服务器上一个接着一个的执行的,此时解决了脏读、不可重复读和幻读问题,并发程度最低,执行速度最慢,隔离性最高,数据最准确。

相关推荐
师太,答应老衲吧2 小时前
SQL实战训练之,力扣:2020. 无流量的帐户数(递归)
数据库·sql·leetcode
Yaml43 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
Channing Lewis3 小时前
salesforce case可以新建一个roll up 字段,统计出这个case下的email数量吗
数据库·salesforce
追风林3 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
毕业设计制作和分享4 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis
ketil274 小时前
Redis - String 字符串
数据库·redis·缓存
Hsu_kk5 小时前
MySQL 批量删除海量数据的几种方法
数据库·mysql
编程学无止境5 小时前
第02章 MySQL环境搭建
数据库·mysql
knight-n5 小时前
MYSQL库的操作
数据库·mysql
包饭厅咸鱼6 小时前
QML----复制指定下标的ListModel数据
开发语言·数据库