樂觀鎖為什麼適用於衝突少的場景,以及應用在內存資料庫中?
網上說樂觀鎖適用於衝突少的場景。因為衝突少,重做的開銷小。但是對於悲觀鎖來說,其開銷主要在於鎖衝突,衝突少的時候,悲觀鎖開銷應該也小,這樣樂觀鎖有什麼意義?另外,為什麼內存資料庫大多使用樂觀鎖?
補充--
看了tikv的文章,也講樂觀鎖在衝突小的時候表現好。 樂觀鎖在衝突檢測的階段要加鎖,並不是完全不加鎖,所以樂觀鎖和悲觀鎖加鎖的次數應該是一樣的,只是樂觀鎖減少了鎖的時間,可能這個和性能相關,但原因我也不知道。樂觀鎖衝突大的時候重做開銷大,衝突小的時候樂觀鎖開銷小這種說法是我之前接受的。 但是如果兩種方法其實都要加鎖,那麼樂觀鎖在衝突小的時候開銷小的原因是什麼?或者為什麼加鎖時間短就減少了開銷?
主要原因是加鎖的時機不同,一個是執行時(慢,耗時長),一個是驗證時(快,耗時短),舉個最簡單的例子來解釋這個問題,在數據是單版本,並且都實現repeatable read隔離級別的情況下,假如資料庫中的數據是這樣的:
id name
1 aa
2 bb
3 cc
...
n xx
兩個事務分別是這樣的:
T1
begin;
update t set name ="dd" where id=3;
select name fro"m"(知乎抽了,不讓打這個關鍵字) t where id=1;
select name fro"m" t where id=2;
...
select name fro"m" t where id=n;
commit;
T2
begin;
select name fro"m" t where id=3;
select name fro"m" t where id=3;
commit;
悲觀並發控制:兩階段鎖為了實現repeatable read,讀的時候加讀鎖(共享鎖),寫的時候加寫鎖(排它鎖),並且到commit的時候才能同時釋放所有鎖,假設T2在開始執行的時候,T1已經對id=3的行加了寫鎖,那麼T1在commit前,T2是一直被阻塞住的。
悲觀並發控制是在事務執行的過程中加鎖,執行的時候消耗的時間可能會很長,比如當數據量特別大的時候,select語句就會比較慢,這個過程中鎖是一直加著的,commit前,都要影響到其它的事務。
樂觀並發控制:單版本的每個事務會有一個私有的工作區,並且維護read set和write set,裡面記錄的是讀或寫的數據的物理位置,執行過程完全不加鎖,也就不會對其它事務造成影響,執行完畢後,對讀寫的數據進行驗證,驗證的時候需要對read set和write set里的數據進行加鎖,然後檢查這些數據是否被其它事務修改了,由於之前select,update的時候已經記錄了這些數據的物理位置,可以馬上找到這些數據並進行加鎖檢查,然後釋放鎖,鎖的時間會少很多,而只有鎖住數據的時候會影響其它事務。
由此可見,雖然都需要加鎖,但樂觀並發控制加鎖的時候少很多,對其它事務造成影響的時間就少很多。不過如果寫寫衝突嚴重的話,一些事務就需要不斷重新執行,浪費了很多操作。
內存資料庫大多數都是OLTP/OLAP混合型,會有很多大查詢,也就是只讀事務,通過上面簡單的例子,可以看出,樂觀並發控制是對只讀事務很友好的。
下面這篇論文對並發控制的方方面面都做了總結解釋,也列舉了一些流行的資料庫所使用的並發控制方法,確實內存資料庫用樂觀並發控制多一些,包括Hekaton,MemSQL,HyPer等
http://www.comp.nus.edu.sg/~yingjun/papers/vldb2017.pdf樂觀鎖,忙等待,耗cpu, 雖然延遲小,但scalability差。
不是,悲觀鎖開銷在於加鎖本身,一般來說悲觀鎖需要很複雜的同步與調度機制,即使沒有真正發生衝突也往往引入了相當大的額外開銷,而樂觀鎖通常來說根本沒有真正的鎖,而只是檢查了一個標記決定是否真的進行提交,開銷要小得多,但是一旦發生衝突需要撤銷整個transaction重做。所以衝突相對少的時候樂觀鎖有優勢。
使用樂觀鎖的時候,每次讀取數據或者修改數據,不需要去檢查鎖,在最後提交的過程中一次性檢查所有的鎖,這樣做會比較高效。悲觀鎖會在每次訪問數據的時候都去檢查一下鎖,效率不如樂觀鎖高。
不過如果衝突嚴重的,使用樂觀鎖的方案就只能不斷重頭再來,這樣就會做大量的無效工作,降低整體的效率。
樂觀鎖在衝突檢測的階段要加鎖,並不是完全不加鎖,所以樂觀鎖和悲觀鎖加鎖的次數應該是一樣的,只是樂觀鎖減少了鎖的時間,可能這個和性能相關,但原因我也不知道。
樂觀鎖適合衝突小的原因是其加鎖消耗的資源小於悲觀鎖加鎖的消耗
推薦閱讀:
※Google Spanner 是一個什麼樣東西?對未來會產生什麼樣的影響?
※有沒有公司在用access建資料庫?
※有哪些靠譜的注入攻擊的掃描工具?
※「用戶標籤」在資料庫設計時應該如何存儲?
※關於InnoDB中mvcc和覆蓋索引查詢的困惑?