国产数据库与Oracle数据库事务差异分析

news2025/4/18 15:31:30

数据库中的ACID是事务的基本特性,而在Oracle等数据库迁移到国产数据库国产中,可能因为不同数据库事务处理机制的不同,在迁移后的业务逻辑处理上存在差异。本文简要介绍了事务的ACID属性、事务的隔离级别、回滚机制和超时机制,并总结SAVEPOINT的使用,以总结。


1、数据库中事务基本概念

事务是数据库中的基本逻辑操作单元,由一组不可分割的数据库操作序列组成,这些操作要么全部成功执行,要么全部失败回滚。其核心目的是确保数据的完整性和一致性,尤其在并发操作或系统故障时维护数据库的可靠状态。

1.1 事务基本属性

ACID是事务的基本特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。

  • 原子性:事务中的所有操作必须作为一个整体执行,要么全部执行成功、要么全部失败回滚,不允许出现部分成功的情况。在数据库中通常是通过日志记录(如undo log)来实现回滚操作,若事务执行失败,系统跟进日志撤销已执行的操作。
  • 一致性:事务执行前后,数据库必须保持一致性状态。所有数据必须满足预定义的完整性约束(如主键、外键、唯一性约束等)。即使事务失败,数据库也不能破坏这些规则。在数据库中通过一些约束和检查来确保数据库的完整性约束。
  • 隔离性:多个事务并发执行时,每个事务的操作应与其他事务相互隔离,使得每个事务感觉不到其他事务的存在,最终效果应与事务串行执行的结果一致。数据库中通过锁机制(Locking)或多版本并发控制(MVCC)实现,不同的隔离级别提供不同程度的隔离性。
  • 持久性:事务一旦提交,其对数据库的修改就是永久性的,即使系统发生故障(如断电、崩溃),修改也不会丢失。数据库中通过重做日志(Redo Log)实现持久性。提交事务时,对数据的修改首先写入日志,再异步写入数据库文件中。当数据库崩溃恢复时,通过重放日志恢复数据。

以转账交易为例,通过undo日志实现原子性,确保“扣款”和“存款”两个操作要么全部成功,要么全部失败;一致性是确保转账前后,数据库必须满足业务规则(如余额不为负、总额不变);通过锁机制和MVCC多版本并发控制来实现事务的隔离性,多个并发转账操作互不干扰,结果与串行执行一致;持久性则是一旦转账成功,即使系统崩溃,修改也不会丢失。

BEGIN TRANSACTION;
-- 1. 检查一致性:用户A余额是否足够(一致性)
SELECT balance FROM accounts WHERE user = 'A' FOR UPDATE;
-- 如果余额 < 100,抛出错误并回滚
-- 2. 扣款(原子性)
UPDATE accounts SET balance = balance - 100 WHERE user = 'A';
-- 3. 存款(原子性)
UPDATE accounts SET balance = balance + 100 WHERE user = 'B';
-- 4. 提交(持久性)
COMMIT;
1.2 事务隔离级别

事务隔离级别是数据库事务处理的基础,SQL-92标准定义了4种隔离级别:读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)、串行化(SERIALIZABLE)。详见下表:

在这里插入图片描述

不同的隔离级别有不同的现象,并有不同的锁和并发机制。隔离级别越高,数据库的并发性能就越差。

1.2.1 脏读/不可重复读/幻读现象

1)脏读
A事务读取B事务尚未提交的更改数据,并在这个数据的基础上进行操作,这时候如果事务B回滚,那么A事务读到的数据是不被承认的。

在这里插入图片描述

2)不可重复读
不可重复读是指在同一个事务中,同一个查询在T1时刻读取一行数据,在T2时刻重新读取这一行数据的时候,发现这一行数据已经发生了修改(被更新或者删除)。假如A在取款事务的过程中,B往该账户转账100,A两次读取的余额发生不一致。

3)幻读
幻读是指在同一个事务中,当同一个查询多次执行的时候,由于其它插入操作的事务提交,会导致每次返回不同的结果集。不可重复读和幻读的区别是:前者是指读到了已经提交的事务的更改数据(修改或删除),后者是指读到了其他已经提交事务的新增数据。

