本文由 dbaplus 社羣授權轉載。
背景
從Oracle數據庫官方服務支持生命週期表,我們可以清晰看到Oracle 11g已過主支持生命週期,2020年後不再支持。基於這個背景,某客戶的應用系統數據庫將從IBM AIX小型機環境遷移到某國產數據庫一體機,同時數據庫版本從11g直接升級爲19c。
LOB字段帶來的問題
經過分析,此數據庫的數據量不大,只有區區3TB,同時由於停機時間非常充分,可以考慮採取數據泵datapump的方式實現數據遷移。但是在仔細查看後,發現數據庫中有個單表2TB,仔細再查2TB基本全是lob字段,且不是分區表,這個問題就有點棘手了。
根據以往的經驗來看,這種大容量TB級的lob表,使用以往常規導出的方式,大概率會報Ora-01555。
稍稍測試一下,果不其然。
解決方法
一般的方法可以修改一下undo_retention參數以及lob字段的retention設置來解決,大致如下:
alter system set undo_retention=7200 scope=both;
alter table table_name MODIFY LOB(col_name)(retention);
然而當前的數據庫是一個生產環境,參數修改這樣的風險工程還是少做爲妙,因此需要另闢蹊徑。既然ORA-01555是由於長時間查詢引起,我們可以嘗試減少倒出的數據量。
最後決定用Expdp的Query試一試,但是2TB的數據量的單表lob還是第一次,那麼根據哪個條件進行Query導出呢?
首先需考慮到是根據主鍵和索引列進行導出,這樣的效率會比較高。確認後,問題又來了,索引列不滿足均勻分批條件,故這個思路走不通了。
要怎樣才能均分呢?看來只能用Rowid試試看。
首先Rowid是用於定位數據庫中一條記錄的一個相對唯一地址值。通常情況下,該值在該行數據插入到數據庫表時即被確定且唯一。Rowid是一個僞列,它並不實際存在於實體表中。
它是Oracle在讀取表中數據行時,根據每一行數據的物理地址信息編碼而成的一個僞列。所以根據一行數據的Rowid能找到一行數據的物理地址信息,從而快速地定位到數據行,而且使用Rowid來進行單記錄定位速度是最快的。
上圖是Rowid的結構圖,主要包含4個部分:
- 第一部分6位表示:該行數據所在的數據對象的Data_object_id
- 第二部分3位表示:該行數據所在的相對數據文件的id
- 第三部分6位表示:該數據行所在的數據塊的編號
- 第四部分3位表示:該行數據的行的編號
一個擴展Rowid採用10個byte來存儲,共80bit,其中obj#32bit, rfile#10bit, block#22bit, row#16bit。所以相對文件號不能超過1023,也就是一個表空間的數據文件不能超過1023個(不存在文件號爲0的文件),一個Datafile只能有2^22=4M個 block,一個block中不能超過2^16=64K行數據的由來。
瞭解了Rowid後,怎麼進行均勻分批呢?我們可以利用Oracle提供的DBMS_ROWID 包。
導出腳本修改如下:
參數說明:
- Content=DATA_ONLY:只導出表中的數據,導出會更快,導入時也更快,index之類的對象在data導入後單獨處理;
- COMPRESSION=DATA_ONLY:數據量太大,節省空間,傳輸到新環境時效更高;
- Query=“……”:將表數據根據條件進行分批導出全部數據。
爲什麼選用rowid_block_number呢?因爲導出這個大表的需求下,Object_id就一個,分不了批次,Fileid只有150個,BLOCK_ID 是126924個,ROW_NUMBER是19,數據量數值進行Mod取餘分批的差異就越小,所以使用rowid_block_number。使用這個方法後還是很順利地導出了數據。
將參數文件複製並修改取模的餘數值,分爲十個進程併發執行。查看全部導出日誌,每個批次耗時相差不大,滿足均勻分批導出的計劃。
小結
遇到超大lob表導出需要,一般的思路是儘可能通過分區或者過濾查詢減少單表數據導出的數據量,減少整體耗時,辦法可以分爲:
1、查看是否是分區表,分區表的話按分區導出:
userid=' / as sysdba'
directory=DMP
dumpfile=export.dmp
logfile=export.log
CONTENT=DATA_ONLY
COMPRESSION=DATA_ONLY
tables=(
onwer.tbale_name:part_name
)
2、業務溝通,是否存在均勻分佈的字段值,按照字段值分批導出,例如:
USERID=' / as sysdba'
directory= DMP
CONTENT=DATA_ONLY
COMPRESSION=DATA_ONLY
dumpfile=export.dmp
logfile=export.log
tables=owner.table_name
QUERY="WHERE column_name > 100000
3、不滿足以上的都可以使用本文rowid方式進行導出:
Cat exp_owner_table_seq.par
USERID='/ as sysdba'
directory= DMP
CONTENT=DATA_ONLY
COMPRESSION=DATA_ONLY
dumpfile=export.dmp
logfile=export.log
tables=owner.table_name
QUERY="wheremod(dbms_rowid.rowid_block_number(rowid),10)=1"
作者介紹:
梁銘圖,新炬網絡首席架構師,十多年數據庫運維、數據庫設計、數據治理以及系統規劃建設經驗,擁有Oracle OCM、Togaf企業架構師(鑑定級)、IBM CATE等認證,曾獲dbaplus年度MVP以及華爲雲MVP等榮譽,並參與數據資產管理國家標準的編寫工作。在數據庫運維管理和架構設計、運維體系規劃、數據資產管理方面有深入研究。
王濤,新炬網絡資深數據庫專家,長期服務於運營商、金融、製造業及政企客戶。紮根客戶一線,多次主導運營商數據庫大版本升級,擅長數據割接及同步技術的研究和應用,割接實戰經驗豐富。
原文鏈接: