3、架构-事务处理

   

目录

   

概述

场景事例

本地事务

实现原子性和持久性

实现隔离性 


概述

    事务处理几乎在每一个信息系统中都会涉及,它存在的意义是为 了保证系统中所有的数据都是符合期望的,且相互关联的数据之间不 会产生矛盾,即数据状态的一致性(Consistency)。按照数据库的经 典理论,要达成这个目标,需要三方面共同努力来保障。

  1. 原子性(Atomic):在同一项业务处理过程中,事务保证了对 多个数据的修改,要么同时成功,要么同时被撤销。
  2. 隔离性(Isolation):在不同的业务处理过程中,事务保证了各 业务正在读、写的数据相互独立,不会彼此影响。
  3. 持久性(Durability):事务应当保证所有成功被提交的数据修 改都能够正确地被持久化,不丢失数据。

以上四种属性即事务的“ACID”特性,但笔者对这种说法其实不 太认同,因为这四种特性并不正交,A、I、D是手段,C是目的,前者 是因,后者是果,弄到一块去完全是为了拼凑个单词缩写。

事务的概念虽然最初起源于数据库系统,但今天已经有所延伸, 不再局限于数据库本身了。所有需要保证数据一致性的应用场景,包 括但不限于数据库、事务内存、缓存、消息队列、分布式存储,等 等,都有可能用到事务。后文里笔者会使用“数据源”来泛指所有这 些场景中提供与存储数据的逻辑设备,但是上述场景所说的事务和一 致性含义可能并不完全一致,说明如下。

  • 当一个服务只使用一个数据源时,通过A、I、D来获得一致性 是最经典的做法,也是相对容易的。此时,多个并发事务所读写的数 据能够被数据源感知是否存在冲突,并发事务的读写在时间线上的最 终顺序是由数据源来确定的,这种事务间一致性被称为“内部一致 性”。
  • 当一个服务使用到多个不同的数据源,甚至多个不同服务同时 涉及多个不同的数据源时,问题就变得困难了许多。此时,并发执行 甚至是先后执行的多个事务,在时间线上的顺序并不由任何一个数据 源来决定,这种涉及多个数据源的事务间一致性被称为“外部一致 性”。

        外部一致性问题通常很难使用A、I、D来解决,因为这样需要付出 很大甚至不切实际的代价;但是外部一致性又是分布式系统中必然会 遇到且必须要解决的问题,为此我们要转变观念,将一致性从“是或 否”的二元属性转变为可以按不同强度分开讨论的多元属性,在确保 代价可承受的前提下获得强度尽可能高的一致性保障,也正因如此, 事务处理才从一个具体操作上的“编程问题”上升成一个需要全局权 衡的“架构问题”。 人们在探索这些解决方案的过程中,产生了许多新的思路和概 念,有一些概念看上去并不那么直观,在本章,笔者会通过同一个场 景事例讲解如何在不同的事务方案中贯穿、理顺这些概念。

场景事例

        Fenix’s Bookstore是一个在线书店。当一本书被成功售出时,需 要确保以下三件事情被正确地处理:

  1. 用户的账号扣减相应的商品款项;
  2. 商品仓库中扣减库存,将商品标识为待配送状态;
  3. 商家的账号增加相应的商品款项。

         接下来,笔者将逐一介绍在“单个服务使用单个数据源”“单个 服务使用多个数据源”“多个服务使用单个数据源”以及“多个服务 使用多个数据源”下,可以采用哪些手段来保证数据在以上场景中被 正确地读写。

本地事务

        本地事务(Local Transaction)其实应该翻译成“局部事务”才 好与稍后的“全局事务”相对应,不过现在“本地事务”的译法似乎 已经成为主流,这里也就不去纠结名称了。本地事务是指仅操作单一 事务资源的、不需要全局事务管理器进行协调的事务。在没有介绍什 么是“全局事务管理器”前,很难从概念入手去讲解“本地事务”, 所以这里先暂且将概念放下,等后面再来对比理解。

        本地事务是一种最基础的事务解决方案,只适用于单个服务使用 单个数据源的场景。从应用角度看,它是直接依赖于数据源本身提供 的事务能力来工作的,在程序代码层面,最多只能对事务接口做一层 标准化的包装(如JDBC接口),并不能深入参与到事务的运作过程 中,事务的开启、终止、提交、回滚、嵌套、设置隔离级别,乃至与 应用代码贴近的事务传播方式,全部都要依赖底层数据源的支持才能 工作,这一点与后续介绍的XA、TCC、SAGA等主要靠应用程序代码来实 现的事务有着十分明显的区别。举个例子,假设你的代码调用了JDBC 中的Transaction::rollback()方法,方法的成功执行也并不一定代表 事务就已经被成功回滚,如果数据表采用的引擎是MyISAM,那 rollback()方法便是一项没有意义的空操作。因此,我们要想深入讨 论本地事务,便不得不越过应用代码的层次,去了解一些数据库本身 的事务实现原理,弄明白传统数据库管理系统是如何通过ACID来实现 事务的。

