基于TairString實現(xiàn)高性能樂觀鎖
在大量請求并發(fā)訪問和更新實例中儲存的共享資源時,必須有一種精準高效的并發(fā)控制機制來防止邏輯異常和數(shù)據(jù)錯誤,樂觀鎖就是這樣一種機制。比起原生Redis,Tair(企業(yè)版)的TairString模塊能幫助您實現(xiàn)性能更高、成本更低的樂觀鎖。
并發(fā)與Last-Writer-Win
下圖展示了一個典型的并發(fā)導(dǎo)致資源競爭的場景:
初始狀態(tài),string類型數(shù)據(jù)key_1的值為
hello
。t1時刻,App1讀取到key_1的值
hello
。t2時刻,App2讀取到key_1的值
hello
。t3時刻,App1將key_1的值修改為
world
。t4時刻,App2將key_1的值修改為
universe
。
key_1的值是由最后一次寫入決定的,到了t4時刻,App1對key_1的認知已經(jīng)出現(xiàn)了明顯的誤差,后續(xù)操作很可能出現(xiàn)問題,這就是所謂的Last-Writer-Win。要解決Last-Writer-Win問題,就需要保證訪問并更新String數(shù)據(jù)這個操作的原子性,或者說,將作為共享資源的String數(shù)據(jù)轉(zhuǎn)變?yōu)榫哂性有缘淖兞?。您可以使?a href="http://bestwisewords.com/zh/redis/developer-reference/tairsting-command" id="09334255a1tri" title="" class="xref">exString數(shù)據(jù)結(jié)構(gòu),構(gòu)建高性能的樂觀鎖來達成這個效果。
使用TairString實現(xiàn)樂觀鎖
TairString,又稱為exString(extended string),是一種帶版本號的String類型數(shù)據(jù)結(jié)構(gòu)。原生Redis String僅由Key和Value組成,而TairString不僅包含Key和Value,還攜帶了版本(Version),非常適合樂觀鎖等場景。更多介紹及命令詳情信息請參見exString。
TairString與Redis原生String是兩種不同的數(shù)據(jù)結(jié)構(gòu),相關(guān)命令不可混用。
TairString有以下特性:
每個Key都帶有Version,用于說明當前Value的版本。使用EXSET命令新建Key時,默認Version為1。
使用EXGET命令查詢Key時,可以獲取到Value和Version兩個字段。
更新Value時,需要校驗Version,如果校驗失敗會返回異常信息
ERR update version is stale
;校驗成功則更新Value,且自動將Version加1。除了比特位(bit)相關(guān)操作外,TairString可以覆蓋原生Redis String的所有其它功能。
因為這些特性,使得TairString類型數(shù)據(jù)具有了鎖的機制,使用TairString實現(xiàn)樂觀鎖就非常方便了,示例如下:
while(true){
{value, version} = EXGET(key); // 獲取Key的Value和Version
value2 = update(...); // 先將新Value保存到Value2
ret = EXSET(key, value2, version); // 嘗試更新Key并將返回值賦予變量ret
if(ret == OK)
break; // 如果返回值為OK則更新成功,跳出循環(huán)
else if (ret.contains("version is stale"))
continue; // 如果返回值包含"version is stale"則更新失敗,重復(fù)循環(huán)
}
刪除TairString后,即便以相同的key重新設(shè)置一條TairString,其Version也會是1,而不會繼承原TairString的Version。
使用ABS選項可以跳過Version校驗強行覆蓋Version并更新TairString,詳情參見EXSET。
降低樂觀鎖的性能消耗
前文的示例代碼中,如果在執(zhí)行EXGET后該共享資源被其它客戶端更新了,當前客戶端會獲取到更新失敗的異常信息,然后重復(fù)循環(huán),再次執(zhí)行EXGET獲取共享資源的當前Value和Version,直到更新成功,這樣每次循環(huán)都有兩次訪問Redis的IO操作。如果使用TairString的EXCAS命令,可以將兩次訪問減少為一次,極大地節(jié)約系統(tǒng)資源消耗,提升高并發(fā)場景下的服務(wù)性能。
EXCAS命令可以在調(diào)用時攜帶一個用于校驗的Version值,如果校驗成功則直接更新TairString的Value,如果校驗失敗則返回三個字段:
"ERR update version is stale"
Value
Version
更新失敗后可以直接得到TairString當前的版本,無需再次查詢,將原本每個循環(huán)需要進行兩次的訪問減少到一次。示例如下:
while(true){
{ret, value, version} = excas(key, new_value, old_version) // 直接嘗試用CAS命令置換Value
if(ret == OK)
break; // 如果返回值為OK則更新成功,跳出循環(huán)
else (if ret.contains("update version is stale")) // 如果返回值包含"update version is stale"則更新失敗,更新兩個變量
update(value);
old_version = version;
}