第一部分 什么是EBR(基于版本的重定义)
一个经常被忽视的高可用性挑战是应用程序升级。应用程序升级涉及对应用程序数据库对象的逻辑方面所做的任何变更。大型任务关键型应用程序无法承受任何停机时间,即使对于计划内维护(如应用程序升级)也是如此。因此,迫切需要零停机时间的应用程序升级。
EBR (基于版本的重定义)* 功能到来前对象只有一个版本,通过*OWNER.OBJECT_NAME 引用,在有EBR 之后,一个对象有多个版本,可以通过切换会话来使用不同版本。在升级过程中,可以在新版本中修改代码,赋权,测试,累积这些变化,最后再数据库层面修改默认版本为新的版本即可。
为什么使用基于版本的重新定义 (EBR)?
很多重要应用程序不允许停机。此要求从硬件一直延伸到应用程序。因此,每次维护都必须在线,即零停机时间。EBR 具有以下主要优点,可最大限度地减少应用程序升级的停机时间:
• 允许 对数据库中应用程序记录一组构件进行任意更改
• 同时利用 升级前和升级后的应用程序 (热切换)
• 跨版本保持 应用程序的不间断可用性 (实时操作)
• 确保 不会对性能产生明显的负面影响
第二部分 EBR基本概念
基于版本的重新定义 (EBR) 的组件
使用 EBR 需要了解各种组件及其功能。
1. 版本
版本是没有任何所有者的非架构对象类型,是 Oracle 数据库的内置功能。任何数据库都必须至少有一个称为基本版本(即 ora$base )的版本。创建的任何新版本都是现有版本的子版本,每个版本只能有一个子版本。每个数据库会话都使用基本版本。更改会话命令允许会话更改其版本。
管理员通常使用版本作为 EBR 的一部分来执行:
• 在新版本的环境中安装代码变更
• 通过写入新列 / 表安全地更改数据
• 屏蔽旧版本或其他版本使其看不到更改
2. 版本视图
在定义版本视图之前,了解版本化对象类型与非版本化对象类型的概念显得非常重要。 Oracle 数据库中支持的各种对象大致可分为两类:
1. 版本化对象 – 既绑定到 schema,也绑定到版本。视图、同义词和所有类型的 PL/SQL 对象(如触发器、函数、库、包和包体、过程、类型和类型主体)都是版本化对象类型。所有其他对象类型都是非版本化的。
2. 非版本对象 – 对所有版本可见,并且不绑定到特定版本。表和索引是非版本化对象类型的示例。
版本视图是一种特殊类型的视图,是一种版本化对象。版本控制视图从单个基表中选择列的子集,并选择性地为它们提供别名。在提供别名时,版本视图将物理列名(由基表使用)映射到逻辑列名(由应用程序使用)。将版本视图视为表的 API 。由于可以对版本视图进行版本化,因此它们允许您将其基表视为子表本身已进行版本化,并允许在不同版本中呈现其逻辑投影的不同实例。
管理员通常使用版本视图作为 EBR 的一部分来:
• 在每个版本中公开表的不同投影
• 允许每个用户只查看自己的列
3. 跨版本触发器
与受 DML 操作限制的普通视图(数据库对象类型)不同,版本视图既可以是只读的,也可以是读写的。如果在基表上定义了跨版本触发器,则使版本控制视图为可读写而不是只读。跨版本触发器是一种特殊类型的触发器,它是一种版本化对象类型。跨版本触发器必须始终由启用版本的用户拥有,因此始终是版本触发器。跨版本触发器直接在基表上创建,而不是在版本视图上创建。有两种类型的跨版本触发器,如下所示。
1. 转发跨版本触发器 – 通过将任何旧版本中所做的数据更改复制到新版本来确保数据一致性。当连接到应用程序的升级前版本的会话发出 DML 语句时,通常会触发此语句。
2. 反向跨版本触发器 – 通过将任何新子版本的数据更改复制到旧版本中来确保数据一致性。当连接到应用程序的升级后版本的会话发出 DML 语句时,通常会触发它。
通常使用跨版本触发器作为 EBR 的一部分,以便:
• 通过使用转发跨版本触发器,将任何旧版本所做的数据更改传播到新版本的列中。这是最常见的用例。
• 通过使用反向跨版本触发器,将任何新子版本所做的数据更改传播到旧版本的列中。
基于版本的重新定义 (EBR) 用例
以下是 EBR 可用于在应用程序升级期间维护应用程序可用性的典型用例。需要一次性配置步骤才能为数据库启用 EBR 。
第三部分 简单用例演示
-- 创建用户DEMO SQL> create user demo identified by demo; User created -- 赋予连接和创建过程权限 SQL> grant create session, create procedure to demo; Grant succeeded. -- 创建新版本version2,作为ORA$BASE的子版本。ORA$BASE是系统默认的版本 SQL> create edition version2 as child of ora$base; Edition created. -- 切换到DEMO用户, 此时使用的是默认版本 SQL> connect demo/demo
Connected. -- 创建过程my_procedure和my_procedure2 DEMO> create or replace procedure my_procedure as begin dbms_output.put_line ( 'I am version 1.0' ); end; / Procedure created. DEMO> create or replace procedure my_procedure2 as begin my_procedure; end; / Procedure created. -- 默认版本中执行,输出1.0 DEMO> exec my_procedure2 I am version 1.0 PL/SQL procedure successfully completed. -- 切换到DBA用户 DEMO> connect / as sysdba Connected. -- 创建新用户SCOTT SQL> create user scott identified by tiger; User created. SQL> grant create session to scott; Grant succeeded. -- 为用户DEMO启用版本 SQL> alter user demo enable editions; User altered. -- 允许用户DEMO使用版本version2 SQL> grant use on edition version2 to demo; Grant succeeded. -- 允许用户SCOTT使用版本version2 SQL> grant use on edition version2 to scott; Grant succeeded. -- 切换到DEMO用户 SQL> connect demo/demo Connected. -- 切换到版本version2 DEMO> alter session set edition = version2; Session altered. -- 此时可看到2个过程,集成至默认版本ORA$BASE DEMO> select object_name, object_type, status, edition_name from user_objects; OBJECT_NAME OBJECT_TYPE STATUS EDITION_NAME ---------------- ----------------------- ------- ---------------- MY_PROCEDURE PROCEDURE VALID ORA$BASE MY_PROCEDURE2 PROCEDURE VALID ORA$BASE -- 修改过程my_procedure,版本为2.0 DEMO> create or replace procedure my_procedure as begin dbms_output.put_line ( 'I am version 2.0' ); end; / -- 过程my_procedure已变为VERSION2 DEMO> select object_name, object_type, status, edition_name from user_objects; OBJECT_NAME OBJECT_TYPE STATUS EDITION_NAME ---------------- ----------------------- ------- ---------------- MY_PROCEDURE2 PROCEDURE VALID ORA$BASE MY_PROCEDURE PROCEDURE VALID VERSION2 -- my_procedure在版本version2中已经实体化了,不再是指向父版本的链接 DEMO> select object_name, edition_name from user_objects_AE; OBJECT_NAME EDITION_NAME ---------------- ---------------- MY_PROCEDURE ORA$BASE MY_PROCEDURE2 ORA$BASE MY_PROCEDURE VERSION2 -- 赋予SCOTT用户my_procedure2执行权限 DEMO> grant execute on my_procedure2 to scott; Grant succeeded. -- 由于权限变化,这会引发my_procedure2在版本version2中实体化 DEMO> select object_name, object_type, status, edition_name from user_objects; OBJECT_NAME OBJECT_TYPE STATUS EDITION_NAME ---------------- ----------------------- ------- ---------------- MY_PROCEDURE PROCEDURE VALID VERSION2 MY_PROCEDURE2 PROCEDURE VALID VERSION2 DEMO> select object_name, edition_name from user_objects_AE; OBJECT_NAME EDITION_NAME ---------------- ---------------- MY_PROCEDURE ORA$BASE MY_PROCEDURE2 ORA$BASE MY_PROCEDURE VERSION2 MY_PROCEDURE2 VERSION2 -- 确认当前版本为VERSION2 DEMO> SELECT SYS_CONTEXT ('userenv', 'current_edition_name') sc FROM DUAL; SC -------------------------------------------------------------------------------- VERSION2 -- 确认执行的是新版本的过程 DEMO> exec my_procedure2 I am version 2.0 PL/SQL procedure successfully completed. -- 切换用户到DEMO DEMO> connect demo/demo Connected. -- 确认是默认版本 DEMO> SELECT SYS_CONTEXT ('userenv', 'current_edition_name') sc 4 FROM DUAL; SC -------------------------------------------------------------------------------- ORA$BASE -- 确认执行的是老版本 DEMO> exec my_procedure2; I am version 1.0 PL/SQL procedure successfully completed. -- 切换到SCOTT用户 DEMO> connect scott/tiger -- 确认是默认版本 SCOTT> SELECT SYS_CONTEXT ('userenv', 'current_edition_name') sc FROM DUAL; SC -------------------------------------------------------------------------------- ORA$BASE -- 由于在默认版本里并没有赋权给SCOTT,因此它无法看到存储过程 SCOTT> exec demo.my_procedure2 BEGIN demo.my_procedure2; END; * ERROR at line 1: ORA-06550: line 1, column 7: PLS-00201: identifier 'DEMO.MY_PROCEDURE2' must be declared ORA-06550: line 1, column 7: PL/SQL: Statement ignored -- 切换到版本version2 SCOTT> alter session set edition = version2; Session altered. -- 由于之前赋过执行权限,因此可正常执行 SCOTT> exec demo.my_procedure2 I am version 2.0 PL/SQL procedure successfully completed. -- 切换到DBA用户 SCOTT> connect / as sysdba Connected. -- 在数据库层面将版本修改为VERSION2,需要重连才生效 SQL> alter database default edition = version2; Database altered. -- 切换到SCOTT用户 SCOTT> connect scott/tiger Connected. -- 确认执行的是新版本 SCOTT> exec demo.my_procedure2 I am version 2.0 PL/SQL procedure successfully completed. -- 此时系统默认的版本已变为VERSION2,不再是ORA$BASE SCOTT> SELECT SYS_CONTEXT ('userenv', 'current_edition_name') sc FROM DUAL; SC -------------------------------------------------------------------------------- VERSION2 -- 当然,也可以将版本改回去 SQL> alter database default edition = ORA$BASE;
第四部分 常见问题
怎么实现 ?
在创建新版本时,为了节省时间和空间,新版本中的所有已编辑对象都将被删除 ,继承自父版本。换句话说,新版本将使用对象的实际定义 。之前的版本,而不是创建一个新的副本。当对象的定义被更新时,对象被重新定义 ,在新版本中创建新副本,以使前版本中的副本不受干扰的过程 ,在新版本中创建对象的新副本称为 “ 实现 ” 。 实现最常用于在安装需要更新的补丁时更新对象的定义 ,更改对象。有时,用户可能希望切断旧副本的继承,并创建一个 最新版本中对象的独立副本,即使不更改定义。这创造了一个新的具有相同定义的复制也被称为 “ 实现 ” 。
当我删除旧版本时,继承的对象会发生什么?
继承对象是当前版本中没有实现的数据库对象。它只是一个存根。
• 如何判断一个对象是继承的 ? 基础对象的哪些变化在后来的版本中是可观察到的 ? 查看 ALL_OBJECTS 表,继承的对象将有一个与当前版本不同的 EDITION_NAME 值。不应该依赖对基对 象进行更改的传播,比如删除它。对任何对象的更改都应该在期望更改是可观察的所有版本中重复。
• 一个人能使一个对象变成现实吗 ? 如果可以,继承对象和实现对象之间是否存在可观察到的区别 ? DDL 语句 ( 例如 CREATE 、 ALTER 、 DROP) 将实现继承对象,即使该语句没有从功能上更改对象。 例如 : 一个被编辑的包可以用 REUSE SETTINGS 子句重新编译,这将在当前版本中实现包,而不改变它的父 版本。这个包上的依赖对象将在需要时被惰性实现。我们可以通过 dbms_utility.validate() 强制实现依赖包。 通过比较 ALL_OBJECTS 中的 EDITION_NAME 列的值,可以将实现的对象与继承的对象区分开来。如果 该值与当前版本的名称相匹配,则为实际。
• 继承是透光模式的核心。透光模型是什么 ? 透光模型指的是,对给定版本中对象的更改可能会反映在属于后代版本的依赖对象中。这种行为不应该被依 赖,任何预期会在后代版本中看到的更改都应该在每个版本的基础上重复。
• 什么是覆盖对象 ? 覆盖对象是一个被编辑的对象,它在任何可用的后代版本中都不再继承。任何不可用版本中的每个被 覆盖对象都将被自动计划的维护过程删除。
grant是否会导致无效?
从技术上讲, DDL 只能在实际对象上执行 ( 与继承对象相反 ) 。因此,任何时候在执行 DDL 的会话的当前版本中 继承的对象上执行一个 DDL ,在 DDL 可以继续之前,必须首先实现该对象。这一步确实适用于授予。实现对 象的一个直接影响是,它的所有依赖项都必须递归地失效,这样所有依赖项都可以重新编译,以引用对象的新 副本,而不是旧副本。如果这种行为不受欢迎,有两种方法可以解决它。 一种方法是在补丁安装期间提前计划拨款。这种方法有两部分。尽可能的, grant 应该在补丁安装过程中执 行,然后补丁才可以被用户会话执行。当无法准确预测哪些用户或角色将获得授予,但可以预测授予将在哪 些对象上执行时,这些对象可以被先发制人地实现,即使实际授予直到稍后才执行。这将确保所有的实现都 发生在用户开始使用新版本之前,这样用户就不会受到任何失效的干扰。 处理赠款的另一种方法是显式地寻找作为赠款目标的实际对象。 (User/All/DBA)_Objects 视图有一列,指定定义 每个已编辑对象的版本。使用 DBMS_SQL 或 alter session ,授权可以在定义了授权目标的版本中执行。这样,在 实际对象上而不是在继承的副本上执行授权将避免任何实现的需要,因此将避免失效。 在任何可能的情况下,应该优先使用先发制人实现对象的第一种方法,而不是在旧版本中执行授予。一般来说, 应该始终认为最好的实践是只在最新版本中执行 ddl ,而不是在任何有子版本的版本中执行。
12.2 中掉落版本级联做了什么?
根版本的掉版级联会判断版本本身是否为空,那么它就会标记该版本不可用,一旦版本为空,它就会在后 台清理该版本。 在 12.2 之前,如果版本中包含了继承自后版本的对象,则删除版本级联将会出错。为了更好地控制何时进行清 理,调用 DBMS_Editions_Utilities 。 Clean_Unusable_Editions 将立即清理当前在可用版本中没有继承的所有对象。
当删除的版本被清理的时间框架可以被控制以适应特定的时间表吗?
最好的做法是让定期安排的清理过程处理时间范围。
• 清理和删除版本之间的关系是什么 ? 删除一个本身是最新版本的版本将导致所有已编辑的对象和版本本身将在 drop edition 语句返回之前被删除。 如果是最老的版本,则该版本将被标记为不可用, drop edition 语句将立即返回。但是,清理过程会定期清理 所有覆盖的对象。当版本中的所有对象被清除时,版本本身将被删除。
• 到底什么东西会被清理 ?
定期计划的清理过程将清理不可用版本中的覆盖对象。一旦所有被覆盖的对象被清理,不可用的版本将被 自动删除。
• 如果清理和掉落版本实际上是耦合的,那么在合适的时间做掉落版本是否有意义 ? 目前还不清楚这种方法是否会产生任何好处,而 “drop edition” 语句的调度可能会给代码增加一个不必要的 层。最好让清理过程来处理删除的版本,除非它导致了具体的问题。
利用物化视图和虚拟列引用可编辑对象的应用程序应该如何利用 EBR?
使用 “evaluate Using current edition” 语法将允许物化视图或虚拟列引用创建物化视图或虚拟列的会话当前使用 的同一版本中的已编辑对象 ( 由 Sys_Context('USERENV' , 'CURRENT_EDITION_NAME') 返回的那个 ) 。物化视 图或虚拟列将继续使用这个特定的版本,并将始终使用这个特定的版本,即使从正在使用不同版本的会话查询 ( 或在物化视图的情况下刷新 ) 物化视图或虚拟列。 为了避免由于陈旧的求值关系而导致的意外行为,物化视图或虚拟列求值子句应该尽可能通过 ALTER materialized view 或 ALTER TABLE 命令前滚到最新版本。 通常情况下,如果正在安装某个特定的补丁或应用程序的升级,它将以某种方式改变定义表达式的结果,那么 将创建一个新的物化视图来存储不同的结果,而不会干扰当前正在运行的应用程序所使用的现有物化视图。考 虑到偶尔需要创建替换的物化视图,一般来说,物化视图应该被编辑视图、传统视图或同义词覆盖,以便任何 通过名称引用物化视图的应用程序代码都将引用一个编辑过的对象,该对象可以在创建替换时更新为指向一个 新的物化视图。 一个用户应该维护多少个版本 ? 用户可以使用删除版本功能删除不再使用的旧版本。通过这种方式,数据库将拥有当前正在运行的应用程序 使用的一个版本,如果正在推出一个补丁或更新,可能还会有一个额外的版本。数据库中的所有其他版本都 将被先前的 “ 删除版本 ” 命令标记为不可用。
用户应该删除 ORA$BASE 吗 ?
在其他旧的、退休的版本可以被删除之前,有必要先删除 ORA$Base 版本。 ORA$Base 版本在任何方面都没有 特别之处,除了它是数据库第一次创建时给数据库中一个版本的名称。我们所有多年来一直愉快地使用基于版 本的重定义的现有客户都在多年前放弃了 ORA$Base 版本,并没有因为它而出现任何问题。
放弃跨版本触发器的最佳实践是什么?
跨版本触发器是具有跨版本可见性的触发器。它的主要目的是保持数据在不同版本之间的同步。 当版本被删除时,跨版本触发器也应该被删除是有意义的。
•一个跨版本的触发器可以在两个以上的版本中显示吗 ?
不,跨版本触发器只能“链接”两个版本,并且只属于它实际所在的版本。
•如果一个跨版本触发器处理已经被删除的版本,那么该触发器是否也会自动删除 ? 反向交叉版本触发器呢 ? 它是否会随着旧版本的删除而删除 ?
转发触发器被自动丢弃。
在删除版本之前,应该首先禁用反向触发器,作为删除过程的一部分。
LINK是可编辑的吗?
目前 (21.3)no 。