5个事务的数据库:如何确保数据一致性? (一个数据库有5个事务)

随着互联网和大数据的迅猛发展,数据在我们生活中扮演着越来越重要的角色。而在数据的存储和管理中,数据库系统是不可或缺的一部分。在数据库系统中,事务是其中最重要的部分之一。事务的作用是确保操作系统中的数据一致性。然而,当我们使用复杂的数据库系统时,如何确保事务的数据一致性呢?本文将介绍5种数据库事务的技术,以及如何使用它们来确保数据一致性。

1. ACID事务

ACID是最常用的数据库事务技术。ACID是Atomicity、Consistency、Isolation、Durability的缩写,分别代表原子性、一致性、隔离性和持久性。ACID作为一个完整的事务,将被数据库系统执行或回滚。

*原子性:一个事务的所有操作在协商一个单独的工作单位时执行,要么全部成功,要么全部失败。

*一致性:一个事务的所有操作都必须遵循一定的约束,以遵循业务逻辑规则。

*隔离性:并发执行事务的能力。各个事务之间可以完全地相互独立,同时也可以互相配合。

*持久性:在事务完成后,所有修改都将永久保存在数据库中。

2. BASE技术

如果ACID技术存在一些缺点,例如对系统进行并发更新和持久性要求高的要求,那么BASE技术就提供了一个更可扩展和更灵活的选择。

*基本可用:尽可能地保证高故障容忍性、面向分布式架构,并实现最小化的故障造成的影响。

*软状态:即让系统在一个时间段内保持中间状态,而不一定偏向某一方向。

*最终一致性:在一段时间后,所有的状态都变为最终一致状态。

3. 分布式事务

分布式事务处理多个分布式系统之间的事务。不同分布式系统的数据分布在不同的物理位置上,可能由不同的管理员进行管理。分布式事务提供了一种数据处理方式,可以确保在分布式环境下,事务的正确执行。

* XA:XA是分布式事务的一种协议,其目的是确保一个多个数据库之间的事务的一致性。

* Two-Phase Commit Protocol:这种协议需要在所有参与节点(数据库服务器)之间进行一个预提交过程,确认所有事务都成功完成,并且所有其他节点都已经做好提交的准备。这样,事务就可以成功提交,所有参与节点的数据都能更新到最新状态,实现数据一致性。

4. NoSQL的事务

NoSQL代表了不仅仅是SQL的数据库系统,但它们的共同点是它们都不使用传统的关系型数据库,因此可能不支持ACID事务。相反,这些数据库系统主要支持基于日志的系统,例如本地存储,支持最终一致性。

5. 原子操作

原子操作是在不用事务的情况下实现数据局部更新的一种技术。原子操作确保如果任何一部分失败,整个操作将被取消并恢复到原始状态。原子操作经常应用于高并发和资源受限的环境,如在开发云服务、移动应用程序和Web系统时会经常用到。

数据库系统必须确保数据的一致性,以便它们不会在事务处理过程中遭受损失。本文介绍了5种常用的事务技术,以及如何使用它们来确保数据的一致性。无论使用哪种技术,都要为系统提供适当的资源和保障,以确保系统在处理事务时保持高效率和一致性。

相关问题拓展阅读:

数据库事务正确执行的四个基本要素包括

ACID,指数据库事务正确执行的四个基本要素的缩写。包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库,必需要具有这四种特性,否则在事务过程(Transaction processing)当中无法保证数据的正确性,交易过程极可能达不到交易方的要求。

原子性

整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这银伍个事务从来没有执行过一样。

一致性

一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。

也就是说:如果事务是并发多个,系统也必须如同串行事务一样操作。其主要特征是保护性和不变性(Preserving an Invariant),以转账案例为例,假设有五个账户,每个账户余额是100元,那么五个账户总额是500元,如果在这个5个账户之间同时发生多个转账,无论并发多少个,比如在A与B账户之间转账5元,在C与D账户之间转账10元,在B与E之间转账15元,五个账户总额也应该还是500元,这就是保护性和不变性

隔离性

隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务正稿的隔离性将确保每一事务在系统中认为只有该事务在使用系统举搏孝。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。

