计算机事务原则的底层分析 算法SQL设计模式领域模型生物
程序员文章站
2022-04-04 14:13:33
...
写这篇文章的初衷是让更多的朋友能深刻的理解到底层事务的运作原理,并且进一步巩固知识。然而,有些问题
事务的起源和操作原理
至于事务的起源我去图书馆查找了一下资料,事务至少已经有千年的历史了,计算机领域专家认为,事务不是一个计算机领域特有的名词,它是有机生物界共有的特性。很多场景都可以用事务来描述。下面是一个例子说明了事务的操作原理。
有一对亲年男女在教堂宣誓结婚,牧师首先要说前面一大堆话,最后他要郑重其事的
问男士或者女士,“您愿意娶某某为妻并且xxxxxxx”,如果双方都回答“我愿意”,
那样才能算是合法夫妻。
翻译成计算机语言
假如任何一方不同意
底层计算机事务
底层计算机事务的理解是需要区分很多角色的,每个角色所面对的底层计算机事务不同,假如我是一名硬件工程师,我可能从计算机信号开始理解事务。如果我是一名系统软件开发者,我会从次磁盘存储起步,如果我是一名数据库开发者,我需要从操作系统开始理解事务。最后,应用程序开发者所涉及的可能就是已经被包装好的事务API起步了。
现今的世界,人类文明的延续已经大部分依赖计算机,然而计算机世界是生存在物理介质上的,如果物理介质发生问题,那么记录下来的东西将是不完整,或者是彻头彻尾错误的。所以我们需要引入事务来确保,有价值的操作必须全部的,完整的,正确的被记录,否则不会留下任何痕迹。
作为开发者,我能力所能涉及的最低层次的事务就是建立在操作系统平台上的事务。具备操作事务能力的系统平台,我们称之为事务操作系统,也就是TP system.在和底层事务打交道的2年里,最核心的需要具备事务能力的非事务资源就是文件。如何理解这句话呢,可以这样想象,需要在博物馆保存下来的东西通常不是存活的,而计算机世界非存活的却要能持久保持的数据大多以文件的形式存储在硬碟或者磁带上。所以,几乎所有的事务都是围绕着文件操作来进行的,例如,数据库,消息系统等。然而不幸的是,计算机本身的设计决定了硬碟和文件之间是完全无法调和称事务的。因为硬碟不知道什么是文件,什么是文件的部分。所以,应用软件要在某个操作系统上具备事务特性,就必须在系统级别上对文件操作进行包装。但是应该如何包装呢,文件事务又应该如何定义?试试下面这句话
文件操作的原子性应该可以这样阐述,要么整个文件的全部的单元都被正确的传入到指定的存储单元上,要么指定的存储单元没有任何的改变。
因此就需要讨论文件事务的4种算法
单文件直接写入
根据上我试着给出的文件事务的定义。我们不难发现真实世界中,大多的硬碟完全的不是这种模式,在尝试对硬碟写数据的时候,主要一开始写,硬碟上就会有数据,并且存放在一个可能的位置上,所以硬碟上最终的数据可能是两种形式。
1/ Half-changed
2/ Total-changed
所以使用这种方式,我们在硬件无故障的情况下可使文件操作具备最低的事务能力。因为我们可以把Half-changed删除从而回滚到原始状态。但是正如刚才所说,这不是一个实际意义上的事务,因为显而易见它存在下面的弊端
1/ 如果写入操作是一个Half-changed,事务本身是不会检查的
2/ 系统断电,或者奔溃后,事务就中断了,并且不会自动回滚。
写 读 再写 再读
这种模式应该算是单文件直接写入模式的一个升级版,部分解决了Half-changed的问题,它的设计思想是首先做一次单文件读写,然后重新读入写入到磁盘上的文件,并且把这个文件与原始的进行比较。如果相同并且正确则默认为commit,否则继续循环执行这个操作,直到通过比较为止。尽管这个比单文件写入要好很多了,但是还是有许多问题没有解决,特别值得一提的是,使用了这个方法反而多出了一个问题。就是摒弃了事务的错误处理能力。使用这个方法是为了保证操作一定成功,并且反复的重新尝试。下面列举了缺点
1/ 不会尝试回滚到最原始的状态
2/ 有可能磁盘某一块是坏道,或者不允许特定用户读写,从而导致程序锁死
3/ 如果系统崩溃,没有任何措施提供保护
复合副本写入
策略演变到这一步已经日趋完善了。复合副本的写入方法是结合单文件直接写入和先写后读法的。它的核心理论是,把目标文件看成若干个比特,然后对于每一个比特给予一个版本号并且创建2个副本,A和B。在接受到写文件操作的指令后,首先将A写入存储A,如果这个操作成功(否则整个操作回滚),则将另外一个副本B写入存储B,存储A和存储B必须是物理上分开的两个存储,这样才能保证在系统奔溃后一样存有一个副本可提供重新读取。同时,重复读写的策略将会穿插其中用于确保不会有Half-changed的情况出现。
日志法写入
这个是一个相当奇怪并且具备挑战思维的的算法。首先必须从老的文件部分开始读(使用先写再读的方法),如果没有完成,则把老的文件的部分拷贝到另外一个存储上(物理分离的),然后覆盖老的文件,如果对老文件的覆盖操作完成,则删除老文件的备份。反复重复这个操作。其实这个算法是数据库事务的基本。我猜想,数据库事务可能是类似如下的操作,本地数据库由于无法具备两块独立的存储,极有可能是通过划分出一块数据空间来做本分存储。然后对于数据库数据做日志法的递增写入。
总而言之,事务就是一个具备ACID特性的行为,那么什么样的行为才有可能具备ACID,什么样的行为永远不能具备ACID呢。这就需要探讨一下行为了。
无约束操作
无约束操作是指操作不是哦到任何的条件限制,不需要执行的条件,不计较执行的结果。这和ACID属性完全的对立,任何的操作都有可能失败。上文提到的单文件读写就是一个这样的操作。举一个简单的例子,无约束操作如同处于猿人阶段的人类。而ACID就是具备技能和一些道德思维进化结果。
闭门造车
这是一个具备ACID特性的操作模型,它表达的思想是,一些列操作封装在一个工厂里,进去的是零件出来的是陈品,你不可能看见半成品。
物理操作
界定这个行为和无约束操作很难,首先该如何定义物理操作呢。可以尝试这样理解,当一个指令发出后,计算机就会对某一介质进行访问并且记录,并且,这个记录"很难"或者"根本无法"回转。
High-Level事务资源的包装思想
那么这些最事务的原则在底层开发者手里应该如何去构成事务呢。其实,你需要做出一个取舍,因为下面这句话很拗口
“事务的本质是不完全的,假想的,需要容忍的”、
那么以数据库事务为例子,应该这样组合,相信,大多的数据库也是这样设计的
首先数据库的查询适配器接受SQL语句,可能有下面的语句
SQL_SELECT 语句: 如果接受的是SELECT语句,就是一个文件的读取操作
1》 整个读取操作是一个闭门造车行为,通俗的说是,要么就是完全正确的结果,否则就是无法返回(文件操作算法的2,3,4都提供一定的ACID方案)。 而闭门造车的工厂里就是无数个无约束操作(从文件存储上读数据<单文件读写>,解析数据,生成视图等),由于SELECT的事务原则是闭门造车,它具备ACID特性,那么它就具备了一定可靠性的事务能力(统一提交,回滚等,不同等级的数据库在这方面的能力区别是很大的)。
SQL_UPDATE 语句: 如果接受的是UPDATE语句,就是一个文件的读取和写入的双向操作
2》如上面所说,读取是一个闭门行为,同理解释写入也是一个闭门行为,因为,文件操作中算法3(复合写入)和算法4(日志法)都提供了一定能力的事务特性。所以整个UPDATE行为本身是 闭门A×闭门B×(其他的非事务行为),因此,其本身就具备了事务行为。
SQL_INSERT 语句:
3》如上面所说的,完完全全的文件写入操作。3种文件写入的算法随便选。
SQL_DELETE 语句:如果接受的是DELETE语句,那么情况就复杂了,各种数据在处理delete语句能力上差别明显。
4》首先我要说,无论怎么看,这是一个物理行为,一旦把一条数据擦除了,是很难或者无法恢复的。那么数据要完成相对可靠的事务行为举必须面对这个问题。该怎么办?一些事务功能简单的数据只是简单的提供一些预防功能,比如在程序中控制尽量避免发生。很显然这样的事务很苍白无力。比较高端的数据库系统会设置很多的预制位,每个预制位标定了当前操作的状态,然后通过用户定义的事务等级来确定是否需要回滚某一操作。其实很有可能有这样的一个场景,在执行大批量删除命令时,当指令到达某一预制位,会先检查磁盘是否可靠,电源是否稳定,机器CPU的状态,或者RAM,然后才来确定是否去执行这条操作。或者更具策略分成N个步骤去完成。不断的备份,运行,备份,运行等。
事务的起源和操作原理
至于事务的起源我去图书馆查找了一下资料,事务至少已经有千年的历史了,计算机领域专家认为,事务不是一个计算机领域特有的名词,它是有机生物界共有的特性。很多场景都可以用事务来描述。下面是一个例子说明了事务的操作原理。
有一对亲年男女在教堂宣誓结婚,牧师首先要说前面一大堆话,最后他要郑重其事的
问男士或者女士,“您愿意娶某某为妻并且xxxxxxx”,如果双方都回答“我愿意”,
那样才能算是合法夫妻。
翻译成计算机语言
GodFather gfather = new GodFather(Man man, Women women); gfather.beginToSay(); man.prepare(); women.prepare(); man.answer(); women.answer(); gfather.clearMarraied();
假如任何一方不同意
gfather.RollBack();
底层计算机事务
底层计算机事务的理解是需要区分很多角色的,每个角色所面对的底层计算机事务不同,假如我是一名硬件工程师,我可能从计算机信号开始理解事务。如果我是一名系统软件开发者,我会从次磁盘存储起步,如果我是一名数据库开发者,我需要从操作系统开始理解事务。最后,应用程序开发者所涉及的可能就是已经被包装好的事务API起步了。
现今的世界,人类文明的延续已经大部分依赖计算机,然而计算机世界是生存在物理介质上的,如果物理介质发生问题,那么记录下来的东西将是不完整,或者是彻头彻尾错误的。所以我们需要引入事务来确保,有价值的操作必须全部的,完整的,正确的被记录,否则不会留下任何痕迹。
作为开发者,我能力所能涉及的最低层次的事务就是建立在操作系统平台上的事务。具备操作事务能力的系统平台,我们称之为事务操作系统,也就是TP system.在和底层事务打交道的2年里,最核心的需要具备事务能力的非事务资源就是文件。如何理解这句话呢,可以这样想象,需要在博物馆保存下来的东西通常不是存活的,而计算机世界非存活的却要能持久保持的数据大多以文件的形式存储在硬碟或者磁带上。所以,几乎所有的事务都是围绕着文件操作来进行的,例如,数据库,消息系统等。然而不幸的是,计算机本身的设计决定了硬碟和文件之间是完全无法调和称事务的。因为硬碟不知道什么是文件,什么是文件的部分。所以,应用软件要在某个操作系统上具备事务特性,就必须在系统级别上对文件操作进行包装。但是应该如何包装呢,文件事务又应该如何定义?试试下面这句话
文件操作的原子性应该可以这样阐述,要么整个文件的全部的单元都被正确的传入到指定的存储单元上,要么指定的存储单元没有任何的改变。
因此就需要讨论文件事务的4种算法
单文件直接写入
根据上我试着给出的文件事务的定义。我们不难发现真实世界中,大多的硬碟完全的不是这种模式,在尝试对硬碟写数据的时候,主要一开始写,硬碟上就会有数据,并且存放在一个可能的位置上,所以硬碟上最终的数据可能是两种形式。
1/ Half-changed
2/ Total-changed
所以使用这种方式,我们在硬件无故障的情况下可使文件操作具备最低的事务能力。因为我们可以把Half-changed删除从而回滚到原始状态。但是正如刚才所说,这不是一个实际意义上的事务,因为显而易见它存在下面的弊端
1/ 如果写入操作是一个Half-changed,事务本身是不会检查的
2/ 系统断电,或者奔溃后,事务就中断了,并且不会自动回滚。
写 读 再写 再读
这种模式应该算是单文件直接写入模式的一个升级版,部分解决了Half-changed的问题,它的设计思想是首先做一次单文件读写,然后重新读入写入到磁盘上的文件,并且把这个文件与原始的进行比较。如果相同并且正确则默认为commit,否则继续循环执行这个操作,直到通过比较为止。尽管这个比单文件写入要好很多了,但是还是有许多问题没有解决,特别值得一提的是,使用了这个方法反而多出了一个问题。就是摒弃了事务的错误处理能力。使用这个方法是为了保证操作一定成功,并且反复的重新尝试。下面列举了缺点
1/ 不会尝试回滚到最原始的状态
2/ 有可能磁盘某一块是坏道,或者不允许特定用户读写,从而导致程序锁死
3/ 如果系统崩溃,没有任何措施提供保护
复合副本写入
策略演变到这一步已经日趋完善了。复合副本的写入方法是结合单文件直接写入和先写后读法的。它的核心理论是,把目标文件看成若干个比特,然后对于每一个比特给予一个版本号并且创建2个副本,A和B。在接受到写文件操作的指令后,首先将A写入存储A,如果这个操作成功(否则整个操作回滚),则将另外一个副本B写入存储B,存储A和存储B必须是物理上分开的两个存储,这样才能保证在系统奔溃后一样存有一个副本可提供重新读取。同时,重复读写的策略将会穿插其中用于确保不会有Half-changed的情况出现。
日志法写入
这个是一个相当奇怪并且具备挑战思维的的算法。首先必须从老的文件部分开始读(使用先写再读的方法),如果没有完成,则把老的文件的部分拷贝到另外一个存储上(物理分离的),然后覆盖老的文件,如果对老文件的覆盖操作完成,则删除老文件的备份。反复重复这个操作。其实这个算法是数据库事务的基本。我猜想,数据库事务可能是类似如下的操作,本地数据库由于无法具备两块独立的存储,极有可能是通过划分出一块数据空间来做本分存储。然后对于数据库数据做日志法的递增写入。
总而言之,事务就是一个具备ACID特性的行为,那么什么样的行为才有可能具备ACID,什么样的行为永远不能具备ACID呢。这就需要探讨一下行为了。
无约束操作
无约束操作是指操作不是哦到任何的条件限制,不需要执行的条件,不计较执行的结果。这和ACID属性完全的对立,任何的操作都有可能失败。上文提到的单文件读写就是一个这样的操作。举一个简单的例子,无约束操作如同处于猿人阶段的人类。而ACID就是具备技能和一些道德思维进化结果。
闭门造车
这是一个具备ACID特性的操作模型,它表达的思想是,一些列操作封装在一个工厂里,进去的是零件出来的是陈品,你不可能看见半成品。
物理操作
界定这个行为和无约束操作很难,首先该如何定义物理操作呢。可以尝试这样理解,当一个指令发出后,计算机就会对某一介质进行访问并且记录,并且,这个记录"很难"或者"根本无法"回转。
High-Level事务资源的包装思想
那么这些最事务的原则在底层开发者手里应该如何去构成事务呢。其实,你需要做出一个取舍,因为下面这句话很拗口
“事务的本质是不完全的,假想的,需要容忍的”、
那么以数据库事务为例子,应该这样组合,相信,大多的数据库也是这样设计的
首先数据库的查询适配器接受SQL语句,可能有下面的语句
SQL_SELECT 语句: 如果接受的是SELECT语句,就是一个文件的读取操作
1》 整个读取操作是一个闭门造车行为,通俗的说是,要么就是完全正确的结果,否则就是无法返回(文件操作算法的2,3,4都提供一定的ACID方案)。 而闭门造车的工厂里就是无数个无约束操作(从文件存储上读数据<单文件读写>,解析数据,生成视图等),由于SELECT的事务原则是闭门造车,它具备ACID特性,那么它就具备了一定可靠性的事务能力(统一提交,回滚等,不同等级的数据库在这方面的能力区别是很大的)。
SQL_UPDATE 语句: 如果接受的是UPDATE语句,就是一个文件的读取和写入的双向操作
2》如上面所说,读取是一个闭门行为,同理解释写入也是一个闭门行为,因为,文件操作中算法3(复合写入)和算法4(日志法)都提供了一定能力的事务特性。所以整个UPDATE行为本身是 闭门A×闭门B×(其他的非事务行为),因此,其本身就具备了事务行为。
SQL_INSERT 语句:
3》如上面所说的,完完全全的文件写入操作。3种文件写入的算法随便选。
SQL_DELETE 语句:如果接受的是DELETE语句,那么情况就复杂了,各种数据在处理delete语句能力上差别明显。
4》首先我要说,无论怎么看,这是一个物理行为,一旦把一条数据擦除了,是很难或者无法恢复的。那么数据要完成相对可靠的事务行为举必须面对这个问题。该怎么办?一些事务功能简单的数据只是简单的提供一些预防功能,比如在程序中控制尽量避免发生。很显然这样的事务很苍白无力。比较高端的数据库系统会设置很多的预制位,每个预制位标定了当前操作的状态,然后通过用户定义的事务等级来确定是否需要回滚某一操作。其实很有可能有这样的一个场景,在执行大批量删除命令时,当指令到达某一预制位,会先检查磁盘是否可靠,电源是否稳定,机器CPU的状态,或者RAM,然后才来确定是否去执行这条操作。或者更具策略分成N个步骤去完成。不断的备份,运行,备份,运行等。