在这里插入图片描述

1.2.2 行锁模式
  • Share:lock owner和任何并发程序可以read但是不能change locked page或row,并发程序可能获得S-lock、U-lock,也可能没有lock就进行读操作
  • Update:lock owner可read但是不能change locked page或row,但是owner可以将U-lock升级为X-lock这样就可以修改page或row
    • 升级为X-lock这个过程可能会引起其它S-lock的并发进程暂停在那
    • 当lock owner读数据的时候并决定是否需要修改它的时候,U-lock会减少deadlocks的几率
  • Exclusive:只有lock owner才能read或change locked page或row,并发程序只有当程序处于UNCOMMITTED read isolation的时候才能访问数据
  • Lock mode compatibility,见下表

在这里插入图片描述

比如说User A对page hold住S-lock,如果User B想对page请求X-lock,则User A的lockmode会拒绝User B的请求。

1.2.3 隔离级别

1)读未提交(Read Uncommitted)
读未提交,就是一个事务可以读取另一个未提交事务的数据,也称为脏读。在读数据时候不加锁,写数据时候加行级别的共享锁,提交时释放锁。行级别的共享锁,不会对读产生影响,但是可以防止两个同时的写操作

2)读已提交隔离级别(Read Committed)
读提交,就是一个事务要等另一个事务提交后才能读取它的数据,否则是读取不到另外一个事务的更改的数据。
事务读取数据(读到数据的时候)加行级共享S锁,读完释放;事务写数据时候(写操作发生的瞬间)加行级独占X锁,事务结束释放。由于事务写操作加上独占X锁,因此事务写操作时,读操作也不能进行,因此,不能读到事务的未提交数据,避免了脏读的问题。但是由于,读操作的锁加在读上面,而不是加在事务之上,所以,在同一事务的两次读操作之间可以插入其他事务的写操作,所以可能发生不可重复读的问题。

3)可重复读隔离级别(Repeatable Read)
当事务隔离级别为可重复读时,只能读到该事务启动时已经提交的其他事务修改的数据,未提交的数据或在事务启动后其他事务提交的数据是不可见的。对于本事务而言,事务语句可以看到之前的语句做出的修改。
事务读取数据在读操作开始的瞬间就加上行级共享S锁,而且在事务结束的时候才释放。但是,由于加的是行级别的锁,仍然可能发生幻读的问题。

4)序列化(Serialization)
最严格的隔离级别,强制事务串行执行,使之不可能冲突,从而解决幻读的问题,资源消耗最大。在读操作时,加表级共享锁,事务结束时释放;写操作时候,加表级独占锁,事务结束时释放。在这个级别,可能会导致大量的锁超时和锁竞争现象,实际上也很少用到。

1.2.4 不同数据库隔离级别

不同数据库支持的隔离级别也不同

在这里插入图片描述

  • Oracle数据库支持读已提交和序列化,默认隔离级别为Read Committed,通过通过多版本并发控制(MVCC)避免脏读,但存在不可重复读和幻读。
  • MySQL数据库支持四种隔离级别,默认为可重复读,通过MVCC和间隙锁来减少幻读问题。
  • MySQL数据库支持四种隔离级别,默认为Read Committed
  • OceanBase(for Oracle)模式支持读已提交和序列化,默认为Read Committed
  • TiDB支持读已提交、可重复读和序列化,默认为Repeatable Read,其实在TiDB中实现是快照隔离,类似可重复读;
  • GoldenDB兼容MySQL的隔离机制,支持4种隔离级别,但是默认的级别是Read Committed,也是并发和一致性平衡的结果;
  • GaussDB支持Read Committed和Repeatable Read,默认隔离级别是Read Committed
1.3 事务回滚机制

事务的原子性要求事务要么全部执行成功、要么全部执行失败回滚,但是对于Oracle数据库支持语句级的原子性,也就是一个事务中单个语句执行失败,则只会回滚该语句执行的操作,不会导致在当前事务中丢失之前的任何工作。如果需要回滚整个事务,需要处理错误并且主动调用ROLLBACK。这种语句级的回滚对于处理一些长时间运行的批处理任务有用,逻辑上希望能够处理错误,不需要回滚已经完成的所有操作。