持久性

在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

由于一项操作通常会包含许多子操作,而这些子操作可能会因为硬件的损坏或其他因素产生问题,要正确实现ACID并不容易。ACID建议数据库将所有需要更新以及修改的资料一次操作完毕,但实际上并不可行。

目前主要有两种方式实现ACID:之一种是Write ahead logging,也就是日志式的方式(现代数据库均基于这种方式)。第二种是Shadow paging。

数据库管理系统 (DBMS) 的五个重要软件组成部分是什么?

DBMS的五个重要的软件组成部分铅歼\x0d\x0a①DBMS引擎\x0d\x0a是DBMS中最重要的部分,它接受来自其他各个DBMS子系统的逻辑查询请求,并将逻辑查询请求转换成其对应的物理形式,实际上对数据库和数据字典的存取感觉上就如同对它们在存储设备上进行的存取。\x0d\x0a②哗李数据定义子系统\x0d\x0a帮助人们在数据库中建立并维护数据字典,以及定义数据库的文件结构。\x0d\x0a③数据操作子系统\x0d\x0a帮助用户增加、修改及删除数据库中的信息,并帮助用户在乱激迟数据库中挖掘有价值的信息。\x0d\x0a④应用程序生成子系统\x0d\x0a包含了用以帮助用户建立面向事务处理的应用程序 。工具包括:建立数据输入屏幕功能,为特定的DBMS选定程序设计语言,并利用程序设计语言为每个独立的DBMS建立一个公共的操作交互界面。\x0d\x0a⑤数据管理子系统\x0d\x0a通过自身提供的备份与恢复工具、安全管理工具、更优化查询工具、并发控制和更新管理工具,帮助人们管理整个数据库环境。

sql运行问题?

sql运行问题?

数据库运行过程中常见的故障有3类:事物故障、系统故障、介质故障。

恢复策略:

1、事物故障:

发生事务故障时,被迫中断的事务可能已对数据库进行丁修改,为了消除该事务对数据库的影响,要利用日志文件中所记载的信息,强行回滚该事务,将数据库恢复到修改前的初始状态。

为此,要检查日志文件中由这些事务所升谨引起的发生变化的记录,取消这些没有完成的事务所做的一切改变,这吵春基类恢复操作称为事务撤销。

2、系统故障:

系统故障的恢复要完成两方面的工作,既要撤销所有末完成的事务,还要重做所有已提交的事务,这样才能将数据库真正恢复到一致的状态。

3、介质故障:

介质故障比事务故障和系统故障发生的可能性要小,但这是最严重的一种故障,破坏性很大,磁盘上的物理数据和日志文件可能被破坏,这需要装入发生介质故障前最新的后备数据库副本,然后利用日志文件重做该副本后所运行的所有事务。

“数据故障恢复”和“完整森肢性约束”、“并e799bee5baa6e4b893e5b19e发控制”一样,都是数据库数据保护机制中的一种完整性控制。所有的系统都免不了会发生故障,有可能是硬件失灵,有可能是软件系统崩溃,也有可能是其他外界的原因,比如断电等等。

数据库运行的突然中断会使数据库处在一个错误的状态,而且故障排除后没有办法让系统精确地从断点继续执行下去。这就要求DBMS要有一套故障后的数据恢复机构,保证数据库能够回复到一致的、正确地状态去。

1、用户进程提交一条sql语句给服务器进程

SELECT * FROM EMP WHERE EMPNO = 7999

2、服务器进程从用户进程把信息接收到后,在PGA中就要此进程分配所需内存,存储相关的信息,如在会话内存存储相关的登录信息等。

3、服务器进程把这个sql语句的字符转化为ASCII等效数字码,接着这个ASCII码被传递给一个HASH 函数,并返回一个hash 值,然后服务器进程将到shared pool中的library cache中去查找是否存在相同的hash值,如果存在,服务器进程将使用这条语句已在高速缓存在 SHARED POOL 的library cache 中的已分析过的执行计划版本来执行。

