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