Oracle Edition-Based Redefinition(EBR)理论与实践
Posted dingdingfish
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle Edition-Based Redefinition(EBR)理论与实践相关的知识,希望对你有一定的参考价值。
理论
Edition-Based Redefinition(EBR)是11gR2的新特性。
在Oracle MAA架构中,常提到在线操作,Edition-Based Redefinition(EBR)即其中一种。EBR可实现代码的升级,包括Synonym,View,Function,Procedure,Package (specification and body),Type (specification and body),Library和Trigger。其它的在线操作包括:
- 修改系统参数
- Reorganizing 对象(例如将非分区表转为分区表)
- 创建索引
- RAC滚动升级
- 数据库升级(major release to major release)
注意,表是没有版本的。因为他是有状态对象
在没有EBR之前,对象只有一个版本,通过OWNER.OBJECTNAME引用,在有EBR之后,一个对象有多个版本,可以通过切换会话来使用不同版本。在升级过程中,我们可以在新版本中修改代码,赋权,测试,累积这些变化,最后再数据库层面修改默认版本为新的版本即可。
实验1
实验依照的是Oracle博文:A Closer Look at the New Edition
操作的数据库为PDB:orclpdb1
connect sys/Welcome1@orclpdb1 as sysdba
-- 创建用户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@orclpdb1
Connected.
-- 创建过程my_procedure和my_procedure2
DEMO> create or replace
procedure my_procedure
as
begin
dbms_output.put_line
( 'I am version 1.0' );
end;
8 /
Procedure created.
DEMO> create or replace
procedure my_procedure2
as
begin
my_procedure;
end;
7 /
Procedure created.
-- 默认版本中执行,输出1.0
DEMO> exec my_procedure2
I am version 1.0
PL/SQL procedure successfully completed.
-- 切换到DBA用户
DEMO> connect sys/Welcome1@orclpdb1 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@orclpdb1
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;
8 /
-- 过程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@orclpdb1
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@orclpdb1
-- 确认是默认版本
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 sys/Welcome1@orclpdb1 as sysdba
Connected.
-- 在数据库层面将版本修改为VERSION2,需要重连才生效
SQL> alter database default edition = version2;
Database altered.
-- 切换到SCOTT用户
SCOTT> connect scott/tiger@orclpdb1
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;
实验2
实验依照的是Oracle博文:Edition-Based Redefinition, Part 2
此实验是在实验1的基础上进行。
SQL> connect sys/Welcome1@orclpdb1 as sysdba
-- 赋予用户DEMO新的权限,其余权限在实验1中已经赋予了
SQL> grant create table, create sequence to demo;
Grant succeeded.
SQL> grant select on hr.employees to demo;
Grant succeeded.
SQL> alter user demo quota unlimited on users;
User altered.
-- 当前的版本是version2
DEMO>
SELECT SYS_CONTEXT
('userenv',
'current_edition_name') sc
FROM DUAL;
SC
--------------------------------------------------------------------------------
VERSION2
-- 创建一个新的版本version3, 并赋权给用户demo
SQL> create edition version3 as child of version2;
Edition created.
SQL> grant use on edition version3 to demo;
Grant succeeded.
-- 创建editioning view 权限
SQL> grant create view to demo;
Grant succeeded.
-- 创建crossedition trigger权限
SQL> grant create trigger to demo;
Grant succeeded.
-- 执行Job权限
SQL> grant create job to demo;
Grant succeeded.
SQL> connect demo/demo@orclpdb1
Connected.
DEMO>
create table
employees
as
select * from hr.employees;
Table created.
DEMO> create sequence emp_seq start with 500;
Sequence created.
-- 创建package,其中过程show用于搜索,函数add用于新增雇员
-- 此package代表我们老版本的应用代码
-- 参数中没有雇员ID,是因为其会使用之前的序列自动生成
SQL>
create or replace package emp_pkg
as
procedure show
( last_name_like in employees.last_name%type );
function add
( FIRST_NAME in employees.FIRST_NAME%type := null,
LAST_NAME in employees.LAST_NAME%type,
EMAIL in employees.EMAIL%type,
PHONE_NUMBER in employees.PHONE_NUMBER%type := null,
HIRE_DATE in employees.HIRE_DATE%type,
JOB_ID in employees.JOB_ID%type,
SALARY in employees.SALARY%type := null,
COMMISSION_PCT in employees.COMMISSION_PCT%type := null,
MANAGER_ID in employees.MANAGER_ID%type := null,
DEPARTMENT_ID in employees.DEPARTMENT_ID%type := null )
return employees.employee_id%type;
end;
/
Package created.
-- 创建Package Body
DEMO>
create or replace package body emp_pkg
as
procedure show
( last_name_like in employees.last_name%type )
as
begin
for x in
( select first_name, last_name,
phone_number, email
from employees
where last_name like
show.last_name_like
order by last_name )
loop
dbms_output.put_line
( rpad( x.first_name || ' ' ||
x.last_name, 40 ) ||
rpad( nvl(x.phone_number, ' '), 20 ) ||
x.email );
end loop;
end show;
function add
( FIRST_NAME in employees.FIRST_NAME%type := null,
LAST_NAME in employees.LAST_NAME%type,
EMAIL in employees.EMAIL%type,
PHONE_NUMBER in employees.PHONE_NUMBER%type := null,
HIRE_DATE in employees.HIRE_DATE%type,
JOB_ID in employees.JOB_ID%type,
SALARY in employees.SALARY%type := null,
COMMISSION_PCT in employees.COMMISSION_PCT%type := null,
MANAGER_ID in employees.MANAGER_ID%type := null,
DEPARTMENT_ID in employees.DEPARTMENT_ID%type := null
)
return employees.employee_id%type
is
employee_id employees.employee_id%type;
begin
insert into employees
( EMPLOYEE_ID, FIRST_NAME, LAST_NAME,
EMAIL, PHONE_NUMBER, HIRE_DATE,
JOB_ID, SALARY, COMMISSION_PCT,
MANAGER_ID, DEPARTMENT_ID )
values
( emp_seq.nextval, add.FIRST_NAME, add.LAST_NAME,
add.EMAIL, add.PHONE_NUMBER, add.HIRE_DATE,
add.JOB_ID, add.SALARY, add.COMMISSION_PCT,
add.MANAGER_ID, add.DEPARTMENT_ID )
returning employee_id into add.employee_id;
return add.employee_id;
end add;
end;
/
Package body created.
-- 测试搜索函数
DEMO> set serveroutput on
DEMO> exec emp_pkg.show( '%K%' );
Payam Kaufling 650.123.3234 PKAUFLIN
Alexander Khoo 515.127.4562 AKHOO
Steven King 515.123.4567 SKING
Janette King 011.44.1345.429268 JKING
Neena Kochhar 515.123.4568 NKOCHHAR
Sundita Kumar 011.44.1343.329268 SKUMAR
PL/SQL procedure successfully completed.
-- 新增员工Tom Kyte,然后在搜索
SQL>
begin
dbms_output.put_line
( emp_pkg.add
( first_name => 'Tom',
last_name => 'Kyte',
email => 'TKYTE',
phone_number => '703.123.9999',
hire_date => sysdate,
job_id => 'IT_PROG' ) );
end;
11 /
500
PL/SQL procedure successfully completed.
DEMO> exec emp_pkg.show( '%K%' );
Payam Kaufling 650.123.3234 PKAUFLIN
Alexander Khoo 515.127.4562 AKHOO
Janette King 011.44.1345.429268 JKING
Steven King 515.123.4567 SKING
Neena Kochhar 515.123.4568 NKOCHHAR
Sundita Kumar 011.44.1343.329268 SKUMAR
Tom Kyte 703.123.9999 TKYTE
PL/SQL procedure successfully completed.
-- 建立Edition View,这是唯一引起中断的地方。rt表示Real Table.
DEMO> alter table employees rename to employees_rt;
Table altered.
create editioning view employees
as
select
EMPLOYEE_ID, FIRST_NAME,
LAST_NAME, EMAIL, PHONE_NUMBER,
HIRE_DATE, JOB_ID, SALARY,
COMMISSION_PCT, MANAGER_ID,
DEPARTMENT_ID
from employees_rt
/
-- 之后,所有的操作都基于Edition View,包括增删改查,例如:
DEMO> delete from employees where employee_id=500;
DEMO> rollback;
-- 好,现在可以修改schema了
-- 从11gR1起,增加列和索引都是在线操作
-- 增加两列,原始的phone_number列仍在,但edition view屏蔽了这两列
DEMO>
alter table employees_rt
add
( country_code varchar2(3),
phone# varchar2(20)
)
/
Table altered.
-- 添加索引,在线但不可见,因此不会被使用。
DEMO>
create index employees_phone#_idx
on employees_rt(phone#)
ONLINE INVISIBLE
/
Index created.
-- 切换到版本version3
DEMO> alter session set edition = version3;
Session altered.
-- 创建cross edition trigger
-- 注意这个trigger是作用在employees_rt表的,他使得老的应用依旧可以运行,同时将改变传递到新的表结构中,注意forward crossedition关键字。
SQL>
create or replace trigger employees_fwdxedition
before insert or update of phone_number on employees_rt
for each row
forward crossedition
declare
first_dot number;
second_dot number;
begin
if :new.phone_number like '011.%'
then
first_dot
:= instr( :new.phone_number, '.' );
second_dot
:= instr( :new.phone_number, '.', 1, 2 );
:new.country_code
:= '+'||
substr( :new.phone_number,
first_dot+1,
second_dot-first_dot-1 );
:new.phone#
:= substr( :new.phone_number,
second_dot+1 );
else
:new.country_code := '+1';
:new.phone# := :new.phone_number;
end if;
end;
/
Trigger created.
-- 目前位置,由于没有做数据迁移,新的表结构中的两个字段都是空值
DEMO> select count(*) from employees_rt where country_code is null;
COUNT(*)
----------
108
-- 执行以下语句模拟老版本(version)应用,检验数据已迁移成功
DEMO> select count(*) from employees;
COUNT(*)
----------
108
DEMO> select count(*) from employees_rt where country_code is null;
COUNT(*)
----------
0
DEMO> select phone_number, phone#, country_code from employees_rt where rownum < 5;
PHONE_NUMBER PHONE# COU
-------------------- -------------------- ---
650.507.9833 650.507.9833 +1
650.507.9844 650.507.9844 +1
515.123.4444 515.123.4444 +1
515.123.5555 515.123.5555 +1
-- 以上方法虽然成功,但是会锁整个表,下面介绍一种并行方法,可以只锁表的一部分
-- 为此,我们先将之前的修改回退
DEMO> update employees_rt set phone#=null, country_code=null;
108 rows updated.
DEMO> commit;
Commit complete.
DEMO> select count(*) from employees_rt where country_code is null;
COUNT(*)
----------
108
-- 这种方法利用了DBMS_PARALLEL_EXECUTE包,即将表按照rowid分为多个部分,然后并行对这些部分操作
-- 为测试并行,我们先将表放大100倍
DEMO>
insert into employees
select * from
(
with data(r)
as
(select 1 r from dual
union all
select r+1 from data where r <= 100
)
select rownum+(select max(employee_id)
from employees_rt),
FIRST_NAME, LAST_NAME, EMAIL,
PHONE_NUMBER, HIRE_DATE, JOB_ID,
SALARY, COMMISSION_PCT, MANAGER_ID,
DEPARTMENT_ID
from employees_rt, data
);
10908 rows created.
-- 查看数据
select phone_number, country_code, phone#
from employees_rt
where country_code is null
and rownum <= 5
union all
select phone_number, country_code, phone#
from employees_rt
where country_code is NOT null
9 and rownum <= 5;
PHONE_NUMBER COU PHONE#
-------------------- --- --------------------
650.507.9833
650.507.9844
515.123.4444
515.123.5555
603.123.6666
650.507.9876 +1 650.507.9876
650.507.9877 +1 650.507.9877
650.507.9878 +1 650.507.9878
650.507.9879 +1 650.507.9879
650.509.1876 +1 650.509.1876
10 rows selected.
-- 以下的数据,两个新增的列是空的
DEMO> select count以上是关于Oracle Edition-Based Redefinition(EBR)理论与实践的主要内容,如果未能解决你的问题,请参考以下文章
no-redeclare (Rules) – Eslint 中文开发手册