4、如果不存在,服务器进程将在CGA中,配合UGA内容对sql,进行语法分析,首先检查语法的正确性,接着对语句中涉及的表,索引,视图等对象进行解析,并对照数据字典检查这些对象的名称以及相关结构,并根据ORACLE 选用的优化模式以及数据字典中是否存在相应对象的统计数据和是否使用了存储大纲来生成一个执行计划或从存储大纲中选用一个执行计划,然后再用数据字典核对此用户对相应对象的执行权限,最后生成一个编译代码。

5、ORACLE 将这条 sql 语句的本身实际文本、HASH 值、编译代码、与此语名相关联的任何统计数据和该语句的执行计划缓存在 SHARED POOL 的 library cache中。服务器进程通过 SHARED POOL 锁存器(shared pool latch)来申请可以向哪些共享 PL/SQL 区中缓存这此内容,也就是说被SHARED POOL 锁存器锁定的 PL/SQL 区中的块不可被覆盖,因为这些块可能被其它进程所使用。

6、在 SQL 分析阶段将用到 LIBRARY CACHE,从数据字典中核对表、视图等结构的时候,需要将数据字典从磁盘读入 LIBRARY CACHE,因此,在读入之前也要使用LIBRARY CACHE 锁存器(library cache pin,library cache lock)来申请用于缓存数据字典。到现在为止,这盯慧腊个 sql 语句已经被编译成可执碧桐行的代码了,但还不知道要操作哪些数据,所以服务器进程还要为这个 sql 准备预处理数据。

7、首先服务器进程要判断所需数据是否在 db buffer 存在,如果存在且可用,则直接获取该数据,同时根据LRU 算法增加其访问计数;如果 buffer不存在所需数据,则要从数据文件上读取首先服务器进程将在表头部请求 TM 锁(保证此事务执行过程其他用户不能修改表的结构),如果成功加 TM 锁,再请求一些行级锁(TX 锁),如果 TM、TX 锁都成功加锁,那么才开始从数据文件读数据,在读数据之前,要先为读取的文件准备好buffer 空间。服务器进程需要扫面 LRU list 寻找 free db buffer,扫描的过程中,服务器进程会把发现的所有已经被凯滑修改过的 db buffer 注册到 dirty list 中, 这些 dirty buffer 会通过 dbwr 的触发条件,随后会被写出到数据文件,找到了足够的空闲 buffer,就可以把请求的数据行所在的数据块放入到 db buffer 的空闲区域或者覆盖已经被挤出 LRU list 的非脏数据块缓冲区,并排列在 LRU list 的头部,也就是在数据块放入 DB BUFFER 之前也是要先申请 db buffer 中的锁存器,成功加锁后,才能读数据到 db buffer。