实现原子性和持久性

        原子性和持久性在事务里是密切相关的两个属性:原子性保证了 事务的多个操作要么都生效要么都不生效,不会存在中间状态;持久 性保证了一旦事务生效,就不会再因为任何原因而导致其修改的内容 被撤销或丢失。

        众所周知,数据必须要成功写入磁盘、磁带等持久化存储器后才 能拥有持久性,只存储在内存中的数据,一旦遇到应用程序忽然崩 溃,或者数据库、操作系统一侧崩溃,甚至是机器突然断电宕机等情 况就会丢失,后文我们将这些意外情况都统称为“崩溃”(Crash)。 实现原子性和持久性的最大困难是“写入磁盘”这个操作并不是原子 的,不仅有“写入”与“未写入”状态,还客观存在着“正在写”的 中间状态。由于写入中间状态与崩溃都不可能消除,所以如果不做额 外保障措施的话,将内存中的数据写入磁盘,并不能保证原子性与持 久性。下面通过具体事例来说明。

        按照前面预设的场景事例,从Fenix’s Bookstore购买一本书需 要修改三个数据:在用户账户中减去货款、在商家账户中增加货款、 在商品仓库中标记一本书为配送状态。由于写入存在中间状态,所以 可能出现以下情形。

  1. 未提交事务,写入后崩溃:程序还没修改完三个数据,但数据 库已经将其中一个或两个数据的变动写入磁盘,若此时出现崩溃,一 旦重启之后,数据库必须要有办法得知崩溃前发生过一次不完整的购 物操作,将已经修改过的数据从磁盘中恢复成没有改过的样子,以保 证原子性。
  2. 已提交事务,写入前崩溃:程序已经修改完三个数据,但数据 库还未将全部三个数据的变动都写入磁盘,若此时出现崩溃,一旦重 启之后,数据库必须要有办法得知崩溃前发生过一次完整的购物操 作,将还没来得及写入磁盘的那部分数据重新写入,以保证持久性。

        由于写入中间状态与崩溃都是无法避免的,为了保证原子性和持 久性,就只能在崩溃后采取恢复的补救措施,这种数据恢复操作被称 为“崩溃恢复”(Crash Recovery,也有资料称作Failure Recovery 或Transaction Recovery)。

        为了能够顺利地完成崩溃恢复,在磁盘中写入数据就不能像程序 修改内存中的变量值那样,直接改变某表某行某列的某个值,而是必 须将修改数据这个操作所需的全部信息,包括修改什么数据、数据物 理上位于哪个内存页和磁盘块中、从什么值改成什么值,等等,以日 志的形式——即以仅进行顺序追加的文件写入的形式(这是最高效的 写入方式)先记录到磁盘中。只有在日志记录全部安全落盘,数据库 在日志中看到代表事务成功提交的“提交记录”(Commit Record) 后,才会根据日志上的信息对真正的数据进行修改,修改完成后,再 在日志中加入一条“结束记录”(End Record)表示事务已完成持久 化,这种事务实现方法被称为“提交日志”(Commit Logging)。

        Commit Logging保障数据持久性、原子性的原理并不难理解:首 先,日志一旦成功写入Commit Record,那整个事务就是成功的,即使 真正修改数据时崩溃了,重启后根据已经写入磁盘的日志信息恢复现 场、继续修改数据即可,这保证了持久性;其次,如果日志没有成功 写入Commit Record就发生崩溃,那整个事务就是失败的,系统重启后 会看到一部分没有Commit Record的日志,将这部分日志标记为回滚状 态即可,整个事务就像完全没有发生过一样,这保证了原子性。

        Commit Logging的原理很清晰,也确实有一些数据库就是直接采 用Commit Logging机制来实现事务的,譬如较具代表性的是阿里的 OceanBase。但是,Commit Logging存在一个巨大的先天缺陷:所有对 数据的真实修改都必须发生在事务提交以后,即日志写入了Commit Record之后。在此之前,即使磁盘I/O有足够空闲,即使某个事务修改 的数据量非常庞大,占用了大量的内存缓冲区,无论何种理由,都决 不允许在事务提交之前就修改磁盘上的数据,这一点是Commit Logging成立的前提,却对提升数据库的性能十分不利。为此,ARIES 提出了“提前写入日志”(Write-Ahead Logging)的日志改进方案, 所谓“提前写入”(Write-Ahead),就是允许在事务提交之前写入变 动数据的意思。

        Write-Ahead Logging按照事务提交时点,将何时写入变动数据划 分为FORCE和STEAL两类情况。

  1. FORCE:当事务提交后,要求变动数据必须同时完成写入则称为 FORCE,如果不强制变动数据必须同时完成写入则称为NO-FORCE。 现实中绝大多数数据库采用的都是NO-FORCE策略,因为只要有了日 志,变动数据随时可以持久化,从优化磁盘I/O性能考虑,没有必要强 制数据写入时立即进行。
  2. STEAL:在事务提交前,允许变动数据提前写入则称为STEAL, 不允许则称为NO-STEAL。从优化磁盘I/O性能考虑,允许数据提前写 入,有利于利用空闲I/O资源,也有利于节省数据库缓存区的内存。

        Commit Logging允许NO-FORCE,但不允许STEAL。因为假如事务提 交前就有部分变动数据写入磁盘,那一旦事务要回滚,或者发生了崩 溃,这些提前写入的变动数据就都成了错误。         Write-Ahead Logging允许NO-FORCE,也允许STEAL,它给出的解 决办法是增加了另一种被称为Undo Log的日志类型,当变动数据写入 磁盘前,必须先记录Undo Log,注明修改了哪个位置的数据、从什么 值改成什么值等,以便在事务回滚或者崩溃恢复时根据Undo Log对提 前写入的数据变动进行擦除。Undo Log现在一般被翻译为“回滚日 志”,此前记录的用于崩溃恢复时重演数据变动的日志就相应被命名 为Redo Log,一般翻译为“重做日志”。由于Undo Log的加入, Write-Ahead Logging在崩溃恢复时会经历以下三个阶段。

  1. 分析阶段(Analysis):该阶段从最后一次检查点(Checkpoint, 可理解为在这个点之前所有应该持久化的变动都已安全落盘)开始扫 描日志,找出所有没有End Record的事务,组成待恢复的事务集合,这 个集合至少会包括事务表(Transaction Table)和脏页表(Dirty Page Table)两个组成部分。
  2. 重做阶段(Redo):该阶段依据分析阶段中产生的待恢复的事 务集合来重演历史(Repeat History),具体操作是找出所有包含 Commit Record的日志,将这些日志修改的数据写入磁盘,写入完成后 在日志中增加一条End Record,然后移出待恢复事务集合。
  3. 回滚阶段(Undo):该阶段处理经过分析、重做阶段后剩余的 恢复事务集合,此时剩下的都是需要回滚的事务,它们被称为Loser, 根据Undo Log中的信息,将已经提前写入磁盘的信息重新改写回去, 以达到回滚这些Loser事务的目的。

        重做阶段和回滚阶段的操作都应该设计为幂等的。为了追求高I/O 性能,以上三个阶段无可避免地会涉及非常烦琐的概念和细节(如 Redo Log、Undo Log的具体数据结构等)。数据库按照是否允许FORCE和STEAL可以产生四种组合,从优化磁 盘I/O的角度看,NO-FORCE加STEAL的组合的性能无疑是最高的;从算 法实现与日志的角度看,NO-FORCE加STEAL的组合的复杂度无疑也是最 高的。

