ORA-00069λ?
ORA-00069 μλ¬λ "cannot acquire lock - table locks disabled", μ¦ ν
μ΄λΈ μ κΈ(Table Lock, μΌλͺ
TM Lock)μ΄ λΉνμ±νλ μνμμ ν΄λΉ ν
μ΄λΈμ μ κΈμ νλνλ € ν λ λ°μνλ μλ¬μ
λλ€. Oracleμ DML(INSERT, UPDATE, DELETE) λ° DDL μμ
μ ν
μ΄λΈ μμ€μ μ κΈμ μ¬μ©νλλ°, νΉμ ν
μ΄λΈμ λν΄ DISABLE TABLE LOCK μ΅μ
μ΄ μ€μ λμ΄ μμΌλ©΄ μ΄ μ κΈ νλ μμ²΄κ° λΆκ°λ₯ν΄μ§λλ€. μ£Όλ‘ κ³ κ°μ©μ± νκ²½μ΄λ Oracle Parallel Server(OPS) / RAC νκ²½μμ μ±λ₯ μ΅μ νλ₯Ό μν΄ μλμ μΌλ‘ ν
μ΄λΈ μ κΈμ λΉνμ±νν κ²½μ°, λλ μ€μλ‘ λΉνμ±νλ κ²½μ°μ μ΄ μλ¬κ° λ°μν©λλ€.
μ£Όμ λ°μ μμΈ
1. μλμ λλ μ€μλ‘ μΈν DISABLE TABLE LOCK μ€μ
κ°μ₯ νν μμΈμΌλ‘, DBA λλ κ°λ°μκ° ALTER TABLE ... DISABLE TABLE LOCK λͺ
λ Ήμ μ€ννμ¬ ν΄λΉ ν
μ΄λΈμ TM μ κΈμ λΉνμ±νν κ²½μ°μ
λλ€. RAC νκ²½μμ μΈν°μ»€λ₯νΈ νΈλν½μ μ€μ΄κΈ° μν μ±λ₯ νλ λͺ©μ μΌλ‘ μ΄ μ€μ μ μ μ©νκΈ°λ νμ§λ§, μ΄ν μΌλ° DML μμ
μ ORA-00069κ° λ°μνκ² λ©λλ€. μ΄μ μ€ λ€λ₯Έ DBAλ μ€ν¬λ¦½νΈμ μν΄ μ€μλ‘ μ μ©λ κ²½μ°λ λ§μ, μ₯μ λ°μ μ λ°λμ μ΄ μ€μ μ¬λΆλ₯Ό μ°μ νμΈν΄μΌ ν©λλ€.
2. DDL μμ (ALTER TABLE, TRUNCATE λ±) μν μ μ κΈ μΆ©λ
TRUNCATE TABLE, ALTER TABLE ... ADD COLUMN λ± DDL μμ
μ λ΄λΆμ μΌλ‘ ν
μ΄λΈ μμ€μ λ°°νμ μ κΈ(Exclusive TM Lock)μ μꡬν©λλ€. ν
μ΄λΈ μ κΈμ΄ λΉνμ±νλ μνμμ μ΄λ¬ν DDLμ μ€ννλ©΄ ORA-00069κ° λ°μν©λλ€. νΉν λ°°μΉ μμ
μ€ν¬λ¦½νΈλ λ§μ΄κ·Έλ μ΄μ
μ€ν¬λ¦½νΈμμ TRUNCATE ν INSERT ν¨ν΄μ μ¬μ©ν λ λΉλ²ν λ§μ£ΌμΉλ μν©μ
λλ€.
3. Oracle Parallel Server(OPS) λλ RAC νκ²½μ μλͺ»λ ꡬμ±
Oracle RAC(Real Application Clusters) νκ²½μμλ λ Έλ κ° κΈλ‘λ² μ κΈ κ΄λ¦¬(Global Enqueue Service, GES)λ₯Ό ν΅ν΄ ν μ΄λΈ μ κΈμ μ²λ¦¬ν©λλ€. μΌλΆ μ€λλ RAC μ€μ κ°μ΄λμμλ μ±λ₯μ μν΄ νΉμ ν μ΄λΈμ μ κΈμ λΉνμ±ννλλ‘ κΆκ³ ν κ²½μ°κ° μμΌλ©°, μ΄λ¬ν μ€μ μ΄ λ¨μμλ μνμμ μ κ· μ ν리μΌμ΄μ μ΄ ν΄λΉ ν μ΄λΈμ μ κ·Όνλ©΄ μλ¬κ° λ°μν©λλ€. μΈνλΌ μ΄μ μ΄λ RAC λ Έλ μ¦μ€ ν κΈ°μ‘΄ μ€μ μ΄ κ·Έλλ‘ μ μ§λμ΄ λ¬Έμ κ° λΆκ±°μ§λ κ²½μ°λ μ€λ¬΄μμ μμ£Ό νμΈλ©λλ€.
ν΄κ²° λ°©λ²
μμΈ 1 ν΄κ²°: ν μ΄λΈ μ κΈ λΉνμ±ν μ¬λΆ νμΈ λ° μ¬νμ±ν
λ¨Όμ νμ¬ ν
μ΄λΈμ μ κΈ μνλ₯Ό νμΈν©λλ€. DBA_TABLES λλ USER_TABLES λ·°μ ROW_MOVEMENT 컬λΌκ³Ό ν¨κ» TABLE_LOCK κ΄λ ¨ μ 보λ₯Ό μ‘°νν μ μμ΅λλ€.
-- ν
μ΄λΈ μ κΈ μν νμΈ (DBA κΆν νμ)
SELECT owner,
table_name,
row_movement,
-- TABLE_LOCK λΉνμ±ν μ¬λΆλ μ§μ 컬λΌμ΄ μμΌλ―λ‘ μλ λ°©λ²μΌλ‘ νμΈ
status
FROM dba_tables
WHERE table_name = 'YOUR_TABLE_NAME'
AND owner = 'YOUR_SCHEMA';
-- v$locked_objectμ ν¨κ» νμ¬ μ κΈ νν© νμΈ
SELECT lo.oracle_username,
lo.os_user_name,
lo.session_id,
lo.locked_mode,
do.object_name,
do.object_type
FROM v$locked_object lo
JOIN dba_objects do ON lo.object_id = do.object_id
WHERE do.object_name = 'YOUR_TABLE_NAME';
ν
μ΄λΈ μ κΈ λΉνμ±ν μ¬λΆλ₯Ό νμΈνλ κ°μ₯ νμ€ν λ°©λ²μ μ€μ λ‘ μ κΈμ μλν΄ λ³΄κ±°λ, DBMS_METADATAλ‘ DDLμ μΆμΆνλ κ²μ
λλ€.
-- ν
μ΄λΈ DDL μΆμΆμ ν΅ν΄ DISABLE TABLE LOCK μ¬λΆ νμΈ
SELECT dbms_metadata.get_ddl('TABLE', 'YOUR_TABLE_NAME', 'YOUR_SCHEMA')
FROM dual;
ν
μ΄λΈ μ κΈμ΄ λΉνμ±νλμ΄ μμμ νμΈνλ€λ©΄, μλ λͺ
λ ΉμΌλ‘ μ¦μ μ¬νμ±νν©λλ€.
-- ν
μ΄λΈ μ κΈ μ¬νμ±ν (ν΅μ¬ ν΄κ²° λͺ
λ Ή)
ALTER TABLE your_schema.your_table_name ENABLE TABLE LOCK;
-- μ¬νμ±ν ν μ μ DML μμ
κ°λ₯ μ¬λΆ ν
μ€νΈ
LOCK TABLE your_schema.your_table_name IN ROW SHARE MODE;
-- μ μμ΄λΌλ©΄ μλ¬ μμ΄ μ κΈ νλλ¨
COMMIT;
μμΈ 2 ν΄κ²°: DDL μν μ μ κΈ μν νμΈ μ μ°¨ μΆκ°
λ°°μΉ μ€ν¬λ¦½νΈλ λ§μ΄κ·Έλ μ΄μ
μ€ν¬λ¦½νΈμμ TRUNCATE μ μ μ κΈ μνλ₯Ό νμΈνκ³ , λΉνμ±νλ κ²½μ° μλμΌλ‘ νμ±ννλ λ‘μ§μ μΆκ°ν©λλ€.
-- μ κΈ λΉνμ±ν μνμμ TRUNCATE μλ μ ORA-00069 λ°μ μμ
-- (μλλ μλ¬ μ¬ν λ° ν΄κ²° νλ¦)
-- Step 1: ν
μ΄λΈ μ κΈ λΉνμ±ν (λ¬Έμ μν© μ¬ν)
ALTER TABLE hr.employees DISABLE TABLE LOCK;
-- Step 2: μ΄ μνμμ TRUNCATE μλ β ORA-00069 λ°μ
-- TRUNCATE TABLE hr.employees; β μλ¬ λ°μ!
-- Step 3: μ κΈ μ¬νμ±ν ν TRUNCATE μ μ μν
ALTER TABLE hr.employees ENABLE TABLE LOCK;
TRUNCATE TABLE hr.employees;
-- λ°°μΉ νλ‘μμ μμ νμ© κ°λ₯ν μμ ν ν¨ν΄
CREATE OR REPLACE PROCEDURE safe_truncate(
p_owner IN VARCHAR2,
p_table_name IN VARCHAR2
) AS
v_sql VARCHAR2(500);
BEGIN
-- 1. ν
μ΄λΈ μ κΈ νμ±ν 보μ₯
v_sql := 'ALTER TABLE ' || p_owner || '.' || p_table_name || ' ENABLE TABLE LOCK';
EXECUTE IMMEDIATE v_sql;
-- 2. μμ νκ² TRUNCATE μν
v_sql := 'TRUNCATE TABLE ' || p_owner || '.' || p_table_name;
EXECUTE IMMEDIATE v_sql;
DBMS_OUTPUT.PUT_LINE(p_owner || '.' || p_table_name || ' truncated successfully.');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
RAISE;
END safe_truncate;
/
-- νλ‘μμ μ€ν
EXEC safe_truncate('HR', 'EMPLOYEES');
μμΈ 3 ν΄κ²°: RAC νκ²½μμ μλͺ» λΉνμ±νλ ν μ΄λΈ μΌκ΄ μ κ² λ° λ³΅κ΅¬
RAC νκ²½μμ μ¬λ¬ ν
μ΄λΈμ κ±Έμ³ μ κΈμ΄ λΉνμ±νλ κ²½μ°, μλ μ€ν¬λ¦½νΈλ‘ μΌκ΄ νμΈ λ° νμ±ν μ€ν¬λ¦½νΈλ₯Ό μμ±ν©λλ€.
-- DISABLE TABLE LOCK μνμΈ ν
μ΄λΈ λͺ©λ‘ λ° νμ±ν μ€ν¬λ¦½νΈ μμ±
-- (μ€μ λ‘ TABLE LOCK λΉνμ±νλ λμ
λ리μ νΉμ νλκ·Έλ‘ μ μ₯λ¨)
-- μλλ ν
μ€νΈ νκ²½μμ κ²μ¦λ νμΈ λ°©λ²
-- sys.obj$ κΈ°λ° νλκ·Έ νμΈ (κ³ κΈ λ°©λ², SYS κΆν νμ)
SELECT o.name AS table_name,
u.name AS owner,
o.flags,
-- flags λΉνΈ 32 = TABLE LOCK DISABLED
CASE WHEN BITAND(o.flags, 32) = 32
THEN 'TABLE LOCK DISABLED'
ELSE 'TABLE LOCK ENABLED'
END AS lock_status,
-- νμ±ν μ€ν¬λ¦½νΈ μλ μμ±
CASE WHEN BITAND(o.flags, 32) = 32
THEN 'ALTER TABLE ' || u.name || '.' || o.name || ' ENABLE TABLE LOCK;'
ELSE NULL
END AS fix_script
FROM sys.obj$ o
JOIN sys.user$ u ON o.owner# = u.user#
WHERE o.type# = 2 -- TABLE
AND BITAND(o.flags, 32) = 32 -- TABLE LOCK DISABLED 쑰건
ORDER BY u.name, o.name;
-- μμ±λ μ€ν¬λ¦½νΈλ₯Ό μ€ννμ¬ μΌκ΄ 볡ꡬ
-- μ 쿼리 κ²°κ³Όμ fix_script μ»¬λΌ κ°μ 볡μ¬νμ¬ μ€ννκ±°λ,
-- μλμ κ°μ΄ λμ μΌλ‘ μ²λ¦¬ κ°λ₯
BEGIN
FOR rec IN (
SELECT 'ALTER TABLE ' || u.name || '.' || o.name || ' ENABLE TABLE LOCK' AS fix_sql
FROM sys.obj$ o
JOIN sys.user$ u ON o.owner# = u.user#
WHERE o.type# = 2
AND BITAND(o.flags, 32) = 32
AND u.name NOT IN ('SYS', 'SYSTEM', 'DBSNMP', 'SYSMAN') -- μμ€ν
μ€ν€λ§ μ μΈ
) LOOP
DBMS_OUTPUT.PUT_LINE('Executing: ' || rec.fix_sql);
EXECUTE IMMEDIATE rec.fix_sql;
END LOOP;
DBMS_OUTPUT.PUT_LINE('All table locks re-enabled successfully.');
END;
/
μλ°© λ°©λ²
1. ν μ΄λΈ μ κΈ λ³κ²½ μμ μ λν κΆν ν΅μ λ° λ³κ²½ κ΄λ¦¬ νλ‘μΈμ€ μ립
ALTER TABLE ... DISABLE TABLE LOCK λͺ
λ Ήμ μ΄μ νκ²½μμ λ§€μ° μ μ€νκ² μ¬μ©ν΄μΌ νλ©°, κ°κΈμ DBA μ μ© κΆνμΌλ‘ μ νν΄μΌ ν©λλ€. DDL λ³κ²½ μ΄λ ₯μ μΆμ νκΈ° μν΄ DDL νΈλ¦¬κ±°λ₯Ό νμ©νμ¬ ν
μ΄λΈ μ κΈ λ³κ²½ μ΄λ²€νΈλ₯Ό λ‘κΉ
νλ 체κ³λ₯Ό κ°μΆλ©΄ μ₯μ λ°μ μ μμΈ μΆμ μ΄ ν¨μ¬ μμν΄μ§λλ€.
-- DDL λ³κ²½ κ°μ¬λ₯Ό μν λ‘κ·Έ ν
μ΄λΈ λ° νΈλ¦¬κ±° ꡬμ±
CREATE TABLE dba_ddl_audit_log (
log_id NUMBER GENERATED ALWAYS AS IDENTITY,
event_date TIMESTAMP DEFAULT SYSTIMESTAMP,
db_user VARCHAR2(100),
os_user VARCHAR2(100),
machine VARCHAR2(100),
object_type VARCHAR2(50),
object_name VARCHAR2(128),
ddl_text CLOB,
CONSTRAINT pk_ddl_audit PRIMARY KEY (log_id)
);
-- DDL μ΄λ²€νΈ νΈλ¦¬κ±° (μμ€ν
λ 벨 λλ μ€ν€λ§ λ 벨)
CREATE OR REPLACE TRIGGER trg_ddl_audit
AFTER DDL ON DATABASE
BEGIN
-- TABLE LOCK κ΄λ ¨ ALTER TABLE κ°μ§
IF ora_dict_obj_type = 'TABLE' THEN
INSERT INTO dba_ddl_audit_log (
db_user, os_user, machine, object_type, object_name
)
SELECT
sys_context('USERENV', 'SESSION_USER'),
sys_context('USERENV', 'OS_USER'),
sys_context('USERENV', 'HOST'),
ora_dict_obj_type,
ora_dict_obj_name
FROM dual;
COMMIT;
END IF;
END;
/
2. μ κΈ°μ μΈ ν μ΄λΈ μ κΈ μν μ κ² μ€ν¬λ¦½νΈλ₯Ό λͺ¨λν°λ§ 체κ³μ νΈμ
μ΄μ νκ²½μμ μ£ΌκΈ°μ (μΌλ³ λλ μ£Όλ³)μΌλ‘ ν μ΄λΈ μ κΈ λΉνμ±ν μ¬λΆλ₯Ό μ κ²νλ 쿼리λ₯Ό μ€μΌμ€λ§νμ¬, λΉμ μ μνκ° κ°μ§λλ©΄ μ¦μ DBAμκ² μλ¦Όμ΄ μ€λλ‘ κ΅¬μ±ν©λλ€. Oracle Enterprise Manager(OEM)μ 컀μ€ν λ©νΈλ¦ κΈ°λ₯μ΄λ μΈλΆ λͺ¨λν°λ§ λꡬ(Zabbix, Grafana λ±)μ μ°λνλ©΄ ν¨κ³Όμ μ λλ€.
sql
-- μ£ΌκΈ°μ μ κ²μ© μ€ν¬λ¦½νΈ (cron λλ DBMS_SCHEDULERλ‘ μ€ν)
-- TABLE LOCKμ΄ λΉνμ±νλ ν
μ΄λΈμ΄ μμΌλ©΄ κ²°κ³Όλ₯Ό λ°ν












