Oracle

更新語句執行時間與預設值的巨大差異

  • July 24, 2017

上週我嘗試在應用伺服器上部署新版本的應用程序。在部署之前,需要在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 表返回至少一行。

引用自:https://dba.stackexchange.com/questions/181623