实现隔离性 

        隔离性保证了每个 事务各自读、写的数据互相独立,不会彼此影响。只从定义上就能嗅 出隔离性肯定与并发密切相关,因为如果没有并发,所有事务全都是 串行的,那就不需要任何隔离,或者说这样的访问具备了天然的隔离 性。但现实情况是不可能没有并发,那么,要如何在并发下实现串行 的数据访问呢?几乎所有程序员都会回答:加锁同步呀!正确,现代 数据库均提供了以下三种锁。

  1. 写锁(Write Lock,也叫作排他锁,eXclusive Lock,简写为XLock):如果数据有加写锁,就只有持有写锁的事务才能对数据进行 写入操作,数据加持着写锁时,其他事务不能写入数据,也不能施加 读锁。
  2. 读锁(Read Lock,也叫作共享锁,Shared Lock,简写为SLock):多个事务可以对同一个数据添加多个读锁,数据被加上读锁 后就不能再被加上写锁,所以其他事务不能对该数据进行写入,但仍 然可以读取。对于持有读锁的事务,如果该数据只有它自己一个事务 加了读锁,则允许直接将其升级为写锁,然后写入数据。
  3. 范围锁(Range Lock):对于某个范围直接加排他锁,在这个 范围内的数据不能被写入。如下语句是典型的加范围锁的例子:

SELECT * FROM books WHERE price < 100 FOR UPDATE;

        请注意“范围不能被写入”与“一批数据不能被写入”的差别, 即不要把范围锁理解成一组排他锁的集合。加了范围锁后,不仅不能 修改该范围内已有的数据,也不能在该范围内新增或删除任何数据, 后者是一组排他锁的集合无法做到的。 

明天继续...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/12560.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙OS开发:【Stage模型应用程序包结构】

Stage模型应用程序包结构 为了让开发者能对应用程序包在不同阶段的形态更有清晰的认知&#xff0c;分别对开发态、编译态、发布态的应用程序结构展开介绍。 开发态包结构 在DevEco Studio上[创建一个项目工程]&#xff0c;并尝试创建多个不同类型的Module。根据实际工程中的…

vs2019 c++静态断言 static_assert ()

(1) 静态断言&#xff0c;就是在编译时候&#xff0c;让编译器测试一下&#xff0c; static_assert( bool a , “字符串”) 的第一个参数&#xff0c;若 第一个参数为 true &#xff0c;则表明程序可以被编译。否则&#xff0c;编译器拒绝编译。这是为了满足某些代码在语法语义…

研究变压器感应耐压试验电源的能效和节能潜力

变压器感应耐压试验电源 变压器感应耐压试验电源在电源行业中是一种非常重要的设备&#xff0c;用于测试和验证变压器的绝缘性能。变压器感应耐压试验电源是一种专门为变压器感应耐压试验而设计的电源设备。它的工作原理基于变压器的感应原理&#xff0c;利用感应电压来对变压器…

实现 Flask 应用的 HTTPS 加密保护

文章目录 1. 获得免费的 SSL 证书步骤&#xff1a; 2. 配置 Flask 应用3. 测试和部署结论结论 在今天的网络环境中&#xff0c;保护网站和用户数据的安全至关重要。通过在 Flask 应用中启用 HTTPS 加密&#xff0c;您可以确保用户的数据在传输过程中得到保护。本文将介绍如何结…

数据分析(二)——导入外部数据,导入Excel数据,CSV文件,txt文件,HTML网页,数据抽取,DataFrame对象的loc属性与iloc属性

一.导入外部数据 1.导入.xIs或.xIsx文件 pd.read_ excel(io,sheet_ name,header) 1.1常用参数说明 ●io:表示.xIs或.xIsx文件路径或类文件对象 ●sheet name:表示工作表&#xff0c;取值如下表所示 ●header:默认值为0&#xff0c;取第一行的值为列名&#xff0c;数据为除列…

接口文档编写注意事项

接口文档编写注意事项 字段方面 ①不需要的字段、逻辑中固定值的字段&#xff08;可写死的字段&#xff09;不提供 ②逻辑上可以合并的字段合并 例如&#xff1a;当一个互斥条件下&#xff0c;分别返回了两个字段&#xff0c;这个时候就可以在这个基础上将两个字段合并成一个…

(十)Python基础练习题一(50道选择题)#Python

本文整理了Python基础知识相关的练习题&#xff0c;共50道&#xff0c;适用于刚入门初级Python想巩固基础的同学。来源&#xff1a;如荷学数据科学题库&#xff08;技术专项-Python一&#xff09;。 1&#xff09; 2&#xff09; 3&#xff09; 4&#xff09; 5&#xff09; 6…

搞懂Servlet(1)- 介绍

介绍Servlet 今天&#xff0c;我们都意识到创建动态网页的必要性。动态网页就是网页内容会随着时间变化&#xff0c;或者内容会根据请求参数的不同而变化。如果你使用java编程&#xff0c;很幸运的是有一种使用Java生成动态网页的方式-Java Servlet。但是&#xff0c;在这之前…

