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