带你理解事务(上)、基本概念

简介: 带你理解事务(上)、基本概念

简介和特性


对于程序猿来说,编程就是将大千世界的各种业务用代码表示出来,所以代码输出的最终结果也要和现实世界能够对应的上。我们用银行转账来举例子,比如我给我的朋友A转100元,最终的结果无非就是有两种,一是成功,我的账户余额减少100,我朋友的账户余额增加100,二是失败,我们两账户余额都没有变。肯定是不允许出现我的账户余额减少了但是我朋友的账户余额没有增加,或者是我朋友的账户余额增加了我的账户余额却没有减少,出现这两种现象都会让我们和银行的关系变得极不和谐。

例子中转账这件事情虽然看起来很简单,但是当变成代码的时候就比较麻烦了,我们来看看变成代码之后会是什么样子:


1686815526835.png


乍看之下感觉很简单好像没有什么问题,但是如果在2和3中间增加了一步检查朋友账户是否为合法账户的步骤变成如下:


1686815533024.png


我们假定,第3步这步出了问题,我朋友的账户是个洗钱账户,这时候转账流程要结束,但是有个问题是我的账户已经扣掉了100元,如果不还我100的话那我不亏死,这样估计银行招牌早就被砸了。所以这个转账流程必须有如下几个特点:


1、原子性(Atomic)

要么转账成功,要不就没转,不能存在转了一半的这种情况。换句话说,转账这个过程的所有环节是不可分割的,不管转账的过程如何复杂,最后的结果是要么成功,我的账户少了钱,我朋友账户钱增加了对应数量的钱,要么是我的账户和我朋友的账户的钱都没有改变。这个特点我们称它为原子性。


2、一致性(Consistency)

这一点网上的很多博客解释的都不正确。这个特点其实指的是,转账这个过程中的数据是要能和现实世界的规则对应起来,或者说是需要满足一定的规则约束,比如,我的账户钱必须大于0,并且扣完钱也是必须大于0的。不满足这样的约束的话则说明数据是有问题的


3、隔离性(Isolation)

现实世界中的同一时刻,是有多个转账同时发生的,这同时发生的多个转账之间的数据不能相互影响,要把这个问题说清楚会稍微复杂一些,我们慢慢来说,我们把我们之前提的转账流程拆的更细一点如下:


  • (1)读取我账户上的金额,假如此时金额101
  • (2)将余额扣除100
  • (3)把剩余的余额写到磁盘上
  • (4)读取朋友账户金额
  • (5)给朋友账户金额增加100
  • (6)将新的余额写到磁盘上 假设现在有两个这样的转账过程同时发生,在计算机中,由于多处理器的原因,这两个转账并不会严格的按照彻底做完一个

时间线 转账1 转账2
1 读取我账户上的金额,此时金额101
2 读取我账户上的金额,此时金额101
3 将余额扣除100
4 把剩余的余额写到磁盘上
5 将余额扣除1
6 把剩余的余额写到磁盘上
7 读取朋友账户金额
8 读取朋友账户金额
9 给朋友账户金额增加100
10 将新的余额写到磁盘上
11 给朋友账户金额增加1
12 将新的余额写到磁盘上


相信不少人已经看出问题了吧,由于在时间线2处读到的钱还是101,因此在第6处写到磁盘上的我的余额是100,这时候银行不得亏死,因此,正确的流程是时间线134这三个事情必须先做,然后时间线2处的才允许发生,正确流程如下:


时间线 转账1 转账2
1 读取我账户上的金额,假如此时金额101
2 将余额扣除100
3 把剩余的余额写到磁盘上
4 读取我账户上的金额,此时金额是1
5 将余额扣除1
6 把剩余的余额写到磁盘上
7 读取朋友账户金额
8 读取朋友账户金额
9 给朋友账户金额增加100
10 将新的余额写到磁盘上
11 给朋友账户金额增加1
12 将新的余额写到磁盘上


4、持久性(Durability)


一旦转账完成之后,数据就永久的被持久化到磁盘上不会丢失。

我们所说的事务(transcation)必须具有这四种特性,否则就会出现问题。


事务并发读写会遇到的问题


我们用修改数据库中我的账户的余额为例子,来看看多个事务同时发生都有可能出现什么问题:


脏写


时间线 事务1 事务2
1 start
2 start
3 update user set money=100 where id=1
4 update user set money=50 where id=1
5 commit
6 select money from user where id=1 (此时查出来money是50)
7 commit


这个案例中,事务2修改了事务1还没有提交的数据,事务1在时间线6的重新查询的时候一脸懵逼,明明自己的钱应该是100的,却不知道为什么变成了50,就这样白白的损失了50块钱,和银行的关系变得极其不和谐。


脏读


时间线 事务1 事务2
1 start
2 start
3 update user set money=100 where id=1
4 select money from user where id=1 (查出来的money是100)
5 update user set money=50 where id=1
6 commit
7 commit


这个案例中,事务1先把数据库中我的账户余额更新为了100,然后事务2来查询发现余额是100,就告诉我余额是100,这种读到了别的事务未提交的数据就叫做脏读。有人不理解这个“脏”字体现在了哪里,因为事务1还是有可能继续修改money字段的,比如在上面的时间线6的时候事务1修改了money,这样的话事务2读到的数据其实是个错误的数据。


不可重复读


时间线 事务1 事务2
1 start
2 start
3 select money from user where id=1 (此时查出来的money是100)
4 update user set money=50 where id=1
5 select money from user where id=1(此时查出来的money是50)
6 commit
7 commit


事务1在时间线5的时候读到了事务2还没有提交的数据,并且和自己在时间线3读到的数据不一样,这时候我们称这种现象称为不可重复读。


幻读


时间线 事务1 事务2
1 start
2 start
3 select * from user where money>100 (假设查到了一条数据)
4 insert into user (money,id) values (200,2)
5 select * from user where money>100 (此时会查到两条数据)
6 commit
7 commit


事务1在时间线3和时间线5查到的数据不一样,并且时间线5读到了时间线3没有读到的数据,我们称这种现象为幻读。

四种现象的严重程度排序 脏写 > 脏读 > 不可重复读 > 幻读


隔离级别


注意,这里是以MySQL为例子,说的是MySQL中使用Innodb时候的事务隔离级别,不同的数据库对隔离级别的支持是不一样的。在MySQL中,支持下面四种事务隔离级别:

  • READ UNCOMMITTED: 读未提交
  • READ COMMITTED: 读已提交
  • REPEATABLE READ: 可重复读
  • SERIALIZABLE: 串行化

事实上这是sql标准中的四种隔离级别,sql标准中还规定了不同隔离级别中可以发生什么问题和不可以发生什么问题,具体如下:


隔离级别 脏写 脏读 不可重复读 幻读
READ UNCOMMITTED 可能出现 可能出现 可能出现
READ COMMITTED 可能出现 可能出现
REPEATABLE READ 可能出现
SERIALIZABLE


我们可以看到,由于脏写太过于严重,所以在哪个事务隔离级别下都不允许发生。

目录
相关文章
|
SQL 关系型数据库 MySQL
MySQL数据库——DML基本操作
本文介绍了MySQL中的DML基本操作,包括查询、插入、更新和删除数据。查询数据使用SELECT语句,插入数据用INSERT INTO,更新数据则依靠UPDATE,而删除数据需用DELETE FROM。这些操作是数据库管理的关键,有效提升数据处理效率和准确性。理解并熟练运用这些语句是确保数据一致性和安全性的基础。参考文献包括MySQL官方文档和W3Schools的MySQL教程。
653 2
|
负载均衡 Linux 数据库
阿里云轻量应用服务器套餐收费标准参考(组合套餐、负载均衡套餐等)
阿里云轻量应用服务器有多种套餐,在购买轻量应用服务器、轻量应用负载均衡、轻量容器服务和轻量数据库服务时,我们可以根据业务需求选择合适的套餐。本文为大家介绍阿里云轻量应用服务器套餐和镜像最新价格表以及相关收费说明。
980 0
阿里云轻量应用服务器套餐收费标准参考(组合套餐、负载均衡套餐等)
|
虚拟化
VMware Tools 失效处理
VMware Tools 失效处理
317 0
|
10月前
|
缓存 负载均衡 算法
深入探索Linux内核的调度机制
本文旨在揭示Linux操作系统核心的心脏——进程调度机制。我们将从Linux内核的架构出发,深入剖析其调度策略、算法以及它们如何共同作用于系统性能优化和资源管理。不同于常规摘要提供文章概览的方式,本摘要将直接带领读者进入Linux调度机制的世界,通过对其工作原理的解析,展现这一复杂系统的精妙设计与实现。
513 8
|
11月前
|
Kubernetes 安全 微服务
使用 Istio 缓解电信 5G IoT 微服务 Pod 架构的安全挑战
使用 Istio 缓解电信 5G IoT 微服务 Pod 架构的安全挑战
202 8
|
10月前
|
数据采集 前端开发 JavaScript
动态与静态网站抓取的区别:从抓取策略到性能优化
本文详细介绍了动态与静态网站抓取的区别、抓取策略及性能优化技巧,并提供了相关代码示例。静态网站抓取通过简单的HTTP请求和解析库实现,而动态网站则需使用Selenium等工具模拟浏览器执行JavaScript。文章还展示了如何使用代理IP、多线程和合理的请求头设置来提高抓取效率。
423 2
动态与静态网站抓取的区别:从抓取策略到性能优化
|
SQL 存储 分布式计算
spark执行sql的原理是什么
spark执行sql的原理是什么
290 1
|
小程序 JavaScript 前端开发
【原力计划小程序】1、一篇文章深入了解小程序的学习路线(以项目驱动的方式带你学习微信小程序)
🎄 随着社会的发展(四级经典开头😄With the development of society),越来越多的人开始使用微信小程序 🎄 虽然博主从事的是 Java 后台开发,但前端也是我的爱好之一,并且小程序如此好用、小程序如此流行、小程序越来越受到大家的喜爱,我怎能不投其所好?怎能不跟紧社会的步伐呢?📱 🎄 大概是2019年,博主偶然刷到一个讲解微信小程序开发的视频。女老师👩‍🏫介绍到:学习微信小程序需要掌握 JavaScript,于是博主果断放弃了微信小程序开发。当时我大二,啥也不会,只知道玩:video_game:,不挂科就不错了,完全不会写代码👨‍💻) 🎄 大
480 0
【原力计划小程序】1、一篇文章深入了解小程序的学习路线(以项目驱动的方式带你学习微信小程序)
|
数据采集 存储 数据管理
OneData:阿里巴巴的数据仓库之旅与统一数据治理实践
OneData 为解决大数据时代的挑战提供了一条可行的道路,对于其他企业和组织来说具有重要的参考意义。随着技术的不断进步和应用场景的扩展,OneData 的未来发展值得期待。
|
SQL 存储 关系型数据库
必知的 MySQL 索引失效场景【包括实践验证】,别再踩坑了!(下)
必知的 MySQL 索引失效场景【包括实践验证】,别再踩坑了!
1400 2