在这里插入图片描述

不过从Oracle数据库迁移到国产数据库中,大部分数据库在事务的回滚机制上并不支持语句级别的,因此需要通过采用SAVEPOINT保存点的方式。使用savepoint虽然可以解决语句级别功能上的需求,但是不合理的使用可能引发其它问题。

1.4 事务超时机制

数据库中事务会设置不同的超时机制,防止因为出现等锁而出现无限等待,超过这个时间后会出现等锁超时,事务会回滚。

在这里插入图片描述

  • Oracle数据库:默认不会主动终止因行锁等待而阻塞的事务,事务会无限期等待锁释放,需由应用层处理或手动终止,行锁在事务提交或回滚是自动释放;事务默认也无超时设置,但是可以限制会话的空闲时间,超过时间后会断开链接。
  • MySQL数据库:通过 innodb_lock_wait_timeout 控制,默认为50秒。当事务等待锁超过此时间时,会抛出错误;事务中无默认超时时间,但是连接的空闲超时设置wait_timeout,默认8小时。
  • PostgreSQL:通过pg_lock_timeout设置行锁等待超时时间,默认为0无限等待;事务中设置statement_timeout 控制单条SQL执行时间,默认无限制。
  • TiDB:兼容MySQL行锁等待设置;如果是悲观事务,默认TTL(Time-To-Live)为 1小时,超时后自动回滚,另外通过tidb_idle_transaction_timeout 控制空闲事务。
  • OceanBase:MySQL模式下兼容MySQL设置;事务中通过 ob_query_timeout 控制事务单条语句执行时间,默认1800s
  • GoldenDB:兼容MySQL设置
  • GaussDB:参数lockwait_timeout控制单个锁的最长等待时间,当申请的锁等待时间超过设定值时,系统会报错,默认为20min;通过通过 statement_timeout 控制单个语句执行时长,默认0表示不控制。
1.5 如何使用SAVEPOINT

前文提到Oracle数据库中支持语句级别的回滚,在迁移到国产数据库后,为了兼容Oracle数据库这个特性,很多数据库支持savepoint机制。SAVEPOINT 是事务中的一个逻辑标记点,用于标识事务执行到某个特定位置的状态。通过ROLLBACK TO SAVEPOINT可以回滚到该标记点,撤销该点之后的所有操作,但保留该点之前的操作。SAVEPOINT可以提供细粒度的事务控制,避免因整个事务回滚导致的数据丢失。对于一些复杂或长时间运行的事务中,可以分阶段提交或回滚操作。

BEGIN
  INSERT INTO orders (id, amount) VALUES (1, 100);
  SAVEPOINT sp1;  -- 设置保存点sp1
  UPDATE inventory SET stock = stock - 1 WHERE product_id = 101;
  SAVEPOINT sp2;  -- 设置保存点sp2
  -- 假设此处发生错误
  ROLLBACK TO sp1;  -- 回滚到sp1,撤销UPDATE操作
  COMMIT;           -- 提交事务(仅保留INSERT操作)
END;

但是在一个长事务中不规范的使用SAVEPOINT可能会导致实例内存上涨、事务执行时间异常的问题。比如在Oracle数据库中的一个游标查询语句,每1K笔执行一次,迁移到GaussDB之后,URL串中指定了autosave参数,这样在每次游标访问时候都会执行一次SAVEPOINT动作,原来几分钟的任务可能几个小时都执行不完,出现很多SAVEPOINT等待事件,并且数据库实例的内存逐渐上涨。

那么对于一些复杂的业务场景,如何合理的使用SAVEPOINT?

  • 禁止自动设置SAVEPOINT,只在必要时设置SAVEPOINT,如在数据更新、复杂逻辑分支操作上设置;
  • 将长事务拆分为多个小事务,每处理一定的数据后提交,以释放资源;
  • 避免在游标循环中频繁执行SAVEPOINT,改用批量处理或分页查询;
  • 及时回滚或释放无用SAVEPOINT,在逻辑分支完成后,主动回滚到最近的SAVEPOINT并释放资源;
  • 避免嵌套过深的SAVEPOINT:过多的嵌套SAVEPOINT会增加回滚段的复杂度。

参考资料:

  1. https://pigsty.cc/blog/db/oracle-pg-xact/
  2. TiDB中的事务处理机制

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

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

相关文章

C++学习记录:

今天我们来学习一门新的语言&#xff0c;也是C语言最著名的一个分支语言&#xff1a;C。 在C的学习中&#xff0c;我们主要学习的三大组成部分&#xff1a;语法、STL、数据结构。 C的介绍 C的历史可追溯至1979年&#xff0c;当时贝尔实验室的本贾尼斯特劳斯特卢普博士在面对复杂…

等离子体浸没离子注入(PIII)

一、PIII 是什么&#xff1f;基本原理和工艺 想象一下&#xff0c;你有一块金属或者硅片&#xff08;就是做芯片的那种材料&#xff09;&#xff0c;你想给它的表面“升级”&#xff0c;让它变得更硬、更耐磨&#xff0c;或者有其他特殊功能。怎么做呢&#xff1f;PIII 就像是用…

idea中提高编译速度研究

探索过程&#xff1a; 有三种情况&#xff1a; 第一种&#xff1a; idea中用eclipse编译器编译springboot项目&#xff0c;然后debug启动Application报错找不到类。 有待继续研究。 第二种&#xff1a; idea中用javac编译器编译springboot项目&#xff0c;重新构建用时&a…

静态链接part2

编译 语义分析 由语义分析器完成&#xff0c;这个步骤只是完成了对表达式的语法层面的分析&#xff0c;它并不了解这个语句是否真的有意义&#xff08;例如在C语言中两个指针做乘法运算&#xff0c;这个语句在语法上是合法的&#xff0c;但是没有什么意义&#xff1b;还有同样…

Vue3+Vite+TypeScript+Element Plus开发-17.Tags-组件构建

系列文档目录 Vue3ViteTypeScript安装 Element Plus安装与配置 主页设计与router配置 静态菜单设计 Pinia引入 Header响应式菜单缩展 Mockjs引用与Axios封装 登录设计 登录成功跳转主页 多用户动态加载菜单 Pinia持久化 动态路由 -动态增加路由 动态路由-动态删除…

3D语义地图中的全局路径规划!iPPD:基于3D语义地图的指令引导路径规划视觉语言导航

作者&#xff1a; Zehao Wang, Mingxiao Li, Minye Wu, Marie-Francine Moens, Tinne Tuytelaars 单位&#xff1a;鲁汶大学电气工程系&#xff0c;鲁汶大学计算机科学系 论文标题&#xff1a; Instruction-guided path planning with 3D semantic maps for vision-language …

ShellScript脚本编程

语法基础 脚本结构 我们先从这个小demo程序来窥探一下我们shell脚本的程序结构 #!/bin/bash# 注释信息echo_str"hello world"test(){echo $echo_str }test echo_str 首先我们可以通过文本编辑器(在这里我们使用linux自带文本编辑神器vim)&#xff0c;新建一个文件…

【HarmonyOS 5】敏感信息本地存储详解

【HarmonyOS 5】敏感信息本地存储详解 前言 鸿蒙其实自身已经通过多层次的安全机制&#xff0c;确保用户敏感信息本地存储安全。不过再此基础上&#xff0c;用户敏感信息一般三方应用还需要再进行加密存储。 本文章会从鸿蒙自身的安全机制进行展开&#xff0c;最后再说明本地…

探索鸿蒙沉浸式:打造无界交互体验

一、鸿蒙沉浸式简介 在鸿蒙系统中&#xff0c;沉浸式是一种极具特色的设计理念&#xff0c;它致力于让用户在使用应用时能够全身心投入到内容本身&#xff0c;而尽可能减少被系统界面元素的干扰。通常来说&#xff0c;就是将应用的内容区巧妙地延伸到状态栏和导航栏所在的界面…

网站301搬家后谷歌一直不收录新页面怎么办?

当网站因更换域名或架构调整启用301重定向后&#xff0c;许多站长发现谷歌迟迟不收录新页面&#xff0c;甚至流量大幅下滑。 例如&#xff0c;301跳转设置错误可能导致权重传递失效&#xff0c;而新站内容与原站高度重复则可能被谷歌判定为“低价值页面”。 即使技术层面无误&a…

