RocksDB作為一個(gè)開(kāi)源的存儲(chǔ)引擎支持事務(wù)的ACID特性,而要支持ACID中的I(Isolation),并發(fā)控制這塊是少不了的,本文主要討論RocksDB的鎖機(jī)制實(shí)現(xiàn),細(xì)節(jié)會(huì)涉及到源碼分析,希望通過(guò)本文讀者可以深入了解RocksDB并發(fā)控制原理。文章主要從以下4方面展開(kāi),首先會(huì)介紹RocksDB鎖的基本結(jié)構(gòu),然后我會(huì)介紹RocksDB行鎖數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)下,鎖空間開(kāi)銷(xiāo),接著我會(huì)介紹幾種典型場(chǎng)景的上鎖流程,最后會(huì)介紹鎖機(jī)制中必不可少的死鎖檢測(cè)機(jī)制。

1.行鎖數(shù)據(jù)結(jié)構(gòu)
    RocksDB鎖粒度最小是行,對(duì)于KV存儲(chǔ)而言,鎖對(duì)象就是key,每一個(gè)key對(duì)應(yīng)一個(gè)LockInfo結(jié)構(gòu)。所有key通過(guò)hash表管理,查找鎖時(shí),直接通過(guò)hash表定位即可確定這個(gè)key是否已經(jīng)被上鎖。但如果全局只有一個(gè)hash表,會(huì)導(dǎo)致這個(gè)訪問(wèn)這個(gè)hash表的沖突很多,影響并發(fā)性能。RocksDB首先按Columnfamily進(jìn)行拆分,每個(gè)Columnfamily中的鎖通過(guò)一個(gè)LockMap管理,而每個(gè)LockMap再拆分成若干個(gè)分片,每個(gè)分片通過(guò)LockMapStripe管理,而hash表(std::unordered_map<std::string, LockInfo>)則存在于Stripe結(jié)構(gòu)中,Stripe結(jié)構(gòu)中還包含一個(gè)mutex和condition_variable,這個(gè)主要作用是,互斥訪問(wèn)hash表,當(dāng)出現(xiàn)鎖沖突時(shí),將線程掛起,解鎖后,喚醒掛起的線程。這種設(shè)計(jì)很簡(jiǎn)單但也帶來(lái)一個(gè)顯而易見(jiàn)的問(wèn)題,就是多個(gè)不相關(guān)的鎖公用一個(gè)condition_variable,導(dǎo)致鎖釋放時(shí),不必要的喚醒一批線程,而這些線程重試后,發(fā)現(xiàn)仍然需要等待,造成了無(wú)效的上下文切換。對(duì)比我們之前討論的InnoDB鎖機(jī)制,我們發(fā)現(xiàn)InnoDB是一個(gè)page里面的記錄復(fù)用一把鎖,而且復(fù)用是有條件的,同一個(gè)事務(wù)對(duì)一個(gè)page的若干條記錄加鎖才能復(fù)用;而且鎖等待隊(duì)列是精確等待,精確到記錄級(jí)別,不會(huì)導(dǎo)致的無(wú)效的喚醒。雖然RocksDB鎖設(shè)計(jì)比較粗糙,但也做了一定的優(yōu)化,比如在管理LockMaps時(shí),通過(guò)在每個(gè)線程本地緩存一份拷貝lock_maps_cache_,通過(guò)全局鏈表將每個(gè)線程的cache鏈起來(lái),當(dāng)LockMaps變更時(shí)(刪除columnfamily),則全局將每個(gè)線程的copy清空,由于columnfamily改動(dòng)很少,所以大部分訪問(wèn)LockMaps操作都是不需要加鎖的,提高了并發(fā)效率。
相關(guān)數(shù)據(jù)結(jié)構(gòu)如下:

網(wǎng)友評(píng)論

    <mark id="nui1h"></mark>
    <fieldset id="nui1h"><optgroup id="nui1h"></optgroup></fieldset>