【web网页开发制作】Html+Css+Js游戏主题特效及轮播效果网页作业天涯明月刀(7页面附源码)

HTMLCSSJS游戏主题轮播效果 &#x1f354;涉及知识&#x1f964;写在前面✨特效展示特效1、轮播幻灯效果特效2和3、鼠标悬浮及点击效果 &#x1f367;一、网页主题&#x1f333;二、网页效果Page1、首页Page2、游戏简介Page3、新闻中心Page4、互动专区Page5、视听盛宴Page6、用…

ctfshow parse_url wp

第一关 这个parse_url函数就是解析URL并且进行拆分的 $url "https://www.example.com/path/to/page?param1value1&param2value2";$parsed_url parse_url($url);print_r($parsed_url); Array ([scheme] > https[host] > www.example.com[path] > /p…

【论文复刻】 堆叠柱状图变形

复刻了一下这篇论文里的fig4a&#xff1a;Impacts of COVID-19 and fiscal stimuli on global emissions and the Paris Agreement | Nature Climate Change 效果图&#xff1a;​ 主要步骤&#xff1a; 1. 数据准备&#xff1a;随机赋值 2. 数据处理&#xff1a;计算了一个…

机器学习中常用的几种距离——欧式、余弦等

目录 一、欧式距离&#xff08;L2距离&#xff09;二、曼哈顿距离&#xff08;L1距离&#xff09;三、汉明距离四、余弦相似度 一、欧式距离&#xff08;L2距离&#xff09; &#xff08;1&#xff09;二维空间的距离公式&#xff08;三维空间的在这个基础上类推&#xff09;&…

4.StableDiffusion各项参数解读

经过前期的努力&#xff0c;我想大家都已经生成了自己的第一张AI作品&#xff0c;但是充满了随机性&#xff0c;每次都是不一样的&#xff0c;并且有时候生成的图片效果还不是很让人满意&#xff0c;暂且先不要着急&#xff0c;先跟着本篇文章&#xff0c;学习一些每个选项和参…

Google Chrome GPU渲染抓包

非安全模式启动 "C:\Program Files\Google\Chrome\Application\chrome.exe" --ignore-certificate-errors --allow-running-insecure-content --disable-web-security 配置环境 set RENDERDOC_HOOK_EGL0 "C:/Program Files/Google/Chrome/Application/chrom…

Windows本地部署直播录屏利器Bililive-go并实现远程添加直播间录屏

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 1. Bililive-go与套件下载1.1 获取ffmpeg1.2 获取Bililive-go1.3 配置套件 2. 本地运行测试3. 录屏…

【MySQL】事务及其隔离性/隔离级别

目录 一、事务的概念 1、事务的四种特性 2、事务的作用 3、存储引擎对事务的支持 4、事务的提交方式 二、事务的启动、回滚与提交 1、准备工作&#xff1a;调整MySQL的默认隔离级别为最低/创建测试表 2、事务的启动、回滚与提交 3、启动事务后未commit&#xff0c;但是…

MVP产品设计与数据指标

MVP&#xff08;minimum viable product&#xff0c;最小化可行产品&#xff09;概念最早由埃里克莱斯提出&#xff0c;刊载于哈弗商业评论&#xff0c;并有出版物《精益创业》 和常规产品不同&#xff0c;MVP更侧重于对未知市场的勘测&#xff0c;用最小的代价接触客户的方法…

学习Nginx(一):基础

介绍 Nginx是一个高性能的HTTP和反向代理的web服务器&#xff0c;它的设计重点是高并发、高性能和低内存消耗。它常被用于提供静态内容、负载均衡和作为Web服务器。 Nginx具有以下功能和特点&#xff1a; 静态文件服务&#xff1a;作为一个Web服务器&#xff0c;Nginx可以处…

GPT-4o可以用了

方法&#xff1a;挂日本/新加坡的梯子就可以了&#xff0c;打开就会弹出以下的弹窗。不过不知道可以用多久呢&#xff1f; 2024/5/15

木里风景文化|基于SSM+vue的木里风景文化管理平台的设计与实现(源码+数据库+文档)

木里风景文化管理平台 目录 基于SSM&#xff0b;vue的木里风景文化管理平台的设计与实现 一、前言 二、系统设计 三、系统功能设计 1 系统功能模块 2 管理员功能模块 3 用户功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源…