1. 程式人生 > >線上重定義操作步驟

線上重定義操作步驟

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;

&n