在線重定義操作步驟


Oracle有個存儲過程,通過在線重定義,可以實現業務表,與臨時表進行轉換,並且不影響業務的情況下(實際還是存在影響)。

本篇文檔根據操作后,進行精簡,重點描述操作步驟,減少操作遇到問題的可能性。

一 實施流程

1)前期調研,查詢表及相關對象大小,臨時表分區創建語法,后期分區表相關索引創建及維護測試;

2)提前創建臨時表,並使用在線重定義進行同步操作;

3)與業務人員溝通確認后,最后一次同步,並且完成在線重定義操作;

 

二 具體操作步驟

2.1 前期調研

檢查是否能夠重定義,當前表無主鍵,使用rowid
EXEC DBMS_REDEFINITION.CAN_REDEF_TABLE('owner','table_name',DBMS_REDEFINITION.cons_use_rowid);
--如果存在主鍵,不需要第三個參數
EXEC DBMS_REDEFINITION.CAN_REDEF_TABLE('xxx','xxx');
查詢是否存在引用表的其它對象 
SQL> select * from DBA_DEPENDENCIES where REFERENCED_OWNER='xxx' and REFERENCED_NAME='xxx';
no rows selected

查詢是否存在單獨授權的grant語句 
SQL>select GRANTEE,OWNER,TABLE_NAME,GRANTOR,PRIVILEGE from dba_tab_privs where OWNER='xxx' and TABLE_NAME='xxx';
no rows selected
--備份,重定義后,重新授權
--select 'grant '||PRIVILEGE||' on '||OWNER||'.'||TABLE_NAME||' to '||GRANTEE||';' as "grant_text" from dba_tab_privs
where OWNER='xxx' and TABLE_NAME='xxx'; 查詢表屬性 SQL>select owner,table_name,compression,compress_for from dba_tables where owner='xx' and table_name='xxx'; SQL>select table_owner,table_name,partition_name,compression,compress_for from dba_tab_partitions where
table_owner='xxx' and table_name='xxx'; 表大小 select owner,segment_name,segment_type,round(sum(bytes)/1024/1024/1024,2) G,tablespace_name from dba_segments
where segment_name='xxx' group by owner,segment_name,segment_type,tablespace_name; 主鍵約束 SQL> select constraint_name,table_name,constraint_type from dba_constraints where owner='BSP' and TABLE_NAME
in('xxx') and constraint_type='P'; SQL> SELECT * FROM DBA_CONS_COLUMNS WHERE OWNER='xxx' AND CONSTRAINT_NAME='xxx'; 表約束信息 SQL> select OWNER,CONSTRAINT_NAME,CONSTRAINT_TYPE,TABLE_NAME,R_OWNER,R_CONSTRAINT_NAME,STATUS,INDEX_OWNER,INDEX_NAME
from dba_constraints where TABLE_NAME='xxx' and owner='xxx'; 查詢數據最小值 SQL> select to_char(min(REPORT_TIME),'yyyy-mm-dd') as "date" from xxxx.xxxx; 表的注釋信息 SQL>select * from dba_col_comments where owner='BSP' AND TABLE_NAME='table&'; 表的索引信息 SQL> SELECT INDEX_OWNER,INDEX_NAME,TABLE_OWNER,TABLE_NAME,COLUMN_NAME,COLUMN_POSITION FROM DBA_IND_COLUMNS
WHERE TABLE_OWNER='xxx' and TABLE_NAME='xxx' order by 2,5; SQL> select dbms_metadata.get_ddl('INDEX','xxx','owner') ddl_text from dual; 備份操作 nohup expdp \'/ as sysdba\' directory=dump dumpfile=xxxxx%u.dmp logfile=xxx.log TABLES=xxxP.xxxx
COMPRESSION=DATA_ONLY CLUSTER=N PARALLEL=4 &

2.2 創建臨時表

與開發人員溝通TIME時間字段,每天一個分區,使用11g新特性間隔范圍分區
CREATE TABLE "owner"."temp_table" ( "ID" NOT NULL ENABLE ) SEGMENT CREATION IMMEDIATE COMPRESS LOGGING TABLESPACE "xxxx" PARTITION BY RANGE (xxx_TIME) INTERVAL (NUMTODSINTERVAL(1, 'day')) (partition part_t1 values less than(to_date('2018-09-27', 'yyyy-mm-dd')))

2.3 初步同步數據

