深入JTS事务引擎:理论与实践相结合,掌握高效事务管理的秘诀

事务是可靠应用程序的构建块

如果您阅读过任何有关 J2EE 的介绍性文章或者书籍,那么就会发现,只有一小部分资料是专门针对 Java Transaction Service(JTS)或 Java Transaction API(JTA)的。这并不是因为 JTS 是 J2EE 中不重要的部分或者可选部分 —— 恰恰相反。JTS 受到的关注之所以会比 EJB 技术少,是因为它为应用程序提供的服务非常透明 —— 很多开发人员甚至没有注意到在他们的应用程序中事务在哪里开始和结束。在某种意义上,JTS 的默默无闻恰恰是它的成功:因为它非常有效地隐藏了事务管理的很多细节,因此,我们没有听说过或者谈论过很多关于它的内容。但是,您可能想了解它在幕后都为您执行什么功能。

毫不夸张地说,没有事务就不能编写可靠的分布式应用程序。事务允许采用某种控制方式修改应用程序的持久性状态,以便使应用程序对于各种各样的系统故障(包括系统崩溃、网络故障、电源故障甚至自然灾害)更加健壮。事务是构建容错、高可靠性以及高可用性应用程序所需的基本构建块之一。

事务的动机

假设您正在从一个账户向另一个账户进行转账个账户差额由数据库表中的某一行来表示。如果您想从账户 A 转账到账户 B,则可能执行如下这些 SQL 代码:

SELECT accountBalance INTO aBalance
FROM Accounts WHERE accountId=aId;
IF (aBalance >= transferAmount) THEN
UPDATE Accounts
SET accountBalance = accountBalance - transferAmount
WHERE accountId = aId;
UPDATE Accounts
SET accountBalance = accountBalance + transferAmount
WHERE accountId = bId;
INSERT INTO AccountJournal (accountId, amount)
VALUES (aId, -transferAmount);
INSERT INTO AccountJournal (accountId, amount)
VALUES (bId, transferAmount);
else
FAIL "Insufficient funds in account";
END if

到目前为止,这段代码看起来非常简单易懂。如果手头的资金充足,则从一个账户中减去资金,并添加到另一个账户中。但是,如果出现系统电源故障或者崩溃,又会发生什么情况呢?表示账户 A 和账户 B 的行可能不会存储在同一个磁盘块中,这意味着要完成转账需要进行多个磁盘 IO。如果在已写入第一个磁盘块之后,在写入第二个磁盘块之前,系统发生故障,又会发生什么情况呢?A 账户中的资金已经划走,但是没有出现在账户 B 中(A 和 B 客户都不会愿意),或者资金将出现在 账户 B 中,但是没有记入账户 A 的借出账中(银行不会愿意)。如果账户已正确更新,而账户日记账没有更新,又会发生什么情况呢?那么账户 A 和账户 B 的每月银行结账单将与它们账户的余额不一致。

不仅不可能同时将多个数据块写入磁盘,而且每当进行修改时马上将每个数据块写入磁盘,也对系统性能有不利影响。将磁盘写入延迟到比较适宜的时间可能会大大改善应用程序的吞吐量,但是,需要采用不损害数据完整性的方式执行。

甚至在系统没有发生故障时,上面讨论的代码还有另一种风险 —— 并发性。如果账户 A 中有 100 美元,但是却同时开始向它的两个不同的账户分别转账 100 美元,那么会发生什么情况呢?如果时间上凑巧,并且没有适当的锁定机制,两次转账都可能成功,从而使账户 A 的余额为负值。

这些情况似乎都是非常可能发生的,因此希望企业数据系统能够解决这些问题是理所应当的。我们希望在发生火灾、洪水、电源故障、磁盘以及系统出现故障时,银行都能够保持正确的账户记录。可以通过冗余(冗余的磁盘、计算机以及数据中心)来提供容错,但是事务 使得构建容错的软件应用程序成为可能。事务提供了一个框架,用于在系统或组件发生故障时保持数据一致性和完整性。

什么是事务?

那么到底什么是事务呢?在定义这个术语之前,我们首先定义应用程序状态 的概念。应用程序的状态包含影响应用程序操作的所有内存和磁盘中的数据项目 —— 应用程序 “知道” 的所有内容。应用程序状态可以存储在内存、文件或者数据库中。如果系统发生故障,例如应用程序、网络或者计算机系统崩溃,则我们想确保当重新启动系统时,可以恢复应用程序的状态。

