資源供給:併發性控制和mutex之一
簡單案例描述: 某商業銀行業務系統表現出在高峰期CPU消耗很高,有些消耗CPU特別高的程式,但總是會變化,業務系統響應還基本可以接受。在CPU
Time表現中Parse CPU相對比較高,Hard Parse不高,系統偶爾出現cursor pin:
S,v$mutex_sleep_history表現出較高的gets和sleeps。經過綜合判斷為mutex的機制衝突引起,應用patch:
6904068之後CPU消耗快速降低,當然效能是不會增加的,只是釋放了CPU資源。
mutex的實現,是Oracle效能最佳化者的幸運,也是效能最佳化者的不幸。幸運在於除了Oracle bug之外,mutex很少會出現問題。不幸當然也在於此,當出現問題的時候,往往只能去查詢是否bug,很可悲呀。
library cache pin 和cursor pin with mutex。
Oracle在11g之前是主要把pin cursor相關的library cache pin轉換成了mutex,所以我們在這裡也主要講pin cursor。
從效能最佳化的角度出發,library cache pin和cursor pin with mutex兩者沒有很大區別,mutex比較library cache pin提供了更加清晰的視角。
library cache pin的實現應該不同於普通的latch,採用某種類似於佇列的形式實現。library cache pin不同於其他的latch,他會針對每個cursor建立pin,也就是可能幾十萬甚至幾百萬的pin,顯然採用普通latch實現成本太高。我們不 管他如何實現,只要知道其基於某種佇列就可以了。cursor mutex相對比較library cache pin要高階,他採用shared latch的類似應用計數實現,也就是 CAS原子操作。
pin cursor在什麼時候發生?
pin cursor for create cursor
pin cursor for execute cursor
可能還會有其他場景會發生pin cursor,但應該主要就是基於上面兩種情況。
為什麼需要pin,pin的主要目的是為了防止被交換出記憶體,同時提供並行訪問控制。
我們來看看:
Pin cursor for Execute Cursor:
這個時候,我們準備執行該SQL語句(Cursor),需要把Cursor Pin(Shared)在Buffer中(child cursor),在Pin Buffer的過程中,我們需要完成以下操作:
(1)、繫結變數填充
(2)、cursor依賴物件Pin以防止被(Shared)
顯然Pin cursor可能的衝突主要來自於以下兩個方面:
(1)、依賴的object被交換出記憶體,導致pin的成本很高
(2)、依賴的物件被ddl操作以至於無法獲得PIN
我一直不明白Oracle為什麼允許在Parse過程中的物件被允許交換到記憶體中,Parse之後按照道理是需要馬上執行引用,不應該被犧牲掉。
在相對記憶體充足的系統中,如果不發生手工清理的flush pool,一般很少會發生以來的object被交換,因為我們剛剛在parse過程中把所依賴的物件都裝載進來。至於ddl操作則需要在依賴物件上獲得 Exclusive Lock and Exclusive PIN,並且導致依賴的cursor無效。從理論上講,cursor pin的程式在被ddl中斷之後應該重新執行parse過程,但在實踐中的發現基本表現為等待library cache pin的程式永久等待,不會進行釋放。
從cursor pin的角度而言,我們只要做到避免在高峰期執行ddl以及相對充足的shared pool,一般來說可以避免library cache pin或者mutex cursor衝突。
mutex:
cursor: pin s wait on x
cursor: pin s
cursor: pin x
以上三個衝突主要在pin cursor的階段。
cursor: pin s wait on x為正常的cursor執行pin,一般為所依賴的物件不再記憶體中導致成本很高導致,等待pin依賴物件。
當然ddl操作也是cursor: pin s wait on x的主要原因。
cursor: pin s為正常的執行,在極高併發的時候會發生,主要原因在於mutex採用CAS機制,在引用計數變更的時候不允許其他session pin cursor,這個時候的等待表示為cursor: pin s。對於cursor: pin s如果不是Oracle bug的話,一般只能透過應用程式層面來解決,避免cursor熱點,這個和以前處理高併發的library cache pin是一致的。
事實上,大家可以發現相當多的cursor: pin s發生在select * from dual之類的語句上,很多應用對於dual的引用極為頻繁,從而導致其產生pin cursor衝突。一般的處理方式為改寫語句:比如select * from dual為cursor pin最嚴重的語句,則改寫為以下方式:
select * from dual D1;
select * from dual D2;
等等,使其成為不同的cursor,自然就不會出現在cursor: pin s層面上的衝突了。
cursor: pin x,這個層面應該很少見,一般只有在cursor在pin過程中被交換到出記憶體,需要從記憶體中再次載入回來才會出現。當然某些需要對於cursor進行清 理的操作也需要獲得cursor: pin x,比如flush shared pool,pugre cursor等等。
繫結變數的填充發生在另一個層面上,一般為cursor: mutex x,一般很少會發生在繫結變數層面的衝突。
cursor: mutex x
cursor: mutex s
應該極少會看到這個層面的衝突。
如果有衝突發生,主要在兩個層面可能發生:
hard parse在生成cursor和children cursor的時候需要cursor: mutex x,雖然大量的文章認為這個時候會在依賴物件上獲得exclusive lock and exclusive pin,但個人並不傾向於在hard parse的需要的在依賴物件施加exclusive pin,個人傾向於施加的是shared pin,除非是不再記憶體中。有興趣的童鞋可以驗證一下。
hard parse僅僅在cursor被重用的時候才有可能會發生cursor: mutex x,所以個人認為hard parse和cursor: mutex x具有一定關係,但並不是絕對的。
另一個可能會產生cursor: mutex x的場景應該是Create New children,也就是高版本場景。高版本需要不斷的更新LCO的children table部分,也就是cursor部分,需要獲得mutex x,大量高版本自然會引起響應的等待。
cursor: mutex s 主要對於parent cursor進行引用檢視的時候發生,什麼時候會對parent cursor做exclusive,似乎只有age in and age out了。
綜合上面而言,對於mutex和library cache pin的最佳化措施事實上是很簡單的:
(1)、足夠的shared pool
(2)、控制DDL
(3)、控制高版本
(4)、對於高併發的SQL,從語句級別進行人工人點劃分。
當然,考慮Oracle bug也是一個最主要的思考方向。
mutex的實現,是Oracle效能最佳化者的幸運,也是效能最佳化者的不幸。幸運在於除了Oracle bug之外,mutex很少會出現問題。不幸當然也在於此,當出現問題的時候,往往只能去查詢是否bug,很可悲呀。
library cache pin 和cursor pin with mutex。
Oracle在11g之前是主要把pin cursor相關的library cache pin轉換成了mutex,所以我們在這裡也主要講pin cursor。
從效能最佳化的角度出發,library cache pin和cursor pin with mutex兩者沒有很大區別,mutex比較library cache pin提供了更加清晰的視角。
library cache pin的實現應該不同於普通的latch,採用某種類似於佇列的形式實現。library cache pin不同於其他的latch,他會針對每個cursor建立pin,也就是可能幾十萬甚至幾百萬的pin,顯然採用普通latch實現成本太高。我們不 管他如何實現,只要知道其基於某種佇列就可以了。cursor mutex相對比較library cache pin要高階,他採用shared latch的類似應用計數實現,也就是 CAS原子操作。
pin cursor在什麼時候發生?
pin cursor for create cursor
pin cursor for execute cursor
可能還會有其他場景會發生pin cursor,但應該主要就是基於上面兩種情況。
為什麼需要pin,pin的主要目的是為了防止被交換出記憶體,同時提供並行訪問控制。
我們來看看:
Pin cursor for Execute Cursor:
這個時候,我們準備執行該SQL語句(Cursor),需要把Cursor Pin(Shared)在Buffer中(child cursor),在Pin Buffer的過程中,我們需要完成以下操作:
(1)、繫結變數填充
(2)、cursor依賴物件Pin以防止被(Shared)
顯然Pin cursor可能的衝突主要來自於以下兩個方面:
(1)、依賴的object被交換出記憶體,導致pin的成本很高
(2)、依賴的物件被ddl操作以至於無法獲得PIN
我一直不明白Oracle為什麼允許在Parse過程中的物件被允許交換到記憶體中,Parse之後按照道理是需要馬上執行引用,不應該被犧牲掉。
在相對記憶體充足的系統中,如果不發生手工清理的flush pool,一般很少會發生以來的object被交換,因為我們剛剛在parse過程中把所依賴的物件都裝載進來。至於ddl操作則需要在依賴物件上獲得 Exclusive Lock and Exclusive PIN,並且導致依賴的cursor無效。從理論上講,cursor pin的程式在被ddl中斷之後應該重新執行parse過程,但在實踐中的發現基本表現為等待library cache pin的程式永久等待,不會進行釋放。
從cursor pin的角度而言,我們只要做到避免在高峰期執行ddl以及相對充足的shared pool,一般來說可以避免library cache pin或者mutex cursor衝突。
mutex:
cursor: pin s wait on x
cursor: pin s
cursor: pin x
以上三個衝突主要在pin cursor的階段。
cursor: pin s wait on x為正常的cursor執行pin,一般為所依賴的物件不再記憶體中導致成本很高導致,等待pin依賴物件。
當然ddl操作也是cursor: pin s wait on x的主要原因。
cursor: pin s為正常的執行,在極高併發的時候會發生,主要原因在於mutex採用CAS機制,在引用計數變更的時候不允許其他session pin cursor,這個時候的等待表示為cursor: pin s。對於cursor: pin s如果不是Oracle bug的話,一般只能透過應用程式層面來解決,避免cursor熱點,這個和以前處理高併發的library cache pin是一致的。
事實上,大家可以發現相當多的cursor: pin s發生在select * from dual之類的語句上,很多應用對於dual的引用極為頻繁,從而導致其產生pin cursor衝突。一般的處理方式為改寫語句:比如select * from dual為cursor pin最嚴重的語句,則改寫為以下方式:
select * from dual D1;
select * from dual D2;
等等,使其成為不同的cursor,自然就不會出現在cursor: pin s層面上的衝突了。
cursor: pin x,這個層面應該很少見,一般只有在cursor在pin過程中被交換到出記憶體,需要從記憶體中再次載入回來才會出現。當然某些需要對於cursor進行清 理的操作也需要獲得cursor: pin x,比如flush shared pool,pugre cursor等等。
繫結變數的填充發生在另一個層面上,一般為cursor: mutex x,一般很少會發生在繫結變數層面的衝突。
cursor: mutex x
cursor: mutex s
應該極少會看到這個層面的衝突。
如果有衝突發生,主要在兩個層面可能發生:
hard parse在生成cursor和children cursor的時候需要cursor: mutex x,雖然大量的文章認為這個時候會在依賴物件上獲得exclusive lock and exclusive pin,但個人並不傾向於在hard parse的需要的在依賴物件施加exclusive pin,個人傾向於施加的是shared pin,除非是不再記憶體中。有興趣的童鞋可以驗證一下。
hard parse僅僅在cursor被重用的時候才有可能會發生cursor: mutex x,所以個人認為hard parse和cursor: mutex x具有一定關係,但並不是絕對的。
另一個可能會產生cursor: mutex x的場景應該是Create New children,也就是高版本場景。高版本需要不斷的更新LCO的children table部分,也就是cursor部分,需要獲得mutex x,大量高版本自然會引起響應的等待。
cursor: mutex s 主要對於parent cursor進行引用檢視的時候發生,什麼時候會對parent cursor做exclusive,似乎只有age in and age out了。
綜合上面而言,對於mutex和library cache pin的最佳化措施事實上是很簡單的:
(1)、足夠的shared pool
(2)、控制DDL
(3)、控制高版本
(4)、對於高併發的SQL,從語句級別進行人工人點劃分。
當然,考慮Oracle bug也是一個最主要的思考方向。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30633755/viewspace-2127743/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 資源供給:併發性控制和mutex之二Mutex
- 資源供給:併發性控制和mutex之三Mutex
- 資源供給:IO子系統之一
- 資源供給:記憶體和虛擬記憶體記憶體
- 資源供給:IO子系統之二
- 資源供給:IO子系統之三
- 資源供給:IO子系統之五
- 資源供給:IO子系統之七
- JVM 併發性: Java 和 Scala 併發性基礎JVMJava
- Go 併發程式設計之 MutexGo程式設計Mutex
- Go併發程式設計--Mutex/RWMutexGo程式設計Mutex
- 資源供給:再談記憶體和虛擬記憶體記憶體
- oracle資料併發性和一致性Oracle
- 【資料庫】併發控制資料庫
- 【Go進階—併發程式設計】MutexGo程式設計Mutex
- Java併發程式設計-併發程式設計的Bug源頭:可見性、原子性和有序性問題Java程式設計
- 資料併發性和一致性——資料庫概念資料庫
- oracle mutex概念掃盲之一OracleMutex
- 資料庫事務和MVCC多版本併發控制資料庫MVC
- go併發之goroutine和channel,併發控制入門篇Go
- 併發控制
- Java併發程式設計Bug源頭:可見性、原子性和有序性問題Java程式設計
- 資料複製的併發控制
- Guava併發:使用Monitor控制併發Guava
- goroutine併發控制Go
- mysql併發控制MySql
- PGSQL併發控制SQL
- oracle併發控制Oracle
- SQLite學習手冊(鎖和併發控制)SQLite
- HBase 事務和併發控制機制原理
- 第10章:併發和分散式程式設計 10.1併發性和執行緒安全性分散式程式設計執行緒
- 構建“資料要素×”的保障中臺和安全供給
- Kubernetes 併發控制與資料一致性的實現原理
- Kubernetes併發控制與資料一致性的實現原理
- 資料庫併發控制幾隻——事務資料庫
- 併發程式設計之:JUC併發控制工具程式設計
- QT分頁控制元件,開源,供大家使用QT控制元件
- PostgreSQL 併發控制機制(3):基於時間戳的併發控制SQL時間戳