如果存在主鍵/唯一約束/索引,一定要刪掉! 因為初步同步數據后,默認創建索引,默認並行度1,非常慢,且需要占用大量temp空間,報錯需要重來。
sqlplus / as sysdba <<EOF
set timing on
alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
select sysdate from dual;
BEGIN
DBMS_REDEFINITION.START_REDEF_TABLE (
   uname         => 'owner',
   orig_table    => '業務表',
   int_table     => '臨時表',
   options_flag  => dbms_redefinition.cons_use_rowid);   --如果是主鍵,刪掉這行參數
END;
/
exit
EOF
[oracle@vplusdb1 ~]$ nohup sh dbms_init.sh &
第一次全量同步,使用Insert append方式寫入,500G大表,使用BASIC壓縮屬性,3小時不到寫入完畢,壓縮后的數據200G

2.4 增量同步數據(多次)

創建索引

!#/bin/bash
export PATH
export ORACLE_HOME=xxx
sqlplus / as sysdba <<EOF
set timing on alter session
set nls_date_format='yyyy-mm-dd hh24:mi:ss';
select sysdate from dual; ALTER SESSION FORCE PARALLEL DDL parallel 4; alter session set workarea_size_policy = manual; alter session set sort_area_size=1024000000; alter session set db_file_multiblock_read_count= 128; alter session set "_sort_multiblock_read_count"= 128; alter session set current_schema=xx;
set timing on time on ; CREATE INDEX "xx"."xx" ON "xx"."xxx" ("Vxx_ID", "Rxx_TIME" DESC) LOCAL tablespace BSP parallel 8; alter index "xx"."xxx" parallel 1; exec dbms_stats.gather_table_stats(ownname => 'xx',tabname => 'xxx',estimate_percent => 0.1,degree => 16,
granularity => 'ALL',cascade => true,no_invalidate => false); --注意,對臨時表收集統計信息
exit;
EOF

 

(小表,幾十G,可以忽略本篇的很多內容,因為數據量少,一些特性可以忽略,但是對於幾百G的大表,建議多關注文字說明,都是坑)
一定要對臨時表創建索引,特別是主鍵索引或者唯一索引,其次是復合索引,創建索引后,收集統計信息使用no_invalidate => false
(沒必要所有的索引都創建,目的是臨時表存在主鍵或者唯一,最次復合索引,再增量同步數據的過程當中,能夠根據索引進行nest loop執行計划進行同步操作,
而非HASH JOIN兩個大表進行關聯,再實際操作中,500G業務表,與200G臨時表,使用HASH JOIN 增量同步,導致占用200G TEMP臨時表空間,同步效率異常緩慢,
最后創建主鍵索引,收集統計信息后,再次進行增量同步,執行計划走索引nest looop)
*增量同步過程中,添加臨時表空間,如果SQL有占用temp,會導致增量同步報錯終止,再次增量同步就好了
BEGIN DBMS_REDEFINITION.SYNC_INTERIM_TABLE ( uname      
=> 'owner',  orig_table  => '業務表', int_table   => '臨時表'); END; /

參考收集統計信息參數
https://blog.csdn.net/cpongo3/article/details/88800429

2.5 復制依賴對象

不復制索引、不忽略錯誤、不復制統計信息。
SET SERVEROUTPUT ON
DECLARE
     error_count pls_integer := 0;
BEGIN
DBMS_REDEFINITION.COPY_TABLE_DEPENDENTS(
   uname               => 'owner',
   orig_table          => '業務表',
   int_table            => '臨時表',
   copy_indexes         => 0,
   copy_triggers        => TRUE,
   copy_constraints     => FALSE,
   copy_privileges      => TRUE,
   ignore_errors        => FALSE,
   num_errors           => error_count,
   copy_statistics      => FALSE); 
DBMS_OUTPUT.PUT_LINE('errors := ' || TO_CHAR(error_count));
END;
/ 

 

2.6 異常處理

刪除結束之前的重定義過程
官方文檔說明
Aborting Online Table Redefinition and Cleaning Up After Errors
In the event that an error is raised during the redefinition process, or if you choose to terminate the redefinition process, 
call ABORT_REDEF_TABLE. This procedure drops temporary logs and tables associated with the redefinition process.
After this procedure is called, you can drop the interim table and its dependent objects. If the online redefinition process must be restarted, if you do not first call ABORT_REDEF_TABLE,
subsequent attempts to redefine the table will fail. SQL
>EXEC DBMS_REDEFINITION.ABORT_REDEF_TABLE('owner','業務表','臨時表');

 