现在,我们将事务 定义为对应用程序状态的相关操作的集合。事务具有原子性一致性隔离性 以及持久性 这几个属性。这些属性统称为 ACID 属性。

原子性 意味着要么所有事务操作都应用于应用程序状态,要么都不应用;事务是不可拆分的工作单元。

一致性 意味着事务代表应用程序状态的正确转换 —— 即事务不能违反应用程序中固有的任何完整性限制。实际上,一致性的概念是特定于应用程序的。例如,在记账应用程序中,一致性可能包括所有资产账户的总和始终等于所有负债账户的总和这个不变式。在本系列的第 3 部分中讨论事务划分时,我们将详细讨论这个需求。

隔离性 意味着一个事务的效果不影响正在同时执行的其他事务。从事务的角度讲,它意味着事务按顺序执行而不是并行执行。在数据库系统中,通常通过使用锁机制来实现隔离性。为了使应用程序获得最佳性能,有时也会对某些事务放松隔离性的要求。

持久性 意味着一旦成功完成某个事务,对应用程序状态所做的更改将 “经得起失败”。

什么是 “经得起失败”呢?它由什么组成?这取决于系统,一个设计良好的系统将明确地标识可以从哪些故障中恢复过来。在我的桌面工作站上运行的事务数据库,对于系统崩溃和电源故障非常稳定健壮,但是对于我的办公大楼发生大火灾却没有任何作用。银行可能不仅仅在数据中心具有冗余的磁盘、网络以及系统,而且还可能在别的城市有冗余的数据中心,该冗余数据中心通过冗余的通信链路连接,目的是允许从严重的故障(如自然灾害)中进行恢复。军用的数据系统甚至可能有更严格的容错要求。

事务的剖析

典型事务有几个参与者 —— 应用程序、事务监视器(TPM)以及一个或多个资源管理器(RM)。资源管理器存储应用程序状态,常常是数据库,但也可能是消息队列服务器(在 J2EE 应用程序中,它们将是 JMS 提供者)或其他事务性资源。TPM 协调 RM 的活动,以确保事务 “要么全有要么全无” 属性。

当应用程序请求容器或事务监视器启动新的事务时,事务开始。由于应用程序访问各种各样的 RM,因此,在事务中对它们进行征用。RM 必须使对应用程序状态所做的任何更改与请求更改的事务相关联。

当发生以下事件之一或者两个事件都发生时,事务结束:事务应用程序提交 该事务;通过应用程序或者由于其中一个 RM 失败,回滚 该事务。如果事务成功提交,则将写入与该事务相关联的更改,以使更改持久化并使其对于新的事务可见。如果事务被回滚,则该事务所做的所有更改都将被丢弃;就好像该事务从来没有发生过一样。

事务日志 —— 持久性的关键

事务 RM 通过在一个事务日志中记录多个事务的结果,获得持久性以及可接受的性能。事务日志存储为连续的磁盘文件(有时存储在原始分区中),并且一般只是用于写入而不用于读取,回滚或恢复的情况例外。在我们的银行账户示例中,与账户 A 和账户 B 相关联的余额将在内存中进行更新,新的余额和旧的余额将被写入到事务日志中。编写事务日志的更新记录不需要将全部数据都写入磁盘(只需要写入已更改的数据,而不需要写入全部磁盘块),而且所需的磁盘寻道时间也会更少(原因是所有更改都包含在日志中连续的磁盘块中)。此外,与多个并发事务关联的更改可以合并到一起,一次写入事务日志,这意味着每次磁盘写入时我们可以处理多个事务,而不需要 每个事务进行几次磁盘写入。之后,RM 将根据所更改的数据更新实际的磁盘块。

重新启动时进行恢复

如果系统出现故障,重新启动时要做的第一件事就是重新应用所有已提交事务的作用,所有这些已提交的事务都 位于日志中,但是它们的数据块尚未更新。采用这种方式,日志保证了故障之间的持久性,而且还能够减少所执行的磁盘 IO 操作的数量,或者至少使它们延迟到对系统性能影响更小的时间。

两阶段提交

很多事务只涉及一个 RM —— 通常是数据库。在这种情况下,RM 通常执行提交或回滚事务所需的大部分工作。(几乎所有事务 RM 都有它们自己的内置的事务管理器,这个管理器可以处理本地事务 —— 只涉及该 RM 的事务)。但是,如果事务涉及两个 RM 或多个 RM —— 可能是两个单独的数据库,或者是一个数据库和一个 JMS 队列,或者是两个单独的 JMS 提供者 —— 我们想确保 “要么全有要么全无” 的语义不仅仅应用于这个 RM 中,而且还应用于事务中的所有 RM。在这种情况下,TPM 将组织一个两阶段提交。在两阶段提交中,TPM 首先向每个 RM 发送一个 “准备” 消息,询问它是否准备就绪以及是否能够提交事务;如果它收到来自所有 RM 的确认应答,则将事务在其自己的事务日志中标记为已提交,然后指示所有 RM 提交事务。如果某个 RM 失败,则重新启动时它将向 TPM 询问有关失败时未处理的所有事务的状态,并提交它们或者对它们执行回滚操作。

两个阶段提交类似于社会上的结婚典礼 —— 牧师或神父询问双方 “您愿意让这个男人/女人作为您的丈夫/妻子吗?” 如果双方都回答是,则将宣布他们成为夫妻;否则,双方不能结婚。不管双方中的哪一方首先说 “我愿意”,在一方没有回答时,另一方决不能完成结婚。

事务作为处理异常的机制

您可能观察到,事务向对块进行同步的应用程序数据提供很多与内存中数据相同的功能 —— 保证原子性、更改的可见性以及显而易见的排序。但是,当同步主要是并发控制的机制时,则事务主要是处理异常的机制。如果在一个磁盘不会发生故障、系统和软件不会崩溃以及电源是百分百可靠的世界中,我们将不需要事务。事务在企业应用程序中所起的作用与合同法在社会上所起的作用一样 —— 它们规定,如果一方不能履行他那一部分合同,则交易将失效。当我们编写合同时,我们通常希望它是多余的,令人感到欣慰的是大部分时候都是如此。

与比较简单的 Java 程序进行类比,事务在应用程序级别所提供的一些优势与 catch 和 finally 块在方法级别所提供的优势相同;它们使我们不用编写很多错误复原代码,即可执行可靠的错误复原。考虑下面这个方法,该方法将一个文件复制到另一个文件:

public boolean copyFile(String inFile, String outFile) {
InputStream is = null;
OutputStream os = null;
byte[] buffer;
boolean success = true;

try {
is = new FileInputStream(inFile);
os = new FileOutputStream(outFile);
buffer = new byte[is.available()];
is.read(buffer);
os.write(buffer);
}
catch {IOException e) {
success = false;
}
catch (OutOfMemoryError e) {
success = false;
}
finally {
if (is != null)
is.close();
if (os != null)
os.close();
}

return success;
}

忽略为整个文件分配一个缓冲区是一个不好的想法,但是在这个方法中哪里错了呢?有很多东西。输入文件可能不存在,或者该用户可能没有这个文件的读权限。用户可能没有输出文件的写权限,或者该文件被另一个用户锁定。可能没有足够的磁盘空间来完成该文件的写操作,或者由于没有足够的内存可用,分配缓冲区可能失败。幸运的是,所有这些都由 finally 语句来处理,该语句释放了 copyFile()所使用的所有资源。

如果您使用原来的 C 语言编写这个方法,则对于每个操作(打开输入、打开输出、malloc、读、写),必须测试返回状态,如果操作失败,则取消以前成功的所有操作,并返回适当的状态代码。由于需要这么多错误处理代码,该代码可能更大,因此更难阅读。同时在错误处理代码(它也是最难测试的部分)中很容易出错,比如不能释放资源、对一个资源释放两次或者释放尚未分配的资源。更复杂的方法可能涉及更多资源,而不仅仅是两个文件和一个缓冲区,这使得问题变得更加复杂。在大量错误复原代码中,很难发现实际的程序逻辑。

现在,假设您正在执行一个复杂的操作,该操作涉及在多个数据库中插入或更新多个行,其中一个操作违反了完整性约束并失败了。如果您管理自己的错误复原,则必须跟踪已经执行的操作,并知道在随后的操作失败的情况下如何取消每个操作。如果工作单元分布在多个方法或组件上,则会更加困难。借助事务来构造应用程序,就可以将所有这些清理工作委托给数据库(即进行 ROLLBACK),并取消自从事务开始所执行的所有操作。

结束语