在Mac上离线安装k3s

目录 首先是安装multipass。 1. 系统要求 2. 环境准备 本来想照着网上文档学习安装一下k3s&#xff0c;没想到在docker被封了之后&#xff0c;现在想通过命令行去下载github的资源也不行了&#xff08;如果有网友看到这个文档、并且知道问题原因的&#xff0c;请留言告知&am…

2025低代码平台选型策略:ROI导向下的功能与成本权衡

在当今快速变化的商业环境中&#xff0c;企业面临着前所未有的挑战与机遇。数字化转型已成为企业提升竞争力的关键&#xff0c;而软件开发的高成本和长周期无疑是实现这一转型的绊脚石。 低代码平台的兴起&#xff0c;为企业提供了一种高效、灵活的解决方案&#xff0c;使得非…

Redis的IO多路复用

1 传统的socket编码模型 传统 Socket 模型通常采用 多线程/多进程 或 阻塞 I/O 的方式处理网络请求。以下是典型实现步骤&#xff1a; 创建套接字&#xff08;Socket&#xff09; 步骤&#xff1a;调用 socket() 创建一个 TCP/UDP 套接字。通常把这个套接字称为【主动套接字】…

基于YOLOv9的课堂行为检测系统

基于YOLOv9的课堂行为检测系统 项目概述 本项目是一个基于YOLOv9深度学习模型的课堂行为检测系统&#xff0c;旨在通过计算机视觉技术自动识别和监测课堂中学生的各种行为状态&#xff0c;帮助教师更好地了解课堂教学效果。 项目结构 课堂行为检测/ ├── data/ │ ├──…

端、管、云一体化原生安全架构 告别外挂式防护!

面对数字化转型浪潮&#xff0c;企业网络安全风险日益凸显。数据泄露、黑客勒索等事件频发&#xff0c;合规要求加速推进。尽管企业纷纷部署了防病毒、身份认证、文件加密、入侵防护、流量监控等多种安全系统&#xff0c;但分散且孤立的架构非但没有有效抵御风险&#xff0c;反…

BI面向模型开发和面向报表开发,有什么区别?

在数字化时代&#xff0c;商业智能&#xff08;BI&#xff09;已成为企业决策不可或缺的工具。BI项目实施时&#xff0c;通常有两种开发模式&#xff1a;面向模型开发和面向报表开发。虽然两者都旨在通过数据驱动决策&#xff0c;但在开发逻辑、目标价值和技术路径上存在显著差…

进程控制(上)【Linux操作系统】

进程控制 写时拷贝 本质是一种减少深拷贝的方法 Linux中有很多拷贝的场景都用得上写时拷贝&#xff0c;下面以创建子进程时的写时拷贝为例&#xff1a; 子进程被创建的时候&#xff1a; 会继承父进程的mm_struct和页表 所以子进程刚刚继承时&#xff0c;父子进程的代码和数据…

5G网络下客户端数据业务掉线频繁

上层应用的日志和界面在待机状态下&#xff08;即没有做通话等业务操作&#xff09;&#xff0c;会频繁提示“离线”。 主要先看有没有丢网&#xff0c;UL BLER有没有问题。确认没有问题。看到业务信道释放后也可以成功重新建链。所以以为这个只是终端业务进入dormant态的提示…

【Docker项目实战】使用Docker部署Gitblit服务器

【Docker项目实战】使用Docker部署Gitblit服务器 一、Gitblit介绍1.1 Gitblit 介绍1.2 主要特点 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Gitblit镜像五、部署Gitbli…

Vitis: 使用自定义IP时 Makefile错误 导致编译报错

参考文章: 【小梅哥FPGA】 Vitis开发中自定义IP的Makefile路径问题解决方案 Vitis IDE自定义IP Makefile错误&#xff08;arm-xilinx-eabi-gcc.exe: error: *.c: Invalid argument&#xff09;解决方法 Vitis 使用自定义IP时: Makefile 文件里的语句是需要修改的&#xff0c;…