拨云见日—深入解析Oracle TX 行锁(上)
在剛剛過去不久的第七屆數據技術嘉年華上,性能優化專家懷曉明老師進行了Oracle性能優化的主題分享。在他多年的優化生涯中,一直遵守的優化理念是,平衡是唯一的核心。我們整理了懷老師大會的演講內容,今天一起來學習,如何在實踐中應用這一理念并實現有效的性能優化。
演講實錄
優化的核心思想是平衡。在數據庫的運行中,平衡取決于三個方面:
需求:指的是要做什么;
資源:是系統中所能提供的內容;
實現:指的是為了滿足需求,應該如何利用提供的資源。
只有三者達到平衡,系統才能夠高效地運行。
今天的內容將會通過Oracle 中一個很具體的等待事件 TX行鎖來剖析數據庫的平衡。
什么是TX行鎖?
等待事件: enq: TX - row lock contention
enq代表的意思是enqueue,事實上代表的是入隊這一個動作。
contention指的是爭用,所以一般意義上的行鎖,其實指的是行鎖爭用。
不管是在Oracle數據庫還是其他關系型數據庫,在修改一條記錄的時候一定會產生行鎖。其目的是為了保證數據的一致性,如果行鎖長久不能得到釋放,當其他進程想要使用的時候,就會產生爭用。這種情況一般發生在先行的會話事務沒有結束的時候。
TX行鎖發生的常見場景:
1、當前會話要更新或刪除的記錄,已經被其他會話更新或刪除。
2、對于表上有唯一索引的情況,多個會話插入或更新為相同的鍵值。
3、對于表上有位圖索引的情況,多個會話即使更新不同記錄,只要這些記錄在位圖索引上的鍵值相同,也會產生行鎖。
一般我們可能認為在發生行鎖的時候,幾個SQL的語句是一樣的,事實上這種理解是錯誤的。我舉一個簡單的例子:
首先在表上找到job為manager的記錄,有三條:
select empno from emp where job='MANAGER';--顯示7566/7698/ 7782 三條記錄。
之后在會話1 將部門ID為10的員工的記錄刪除掉
sess1:delete from emp where deptno=10;
--?7782/7839/ 7934 三條記錄被刪除,但并未提交。其中7782的記錄剛好是job為manager的。
接下來在session2做一個delete的操作,此時就會被hang住。
sess2:delete from empwherejob='MANAGER';
那么hang的情況說明時候會結束呢?
--if sess 1 rollback, 7566/ 7698/7782將被刪除 --if sess 1 commit, 7566/ 7698將被刪除也就是說只有資源被釋放,系統才會解除TX行鎖。
TX行鎖的危害:會導致其他會話的相關業務操作hang住
1、業務操作長時間無法完成
用戶投訴
2、會導致會話積壓
數據庫連接池逐漸被占滿
- 應用獲取不到數據源無法創建新的數據庫連接
- 或操作系統CPU、內存資源逐漸耗盡,無法創建新的數據庫連接
3、會導致產生其他爭用,如bufferbusy wait, ITL contention等
TX行鎖的解決方案:
1、先行會話需要結束事務(transaction):commit或者rollback
2、強制結束先行會話:kill session。
真實案例深入解析
來自雙11的真實案例:雙11早08:45,我方接到客戶反映,在當天凌晨04:00~08:00,enq:TX - row lock contention等待嚴重。
當用戶在投訴數據庫嚴重的行鎖問題的時候,我們首先會想到,在發生TX行鎖時,由于資源久久得不到釋放,系統中會話積壓,導致DBtime會變得很高。
從圖上看出,在問題發生的前兩條開始采樣,DBtime一直處于相對較低的狀態,大概是100。在故障點,DBtime超過了800。
問題初現:從11月11日約00:00開始
高峰時刻:11月11日凌晨04:00達到峰值。
高峰值:該時刻DBTimes峰值為835.86,是該節點平日壓力的十幾倍,可見問題十分嚴重。
接下來我們查看了當時的AWR的報告。在AWR報告里面,我們首先要關注的是等待事件。我們看到其中TX行鎖占用了大部分的等待時間,因此初步推斷行鎖就是導致故障的原因。
那么具體的行鎖在什么地方呢?
通過top SQL查行鎖的話,可能會比較困難。推薦大家通過segment部分進行查詢。在segment模塊,有一個專門針對行鎖的統計, segment by row lock waits.從這個統計中我們看到,有一張命名為_manager_tp的表,占用了99%的行鎖爭用。
明確了爭用對象以后,我們再來找對應的SQL語句。
在以耗時排名的top SQL 中,有一條SQL占比達到98%,這條SQL語句正在對_manager這張表進行update操作。但是我們之前看到的那張爭用的表是_manager_tp, 跟這里查出來的manager不是同一張表。
原因是什么呢?
后經查證,MANAGER是指向表*MANAGER_TP的同義詞。我們知道在運營商的環境中,他們很喜歡用同義詞指向一張表。
因此,現在確認在top SQL里面涉及的對象和爭用的segment的對象是匹配的。
接下來我們看一下從ASH分析出來的00:00 到08:00的趨勢:
我們看到在整體的上升的趨勢中,有一些點會產生向下的波動,向下的波動在行鎖爭用中是很常見的一種現象。當有一部分行鎖被釋放之后,被阻塞的量就會往下降。但是在持續的行鎖阻塞中,雖然會有短暫的釋放和緩解,但更多的會話會很快擁堵上來。
我們再對峰值期間的的行鎖爭用進行詳細分析:
我們看到每一秒行鎖爭用的個數都達到了800+,只有在少數的時刻降到了幾十。
因此到這一步,我們首先明確了分析方向:
該SQL自身導致的行鎖,與其他無關
排除了存儲不穩定可能導致SQL運行緩慢的可能性
第二條是因為在客戶的機房環境下,經常會由于存儲的不穩定導致應用SQL變慢。所以在故障發生的時候,也首先查看了操作系統的錯誤日志。
當我們確定了是某一條SQL導致的行鎖,接下來我們對該SQL語句進行深入分析:
SQL全文如下:從SQL文本來看,對應到的應該是比較少的記錄。
UPDATE *MANAGE SET EXPIRE_DATE= SYSDATE WHERE ACCESS_NUM = :1 AND IDENT_CODE_LEVEL= :2 AND IDENT_CODE_TYPE= :3
其執行計劃如下:走的是索引范圍掃。從TP_AN看到,并不是一個唯一索引。
相關的表和索引信息如下:
我們看到表有200w的記錄,access_num為40w左右。因此平均每行的訪問為6。
然后我們把SQL的AWR報告導出來一看,我們可以看到平均每次要處理7w多條記錄,和6相比差別很大。這說明數據存在嚴重的傾斜。
因此我們做了一個查詢,結果如下:
有些號碼對應到十幾萬條記錄,數據傾斜嚴重。
因此我們推測,發生故障是因為多會話在更新相同的access_number
深度分析:數據為什么會分布不均?
經過跟業務部溝通,發現:
每次用戶申請憑證,表內就會記錄一條憑證信息。
如果用戶反復申請,表內對同一手機號就會記錄多條信息。
問題為何產生?
一次就將表內一個手機號對應的所有記錄的過期日期都更新為當前日期,是不合理的做法。
后來跟開發商進行溝通,得到以下結論:不是代碼問題,就是設計問題
方案1:允許存在相同ACCESS_NUM對應多條記錄的情況:正確的做法應該是只更新最新的記錄,而早之前的記錄不應該更新,因為其早已過期(過期日期比當前日期小)。
并將單表改為主子表關系,主表存最新的記錄,子表存歷史記錄。
方案2:1個ACCESS_NUM在該表只應有一行記錄的情況:應根據判斷新進入該表的數據是否已經存在在表內,若是,則更新數據,若否,則插入數據。
因此開發商給出的方案:
后續得知,該問題不是第一次出現,曾經*MANAGER就是指向表*MANAGER的同義詞!!!
我們根據前面的觀點判斷,在本案例當中,平衡三要素中的“實現”出現了問題。是由于開發設計不合理導致的行鎖競爭。
原文發布時間為:2017-12-1
本文作者:懷曉明
本文來自云棲社區合作伙伴“數據和云”,了解相關信息可以關注“數據和云”微信公眾號
總結
以上是生活随笔為你收集整理的拨云见日—深入解析Oracle TX 行锁(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS变量对象详解
- 下一篇: 实操《深入浅出React和Redux》第