8、记日志现在数据已经被读入到db buffer了,现在服务器进程将该语句所影响的并被读入db buffer 中的这些行数据的 rowid 及要更新的原值和新值及 scn 等信息从 PGA 逐条的写入 redo log buffer 中。在写入 redo log buffer 之前也要事先请求 redo log buffer 的锁存器,成功加锁后才开始写入,当写入达到 redo log buffer 大小的三分之一或写入量达到 1M 或超过三秒后或发生检查点时或者 dbwr 之前发生,都会触发lgwr进程把redo log buffer 的数据写入磁盘上的 redo file 文件中(这个时候会产生log file sync 等待事件)已经被写入 redofile 的 redo log buffer 所持有的锁存器会被释放,并可被后来的写入信息覆盖,redo log buffer是循环使用的。Redo file 也是循环使用的,当一个 redo file 写满后,lgwr 进程会自动切换到下一 redo file(这个时候可能出现 log fileswitch(checkpoint complete)等待事件)。如果是归档模式,归档进程还要将前一个写满的 redo file 文件的内容写到归档日志文件中(这个时候可能出现 log file switch(archiving needed)。

9、为事务建立回滚段在完成本事务所有相关的 redo log buffer 之后,服务器进程开始改写这个 db buffer的块头部事务列表并写入 scn,然后 copy 包含这个块的头部事务列表及 scn 信息的数据副本放入回滚段中,将这时回滚段中的信息称为数据块的 前映 像 , 这个前映像用于以后的回滚、恢复和一致性读。(回滚段可以存储在专门的回滚表空间中,这个表空间由一个或多个物理文件组成,并专用于回滚表空间,回滚段也可在其它 表空间中的数据文件中开辟。

10、本事务修改数据块 准备工作都已经做好了,现在可以改写 db buffer 块的数据内容了,并在块的头部写 入回滚段的地址。

11、放入 dirty list 如果一个行数据多次 update 而未 commit,则在回滚段中将会有多个 前映像除了之一个前映像含有scn信息外,其他每个前映像的头部都有scn信息和前前映像回滚段地址。一个update 只对应一个scn,然后服务器进程将在 dirty list中建立一 条指向此db buffer 块的指针(方便 dbwr 进程可以找到 dirty list 的 db buffer 数据块并写入数据文件中)。 接着服务器进程会从数据文件中继续读入第二个数据块,重复前一数据块的动作,数据块的读入、记日志、建 立回滚段、修改数据块、放入 dirty list。当 dirty queue 的长度达到阀值(一般是 25%),服务器进程将通知dbwr 把脏数据写出,就是释放 db buffer 上的锁存器,腾出更多的 free db buffer。前面一直都是在说明oracle 一次读一个数据块,其实 oracle 可以一次读入多个数据块(db_file_multiblock_read_count 来设置一 次读入块的个数)

12、在预处理的数据已经缓存在 db buffer 或刚刚被从数据文件读入到 db buffer 中,就要根据 sql 语句的类型来决定接下来如何操作。

(1)如果是 select 语句,则要查看 db buffer 块的头部是否有事务,如果有事务,则从回滚段中读取数据;如果没有事务,则比较 select 的 scn 和 db buffer 块头部的 scn,如果前者小于后者,仍然要从回滚段中读取数据;如果前者大于后者,说明这是一非脏缓存,可以直接读取这个 db buffer 块的中内容。

(2)如果是 DML 操作,则即使在 db buffer 中找到一个没有事务,而且 SCN 比自己小的非脏缓存数据块,服务器进程仍然要到表的头部对这条记录申请加锁,加锁成功才能进行后续动作,如果不成功,则要等待前面的进程解锁后才能进行动作(这个时候阻塞是 tx 锁阻塞)。用户 commit 或 rollback 到现在为止,数据已经在 db buffer 或数据文件中修改完成,但是否要永久写到数文件中,要由用户来决定 commit(保存更改到数据文件) rollback 撤销数据的更改)。

如果用户执行 commit 命令:

只有当 sql 语句所影响的所有行所在的最后一个块被读入 db buffer 并且重做信息被写入 redo log buffer(仅指日志缓冲区,而不包括日志文件)之后,用户才可以发去 commit 命令,commit 触发 lgwr 进程,但不强制立即 dbwr来释放所有相应 db buffer 块的锁(也就是no-force-at-commit,即提交不强制写),也就是说有可能虽然已经 commit 了,但在随后的一段时间内 dbwr 还在写这条 sql 语句所涉及的数据块。表头部的行锁并不在 commit 之后立即释放,而是要等 dbwr 进程完成之后才释放,这就可能会出现一个用户请求另一用户已经 commit 的资源不成功的现象。

A、从 Commit 和 dbwr 进程结束之间的时间很短,如果恰巧在 commit 之后,dbwr 未结束之前断电,因为commit 之后的数据已经属于数据文件的内容,但这部分文件没有完全写入到数据文件中。所以需要前滚。由于 commit 已经触发 lgwr,这些所有未来得及写入数据文件的更改会在实例重启后,由 on 进程根据重做日 志文件来前滚,完成之前 commit 未完成的工作(即把更改写入数据文件)。

B、如果未 commit 就断电了,因为数据已经在 db buffer 更改了,没有 commit,说明这部分数据不属于数据文件,由于 dbwr 之前触发 lgwr 也就是只要数据更改,(肯定要先有 log) 所有 DBWR,在数据文件上的修改都会被先一步记入重做日志文件,实例重启后,ON 进程再根据重做日志文件来回滚。其实 on 的前滚回滚是根据检查点来完成的,当一个全部检查点发生的时候,首先让 LGWR 进程将redo log buffer 中的所有缓冲(包含未提交的重做信息)写入重做日志文件,然后让 dbwr 进程将 db buffer 已 提交的缓冲写入数据文件(不强制写未提交的)。然后更新控制文件和数据文件头部的 SCN,表明当前数据库是一致的,在相邻的两个检查点之间有很多事务,有提交和未提交的。像前面的前滚回滚比较完整的说法是如下的说明:

AA、发生检查点之前断电,并且当时有一个未提交的改变正在进行,实例重启之后,ON 进程将从上一个检查点开始核对这个检查点之后记录在重做日志文件中已提交的和未提交改变,因为dbwr 之前会触发 lgwr,所以 dbwr 对数据文件的修改一定会被先记录在重做日志文件中。因此,断电前被DBWN 写进数据文件的改变将通过重做日志文件中的记录进行还原,叫做回滚。

BB. 如果断电时有一个已提交,但 dbwr 动作还没有完全完成的改变存在,因为已经提交,提交会触发 lgwr进程,所以不管 dbwr 动作是否已完成,该语句将要影响的行及其产生的结果一定已经记录在重做日志文件中了,则实例重启后,ON 进程根据重做日志文件进行前滚.实例失败后用于恢复的时间由两个检查点之间的间隔大小来决定,可以通个四个参数设置检查点执行的频率:

Log_checkpoint_interval:

决定两个检查点之间写入重做日志文件的系统物理块(redo blocks)的大小,默认值是 0,无限制。

log_checkpoint_timeout:

两 个 检 查 点 之 间 的 时 间 长 度(秒)默 认 值 1800s。

fast_start_io_target:

决定了用于恢复时需要处理的块的多少,默认值是 0,无限制。

fast_start_mttr_target:

直接决定了用于恢复的时间的长短,默认值是 0,无限制(ON 进程执行的前滚 和回滚与用户的回滚是不同的,ON 是根据重做日志文件进行前滚或回滚,而用户的回滚一定是根据回滚段的内容进行回滚的。在这里要说一下回滚段存储的数据,假如是 delete 操作,则回滚段将会记录整个行的数据,假如是 update,则回滚段只记录被修改了的字段的变化前的数据(前映像),也就是没有被修改的字段是不会被记录的,假如是insert,则回滚段只记录插入记录的 rowid。 这样假如事务提交,那回滚段中简单标记该事务已经提交;假如是 回退,则如果操作是 delete,回退的时候把回滚段中数据重新写回数据块,操作如果是 update,则把变化前数据修改回去,操作如果是 insert,则根据记录的 rowid 把该记录删除。

如果用户 rollback:

则服务器进程会根据数据文件块和 DB BUFFER 中块的头部的事务列表和 SCN 以及回滚段地址找到回滚段中相应的修改前的副本,并且用这些原值来还原当前数据文件中已修改但未提交的改变。如果有多个前映像 服务器进程会在一个前映像的头部找到 前前映像 的回滚段地址,一直找到同一事务下的最早的一个前映像 为止。一旦发出了COMMIT,用户就不能rollback,这使得 COMMIT 后 DBWR 进程还没有全部完成的后续动作得到了保障。到现在为例一个事务已经结束了。

本篇文章会分析下一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的。

在分析之前我会先带着你看看 MySQL 的基础架构,知道了 MySQL 由那些组件组成以及这些组件的作用是什么,可以帮助我们理解和解决这些问题。

一 MySQL 基础架构分析

MySQL 基本架构毕租概览

下图是 MySQL 的一个简手竖兆要架构图,从下图你可以很清晰的看到用户的 SQL 语句在 MySQL 内部是如何执行的。

先简单介绍一下下图涉及的一些组件的基本作用帮助大家理解这幅图,在 1.2 节中会详细介绍到这些组件的作用。

连接器: 身份认证和权限相关(登录 MySQL 的时候)。

查询缓存: 执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除,因为这个功能不太实用)。

分析器: 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。

优化器: 按照 MySQL 认为更优的纤竖方案去执行。

1、用户进程提交一条sql语句给服务器进程

SELECT * FROM EMP WHERE EMPNO = 7999

2、服务器进程从用户进程把信息接收到后,在PGA中就要此进程分配所需内存,存储相关的信息,如在会话内存存储相关的登录信息等。

3、服务器进程把这个sql语句的字符转化为ASCII等效数字码,接着这个ASCII码被传递给一个HASH 函数,并返回一个hash 值,然后服务器进程将到shared pool中的library cache中去查找是否存在相同的hash值,如果存在,服务器进程将使用这条语句已在高速缓存在 SHARED POOL 的library cache 中的已分析过的执行计划版本来执行。

4、如果不存在,服务器进程将在CGA中,配合UGA内容对sql,进行语法分析,首先检查语法的正确性,接着对语句中涉及的表,索引,视图等对象进行解析,并对照数据字典检查这些对象的名称以及相关结构,并根据ORACLE 选用的优化模式以及数据字典中是否存在相应对象的统计数据和是否使用了存储大纲来生成一个执行计划或从存储大纲中选用一个执行计划,然后再用数据字典核对此用户对相应对象的执行权限,最后生成一个编译代码。

5、ORACLE 将这条 sql 语句的本身实际文本、HASH 值、编译代码、与此语名相关联的任何统计数据和该语句的执行计划缓存在 SHARED POOL 的 library cache中。服务器进程通过 SHARED POOL 锁存器(shared pool latch)来申请可以向哪些共享 PL/SQL 区中缓存这此内容,也就是说被SHARED POOL 锁存器锁定的 PL/SQL 区中的块不可被覆盖,因为这些块可能被其它进程所使用。

6、在 SQL 分析阶段将用到 LIBRARY CACHE,从数据字典中核对表、视图等结构的时候,需要将数据字典从磁盘读入 LIBRARY CACHE,因此,在读入之前也要使用LIBRARY CACHE 锁存器(library cache pin,library cache lock)来申请用于缓存数据字典。到现在为止,这盯慧腊个 sql 语句已经被编译成可执碧桐行的代码了,但还不知道要操作哪些数据,所以服务器进程还要为这个 sql 准备预处理数据。

7、首先服务器进程要判断所需数据是否在 db buffer 存在,如果存在且可用,则直接获取该数据,同时根据LRU 算法增加其访问计数;如果 buffer不存在所需数据,则要从数据文件上读取首先服务器进程将在表头部请求 TM 锁(保证此事务执行过程其他用户不能修改表的结构),如果成功加 TM 锁,再请求一些行级锁(TX 锁),如果 TM、TX 锁都成功加锁,那么才开始从数据文件读数据,在读数据之前,要先为读取的文件准备好buffer 空间。服务器进程需要扫面 LRU list 寻找 free db buffer,扫描的过程中,服务器进程会把发现的所有已经被凯滑修改过的 db buffer 注册到 dirty list 中, 这些 dirty buffer 会通过 dbwr 的触发条件,随后会被写出到数据文件,找到了足够的空闲 buffer,就可以把请求的数据行所在的数据块放入到 db buffer 的空闲区域或者覆盖已经被挤出 LRU list 的非脏数据块缓冲区,并排列在 LRU list 的头部,也就是在数据块放入 DB BUFFER 之前也是要先申请 db buffer 中的锁存器,成功加锁后,才能读数据到 db buffer。

8、记日志现在数据已经被读入到db buffer了,现在服务器进程将该语句所影响的并被读入db buffer 中的这些行数据的 rowid 及要更新的原值和新值及 scn 等信息从 PGA 逐条的写入 redo log buffer 中。在写入 redo log buffer 之前也要事先请求 redo log buffer 的锁存器,成功加锁后才开始写入,当写入达到 redo log buffer 大小的三分之一或写入量达到 1M 或超过三秒后或发生检查点时或者 dbwr 之前发生,都会触发lgwr进程把redo log buffer 的数据写入磁盘上的 redo file 文件中(这个时候会产生log file sync 等待事件)已经被写入 redofile 的 redo log buffer 所持有的锁存器会被释放,并可被后来的写入信息覆盖,redo log buffer是循环使用的。Redo file 也是循环使用的,当一个 redo file 写满后,lgwr 进程会自动切换到下一 redo file(这个时候可能出现 log fileswitch(checkpoint complete)等待事件)。如果是归档模式,归档进程还要将前一个写满的 redo file 文件的内容写到归档日志文件中(这个时候可能出现 log file switch(archiving needed)。

9、为事务建立回滚段在完成本事务所有相关的 redo log buffer 之后,服务器进程开始改写这个 db buffer的块头部事务列表并写入 scn,然后 copy 包含这个块的头部事务列表及 scn 信息的数据副本放入回滚段中,将这时回滚段中的信息称为数据块的 前映 像 , 这个前映像用于以后的回滚、恢复和一致性读。(回滚段可以存储在专门的回滚表空间中,这个表空间由一个或多个物理文件组成,并专用于回滚表空间,回滚段也可在其它 表空间中的数据文件中开辟。

10、本事务修改数据块 准备工作都已经做好了,现在可以改写 db buffer 块的数据内容了,并在块的头部写 入回滚段的地址。

11、放入 dirty list 如果一个行数据多次 update 而未 commit,则在回滚段中将会有多个 前映像除了之一个前映像含有scn信息外,其他每个前映像的头部都有scn信息和前前映像回滚段地址。一个update 只对应一个scn,然后服务器进程将在 dirty list中建立一 条指向此db buffer 块的指针(方便 dbwr 进程可以找到 dirty list 的 db buffer 数据块并写入数据文件中)。 接着服务器进程会从数据文件中继续读入第二个数据块,重复前一数据块的动作,数据块的读入、记日志、建 立回滚段、修改数据块、放入 dirty list。当 dirty queue 的长度达到阀值(一般是 25%),服务器进程将通知dbwr 把脏数据写出,就是释放 db buffer 上的锁存器,腾出更多的 free db buffer。前面一直都是在说明oracle 一次读一个数据块,其实 oracle 可以一次读入多个数据块(db_file_multiblock_read_count 来设置一 次读入块的个数)

12、在预处理的数据已经缓存在 db buffer 或刚刚被从数据文件读入到 db buffer 中,就要根据 sql 语句的类型来决定接下来如何操作。

(1)如果是 select 语句,则要查看 db buffer 块的头部是否有事务,如果有事务,则从回滚段中读取数据;如果没有事务,则比较 select 的 scn 和 db buffer 块头部的 scn,如果前者小于后者,仍然要从回滚段中读取数据;如果前者大于后者,说明这是一非脏缓存,可以直接读取这个 db buffer 块的中内容。

(2)如果是 DML 操作,则即使在 db buffer 中找到一个没有事务,而且 SCN 比自己小的非脏缓存数据块,服务器进程仍然要到表的头部对这条记录申请加锁,加锁成功才能进行后续动作,如果不成功,则要等待前面的进程解锁后才能进行动作(这个时候阻塞是 tx 锁阻塞)。用户 commit 或 rollback 到现在为止,数据已经在 db buffer 或数据文件中修改完成,但是否要永久写到数文件中,要由用户来决定 commit(保存更改到数据文件) rollback 撤销数据的更改)。

如果用户执行 commit 命令:

只有当 sql 语句所影响的所有行所在的最后一个块被读入 db buffer 并且重做信息被写入 redo log buffer(仅指日志缓冲区,而不包括日志文件)之后,用户才可以发去 commit 命令,commit 触发 lgwr 进程,但不强制立即 dbwr来释放所有相应 db buffer 块的锁(也就是no-force-at-commit,即提交不强制写),也就是说有可能虽然已经 commit 了,但在随后的一段时间内 dbwr 还在写这条 sql 语句所涉及的数据块。表头部的行锁并不在 commit 之后立即释放,而是要等 dbwr 进程完成之后才释放,这就可能会出现一个用户请求另一用户已经 commit 的资源不成功的现象。

A、从 Commit 和 dbwr 进程结束之间的时间很短,如果恰巧在 commit 之后,dbwr 未结束之前断电,因为commit 之后的数据已经属于数据文件的内容,但这部分文件没有完全写入到数据文件中。所以需要前滚。由于 commit 已经触发 lgwr,这些所有未来得及写入数据文件的更改会在实例重启后,由 on 进程根据重做日 志文件来前滚,完成之前 commit 未完成的工作(即把更改写入数据文件)。

B、如果未 commit 就断电了,因为数据已经在 db buffer 更改了,没有 commit,说明这部分数据不属于数据文件,由于 dbwr 之前触发 lgwr 也就是只要数据更改,(肯定要先有 log) 所有 DBWR,在数据文件上的修改都会被先一步记入重做日志文件,实例重启后,ON 进程再根据重做日志文件来回滚。其实 on 的前滚回滚是根据检查点来完成的,当一个全部检查点发生的时候,首先让 LGWR 进程将redo log buffer 中的所有缓冲(包含未提交的重做信息)写入重做日志文件,然后让 dbwr 进程将 db buffer 已 提交的缓冲写入数据文件(不强制写未提交的)。然后更新控制文件和数据文件头部的 SCN,表明当前数据库是一致的,在相邻的两个检查点之间有很多事务,有提交和未提交的。像前面的前滚回滚比较完整的说法是如下的说明:

AA、发生检查点之前断电,并且当时有一个未提交的改变正在进行,实例重启之后,ON 进程将从上一个检查点开始核对这个检查点之后记录在重做日志文件中已提交的和未提交改变,因为dbwr 之前会触发 lgwr,所以 dbwr 对数据文件的修改一定会被先记录在重做日志文件中。因此,断电前被DBWN 写进数据文件的改变将通过重做日志文件中的记录进行还原,叫做回滚。

BB. 如果断电时有一个已提交,但 dbwr 动作还没有完全完成的改变存在,因为已经提交,提交会触发 lgwr进程,所以不管 dbwr 动作是否已完成,该语句将要影响的行及其产生的结果一定已经记录在重做日志文件中了,则实例重启后,ON 进程根据重做日志文件进行前滚.实例失败后用于恢复的时间由两个检查点之间的间隔大小来决定,可以通个四个参数设置检查点执行的频率:

Log_checkpoint_interval:

决定两个检查点之间写入重做日志文件的系统物理块(redo blocks)的大小,默认值是 0,无限制。

log_checkpoint_timeout:

两 个 检 查 点 之 间 的 时 间 长 度(秒)默 认 值 1800s。

fast_start_io_target:

决定了用于恢复时需要处理的块的多少,默认值是 0,无限制。

fast_start_mttr_target:

直接决定了用于恢复的时间的长短,默认值是 0,无限制(ON 进程执行的前滚 和回滚与用户的回滚是不同的,ON 是根据重做日志文件进行前滚或回滚,而用户的回滚一定是根据回滚段的内容进行回滚的。在这里要说一下回滚段存储的数据,假如是 delete 操作,则回滚段将会记录整个行的数据,假如是 update,则回滚段只记录被修改了的字段的变化前的数据(前映像),也就是没有被修改的字段是不会被记录的,假如是insert,则回滚段只记录插入记录的 rowid。 这样假如事务提交,那回滚段中简单标记该事务已经提交;假如是 回退,则如果操作是 delete,回退的时候把回滚段中数据重新写回数据块,操作如果是 update,则把变化前数据修改回去,操作如果是 insert,则根据记录的 rowid 把该记录删除。

如果用户 rollback:

则服务器进程会根据数据文件块和 DB BUFFER 中块的头部的事务列表和 SCN 以及回滚段地址找到回滚段中相应的修改前的副本,并且用这些原值来还原当前数据文件中已修改但未提交的改变。如果有多个前映像 服务器进程会在一个前映像的头部找到 前前映像 的回滚段地址,一直找到同一事务下的最早的一个前映像 为止。一旦发出了COMMIT,用户就不能rollback,这使得 COMMIT 后 DBWR 进程还没有全部完成的后续动作得到了保障。到现在为例一个事务已经结束了。

一个数据库有5个事务的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于一个数据库有5个事务,5个事务的数据库:如何确保数据一致性?,数据库事务正确执行的四个基本要素包括,数据库管理系统 (DBMS) 的五个重要软件组成部分是什么?,sql运行问题?的信息别忘了在本站进行查找喔。


数据运维技术 » 5个事务的数据库:如何确保数据一致性? (一个数据库有5个事务)