通过借助事务构造应用程序,我们定义一组正确的应用程序状态转换,并确保应用程序始终处于正确的状态,甚至在系统或组件发生故障之后也是如此。事务使我们能够将很多异常处理和恢复工作委托给 TPM 和 RM,从而简化了我们的代码,并使我们能够空出更多时间来考虑应用程序逻辑。

在此系列的第 2 部分中,我们将探讨这对于 J2EE 应用程序意味着什么 —— J2EE 如何使我们能够将事务语义告知 J2EE 组件(EJB 组件、servlet 以及 JSP 页面);它如何使资源征用对应用程序(甚至对于 bean 管理的事务)完全透明;单个事务如何 透明地遵循从一个 EJB 组件到另一个 EJB 组件,或者从一个 servlet 到一个 EJB 组件,甚至跨越多个系统的控制流程。

尽管 J2EE 提供了相当透明的对象事务服务,但是应用程序设计者仍然必须仔细考虑在哪里划分事务,以及如何在应用程序中使用事务资源 —— 不正确的事务划分可能会使应用程序处于不一致的状态,而不正确地使用事务资源可能会造成非常严重的性能问题。在此系列的第 3 部分中,我们将讨论这些问题并提供一些关于如何构造应用程序的建议。

7e6ee27100758030a7ba2e1cea25e78b.jpeg

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

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

相关文章

基于SpringBoot的网上订餐系统

基于SpringBoot的网上订餐系统的设计与实现 开发语言:Java数据库:MySQL技术:SpringBootMyBatisVue工具:IDEA/Ecilpse、Navicat、Maven 【主要功能】 角色:用户、管理员管理员:登录、个人中心、会员管理、…

网络工程师--网络安全与应用案例分析

前言 需要网络安全学习资料的点击链接:【282G】网络安全&黑客技术零基础到进阶全套学习大礼包,免费分享! 案例一: 某单位现有网络拓扑结构如下图所示,实现用户上网功能,该网络使用的网络交换机均为三…

边端小场景音视频流分发架构

备注:绿色线条,红色线条,蓝色线条,均是表示同一路流的不同的协议而已 1)IPC本身的流媒体的能力有限,一般IPC支持的客户端数10~50个,媒体分发能力:10~20路,看设备品牌能力…

linux中搭建c语言环境并编译

安装gcc 安装 yum install gcc 检查 gcc --version 编译文件 1.编写test.c vim test.c #include <stdio.h>int main() {printf(" ***** \n");printf(" * o o * \n");printf("* ^ *\n");printf("* - *\n");printf…

小程序如何设置各种时间参数

在小程序管理员后台->基本设置处&#xff0c;可以设置各种时间。例如待支付提醒时间、待支付取消时间、自动发货时间、自动收货时间、自动评价时间等等。下面具体解释一下各个时间的意思。 1. 待支付提醒时间&#xff1a;在用户下单后&#xff0c;如果一段时间内没有完成支付…

RS485电路设计

引言 今天学习RS485电路的设计。 首先先来了解一下RS485电路是什么干什么。 RS485是一种串行通信协议&#xff0c;也是一种电气标准。它可以用于在远距离范围内传送数据&#xff0c;最长传输距离可以达到1200米&#xff0c;可以支持多个设备同时通信。RS485通常应用于工业自…

【漏洞复现】安全云平台存在任意文件下载getshell

漏洞描述 深圳市强鸿电子有限公司鸿运主动安全云平台存在任意文件下载漏洞,攻击者可通过此漏洞下载敏感文件信息。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事危害国家安全、荣誉和利益,未经授权…

SQL 教程||SQL 简介

SQL 简介&#xff08;开个新坑&#xff09; SQL&#xff08;结构化查询语言&#xff09;是用于访问和操作数据库中的数据的标准数据库编程语言。 SQL是关系数据库系统的标准语言。所有关系数据库管理系统(RDMS)&#xff0c;如MySQL、MS Access、Oracle、Sybase、Informix、Po…

开源任务调度框架

本文主要介绍一下任务调度框架Flowjob的整体结构&#xff0c;以及整体的心路历程。 功能介绍 flowjob主要用于搭建统一的任务调度平台&#xff0c;方便各个业务方进行接入使用。 项目在设计的时候&#xff0c;考虑了扩展性、稳定性、伸缩性等相关问题&#xff0c;可以作为公司…

