ORA-00060λ?
ORA-00060μ Oracle λ°μ΄ν°λ² μ΄μ€μμ λ κ° μ΄μμ μΈμ μ΄ μλ‘ μλλ°©μ΄ λ³΄μ ν 리μμ€(μ£Όλ‘ ν μ΄λΈ ν μ κΈ)λ₯Ό κΈ°λ€λ¦¬λ κ΅μ°© μν(Deadlock)κ° λ°μνμ λ λνλλ μλ¬μ λλ€. μλ₯Ό λ€μ΄ μΈμ Aκ° ν1μ μ κ·Έκ³ ν2λ₯Ό κΈ°λ€λ¦¬λ λμ, μΈμ Bλ ν2λ₯Ό μ κ·Ό μ± ν1μ κΈ°λ€λ¦¬λ μν©μ΄ μ νμ μΈ μ¬λ‘μ λλ€. Oracleμ μ΄ μνλ₯Ό μλμΌλ‘ κ°μ§νμ¬ λ μΈμ μ€ νλλ₯Ό ν¬μμ(Victim)λ‘ μ ννκ³ ν΄λΉ νΈλμμ μ λ‘€λ°±νλ©΄μ μ΄ μλ¬λ₯Ό λ°μμν΅λλ€. μ€μν μ μ Oracleμ΄ μ 체 νΈλμμ μ΄ μλ λ§μ§λ§ DML λ¬Έμ₯ νλλ§ λ‘€λ°±νλ€λ μ¬μ€μ΄λ©°, λ°λΌμ μ ν리μΌμ΄μ λ 벨μμ λ°λμ μλ¬λ₯Ό μ²λ¦¬νκ³ μ μ ν λμν΄μΌ ν©λλ€.
μ£Όμ λ°μ μμΈ
1. μλ‘ λ€λ₯Έ μμλ‘ λμΌν 리μμ€μ μ κ·Όνλ νΈλμμ
κ°μ₯ λΉλ²ν μμΈμΌλ‘, μ¬λ¬ νΈλμμ
μ΄ λμΌν ν
μ΄λΈμ νλ€μ μλ‘ λ€λ₯Έ μμλ‘ μ κΈ(Lock)μ νλν λ λ°μν©λλ€. μλ₯Ό λ€μ΄ μ£Όλ¬Έ μ²λ¦¬ λ‘μ§μμ νλμ νλ‘μΈμ€λ ORDER β ORDER_ITEM μμλ‘, λ€λ₯Έ νλ‘μΈμ€λ ORDER_ITEM β ORDER μμλ‘ μ
λ°μ΄νΈλ₯Ό μλνλ©΄ κ΅μ°© μνκ° μ¦μ λ°μν©λλ€. μ΄λ μ ν리μΌμ΄μ
μ€κ³ λ¨κ³μμ μ κΈ νλ μμλ₯Ό ν΅μΌνμ§ μμ κ²½μ°μ λΉλ²ν λνλ©λλ€.
2. μΈλ±μ€ λΆμ¬λ‘ μΈν λΆνμν μ 체 ν μ΄λΈ μ κΈ
μΈλ ν€(Foreign Key) 컬λΌμ μΈλ±μ€κ° μμ κ²½μ°, λΆλͺ¨ ν μ΄λΈμ νμ μ λ°μ΄νΈνκ±°λ μμ ν λ Oracleμ μμ ν μ΄λΈ μ 체μ ν μ΄λΈ λ 벨 μ κΈ(Table-Level Lock)μ κ±Έκ² λ©λλ€. μ΄ μν©μμ λ€λ₯Έ μΈμ μ΄ μμ ν μ΄λΈμ DMLμ μλνλ©΄ μ κΈ μΆ©λμ΄ λ°μνκ³ κ΅μ°© μνλ‘ μ΄μ΄μ§λλ€. νΉν λμ©λ OLTP νκ²½μμ μ΄ μμΈμ μ±λ₯ μ νμ ν¨κ» λ°λλ½μ νλ°μ μΌλ‘ μ¦κ°μν€λ μ£Όλ²μ λλ€.
3. λΉνΈλ§΅ μΈλ±μ€(Bitmap Index)κ° μλ ν μ΄λΈμ λν λμ DML
λΉνΈλ§΅ μΈλ±μ€λ νλμ μΈλ±μ€ μνΈλ¦¬κ° μ¬λ¬ νμ μ 보λ₯Ό λ΄κΈ° λλ¬Έμ, ν νμ μ λ°μ΄νΈνλ©΄ λμΌν λΉνΈλ§΅ μΈκ·Έλ¨ΌνΈλ₯Ό 곡μ νλ λ€λ₯Έ νλ€λ μ κΈ λ²μμ ν¬ν¨λ©λλ€. μ¬λ¬ μΈμ μ΄ λμμ λΉνΈλ§΅ μΈλ±μ€κ° μ€μ λ 컬λΌμ κ°μ μ λ°μ΄νΈνλ©΄ μ κΈ λ²μκ° κ²ΉμΉλ©΄μ κ΅μ°© μνκ° λ§€μ° μ½κ² λ°μν©λλ€. μ΄ μμΈμ DW(Data Warehouse) νκ²½μμ λ°μ΄ν° λ‘λ© μ μ’ μ’ λνλ©λλ€.
ν΄κ²° λ°©λ²
μμΈ 1 ν΄κ²°: νΈλμμ μ κΈ μμ ν΅μΌ
λ°λλ½μ΄ λ°μν μΈμ
μ λ¨Όμ V$LOCKED_OBJECTμ νΈλ μ΄μ€ νμΌλ‘ νμΈν©λλ€.
-- νμ¬ μ κΈ μν© λ° λΈλ‘νΉ μΈμ
νμΈ
SELECT
l.session_id,
s.serial#,
s.username,
s.program,
l.oracle_username,
l.os_user_name,
o.object_name,
o.object_type,
DECODE(l.locked_mode,
0, 'None',
1, 'Null',
2, 'Row-S (SS)',
3, 'Row-X (SX)',
4, 'Share',
5, 'S/Row-X (SSX)',
6, 'Exclusive', 'Unknown') AS lock_mode
FROM
v$locked_object l
JOIN dba_objects o ON l.object_id = o.object_id
JOIN v$session s ON l.session_id = s.sid
ORDER BY
l.session_id;
-- λ°λλ½ νΈλ μ΄μ€ νμΌ μμΉ νμΈ
SELECT value FROM v$diag_info WHERE name = 'Default Trace File';
-- νΉμ μλ¦Ό λ‘κ·Έμμ λ°λλ½ λ°μ μκ° νμΈ
SELECT originating_timestamp, message_text
FROM v$diag_alert_ext
WHERE message_text LIKE '%deadlock%'
ORDER BY originating_timestamp DESC
FETCH FIRST 20 ROWS ONLY;
μ κΈ μμλ₯Ό ν΅μΌνλ μ€λ¬΄ ν¨ν΄ μμμ
λλ€. λ°λμ μμ PRIMARY KEY μμλ‘ μ λ ¬νμ¬ μ
λ°μ΄νΈν©λλ€.
-- λμ μ: μμκ° λ³΄μ₯λμ§ μμ λ°λλ½ λ°μ κ°λ₯
-- μΈμ
A
UPDATE orders SET status = 'PROCESSING' WHERE order_id = 1001;
UPDATE order_items SET qty = qty - 1 WHERE item_id = 5001;
-- μΈμ
B (μμ μ κ·Ό β λ°λλ½ μν)
UPDATE order_items SET qty = qty - 1 WHERE item_id = 5001;
UPDATE orders SET status = 'PROCESSING' WHERE order_id = 1001;
-- μ’μ μ: νμ λμΌν μμ(orders β order_items)λ‘ μ κ·Ό
-- μΈμ
A
BEGIN
-- 1λ¨κ³: νμ λΆλͺ¨ ν
μ΄λΈ λ¨Όμ
UPDATE orders
SET status = 'PROCESSING'
WHERE order_id = 1001;
-- 2λ¨κ³: μμ ν
μ΄λΈμ νμ λμ€μ, PK μ€λ¦μ°¨μ μ λ ¬
UPDATE order_items
SET qty = qty - 1
WHERE order_id = 1001
ORDER BY item_id; -- μμ 보μ₯
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
/
μμΈ 2 ν΄κ²°: μΈλ ν€ μΈλ±μ€ μμ±
-- μΈλ ν€κ° μμΌλ μΈλ±μ€κ° μλ μ»¬λΌ μ°ΎκΈ° (μ€λ¬΄ νμ 쿼리)
SELECT
c.table_name,
c.constraint_name,
c.column_name,
c.position
FROM
user_cons_columns c
JOIN user_constraints uc
ON c.constraint_name = uc.constraint_name
AND uc.constraint_type = 'R' -- R = Foreign Key
WHERE
NOT EXISTS (
SELECT 1
FROM user_ind_columns ic
WHERE ic.table_name = c.table_name
AND ic.column_name = c.column_name
)
ORDER BY
c.table_name, c.position;
-- λ°κ²¬λ FK 컬λΌμ μΈλ±μ€ μμ±
-- μμ: ORDER_ITEMS ν
μ΄λΈμ ORDER_ID(FK) 컬λΌ
CREATE INDEX idx_order_items_order_id
ON order_items (order_id)
TABLESPACE users
PARALLEL 4
NOLOGGING;
-- μΈλ±μ€ μμ± ν ν΅κ³ μμ§
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => USER,
tabname => 'ORDER_ITEMS',
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
cascade => TRUE
);
END;
/
μμΈ 3 ν΄κ²°: λΉνΈλ§΅ μΈλ±μ€ νκ²½μμμ λμμ± μ μ΄
-- OLTP νκ²½μ΄λΌλ©΄ λΉνΈλ§΅ μΈλ±μ€λ₯Ό B-Tree μΈλ±μ€λ‘ κ΅μ²΄
-- κΈ°μ‘΄ λΉνΈλ§΅ μΈλ±μ€ νμΈ
SELECT index_name, index_type, table_name
FROM user_indexes
WHERE index_type = 'BITMAP';
-- λΉνΈλ§΅ μΈλ±μ€ μ κ±° ν B-Tree μΈλ±μ€λ‘ λ체
DROP INDEX bitmap_idx_status;
CREATE INDEX btree_idx_status
ON orders (status)
TABLESPACE users;
-- λμ©λ λ°°μΉ λ‘λ© μ λΉνΈλ§΅ μΈλ±μ€κ° νμν κ²½μ°:
-- λ‘λ© μ μΈλ±μ€ λΉνμ±ν β λ‘λ© β μΈλ±μ€ μ¬μμ± ν¨ν΄ μ¬μ©
ALTER INDEX bitmap_idx_status UNUSABLE;
-- λ°μ΄ν° λ‘λ© μν
INSERT /*+ APPEND */ INTO orders SELECT * FROM orders_stg;
COMMIT;
-- μΈλ±μ€ μ¬μμ±
ALTER INDEX bitmap_idx_status REBUILD PARALLEL 8 NOLOGGING;
ALTER INDEX bitmap_idx_status NOPARALLEL;
ALTER INDEX bitmap_idx_status LOGGING;
λ°λλ½ λ°μ μ μ ν리μΌμ΄μ μ¬μλ λ‘μ§ (PL/SQL)
-- μ€λ¬΄μμ λ°λ‘ νμ© κ°λ₯ν λ°λλ½ μ¬μλ νλ‘μμ
CREATE OR REPLACE PROCEDURE process_order_with_retry(
p_order_id IN orders.order_id%TYPE,
p_max_retry IN INTEGER DEFAULT 3
)
AS
v_retry_count INTEGER := 0;
deadlock_detected EXCEPTION;
PRAGMA EXCEPTION_INIT(deadlock_detected, -60); -- ORA-00060
BEGIN
LOOP
BEGIN
-- μ€μ λΉμ¦λμ€ λ‘μ§
UPDATE orders
SET status = 'PROCESSING',
updated_dt = SYSDATE
WHERE order_id = p_order_id;
UPDATE order_items
SET status = 'PROCESSING'
WHERE order_id = p_order_id;
COMMIT;
EXIT; -- μ±κ³΅ μ 루ν νμΆ
EXCEPTION
WHEN deadlock_detected THEN
ROLLBACK;
v_retry_count := v_retry_count + 1;
IF v_retry_count >= p_max_retry THEN
-- μ΅λ μ¬μλ νμ μ΄κ³Ό μ λ‘κ·Έ κΈ°λ‘ ν μμΈ μ ν
INSERT INTO error_log (
error_code, error_msg,
session_id, occurred_dt
) VALUES (
-60, 'Deadlock exceeded max retry: ' || p_order_id,
SYS_CONTEXT('USERENV','SID'), SYSTIMESTAMP
);
COMMIT;
RAISE;
END IF;
-- μ μ λκΈ° ν μ¬μλ (μ§μ λ°±μ€ν μ μ©)
DBMS_LOCK.SLEEP(v_retry_count * 0.5);
END;
END LOOP;
END process_order_with_retry;
/
μλ°© λ°©λ²
1. μΌκ΄λ μ€λΈμ νΈ μ κ·Ό μμ νμ€ν λ° μ½λ 리뷰 μ무ν
λͺ¨λ κ°λ°μκ° λμΌν ν μ΄λΈ μ κ·Ό μμλ₯Ό λ°λ₯΄λλ‘ κ°λ° νμ€ λ¬Έμμ λͺ μν΄μΌ ν©λλ€. ꡬ체μ μΌλ‘λ "νμ λΆλͺ¨ ν μ΄λΈ β μμ ν μ΄λΈ μμλ‘ DMLμ μννκ³ , λμΌ ν μ΄λΈ λ΄ λ€μ€ ν μ λ°μ΄νΈ μμλ λ°λμ PK μ€λ¦μ°¨μμΌλ‘ μ λ ¬νλ€"λ κ·μΉμ μ 립ν©λλ€. μ½λ 리뷰 λ¨κ³μμ μ΄ μμλ₯Ό μλ°νλ νΈλμμ μ μ¬μ μ μ°¨λ¨νκ³ , CI/CD νμ΄νλΌμΈμ μ μ λΆμ λꡬλ₯Ό ν΅ν©νμ¬ μλ κ²μ¬νλ κ²μ΄ μ΄μμ μ λλ€. λν μ κ· κΈ°λ₯ κ°λ° μ λ°μ΄ν° λͺ¨λΈ 리뷰 λ¨κ³μμ DBAκ° μ κΈ κ²½ν© κ°λ₯μ±μ κ²ν νλ κ²μ΄ν νλ‘μΈμ€λ₯Ό λμ νλ©΄ μ΄μ νκ²½μμμ λ°λλ½ λ°μμ ν¬κ² μ€μΌ μ μμ΅λλ€.
2. μ£ΌκΈ°μ μΈ FK μΈλ±μ€ μ κ² λ° AWR/νΈλ μ΄μ€ λͺ¨λν°λ§ μλν
λ°μ΄ν°λ² μ΄μ€ μ΄μ μ€ μλ‘μ΄ ν
μ΄λΈκ³Ό FK μ μ½μ‘°κ±΄μ΄ μΆκ°λ λλ§λ€ μΈλ±μ€ λλ½ μ¬λΆλ₯Ό μλμΌλ‘ μ κ²νλ μ€ν¬λ¦½νΈλ₯Ό μ€μΌμ€λ¬(DBMS_SCHEDULER)μ λ±λ‘ν΄ λμ΄μΌ ν©λλ€. AWR(Automatic Workload Repository) 리ν¬νΈμμ enq: TX - row lock contention λκΈ° μ΄λ²€νΈκ° κΈμ¦νλ κ²½μ° λ°λλ½ μ μ‘°μΌ μ μμΌλ―λ‘, μκ³κ° μ΄κ³Ό μ μλ¦Όμ λ°μ‘νλ λͺ¨λν°λ§ 체κ³λ₯Ό ꡬμΆνλ κ²μ΄ μ€μν©λλ€. Oracle Alert Logμ νΈλ μ΄μ€ νμΌμ μλμΌλ‘ νμ±νμ¬ ORA-00060 λ°μ λΉλλ₯Ό μΆμ νκ³ , λ°μ μΆμΈκ° μ¦κ°ν λ μ¦μ λΆμμ μ°©μν μ μλ μ΄μ












