数据库事务的三个元问题
✏️ 编者按:
在《一文解析数据库的三生三世》这篇文章中,我们站在历史的角度认识了数据库的「前世今生」。文中提到在线事务处理等关键场景,那究竟什么是数据库的事务?为什么数据库需要支持事务?为了实现数据库事务,各种数据库是如何设计的?让我们一起来看看数据库事务的三个元问题吧!
首发|AI 前线
作者|Zilliz
什么是数据库事务
事务,就是用来保证数据操作符合业务逻辑要求而实现的一系列功能。换句话说,如果数据库不支持事务,上层业务系统的程序员就需要自己写代码,以保证相关数据处理逻辑的正确性。举个例子,数据库最开始普及就是在金融业,银行的存取款场景就是一个最典型的 OLTP 数据库场景,而事务就是用来保证类似场景的业务逻辑正确性的。
原子性:如果你要给家人转账,必须在你的账户里扣掉 100 块,在家人账户里加上 100 块,这两笔操作需要一起完成,业务逻辑才是正确的。但是程序在做修改时,肯定会有先后顺序,试想一下程序扣了你的钱,这个时候程序崩溃了,家人账户的钱没有加上,那这 100 块是不是消失了?你是不是要发疯?那么,就把这两笔操作放进一个事务里,通过原子性保证,这两笔操作要么都成功,要么都失败。这样才能保证业务逻辑的正确性。
一致性:有很多文章讲过一致性,但是很多人会把一致性跟原子性混在一起说。事务的一致性指的是,每一个事务必须保证执行之后所有库内的规则依旧成立,比如内外键、constraint、触发器等。举例来说,你在储蓄卡里有 100 元,理财账户里有 100 元,基金账户有 100 元,那么你在资产总和里会看到 300 元,这 300 元必须是三个账户余额加在一起得到的。你从储蓄卡里转出去了 100 元给家人,那么可以在数据库上创建触发器,当储蓄卡余额账户减 100 元的同时,把资产总和也同步减去 100 元,不然就会出现逻辑上的错误。你已经转走了 100 元储蓄卡余额,实际资产总和应该是 200 元,若还是 300 元,数据库状态就不一致了。因此实现事务的时候,必须要保证相关联的触发器以及其他内部规则都执行成功,事务才算执行成功。如果在减去资产总额时出错,数据库就会进入不一致的状态,那么这笔转帐交易也不能成功。
那么一致性跟原子性的区别到底在哪里呢?原子性是指多个用户指令之间必须作为一个整体完成或失败,而一致性更多是数据库内的相关数据规则同时完成或失败。
持久性:事务只要提交了,对数据库的修改就会保存下来不会丢了。简单来说,只要提交了,数据库就算崩溃了,重启之后你刚存的 100 块依然在你的账户里。
隔离性:每个事务相对于其他的事务有一定独立性、不能互相影响,因为数据库需要支持并发的操作来提高效率。在并发操作时,一定要通过操作之间的隔离来保证业务逻辑的正确性。比如,你转帐 100 块给家人,一系列操作的最后一步可能是输入验证码,这个时候转帐还没有完成,但是在数据库里,你的账户对应的记录中已经减去 100 块,家人账户也加了 100 块,就等着验证码输入以后,事务提交,完成操作。那么,这个时候,家人通过手机银行能够查到这 100 块么?你的答案可能是不能,因为你的转帐操作还没有提交,事务还没有完成,那么数据库就应该保证这两个并发操作之间具有一定的隔离性,这样才符合业务逻辑。
到底应该隔离到什么程度呢?隔离性又分为 4 个等级:由低到高依次为 Read uncommitted(读未提交)、Read committed(读提交)、Repeatable read(可重复读取)、Serializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻象读这几类问题。
怎么理解不同的隔离等级呢?首先要理解并发操作,并发操作就是指有不同的用户同时对一个数据进行读、写操作,那么在这个过程中,每个用户应该看到什么数据才能保证业务逻辑的正确性呢?
如果是前面存取款的场景,我看到的是已经存进来的钱,也就是必须是已经提交的事务。而 12306 刷火车票,你可以看到有 10 张余票,但是在下单的时候告诉你票卖完了,因为同时有 10 个用户把票买掉了,你需要重新刷余票。这个也是可以接受的。也就是说我可以读到一些虚假的余票,在业务上也没有什么问题。那么在设计这两个不同系统时,就可以选择不同的事务隔离级别来实现不同的并发效果。不同的隔离等级就是在系统的并发性和数据逻辑的严谨性之间做出的平衡。
数据库如何实现事务
MVCC 实现原理
所谓 MVCC,就是数据库中的同一查询根据相关事务执行的先后顺序以及隔离级别的不同,可能会存在不同版本的结果,通过这样的手段来保证大部分查询操作不会被修改操作阻塞并保证数据逻辑的正确性。简单来说就是,用存储空间来交换并发能力。
(图片来自网络,侵删)
首先,Postgres 里的每一个事务都有编号,这里可以简单理解为时间顺序编号,编号越大的事务发生越晚。然后,数据库里的每一行记录都会保存创建这条记录的事务号(Cre),也会在记录删除时保存删除这条记录的事务号(Exp),换句话说,只要 Exp 这里一列里记录了事务编号,就说明这条记录被删除了。那么一个事务应该能看见哪些记录呢?Postgres 里每一个事务都会保存一个当前系统的事务快照(Snapshot),这个快照里会保存事务创建时当前系统的最高(最晚)事务编号,以及目前还在进行中的事务编号。在如上图所示的一个事务的快照里,最高事务编号为 100,目前正在进行的事务有 25、50 和 75。对应左边数据记录,这 6 行数据的可见性就如同标注的一般:
-
第一行,Cre 30,没有删除,在 100 这个时间点,应该能看到。 -
第二行,Cre 50,没有删除,但是 50 这个事务还没有提交,正在进行中,所以看不见。 -
第三行,Cre 110,没有删除,但是 100 这个时间点 110 事务还没有发生,所以看不见。 -
第四行,Cre 30,Exp 80,在 80 的时候数据被删掉了,所以看不见。 -
第五行,Cre 30,Exp 75,在 30 的时候被创建,75 时候被删掉了,但是 75 这个事务在 100 的时候还没有提交,这条记录在 100 的时候还没有删掉,所以看得见。 -
第六行,Cre30,Exp 110,在 30 的时被创建,110 时候被删掉,但是在 100 时候,110 还没有发生,所以看得见。
本文分享自微信公众号 - ZILLIZ(Zilliztech)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
微众开源项目进展汇总【2021年11月】
FISCO-BCOS 适用于金融行业的区块链底层平台 FISCO BCOS v3.0.0:一期完成 SIT,主要涵盖微服务架构和流水线共识功能;二期 SIT 中,包含混合并行机制、存储扩展方案、多群组调度与新的权限策略等;预计 12 月份在金链盟生态大会上重磅首发。 2.CC-SIG: 陆羽跨链协议由微众银行、中钞区块链等18家发起单位共同发起,以期建立链间、区块链与应用之间的“通用语言”。继11月18日,该协议发布后。CC-SIG 内部普及陆羽跨链协议理念,后续计划围绕 WeCross 和陆羽跨链协议开展小组代码编写工作。 SUIBE-SIG: 发布结合数字徽章的交互式区块链项目;更新"基于 FISCO BCOS 的 mysql 存储 demo"视频资料。 WeBASE 区块链中间件平台 v1.5.4:已提测,研发实训课程案例集,准备新增至WeBASE文档中,贡献给社区;新增WeBASE操作指引页、优化合约IDE交互。 WeBASE-Lab-rc1:SIT中,计划12月发布,适配FISCO BCOS v3.0.0,主要功能:区块链数据浏览,合约管理,私钥管理,系统管理和交易审计等功能...
- 下一篇
如何设置CUDA Kernel中的grid_size和block_size?
撰文 | 柳俊丞 导语:在刚接触 CUDA 编程时,很多人都会疑惑在启动一个kernel时,三个尖括号里面的参数应该如何设置?这些参数受到哪些因素的制约?以及他们如何影响 kernel 运行的性能?本文参考 CUDA 官方文档,分析了这些参数应该如何设置。 一般而言,我们在代码中会看到使用以下方式启动一个 CUDA kernel: cuda_kernel<<<grid_size,block_size, 0,stream >>>(...) cuda_kernel 是 global function 的标识, (...) 中是调用 cuda_kernel 对应的参数,这两者和 C++ 的语法是一样的,而 <<<grid_size, block_size, 0, stream>>> 是 CUDA 对 C++ 的扩展,称之为Execution Configuration( https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#executi...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS关闭SELinux安全模块
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- 2048小游戏-低调大师作品
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池