用超声波清洗机来洗眼镜好不好?眼镜超声波清洗机推荐

超声波清洗机不单单可以清洗眼镜了&#xff0c;已经衍生到可以清洗项链首饰化妆刷等物件了&#xff0c;要说利用超声波清洗机洗眼镜好不好的话其实毋庸置疑是不错的&#xff0c;毕竟现在眼镜店都会有一部超声波清洗机放在店里给顾客清洗&#xff0c;也是亲眼所见一副很久没清洗…

3.MySQL数据类型详解

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 目录 1.数据类型分类 2.数值类型 (1).tinyint&#xff0c;smallint类型等 (2)bit类型 (3)小数类型 1).float 2).decimal 3.字符串类型 (1)char (2)varchar (3)char和varchar比较 (4)日期和时间类型 (5)enum和se…

TensorFlow入门(二十五、单个神经元的扩展——Maxout网络)

Maxout网络的原理 Maxout是Goodfellow在2013年提出的一个新的激活函数,与其它的激活函数相比,Maxout是需要参数的,且参数可以通过网络的反向传播得到学习,因此它比其它激活函数有着更好的性能,理论上可以拟合任意凸函数,进而使得网络取得更好的性能。 Maxout网络主要是扩展单个…

React js原生 详解 HTML 拖放 API(鼠标拖放功能)

最近碰到了个需求&#xff0c;大概就是要通过可视化拖拽的方式配置一个冰柜&#xff0c;需要把预设好的冰柜内部架子模板一个个拖到冰箱内。一开始的想法是用鼠标事件&#xff08;mousedown、mouseup等&#xff09;那一套去实现&#xff0c;能实现但是过程过于复杂&#xff0c;…

Blender:制作一个变形动画

就是一个球逐渐地变为一个立方体 首先创建一个球和一个立方体 然后把两个物体放在一起&#xff0c;放缩球&#xff0c;让球包含立方体 之后选中球&#xff0c;为其添加修改器&#xff0c;缩裹 在这里选择缩裹对象为立方体 然后在应用下拉箭头中选择“应用为形态键” 下一步选中…

超越平凡:Topaz Photo AI for Mac带您领略人工智能降噪的魅力

在这个充满噪点和高频信息的时代&#xff0c;照片和视频的降噪成为了一个重要而迫切的需求。Mac用户现在有了一个强大的新工具——Topaz Photo AI for Mac&#xff0c;这是一款利用人工智能技术进行降噪和优化的软件。通过这款软件&#xff0c;您可以轻松地改善图像质量&#x…

如何选择高防CDN和高防IP?

目录 前言 一、对高防CDN的选择 1. 加速性能 2. 抗攻击能力 3. 全球覆盖能力 4. 可靠性和稳定性 二、对高防IP的选择 1. 防御能力 2. 服务质量 3. 安全性 4. 价格 三、高防CDN和高防IP的优缺点对比 1. 高防CDN的优缺点 2. 高防IP的优缺点 总结 前言 随着互联网…

翻译docker官方文档(残缺版)

Build with docker(使用 Docker 技术构建应用程序或系统镜像) Overview (概述) 介绍&#xff08;instruction&#xff09; 层次结构&#xff08;Layers&#xff09; The order of Dockerfile instructions matters. A Docker build consists of a series of ordered build ins…

CANdb++数据库操作

CANdb数据库操作 创建工程结构文件夹新建数据库&总线描述节点设置节点创建配置Message属性信号设置节点收发信号 环境变量配置一致性检验数据库工程XVehicle.dbc导入工程文件总结 创建工程结构文件夹 在文件夹X-Vehicle-1下&#xff0c;建立工程目录文件夹CANdb&#xff0…

python 如何获取url的名称

一、使用os模块 os模块是Python内置的一个操作系统接口模块&#xff0c;提供了许多与操作系统相关的函数和变量。其中&#xff0c;os.path模块用于处理路径相关的操作&#xff0c;包括文件名、目录名等。 os.path.basename()函数可以用来获取路径中的文件名部分 imp…

每日一题 136. 只出现一次的数字(简单,位运算)

异或运算性质&#xff0c;两个相等的数作异或运算得零&#xff0c;任何数与零作异或运算保持不变 所以整个数组的异或和就是答案 class Solution:def singleNumber(self, nums: List[int]) -> int:ans 0for i in nums:ans ^ ireturn ans一行代码&#xff0c;reduce作累积操…