2.7 監控並調整

SQL>select * from table(dbms_xplan.display_cursor('sqL_id&'));
SQL>select a.TEMP_SPACE_ALLOCATED/1024/1024/1024,to_char(a.SAMPLE_TIME,'yyyy-mm-dd hh24:mi') as "date",a.inst_id,
a.SESSION_ID,a.SESSION_SERIAL#,b.username,a.event,a.sql_id,a.MACHINE from gv$active_session_history a,dba_users b where a.user_id=b.user_id and a.SAMPLE_TIME>sysdate-1 and a.sql_id='sqL_id&' order by 2,1; SQL> select file_name,file_id,tablespace_name,bytes/1024/1024/1024,maxbytes/1024/1024/1024 from dba_temp_files;

版本(11.2.0.4)本次使用sql moniter進行監控sql執行進度!!!

SQL>  select INST_ID,sid,serial#,USERNAME,STATUS,MACHINE,SQL_ID,EVENT,(sysdate-LOGON_TIME)*86400 as "s",LAST_CALL_ET from gv$session where sid=xxx and SERIAL#=xxx;

SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF
SELECT DBMS_SQLTUNE.report_sql_monitor(sql_id => 'xxx', type => 'TEXT') AS report FROM dual;

查詢需要增量數據的類型(dml)以及行數

SQL> select DMLTYPE$$,count(*) from ower.mlog$_業務表 group by DMLTYPE$$ ;(表名稱很長通過新建對象獲取一下,物化視圖日志)

 

2.8 最后一次同步

(上述操作均可以提前操作,但是最后一步的操作,需要和業務確認,最好沒有業務或者業務數據量少的時候操作)

正常情況下,執行次操作,同步完成。
EXEC DBMS_REDEFINITION.FINISH_REDEF_TABLE('owner','業務表','臨時表');

 

3.1 檢測

  1.應用驗證功能沒有問題;
  2.驗證數據沒有問題;
  3.驗證表約束是否OK
  4.查詢表大小對比, 舊表480G 新表208G    近50%壓縮比例
  5.索引狀態及是否分區
  6.表的壓縮屬性
  7.表的注釋信息同步
  8.臨時表空間擴容的數據文件進行刪除回收
  9.DB 歸檔空間,表空間使用情況

SQL> select OWNER,CONSTRAINT_NAME,CONSTRAINT_TYPE,TABLE_NAME,STATUS,INDEX_NAME from DBA_constraints
where owner='xx' and table_name in('xx','xx'); select owner,segment_name,segment_type,round(sum(bytes)/1024/1024/1024,2) g,TABLESPACE_NAME,count(*)
from dba_segments where owner='xx' AND SEGMENT_NAME in ('xx','xx') group by owner,segment_name,segment_type,TABLESPACE_NAME ; OWNER SEGMENT_NAME SEGMENT_TYPE G TABLESPACE COUNT(*) ------ -------------------------------- ------------------ ---------- ---------------- select owner,segment_name,segment_type,partition_name,round((bytes)/1024/1024,2) MBYTES,TABLESPACE_NAME
from dba_segments where owner='xxx' AND SEGMENT_NAME in ('xxx') order by 5; OWNER SEGMENT_NAME SEGMENT_TYPE PARTITION_NAME MBYTES ---------- --------------------------- ------------------ ------------------------------ ---------- SQL> select table_owner,table_name,partition_name,high_value,tablespace_name from dba_tab_partitions
where table_owner='xxx' and table_name='xxx' and partition_name='PART_T1' PARTITION_NAME HIGH_VALUE ----------------------------------------------------------------------------------------------------- PART_T1 TO_DATE(' 2018-09-27 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA SQL>select OWNER,INDEX_NAME,TABLE_OWNER,TABLE_NAME,STATUS from dba_indexes where table_owner='xx' AND TABLE_NAME='xxx'; OWNER INDEX_NAME TABLE_NAME STATUS ------- ------------------------------ ------------- ------------------------------ ---------- IDX_TB_VEHICLE_COM_2NEW xxx N/A PK_TB_VEHICLE_COMPONENT_NO_NEW xxx VALID IDX_TB_VEHICLE_COM_1NEW xxx N/A IDX_TB_VEHICLE_COM_3NEW xxx N/A SQL>select index_name,STATUS,TABLESPACE_NAME,count(*) from dba_ind_partitions where index_owner='xx'
and index_name in('xxx','xx','xxx') group by index_name,STATUS,TABLESPACE_NAME; INDEX_NAME STATUS TABLESPACE_NAME COUNT(*) ------------------------------ ---------- -------------------------- IDX_TB_VEHICLE_COM_1NEW USABLE 513 IDX_TB_VEHICLE_COM_3NEW USABLE 513 IDX_TB_VEHICLE_COM_2NEW USABLE 513 SQL> select owner,table_name,COMPRESSION,COMPRESS_FOR from dba_tables where owner='xx' and table_name='xxx'; OWNER TABLE_NAME COMPRESS COMPRESS_FOR ------------------------------ ------------------------------ -------- ------------ SQL> SELECT TABLE_OWNER,TABLE_NAME,COMPRESSION,COMPRESS_FOR,count(*) FROM DBA_TAB_PARTITIONS
WHERE TABLE_OWNER='xxx' AND TABLE_NAME='xxx' group by TABLE_OWNER,TABLE_NAME,COMPRESSION,COMPRESS_FOR; --PARTITION_NAME TABLE_NAME COMPRESS COMPRESS_FOR COUNT(*) ------------------------------ -------- ------------ ---------- xxx ENABLED BASIC 513 select 'comment on column '||owner||'.xxx.'||COLUMN_NAME||' is '''||COMMENTS||''';' as "sql"
from dba_col_comments where table_name='xxx' and COMMENTS is not null and owner='xxx'; sql---------------------------------------------------------------------- comment on column xx.xxx.ID is 'ID'; SQL> select OBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID,OBJECT_TYPE,STATUS from
user_objects where created>sysdate-5 and OBJECT_TYPE not in('INDEX PARTITION','INDEX','LOB','TABLE','TABLE PARTITION') no rows selected

 

3.2 再上述重定義完成后,業務也並未反饋異常,清理歷史數據

1)對單個分區進行expdp導出備份

nohup expdp \'/ as sysdba\' directory=dump dumpfile=xx%u.dmp logfile=xxx.log TABLES=owner.table:part_name
COMPRESSION=DATA_ONLY CLUSTER=N PARALLEL=4 &
Processing object type TABLE_EXPORT/TABLE/TABLE_DATA Total estimation using BLOCKS method: 63.05 GB ······ . . exported 37.05 GB 455139131 rows Master table "SYS"."SYS_EXPORT_TABLE_01" successfully loaded/unloaded ****************************************************************************** Dump file set for SYS.SYS_EXPORT_TABLE_01 is: Job "SYS"."SYS_EXPORT_TABLE_01" successfully completed at Tue Nov 26 17:42:07 2019 elapsed 0 00:27:34 使用導出的備份,導出sql,觀察如果需要恢復,是記錄全表還是只是單純一個分區信息 impdp \'/ as sysdba\' directory=dump dumpfile=xxx%u.dmp logfile=xxx.log sqlfile=part_sql.log Processing object type TABLE_EXPORT/TABLE/TABLE Processing object type TABLE_EXPORT/TABLE/COMMENT Processing object type TABLE_EXPORT/TABLE/INDEX/INDEX Processing object type TABLE_EXPORT/TABLE/INDEX/FUNCTIONAL_INDEX/INDEX Processing object type TABLE_EXPORT/TABLE/CONSTRAINT/CONSTRAINT Processing object type TABLE_EXPORT/TABLE/INDEX/STATISTICS/INDEX_STATISTICS Processing object type TABLE_EXPORT/TABLE/INDEX/STATISTICS/FUNCTIONAL_INDEX/INDEX_STATISTICS Processing object type TABLE_EXPORT/TABLE/STATISTICS/TABLE_STATISTICS Job "SYS"."SYS_SQL_FILE_FULL_01" successfully completed at Tue Nov 26 21:38:59 2019 elapsed 0 00:01:07 -- new object type path: TABLE_EXPORT/TABLE/TABLE CREATE TABLE "xxx"."xxx"xxxxxx COMPRESS BASIC PARTITION BY RANGE ("xxx") INTERVAL (NUMTODSINTERVAL(1, 'DAY')) TRANSITION ("PART_T1") (PARTITION "PART_T1" VALUES LESS THAN (TO_DATE(' 2018-09-27 00:00:00', 'SYYYY-MM-DD HH24:MI:SS',
'NLS_CALENDAR=GREGORIAN')) SEGMENT CREATION IMMEDIATE PARTITION "SYS_P26401" VALUES LESS THAN (TO_DATE(' 2018-09-28 00:00:00', 'SYYYY-MM-DD HH24:MI:SS',
'NLS_CALENDAR=GREGORIAN')) SEGMENT CREATION IMMEDIATE
····· 從上述信息,可以發現導出單個分區(從統計的blocks大小可以得到百分百是對分區數據進行導出)但是表結構是全表
2)刪除前,檢查查詢 SQL>select table_owner,table_name,partition_name,high_value,tablespace_name from dba_tab_partitions
where table_owner='xxx' and table_name='xxx' and partition_name='PART_T1'; TABLE_OWNE TABLE_NAME PARTITION_NAME HIGH_VALUE TABLESPACE_NAME ------------------------------------------------------------------------------------------------------------------------------- PART_T1 TO_DATE(' 2018-09-27 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIA BSP XXX表 備份並清理xxx<20181001的數據 3)刪除操作 vi 20191126_truncate.sh #!/bin/bash sqlplus / as sysdba <<EOF alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss'; select sysdate from dual; set timin on alter table xx.xxx truncate partition PART_T1 update global indexes; exit; EOF Elapsed: 01:47:28.50 https://www.cnblogs.com/lvcha001/p/11930072.html SQL> select sql_text from v$sql where sql_id='723qvrhgxzavn'; SQL_TEXT --------------------------------------------------------------------------------
alter table xx.xxx truncate partition PART_T1 update global indexes;
-- select distinct round(a.TEMP_SPACE_ALLOCATED/1024/1024/1024,1) as "used_g",
to_char(a.SAMPLE_TIME,'yyyy-mm-dd hh24:mi') as "date",a.inst_id,a.SESSION_ID,
a.SESSION_SERIAL#,b.username,a.event,a.sql_id,a.MACHINE from gv$active_session_history a,
dba_users b
where a.user_id=b.user_id and a.SAMPLE_TIME>to_date('20191126 11','yyyymmdd hh24')
and a.sql_id='723qvrhgxzavn' group by a.TEMP_SPACE_ALLOCATED/1024/1024/1024,to_char(a.SAMPLE_TIME,'yyyy-mm-dd hh24:mi'),
a.inst_id,a.SESSION_ID,a.SESSION_SERIAL#,b.username,a.
event,a.sql_id,a.MACHINE order by 2,1;
used_g date EVENT SQL_ID

---------- ---------------- - -------------------------- -------------------

0 2019-11-26 18:06 723qvrhgxzavn

23.8 2019-11-26 18:44 723qvrhgxzavn

26.1 2019-11-26 19:01 db file sequential read 723qvrhgxzavn

26.1 2019-11-26 19:01 gc current grant 2-way 723qvrhgxzavn

26.1 2019-11-26 19:01 direct path read temp 723qvrhgxzavn --占用26g temp

4)確認 重建完畢。。。查詢分區數據信息,發現存在心得錯誤數據產生!挺好
SQL
> select count(*) from xxx partition(PART_T1);
COUNT(
*)
----------

38
SQL
>select OWNER,INDEX_NAME,TABLE_OWNER,TABLE_NAME,STATUS from dba_indexes
where table_owner='xxx' AND TABLE_NAME='xxx';
OWNER INDEX_NAME TABLE_OWNER TABLE_NAME STATUS
------------------------------ ------------------------------ ------------
IDX_TB_VEHICLE_COM_2NEW N/A
PK_TB_VEHICLE_COMPONENT_NO_NEW VALID
IDX_TB_VEHICLE_COM_1NEW N
/A
IDX_TB_VEHICLE_COM_3NEW N
/A
select owner,segment_name,segment_type,round(sum(bytes)/1024/1024/1024,2) g,TABLESPACE_NAME,count(*)
from dba_segments where owner='xx' AND SEGMENT_NAME
in ('xx','xx') group by owner,segment_name,segment_type,TABLESPACE_NAME ;
----------------------------------------------------------------------------------- ----------
TABLE
479.141
TABLE PARTITION 146.91 516 --刪除歷史分區前,釋放了60G數據
在線重定義之前得表,
后續進行drop table xxx; or drop table xx purge;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM