Oracle鎖表的解決方法及避免鎖表問題的最佳實踐
背景介紹
在 Oracle 數(shù)據(jù)庫中,鎖表或鎖超時相信大家都不陌生,是一個常見的問題,尤其是在執(zhí)行 DML(數(shù)據(jù)操作語言)語句時。當一個會話對表或行進行鎖定但未提交事務時,其他會話可能會因為等待鎖資源而出現(xiàn)超時。這種情況不僅會影響數(shù)據(jù)庫性能,還可能導致應用程序異常(java.sql.SQLException: Lock wait timeout exceeded)。
本文將詳細介紹如何解決鎖表問題以及如何查找引起鎖表的 SQL 語句,并提供避免鎖表問題的最佳實踐。
鎖表的原因
- 獨占式封鎖機制:Oracle 使用獨占式封鎖機制來確保數(shù)據(jù)的一致性。當一個會話對數(shù)據(jù)進行修改時,會對其加鎖,直到事務提交或回滾。
- 長時間運行的 SQL 語句:某些 SQL 語句可能由于性能問題或其他原因而長時間運行,導致鎖資源一直被占用。
- 高并發(fā)場景:在高并發(fā)環(huán)境下,多個會話同時訪問相同的數(shù)據(jù),可能會導致鎖競爭,從而引發(fā)死鎖。
解決鎖表的方法
臨時解決方案
- 找出鎖資源競爭的會話
SELECT L.SESSION_ID, S.SERIAL#, L.LOCKED_MODE AS "鎖模式", L.ORACLE_USERNAME AS "所有者", L.OS_USER_NAME AS "登錄系統(tǒng)用戶名", S.MACHINE AS "系統(tǒng)名", S.TERMINAL AS "終端用戶名", O.OBJECT_NAME AS "被鎖表對象名", S.LOGON_TIME AS "登錄數(shù)據(jù)庫時間" FROM V$LOCKED_OBJECT L INNER JOIN ALL_OBJECTS O ON O.OBJECT_ID = L.OBJECT_ID INNER JOIN V$SESSION S ON S.SID = L.SESSION_ID;
- sql強制結束會話
ALTER SYSTEM KILL SESSION 'SESSION_ID, SERIAL#';
示例
假設 session1
修改了某條數(shù)據(jù)但未提交事務,session2
查詢未提交事務的那條記錄時會被阻塞。
- 查詢未提交事務的會話信息
SELECT L.SESSION_ID, S.SERIAL#, L.LOCKED_MODE AS "鎖模式", L.ORACLE_USERNAME AS "所有者", L.OS_USER_NAME AS "登錄系統(tǒng)用戶名", S.MACHINE AS "系統(tǒng)名", S.TERMINAL AS "終端用戶名", O.OBJECT_NAME AS "被鎖表對象名", S.LOGON_TIME AS "登錄數(shù)據(jù)庫時間" FROM V$LOCKED_OBJECT L INNER JOIN ALL_OBJECTS O ON O.OBJECT_ID = L.OBJECT_ID INNER JOIN V$SESSION S ON S.SID = L.SESSION_ID; SESSION_ID SERIAL# 鎖模式 所有者 登錄系統(tǒng)用戶名 系統(tǒng)名 終端用戶名 被鎖表對象名 登錄數(shù)據(jù)庫時間 ---------- ------- ----- ------ ------------- ----- --------- --------- ------------ 29 84 3 IN test WORKGROUP\LA... LAPTOP-9FDC2903 LIN_USER 2023/2/26 11:08:08
- 強制結束
session1
ALTER SYSTEM KILL SESSION '29, 84';
- 驗證
session2
的執(zhí)行情況
- 強制結束
session1
后,session2
的等待會立即終止并執(zhí)行。
- 強制結束
查找被鎖對象
- 查詢被鎖對象數(shù)目
SELECT COUNT(1) FROM V$LOCKED_OBJECT;
- 查詢被鎖對象
SELECT B.OWNER, B.OBJECT_NAME, A.SESSION_ID, A.LOCKED_MODE FROM V$LOCKED_OBJECT A, DBA_OBJECTS B WHERE B.OBJECT_ID = A.OBJECT_ID;
- 查詢被鎖對象的連接
SELECT T2.USERNAME, T2.SID, T2.SERIAL, T2.LOGON_TIME FROM V$LOCKED_OBJECT T1, V$SESSION T2 WHERE T1.SESSION_ID = T2.SID ORDER BY T2.LOGON_TIME;
- 關閉被鎖對象連接
ALTER SYSTEM KILL SESSION '253, 9542';
查看當前系統(tǒng)中鎖表情況
- 查詢所有被鎖對象
SELECT * FROM V$LOCKED_OBJECT;
- 查詢詳細的鎖表情況
SELECT SESS.SID, SESS.SERIAL#, LO.ORACLE_USERNAME, LO.OS_USER_NAME, AO.OBJECT_NAME, LO.LOCKED_MODE FROM V$LOCKED_OBJECT LO, DBA_OBJECTS AO, V$SESSION SESS, V$PROCESS P WHERE AO.OBJECT_ID = LO.OBJECT_ID AND LO.SESSION_ID = SESS.SID;
查找引起鎖表的 SQL 語句
- 查詢引起鎖表的 SQL 語句
SELECT L.SESSION_ID SID, S.SERIAL#, L.LOCKED_MODE, L.ORACLE_USERNAME, S.USER#, L.OS_USER_NAME, S.MACHINE, S.TERMINAL, A.SQL_TEXT, A.ACTION FROM V$SQLAREA A, V$SESSION S, V$LOCKED_OBJECT L WHERE L.SESSION_ID = S.SID AND S.PREV_SQL_ADDR = A.ADDRESS ORDER BY SID, S.SERIAL#;
- 查看所有被阻塞的會話
SET LINE 200; COL TERMINAL FORMAT A10; COL PROGRAM FORMAT A20; COL USERNAME FORMAT A10; COL MACHINE FORMAT A10; COL SQL_TEXT FORMAT A40; SELECT A.SID, A.SERIAL#, A.USERNAME, A.COMMAND, A.LOCKWAIT, A.STATUS, A.MACHINE, A.TERMINAL, A.PROGRAM, A.SECONDS_IN_WAIT, B.SQL_TEXT FROM V$SESSION A, V$SQL B WHERE B.SQL_ID = A.SQL_ID AND (A.BLOCKING_INSTANCE IS NOT NULL AND A.BLOCKING_SESSION IS NOT NULL);
- 展示阻塞的樹形結構
WITH lk AS ( SELECT BLOCKING_INSTANCE || '.' || BLOCKING_SESSION AS blocker, INST_ID || '.' || SID AS waiter FROM GV$SESSION WHERE BLOCKING_INSTANCE IS NOT NULL AND BLOCKING_SESSION IS NOT NULL ) SELECT LPAD(' ', 2 * (LEVEL - 1)) || WAITER LOCK_TREE FROM ( SELECT * FROM lk UNION ALL SELECT DISTINCT 'root', BLOCKER FROM lk WHERE BLOCKER NOT IN (SELECT WAITER FROM lk) ) CONNECT BY PRIOR WAITER = BLOCKER START WITH BLOCKER = 'root';
- 展示阻塞的樹形結構,并輸出阻塞語句、被阻塞語句,并給出殺會話語句
WITH lk AS ( SELECT A.BLOCKING_INSTANCE || '.' || A.BLOCKING_SESSION AS blocker, A.INST_ID || '.' || A.SID AS waiter, (SELECT B.SQL_TEXT || ' ALTER SYSTEM KILL SESSION ''' || C.SID || ', ' || C.SERIAL# || '''' FROM GV$SQLAREA B, GV$SESSION C WHERE A.BLOCKING_INSTANCE = C.INST_ID AND C.SID = A.BLOCKING_SESSION AND (C.SQL_ID = B.SQL_ID OR C.PREV_SQL_ID = B.SQL_ID)) AS kill_block_sql, (SELECT B.SQL_TEXT || ' ALTER SYSTEM KILL SESSION ''' || A.SID || ', ' || A.SERIAL# || '''' FROM GV$SQLAREA B WHERE A.INST_ID = B.INST_ID AND A.SQL_ID = B.SQL_ID) AS kill_waiter_sql FROM GV$SESSION A WHERE A.BLOCKING_INSTANCE IS NOT NULL AND A.BLOCKING_SESSION IS NOT NULL ) SELECT LPAD(' ', 2 * (LEVEL - 1)) || WAITER || ' ' || KILL_WAITER_SQL LOCK_TREE FROM ( SELECT BLOCKER, WAITER, KILL_WAITER_SQL FROM lk UNION ALL SELECT DISTINCT 'root', BLOCKER, KILL_BLOCK_SQL FROM lk WHERE BLOCKER NOT IN (SELECT WAITER FROM lk) ) CONNECT BY PRIOR WAITER = BLOCKER START WITH BLOCKER = 'root';
- 直接顯示阻塞關系
COL BLOCK_MSG FOR A80 SELECT C.TERMINAL || ' (''' || A.SID || ',' || C.SERIAL# || ''') is blocking ' || B.SID BLOCK_MSG FROM V$LOCK A, V$LOCK B, V$SESSION C WHERE A.ID1 = B.ID1 AND A.ID2 = B.ID2 AND A.BLOCK > 0 AND A.SID <> B.SID AND A.SID = C.SID;
避免鎖表問題的最佳實踐
1. 優(yōu)化 SQL 語句
- 減少鎖定范圍:盡量使用行級鎖而不是表級鎖。例如,使用
SELECT ... FOR UPDATE
時,只鎖定需要更新的行。 - 避免長時間運行的事務:確保事務盡可能短,盡快提交或回滾事務,減少鎖的持有時間。
- 批量處理:對于大量數(shù)據(jù)的操作,考慮分批處理,以減少單個事務的持續(xù)時間和鎖的持有時間。
2. 使用合適的隔離級別
- 調整隔離級別:根據(jù)應用需求選擇合適的隔離級別。例如,使用
READ COMMITTED
而不是SERIALIZABLE
,以減少鎖的競爭。 - 避免不必要的鎖:在某些情況下,可以使用
NOLOCK
提示來避免讀取操作時的鎖,但這可能會導致臟讀。
3. 優(yōu)化索引
- 創(chuàng)建適當?shù)乃饕?/strong>:確保經(jīng)常查詢的列上有適當?shù)乃饕詼p少全表掃描和鎖的競爭。
- 維護索引:定期重建和重組索引,以保持其效率。
4. 使用分區(qū)表
- 分區(qū)表:對于大型表,可以使用分區(qū)技術來減少鎖的競爭。分區(qū)表可以將數(shù)據(jù)分成多個部分,每個部分可以獨立地進行操作,從而減少鎖的影響。
5. 優(yōu)化應用程序邏輯
- 減少并發(fā)沖突:設計應用程序邏輯時,盡量減少對同一數(shù)據(jù)的并發(fā)訪問。例如,通過使用隊列或其他機制來序列化對共享資源的訪問。
- 使用樂觀鎖:對于一些非關鍵性操作,可以使用樂觀鎖(如版本號控制)來替代悲觀鎖,減少鎖的競爭。
6. 監(jiān)控和調優(yōu)
- 監(jiān)控鎖情況:定期監(jiān)控數(shù)據(jù)庫中的鎖情況,使用
V$LOCKED_OBJECT
、V$SESSION
和V$SQLAREA
等視圖來識別潛在的鎖問題。 - 設置超時:為會話設置合理的鎖等待超時時間,防止某個會話長時間占用鎖資源??梢酝ㄟ^
ALTER SYSTEM SET LOCK_TIMEOUT = <seconds>
來設置。
7. 使用數(shù)據(jù)庫特性
- 閃回技術:利用 Oracle 的閃回技術(如 Flashback Query)來恢復數(shù)據(jù),而不是依賴于復雜的事務回滾。
- 在線重定義:使用在線重定義(Online Redefinition)來修改表結構,而不影響現(xiàn)有事務。
8. 事務管理
- 最小化事務大小:盡量將大事務拆分為多個小事務,以減少鎖的持有時間。
- 使用保存點:在長事務中使用保存點(SAVEPOINT),以便在發(fā)生錯誤時可以回滾到特定點,而不是整個事務。
9. 數(shù)據(jù)庫配置
- 調整參數(shù):根據(jù)實際情況調整數(shù)據(jù)庫參數(shù),如
UNDO_RETENTION
、DB_FILE_MULTIBLOCK_READ_COUNT
等,以優(yōu)化數(shù)據(jù)庫性能。 - 使用并行處理:對于大規(guī)模數(shù)據(jù)操作,可以考慮使用并行處理來提高性能和減少鎖的競爭。
10. 定期維護
- 定期分析和優(yōu)化:定期分析數(shù)據(jù)庫性能,找出瓶頸并進行優(yōu)化。
- 清理無用數(shù)據(jù):定期清理不再需要的數(shù)據(jù),減少表的大小,從而減少鎖的競爭。
總結
通過上述步驟,可以有效地解決 Oracle 數(shù)據(jù)庫中的鎖表問題,并找到引起鎖表的 SQL 語句。同時,通過實施最佳實踐,可以顯著減少鎖表問題的發(fā)生,提高系統(tǒng)的并發(fā)性能和穩(wěn)定性。
以上就是Oracle鎖表的解決方法及避免鎖表問題的最佳實踐的詳細內容,更多關于Oracle鎖表的解決及避免的資料請關注腳本之家其它相關文章!
相關文章
oracle 11g數(shù)據(jù)庫安全加固注意事項
這篇文章主要介紹了oracle11g數(shù)據(jù)庫安全加固須謹慎 ,需要的朋友可以參考下2015-08-08Oracle rac環(huán)境的數(shù)據(jù)庫導入操作步驟
Oracle RAC是一種基于共享存儲和共享數(shù)據(jù)庫的集群解決方案,可以將多個 Oracle 數(shù)據(jù)庫實例連接成一個邏輯上的單一數(shù)據(jù)庫,提供高可用性、靈活性和可伸縮性,本文給大家介紹Oracle rac環(huán)境的數(shù)據(jù)庫導入操作,感興趣的朋友一起看看吧2023-06-06Oracle通過procedure調用webservice接口的全過程
存儲過程是一組為了完成特定功能的sql語句集合,經(jīng)過編譯后存儲在數(shù)據(jù)庫中,用戶通過制定存儲過程的名字并給出參數(shù)(如果該過程帶有參數(shù))來執(zhí)行他,本文介紹了Oracle通過procedure調用webservice接口的全過程,需要的朋友可以參考下2024-07-07Oracle監(jiān)控數(shù)據(jù)庫性能的方法步驟
監(jiān)控數(shù)據(jù)庫性能是確保數(shù)據(jù)庫系統(tǒng)高效運行并快速響應用戶請求的關鍵步驟,有效的數(shù)據(jù)庫性能監(jiān)控可以幫助識別和解決性能瓶頸,預測潛在問題,并優(yōu)化資源使用,以下是詳細的步驟和代碼示例,指導你如何監(jiān)控數(shù)據(jù)庫性能,需要的朋友可以參考下2024-08-08Oracle(90)數(shù)據(jù)庫如何創(chuàng)建用戶(User)
這篇文章主要介紹了在Oracle數(shù)據(jù)庫中創(chuàng)建用戶的過程,包括連接到數(shù)據(jù)庫、創(chuàng)建用戶、分配權限、分配表空間和設置賬戶狀態(tài),提供了詳細的步驟和代碼示例,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-12-12