一、OCC和MVCC的區別
最簡單的并發控制算法是2PL(2 Phase Locking),分為兩階段:
1)獲得鎖階段;
2)釋放鎖階段。
一般2PL被稱為是悲觀并發控制。
與之相對的是樂觀并發控制OCC( Optimistic Concurrency Control)。OCC假設事務會成功,開始事務時該讀讀,該寫寫,不加鎖。只有到提交時做一下驗證,驗證這個事務是不是能夠成功提交。 OCC分為三階段:
1)Read Phase, 對于讀,放到Read Set,對于寫,把寫記到臨時副本,放到Write Set。因為寫是寫到臨時區的,屬于未提交結果,其它事務讀不到(這點是和MVCC的重要區別);
2)Validation Phase,重掃Read Set,Write Set,檢驗數據是否滿足Isolation Level,如果滿足則Commit,否則Abort;
3)WritePhase,或者叫做Commit Phase,把臨時副本區的數據更新到數據庫中,完成事務提交。
MVCC(Multiversion Concurrency Control)是另一種并發控制算法。MVCC為每條記錄維護多個快照(Snapshot),通過起止兩個時間戳(Begin Timestamp / End Timestamp)維護副本的可見性。讀寫進行的不同操作如下:
Update,創建一個新的版本(Version);
Delete,更新End Timestamp。
Read,通過起止時間戳判定記錄是否對當前事務可見(OCC讀不到未提交的記錄,所以不需要做這個判斷)。
這樣,通過Snapshot,實現了讀寫互不阻塞。但為了實現Serializable,對讀寫規則還是要進行一定的限制。MVCC通過不同的方法實現。有基于鎖定的,MV-2PL,如MySQL。有基于時間排序(Time Ordering)的,叫MV-TO,如PostgreSQL。其實準確來說,PG的實現叫SSI(Serializable Snapshot Isolation),不算MV-TO。也有像OCC那樣基于樂觀算法的,MV-OCC,即讀寫時不做驗證,延遲到提交時驗證。
效率上,2PL讀寫阻塞,在維護鎖開銷較小時較好;OCC不維護鎖,一些比較新的OCC算法吞吐可以做得很高,不過相應回滾也會比較高,沖突比較小和驗證開銷小時比較好;MVCC對不同類型的workload都有很好的適應性,讀寫互不阻塞,回滾率也比OCC好,很多RDBMS也都用MVCC,如Oracle,PostgreSQL,MySQL。還有一個效率問題,隨著現在CPU核心數越來越多,考慮并發控制算法往往需要考慮它的多核擴展性好不好。由于多數MVCC,OCC算法都需要時間戳分配,時間戳通常對全局變量進行CAS(Compare And Swap)操作來計算,當核心數變大時,CAS的爭用也變大了。
另外,現在的許多并發控制方法經常混合了多種算法。先有人提出了A,后有人提出了B,再后來就有人提出了A+B,那么A+B應該是叫A呢還是叫B呢?就像上面提到的MV-2PL,MV-TO,MV-OCC。
延伸閱讀:
二、id的一些典型的類型
整型:整型通常來說是優異的選擇,這是因為整型的運算和比較都很快,而且還可以設置 AUTO_INCREMENT 屬性自動遞增。ENUM 和 SET:通常不會選擇枚舉和集合作為 id,然后對于那些包含有“類型”、“狀態”、“性別”這類型的列來說是挺合適的。例如我們需要有一張表存儲下拉菜單時,通常會有一個值和一個名稱,這個時候值使用枚舉作為主鍵也是可以的。字符串:盡可能地避免使用字符串作為 id,一是字符串占據的空間更大,二是通常會比整型慢。選用字符串作為 id 時,還需要特別注意 MD5、SHA1和 UUID 這些函數。每個值是在很大范圍的隨機值,沒有次序,這會導致插入和查詢更慢:插入的時候,由于建立索引是隨機位置(會導致分頁、隨機磁盤訪問和聚集索引碎片),會降低插入速度。查詢的時候,相鄰的數據行在磁盤或內存上上可能跨度很大,也會導致速度更慢。如果確實要使用 UUID 值,應當移除掉“-”字符,或者是使用 UNHEX 函數將其轉換為16字節數字,并使用 BINARY(16)存儲。然后可以使用 HEX 函數以十六進制的方式進行獲取。UUID 產生的方法有很多,有些是隨機分布的,有些是有序的,但是即便是有序的性能也不如整型。