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 中文开发手册

Xcode8之后 XMPP 重定义问题 Redefinition of module 'dnssd'

怎么把数据从oracle11迁到oracle12

linux 怎么查找oracle11g的安装目录

windows安装oracle76%卡了

oracle 语法