更新語句執行時間與預設值的巨大差異
上週我嘗試在應用伺服器上部署新版本的應用程序。在部署之前,需要在Oracle 11g數據庫上執行一些數據庫腳本。
該數據庫的確切版本是 Oracle Database 11g Release 11.2.0.1.0 - 64bit Production。桌子上沒有觸發器。
引起頭痛的陳述是這樣的:
ALTER TABLE EMS_DATA ADD STATUS VARCHAR2(1) DEFAULT 'V' NOT NULL;
在一個看起來像這樣的 DDL 上:
create table EMS_DATA ( CHANNEL_ID NUMBER not null, TSTAMP TIMESTAMP(3) WITH LOCAL TIME ZONE not null, VAL FLOAT, constraint EMS_DATA_PK primary key (CHANNEL_ID, TSTAMP) )
PROD表
EMS_DATA
包含大約 3.5 億條記錄,我們的 ACC 表包含大約 2.8 億條記錄。兩者都是 Oracle 11g 實例。我在 ACC 表上執行了兩次語句,執行時間約為。50 毫秒和 300 毫秒。它應該很快的原因如下:
僅元數據的預設值
在 Oracle 11g 之前,向現有表添加新列需要修改該表中的所有行以添加新列。
Oracle 11g 引入了僅元數據預設值的概念。將帶有 DEFAULT 子句的 NOT NULL 列添加到現有表僅涉及元數據更改,而不是更改表中的所有行。新列的查詢由優化器重寫,以確保結果與預設定義一致。
參考:https ://oracle-base.com/articles/12c/default-values-for-table-columns-enhancements-12cr1
所以是的,我相當有信心更新不會對 PROD 數據庫造成任何問題。相反,這發生了;
該語句的執行耗時 > 120 分鐘並最終停止,因為發生的事情是磁碟驅動器已滿。經過檢查,我們看到在這段時間內寫入了180GB 的
.ARC
(存檔重做日誌)文件。TL;DR 是什麼導致這個 alter table 語句在持續時間上有如此巨大的差異,為什麼
.ARC
當表中的數據總量甚至不接近 180GB 的磁碟空間時,一個數據庫會生成大量的文件。可以關閉此僅元數據優化嗎?需要注意的是,應用程序伺服器在腳本執行時已關閉,因此沒有外部程序可能導致此問題。
來自數據庫 SQL 語言參考,11g 第 2 版 (11.2):
添加列時,新列的每一行的初始值為空。
- 如果為 NOT NULL 列指定 DEFAULT 子句,則預設值將儲存為元數據,但列本身不會填充數據。但是,指定新列的後續查詢將被重寫,以便在結果集中返回預設值。
這種優化行為與早期版本不同,當作為 ALTER TABLE 操作的一部分時,Oracle 數據庫使用預設值更新新創建的列中的每一行,然後觸發表上定義的任何更新觸發器。在此版本中,不會觸發任何觸發器,因為預設值僅儲存為元數據。優化的行為受到以下限制:
+ 該表不能有任何 LOB 列。它不能是索引組織的、臨時的或集群的一部分。它也不能是隊列表、對象表或物化視圖的容器表。 + 如果表上有虛擬專用數據庫 (VPD) 策略,那麼優化行為將不會發生,除非發出 ALTER TABLE … ADD 語句的使用者具有 EXEMPT ACCESS POLICY 系統特權。 + 添加的列不能加密,不能是對象列、嵌套表列或 LOB 列。
- 如果您為可空列指定 DEFAULT 子句,則預設值將作為此 ALTER TABLE 語句的一部分添加到現有行,並觸發在表上定義的任何更新觸發器。如果您將具有預設值的 NOT NULL 列更改為可為空,也會導致此行為。
也許預設值實際上已寫入行。所以檢查你的數據庫的版本和你的表的結構!
11g 中有一個隱藏參數 _ADD_COL_OPTIM_ENABLED可用於禁用此元數據優化,也可能是參數 COMPATIBLE。這在metalink note中有所描述
Init.ora 參數“_ADD_COL_OPTIM_ENABLED”$$ Hidden $$參考說明(文件 ID 1492674.1)
但是所有這些參數在系統範圍內都是有效的,並且不僅適用於表格。因此,如果對於一個表,一切都按預期工作,則似乎為該實例正確設置了參數。
在上面提到的這個 metalink 中,還發布了以下查詢,顯示了使用此元數據優化添加的列:
select owner, object_name, name from dba_objects, col$ where bitand(col$.PROPERTY,1073741824)=1073741824 and object_id=obj#;
使用此查詢,您可以檢查您的列是否設置了此元數據優化。您可以通過以下方式搜尋您的表格和列
select owner, object_name, name from dba_objects, col$ where bitand(col$.PROPERTY,1073741824)=1073741824 and object_id=obj# and object_name='EMS_DATA' and name='STATUS'
如果這返回
no rows selected
然後更新所有行。查詢
select owner, object_name, name from dba_objects, col$ where bitand(col$.PROPERTY,1073741824)=1073741824 and object_id=obj# and object_name='ACC' and name='STATUS'
應該為您的 ACC 表返回至少一行。