精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

TiKV 源碼分析之 PointGet

數(shù)據(jù)庫
本文介紹了TiDB中最基本的PointGet算子在存儲層TiKV中的執(zhí)行流程。

一、背景介紹

TiDB是一款具有HTAP能力(同時支持在線事務(wù)處理與在線分析處理 )的融合型分布式數(shù)據(jù)庫產(chǎn)品,具備水平擴(kuò)容或者縮容等重要特性。TiDB 采用多副本+Multi-Raft 算法的方式將數(shù)據(jù)調(diào)度到不同的機器節(jié)點上,具備較高的可靠性和容災(zāi)能力。TiDB中的存儲層TiKV組件,能夠獨立于TiDB作為一款分布式KV數(shù)據(jù)庫使用,目前已經(jīng)捐贈給CNCF并于2020年正式畢業(yè)。目前vivo公司內(nèi)部的磁盤KV產(chǎn)品采用了開源的TiKV作為存儲層實現(xiàn), 目前已經(jīng)在公司的不同業(yè)務(wù)產(chǎn)品中有深度實踐。

TiKV作為一款KV數(shù)據(jù)庫產(chǎn)品,同時提供了RawAPI和TxnAPI兩套接口:

  • RawAPI僅支持最基本的針對單Key操作的Set/Get/Del及Scan語義
  • TxnAPI提供了基于ACID事務(wù)標(biāo)準(zhǔn)的接口,支持多Key寫入的原子性

TxnAPI采用了分布式事務(wù)來保證多Key寫入的原子性,其適用的業(yè)務(wù)場景與RawAPI相比來說更為廣泛。本文后續(xù)內(nèi)容將重點對PointGet在TiKV側(cè)的執(zhí)行流程進(jìn)行分析,其內(nèi)容涉及到storage和txn模塊。閱讀本文后,讀者將會深入了解TiKV源碼中Get流程的實現(xiàn)細(xì)節(jié),包括如何處理讀請求、如何進(jìn)行數(shù)據(jù)定位和讀取、如何實現(xiàn)事務(wù)隔離級別等方面,并且能夠更好地理解TiKV的內(nèi)部工作原理和性能優(yōu)化。

二、PointGet介紹

 2.1 TiDB視角中的PointGet

PointGet顧名思義即"點查", 它是TiDB中最為基本的幾種算子之一,以下列舉了兩個常見的PointGet算子的使用場景:

根據(jù)主鍵Id查詢

MySQL [test]> explain select * from user where id = 1024;
+-------------+---------+------+-------------------------------+---------------+
| id          | estRows | task | access object                 | operator info |
+-------------+---------+------+-------------------------------+---------------+
| Point_Get_1 | 1.00    | root | table:user, index:PRIMARY(id) |               |
+-------------+---------+------+-------------------------------+---------------+

根據(jù)唯一索引查詢

MySQL [test]> explain select * from users where name = "test";
+-------------+---------+------+-------------------------------+---------------+
| id          | estRows | task | access object                 | operator info |
+-------------+---------+------+-------------------------------+---------------+
| Point_Get_1 | 1.00    | root | table:users, index:name(name) |               |
+-------------+---------+------+-------------------------------+---------------+

2.2 純KV用戶視角中的PointGet

部分業(yè)務(wù)沒有完整地使用TiDB組件,而是使用官方提供的client-go/client-rust直接訪問PD和TiKV。

func testGet(k []byte) (error) {
    txn, err := client.Begin()
    if err != nil {
        return err
    }
    v, err := txn.Get(context.TODO(), k)
    if err != nil {
        return err
    }
    fmt.Printf("value of key is: %+v", v)
    return nil
}

三、PointGet在TiDB中的實現(xiàn)

TiDB層為計算層,其主要職能為MySQL協(xié)議的實現(xiàn)以及SQL優(yōu)化器和執(zhí)行器的構(gòu)建。客戶端發(fā)起的所有SQL, 都會經(jīng)過以下生命周期流程:

  1. Lexer/Parser解析后得到AST,并轉(zhuǎn)換為執(zhí)行計劃。
  2. 執(zhí)行計劃經(jīng)過RBO/CBO后得到優(yōu)化過后的執(zhí)行計劃。
  3. 基于執(zhí)行計劃構(gòu)建執(zhí)行器,其本質(zhì)是不同的算子"套娃",整體構(gòu)成一個樹型結(jié)構(gòu)。

TiDB的執(zhí)行器基于"火山模型"構(gòu)建,不同的操作算子具有不同的Executor實現(xiàn):

type Executor interface {   
    base() *baseExecutor
    Open(context.Context) error
    Next(ctx context.Context, req *chunk.Chunk) error
    Close() error
    Schema() *expression.Schema
}

Executor中最為核心的是三個函數(shù)分別是Open/Next/Close,分別對應(yīng)算子的初始化、迭代以及收尾邏輯。本文涉及的PointGet算子由PointGetExector實現(xiàn),其核心的查詢邏輯位于PointGetExector::Next()函數(shù)中。由于相關(guān)邏輯耦合了悲觀事務(wù),以及tikv/client-go中部分Percolator的實現(xiàn),且不屬于本文重點分析的主要內(nèi)容,這里不展開描述,感興趣的讀者可以自行閱讀。

四、PointGet在TiKV中的實現(xiàn)

4.1 PointGet接口定義

TiKV和TiDB使用gRPC進(jìn)行通信,其接口契約定義采用了protobuf,我們可以在pingcap/kvproto項目中找到與PointGet相關(guān)的接口定義KvGet如下:

// Key/value store API for TiKV.
service Tikv {
    // Commands using a transactional interface.
    rpc KvGet(kvrpcpb.GetRequest) returns (kvrpcpb.GetResponse) {}
    // ... other api definations ...
}

其中入?yún)etRequest定義如下代碼片段,我們可以看到,TiKV的點查接口除了key之外,還額外需要一個名為version的參數(shù),即當(dāng)前事務(wù)的start_ts(事務(wù)開始時間戳),這個時間戳是由TiDB在啟動事務(wù)時從Pd組件申請而來。與很多數(shù)據(jù)庫類似,TiKV也采用了MVCC機制,即同一個key在底層的存儲中在不同時刻擁有不同的值,因此要想進(jìn)行查詢,除了key之外,還需要帶上版本。

// A transactional get command. Lookup a value for `key` in the transaction with
// starting timestamp = `version`.
message GetRequest {
    Context context = 1;
    bytes key = 2;
    uint64 version = 3;
}

4.2 TiKV側(cè)調(diào)用堆棧

TiKV作為gRPC的Server端,提供了KvGet接口的實現(xiàn),相關(guān)調(diào)用堆棧為:

+TiKV::kv_get (grpc-poll-thread)
 +future_get
  +Storage::get
   +Storage::snapshot (readpool-thread)
   +SnapshotStore::get
     +PointGetterBuilder::build
     +PointGetter::get

在一次KvGet調(diào)用中,函數(shù)執(zhí)行流程會在grpc-poll-thread和readpool-thread中切換,其中前者為gRPC的poll thread,請求在被路由到Storage層后,會根據(jù)讀寫屬性路由到不同的線程池中,只讀語義的Get/Scan請求都會被路由到ReadPool中執(zhí)行,這是一個特定用于處理只讀請求的線程池。

4.3 Read through locks介紹

在分析后續(xù)邏輯之前,我們需要對Read through locks機制先做個簡單介紹。TiKV使用Percoaltor模型來實現(xiàn)分布式事務(wù),同時也引入了MVCC機制。然而其實現(xiàn)和傳統(tǒng)的MVCC實現(xiàn)略有差異:TiKV的讀取過程中若遇到其他事務(wù)提交時寫入的Lock, 則需要等待或者嘗試解鎖,這會阻塞讀取直到事務(wù)狀態(tài)確定,一定程度上會損失并發(fā)性能。

然而在一些場景(如SecondaryLocks),在Key對應(yīng)的鎖仍然存在的情況下,我們已經(jīng)知道相關(guān)事務(wù)的最終狀態(tài)(提交或回滾)。如果我們將這些事務(wù)的最終狀態(tài)與查詢請求一起發(fā)送給TiKV, 那么TiKV可以根據(jù)這些事務(wù)狀態(tài)來確定能否在有Lock的情況下安全讀取,避免不必要的等待, 即本小節(jié)提到的Read through lock機制。

Context是所有的TiKV請求都會攜帶的上下文信息,為了實現(xiàn)Read through lock, 

https://github.com/pingcap/kvproto/pull/833 這個PR在Context中添加了如下字段:

message Context {
    // Read requests can ignore locks belonging to these transactions because either
    // these transactions are rolled back or theirs commit_ts > read request's start_ts.
    repeated uint64 resolved_locks = 13;
  
    // Read request should read through locks belonging to these transactions because these
    // transactions are committed and theirs commit_ts <= read request's start_ts.
    repeated uint64 committed_locks = 22;
}

其中resolved_locks用于記錄讀取時可以忽略的鎖,這些鎖對應(yīng)的事務(wù)可能已被回滾,或者已成功提交但CommitTS大于當(dāng)前的讀StartTS,直接忽略這些鎖也不影響快照一致性。

其中committed_locks則用于記錄邏輯上已被正確提交但物理上Lock還未被清理的、且CommitTS小于當(dāng)前讀取使用的StartTS的事務(wù)。由于事務(wù)本質(zhì)上已經(jīng)被提交,因此讀取時可以不需要返回等待,只需要通過Lock查詢DefaultCF中的數(shù)據(jù)即可。

通過Read through lock機制,TiKV可以在一些Lock尚未被清理的情況下直接返回正確的結(jié)果,避免了客戶端層面的Wait和ResolveLock,其具體實現(xiàn)在后續(xù)小節(jié)會涉及到。

4.4 Storage::get流程分析

下方代碼塊是經(jīng)過精簡過后的偽代碼,主要標(biāo)注了get流程中一些比較關(guān)鍵的步驟。

pub fn get(&self, mut ctx: Context, key: Key, start_ts: TimeStamp) -> impl Future<Output = ... >> {
  self.read_pool.spawn_handle(async move {
    
     // 1. 創(chuàng)建創(chuàng)建快照需要的上下文
     let snap_ctx = prepare_snap_ctx(...);
  
     // 2. 申請一個快照
     let snapshot = Self::with_tls_engine(|engine| Self::snapshot(engine, snap_ctx)).await?;
       
     // 3. 創(chuàng)建SnapshotStore對象并執(zhí)行查詢
     let snap_store = SnapshotStore::new(...);
     let result = snap_store.get(key);
  
     // 4. 更新Metrics和Stats統(tǒng)計信息
  });
}

4.4.1 準(zhǔn)備快照上下文

prepare_snap_ctx顧名思義即準(zhǔn)備用于創(chuàng)建快照所需要的上下文對象,即SnapContext對象,其完整定義如下:

pub struct SnapContext<'a> {
    pub pb_ctx: &'a Context,
    pub read_id: Option<ThreadReadId>,
    // When start_ts is None and `stale_read` is true, it means acquire a snapshot without any
    // consistency guarantee.
    pub start_ts: Option<TimeStamp>,
    // `key_ranges` is used in replica read. It will send to
    // the leader via raft "read index" to check memory locks.
    pub key_ranges: Vec<KeyRange>,
    // Marks that this snapshot request is allowed in the flashback state.
    pub allowed_in_flashback: bool,
}
 
fn prepare_snap_ctx<'a>(...) -> Result<SnapContext<'a>> {
    if !pb_ctx.get_stale_read() {
        concurrency_manager.update_max_ts(start_ts);
    }
    if need_check_locks(isolation_level) {
       concurrency_manager.read_key_check(...)
    }
    let mut snap_ctx = SnapContext {...};
    if need_check_locks_in_replica_read(pb_ctx) {
       snap_ctx.key_ranges = ...
    }
}

prepare_snap_ctx只需要創(chuàng)建一個SnapContext對象,但目前實現(xiàn)中多出了如下判斷或操作,絕大部分都源于TiKV5.0中的AsyncCommit特性所需。

1.當(dāng)本次讀取非StaleRead時,需要將當(dāng)前讀取請求的start_ts與CurrencyManager中的max_ts進(jìn)行比較,并將二者中的最大值更新為全局max_ts。這一操作用于保證異步提交事務(wù)計算出來的MinCommitTs不會破壞快照一致性。

2. 若當(dāng)前的隔離級別是SnapshotIsolation或者RcCheckTs時, 則需要額外檢查CurrencyManager中的內(nèi)存鎖。如果存在鎖且當(dāng)前start_ts大于鎖中的MinCommitTs,TiKV會直接拒絕本次讀取請求。其原因在于AsyncCommit事務(wù)Prewrite結(jié)束之前需要暫時阻止使用更新的start_ts發(fā)起的快照讀,否則會導(dǎo)致正在異步提交的事務(wù)計算出的MinCommitTS無法滿足快照一致性。

4.4.2 向Engine申請Snapshot

Engine是TiKV中對上層存儲組件的一次抽象,所有實現(xiàn)了Engine Trait的具體實現(xiàn)都可以作為TiKV中的存儲層組件。目前TiKV中已經(jīng)實現(xiàn)了BTreeEngine/MockEngine/RocksEngine/RaftKV等多個實現(xiàn)。

pub trait Engine: Send + Clone + 'static {
   // 獲取用于查詢的快照
   fn async_snapshot(&mut self, ctx: SnapContext<'_>) -> Self::SnapshotRes;
     
   // 提交寫入的Mutation
   fn async_write(&self,ctx: &Context,batch: WriteData,subscribed: u8, on_applied: Option<OnAppliedCb>) -> Self::WriteRes;
 
   // 其他接口...
}

Engine的接口定義中與讀寫相關(guān)的接口分別是async_snapshot和async_write。目前TiKV中的默認(rèn)Engine實現(xiàn)為RaftKV,即一個基于Raftstore的實現(xiàn)。在RaftKV中,所有的寫入都會通過Raft狀態(tài)機進(jìn)行propose/commit/apply流程,用戶可以基于訂閱機制獲得這3個事件的通知從而做出不同處理,默認(rèn)情況下,TiKV會在一次寫入請求被RaftLeader apply成功后返回用戶。而讀取操作則需要遵循先行一致性讀取,在早期版本中,一次讀取需要通過Raft狀態(tài)機進(jìn)行一次ReadIndex才能進(jìn)行,在新版中TiKV實現(xiàn)了基于租約的LeaseRead, 簡化了讀取流程。本次介紹的PointGet讀取流程中,會涉及到使用async_snapshot獲取一個Engine在當(dāng)前時刻的快照,并基于快照進(jìn)行讀取。

TiKV按照KeyRange將Key拆分為不同的Region, 每個Region都是一個RaftGroup,且擁有獨立的狀態(tài)機推進(jìn)運轉(zhuǎn)。因此,RaftKV-Engine中async_snapshot返回的是一個名為RegionSnapshot的對象,其定義如下:

pub struct RegionSnapshot<S: Snapshot> {
    snap: Arc<S>,
    region: Arc<Region>,
    apply_index: Arc<AtomicU64>,
    pub term: Option<NonZeroU64>,
    pub txn_extra_op: TxnExtraOp,
    // `None` means the snapshot does not provide peer related transaction extensions.
    pub txn_ext: Option<Arc<TxnExt>>,
    pub bucket_meta: Option<Arc<BucketMeta>>,
}

RegionSnapshot本質(zhì)是對底層的KV引擎RocksDB層面的快照的封裝,其邏輯視圖如下:

圖片

4.4.3 MVCC實現(xiàn)和快照隔離級別實現(xiàn)

前文提到的Engine::async_snapshot接口返回的快照本質(zhì)是Engine在當(dāng)下時刻的快照,并不等于事務(wù)層面的MVCC快照,因此在具體查詢時,需要配合StartTS進(jìn)行使用。TiKV中封裝了一個SnapshotStore用于輔助MVCC層面的查詢。其定義如下:

pub struct SnapshotStore<S: Snapshot> {
    snapshot: S,
    start_ts: TimeStamp,
    isolation_level: IsolationLevel,
    fill_cache: bool,
    bypass_locks: TsSet,
    access_locks: TsSet,
    check_has_newer_ts_data: bool,
    point_getter_cache: Option<PointGetter<S>>,
}

SnapshotStore中集合了從Engine獲取的快照和客戶端請求附帶的StartTS, 因此可以被認(rèn)為是一個MVCC層面的快照。用戶對SnapshotStore發(fā)起的點查會被委托給內(nèi)部的PointGetter。

// PointGetter::get
pub fn get(&mut self, user_key: &Key) -> Result<Option<Value>> {
        fail_point!("point_getter_get");
          
        // 根據(jù)當(dāng)前請求使用的隔離級別判定是否需要檢查鎖
        if need_check_locks(self.isolation_level) {
            // 如果需要檢查鎖且鎖存在,則需要根據(jù)判定鎖
            if let Some(lock) = self.load_and_check_lock(user_key)? {
                return self.load_data_from_lock(user_key, lock);
            }
        }
          
        // Percoaltor正常讀取流程:從WriteCF中找到<=start_ts中最大的commit_ts,并基于其存儲的start_ts到DefaultCF中讀取        
        self.load_data(user_key)
}

在執(zhí)行查詢前,TiKV需要根據(jù)當(dāng)前請求的隔離級別判定是否需要檢查鎖。

pub fn need_check_locks(iso_level: IsolationLevel) -> bool {
    matches!(iso_level, IsolationLevel::Si | IsolationLevel::RcCheckTs)
}

TiKV支持SnapshotIsolation/ReadCommitted/ReadCommittedCheckTs三種隔離級別,其中前兩種需要檢查鎖。其原因在于LockCf中的鎖是由于事務(wù)在2PC的第一階段提交階段寫入的,事務(wù)的最終狀態(tài)無法確定,如果不檢查鎖直接讀取,那么可能導(dǎo)致快照讀取被破壞。

fn load_and_check_lock(&mut self, user_key: &Key) -> Result<Option<Lock>> {
        // 從LockCf查詢該Key的鎖信息
        let lock_value = self.snapshot.get_cf(CF_LOCK, user_key)?;
  
        if let Some(ref lock_value) = lock_value {
            let lock = Lock::parse(lock_value)?;
            // 如果存在鎖則檢查鎖是否沖突
            if let Err(e) = Lock::check_ts_conflict(
                Cow::Borrowed(&lock),
                user_key,
                self.ts,
                &self.bypass_locks,
                self.isolation_level,
            )
        // ...
}

其中Lock::check_ts_conflict的實現(xiàn)中會根據(jù)當(dāng)前的事務(wù)隔離級別進(jìn)行判定,不同的隔離級別的判定邏輯略有差異。由于本文篇幅有限,這里只分析我們常用的快照隔離級別的實現(xiàn)。

fn check_ts_conflict_si(lock: Cow<'_, Self>, key: &Key, ts: TimeStamp, bypass_locks: &TsSet ) -> Result<()> {
        if lock.ts > ts
            || lock.lock_type == LockType::Lock
            || lock.lock_type == LockType::Pessimistic
        {
            return Ok(());
        }
  
        if lock.min_commit_ts > ts {
            // Ignore lock when min_commit_ts > ts
            return Ok(());
        }
  
        if bypass_locks.contains(lock.ts) {
            return Ok(());
        }
  
        let raw_key = key.to_raw()?;
  
        if ts == TimeStamp::max() && raw_key == lock.primary && !lock.use_async_commit {
            // When `ts == TimeStamp::max()` (which means to get latest committed version
            // for primary key), and current key is the primary key, we ignore
            // this lock.
            return Ok(());
        }
  
        // There is a pending lock. Client should wait or clean it.
        Err(Error::from(ErrorInner::KeyIsLocked(
            lock.into_owned().into_lock_info(raw_key),
        )))
}
  • 當(dāng)lock.ts > ts時,當(dāng)前查詢請求可以直接忽略這個鎖。其原因在于當(dāng)前的lock是由具有更高start_ts的事務(wù)寫入,因此即便這個事務(wù)后續(xù)被提交,其commit_ts一定大于當(dāng)前的start_ts,其新寫入的數(shù)據(jù)是不可見的,不會破壞快照一致性。
  • 當(dāng)lock_type==Lock時,也可以直接忽略這個鎖突, 其原因在于LockType::Lock是由于創(chuàng)建索引產(chǎn)生,它只用于指示被鎖定但不會修改數(shù)據(jù),因此也可以直接被忽略。
  • 當(dāng)lock_type==Pessistics時,也可以直接忽略這個鎖突,LockType::Pessistics是由于悲觀事務(wù)執(zhí)行DML時寫入,并未進(jìn)行到事務(wù)提交階段,即使這個事務(wù)很快被提交,由于其commit_ts也一定大于當(dāng)前讀取的start_ts, 直接忽略并不會影響快照一致性。
  • 當(dāng)lock.min_commit_ts > ts時,也可以直接忽略這個鎖,其原因在于它能保證這個AsyncCommit事務(wù)的最終計算出的commit_ts一定大于ts,即使這個事務(wù)會被提交,也不會破壞快照一致性。
  • 當(dāng)bypass_locks中包含了當(dāng)前鎖的start_ts時, 也可以直接忽略這個鎖。bypass_locks即前面Read through locks小節(jié)中提到了resloved_locks,這些鎖雖然存在,但它們對應(yīng)事務(wù)要么已經(jīng)被回滾,要么使用了大于當(dāng)前讀取start_ts的commit_ts進(jìn)行提交,無論是哪種情況都不會破壞快照一致性。
  • 其他情況則需要返回KeyIsLocked錯誤給客戶端,客戶端收到這個錯誤后則會檢查這個鎖的過期時間,如果鎖尚未過期則需要做wait,否則會嘗試進(jìn)行解鎖恢復(fù)這個事務(wù)的狀態(tài)。

若check_ts_conflict_si返回KeyIsLocked或其他錯誤后,TiKV會額外檢查access_locks里是否包含該鎖,如果該鎖存在,則KeyIsLocked錯誤則會被忽略,同時鎖會被直接返回,外層函數(shù)可以通過鎖找到start_ts從而直接讀取DefaultCF中的數(shù)據(jù)。這里的access_locks即Read through locks中的committed_locks,即已經(jīng)知曉被提交的且commit_ts小于當(dāng)前快照讀start_ts的事務(wù),在這種情況下,直接讀取DefaultCF是一個超前但安全的操作,原因在在于一旦這個Lock被Resolve,用戶通過新的commit_ts可以定位到同一個start_ts。

if let Err(e) = Lock::check_ts_conflict(Cow::Borrowed(&lock),user_key,self.ts,&self.bypass_locks,self.isolation_level) {
    if self.access_locks.contains(lock.ts) {
        return Ok(Some(lock));
   }
    Err(e.into())
}

在不存在Key被鎖定或沖突,且沒有使用Read through locks讀取后,TiKV則會進(jìn)行正常的Percolator讀取流程,即從WriteCF中找到<=start_ts中最大的commit_ts,并基于其存儲的start_ts到DefaultCF中讀取。

4.4.4 RegionSnapshot的Get實現(xiàn)

RegionSnapshot::get的實現(xiàn)相對比較簡單,邏輯如下:

fn get_value_cf_opt(&self, opts: &ReadOptions, cf: &str, key: &[u8]) -> EngineResult<Option<Self::DbVector>> {
    // 1. 檢查查詢的key是否在Region的范圍內(nèi), 如果不在則直接返回錯誤。
    check_key_in_range(key,self.region.get_id(),self.region.get_start_key(),self.region.get_end_key()).map_err(|e| EngineError::Other(box_err!(e)))?;
      
    // 2. 基于查詢的key拼接出raftstore層面的DataKey (raftstore在寫入時會給用戶key前添加一個前綴'z')。
    let data_key = keys::data_key(key);
      
    // 3. 使用內(nèi)部的RocksSnapshot查詢RocksDB獲取key對應(yīng)的值。
    self.snap.get_value_cf_opt(opts, cf, &data_key).map_err(|e| self.handle_get_value_error(e, cf, key))
}

4.4.5 RocksDB/Titan的Get實現(xiàn)

TiKV使用rust-rocksdb庫使用FFI實現(xiàn)與RocksDB C-API的交互,RocksSnapshot::get會通過crocksdb_get_pinned_cf將查詢接口委托給底層的RocksDB。值得注意的是,TiKV使用的并不是官方的RocksDB,而是自行維護(hù)的一個整合了Titan插件的版本。Titan是一個受WiscKey論文啟發(fā)而創(chuàng)建的項目,其主要目的是將存入RocksDB的大Value從LSM-Tree中分離出來,存儲到額外的Blob文件中,從而達(dá)到減小寫放大的目的。

本小節(jié)我們著重分析一下TitanDB中一次查詢的實現(xiàn)過程(做過大量精簡):

Status TitanDBImpl::GetImpl(const ReadOptions& options,
                            ColumnFamilyHandle* handle, const Slice& key,
                            PinnableSlice* value) {
    
  // 先查詢RocksDB
  s = db_impl_->GetImpl(options, key, gopts);
  
  // 如果Key的Value不存在或者不是BlobIndex, 則直接返回
  if (!s.ok() || !is_blob_index) return s;
    
  // Value是BlobIndex,說明這是一個索引,還需要額外查詢BlobStorage
  BlobIndex index;
  s = index.DecodeFrom(value);
  assert(s.ok());
  if (!s.ok()) return s;
  
  BlobRecord record;
  PinnableSlice buffer;
  
  mutex_.Lock();
  // 根據(jù)索引查詢BlobStorage
  auto storage = blob_file_set_->GetBlobStorage(handle->GetID()).lock();
  mutex_.Unlock();
  
  if (s.ok()) {
    value->Reset();
    value->PinSelf(record.value);
  }
  return s;
}

五、總結(jié)

  1. TiKV對數(shù)據(jù)存儲層的職能進(jìn)行了非常合理的抽象,通過Engine/Snapshot/Iterator等trait定義實現(xiàn)了存儲層與上層的解耦。
  2. TiKV在RocksDB提供的多列族原子性寫入能力之上實現(xiàn)了Percolator模型,提供了分布式事務(wù)和MVCC等能力,并實現(xiàn)了AsyncCommit和1PC等改善了事務(wù)提交延遲。
  3. TiKV實現(xiàn)了一個基于RocksDB的KV分離插件titan, 借鑒了Wisckey的思想將大Value從LSM-Tree中分離,在大Value的業(yè)務(wù)場景下能夠通過降低寫放大改善性能。
  4. 從PointGet的實現(xiàn)我們可以看到在使用了MVCC的情況下,查詢時遇到前一事務(wù)Prewrite產(chǎn)生的Lock仍然需要等待Resolve,因此在AsyncCommit開啟的前提下,業(yè)務(wù)開發(fā)需要盡量避免設(shè)計事務(wù)提交后即刻發(fā)起查詢的場景,此外也要盡量避免由于大事務(wù)提交延遲高影響相關(guān)的查詢。

參考資料:

責(zé)任編輯:龐桂玉 來源: vivo互聯(lián)網(wǎng)技術(shù)
相關(guān)推薦

2011-05-26 10:05:48

MongoDB

2021-07-06 09:29:38

Cobar源碼AST

2021-03-23 09:17:58

SpringMVCHttpServletJavaEE

2023-02-26 08:42:10

源碼demouseEffect

2012-09-20 10:07:29

Nginx源碼分析Web服務(wù)器

2011-05-26 16:18:51

Mongodb

2020-07-28 08:54:39

內(nèi)核通信Netlink

2022-01-06 07:06:52

KubernetesResourceAPI

2017-01-12 14:52:03

JVMFinalRefere源碼

2022-08-27 08:02:09

SQL函數(shù)語法

2009-07-08 13:22:30

JDK源碼分析Set

2022-05-30 07:36:54

vmstoragevmselect

2021-09-05 07:35:58

lifecycleAndroid組件原理

2012-09-06 10:07:26

jQuery

2019-09-09 06:30:06

Springboot程序員開發(fā)

2023-03-17 07:53:20

K8sAPIServerKubernetes

2021-02-19 06:56:33

架構(gòu)協(xié)程應(yīng)用

2017-01-11 14:02:32

JVM源碼內(nèi)存

2022-07-19 20:04:31

NAPI模塊鴻蒙

2017-02-27 11:48:58

JVM源碼分析Java
點贊
收藏

51CTO技術(shù)棧公眾號

久久久久久久久久久久久久免费看| 亚洲国产高清av| 污污的视频网站在线观看| 免费亚洲网站| 久久久精品视频成人| 欧美做受高潮中文字幕 | 欧美福利小视频| 中文字幕第4页| 美女国产精品久久久| 欧美性少妇18aaaa视频| 国产又粗又爽又黄的视频| 色视频在线看| 国产精品一品二品| 国产精品成人av在线| 久久免费播放视频| 999国产精品| 精品视频在线观看日韩| 亚洲三级在线视频| 欧美aaa大片视频一二区| 亚洲一区二区成人在线观看| 亚洲精品中字| 欧美zozo| 99精品视频在线观看免费| 91色在线视频| 99成人精品视频| 亚洲男人影院| 国产+人+亚洲| 男人av资源站| 日韩精品一区二区久久| 亚洲美女黄色片| 中文字幕一区二区人妻电影丶| 9999在线精品视频| 欧美日韩色综合| 日本在线视频www| www555久久| 亚洲美女区一区| 在线看成人av电影| 午夜视频在线看| 久久―日本道色综合久久| 韩国成人av| 丰满人妻一区二区三区四区53| 国产在线视频不卡二| 国产又爽又黄的激情精品视频 | 91三级在线| 亚洲欧洲国产日韩| 三级网在线观看| 日本三级在线播放完整版| 国产欧美日韩在线| 日本一区二区视频| 搞黄视频在线观看| 亚洲国产精品成人综合| 欧美在线一二三区| 毛片在线播放网址| 国产亚洲精品资源在线26u| 欧美日韩一区二区视频在线| 青青草在线免费视频| 久久日韩精品一区二区五区| 欧美日本亚洲| 国产黄在线看| 中文字幕中文在线不卡住| 手机福利在线视频| 影音先锋中文在线视频| 亚洲综合久久av| 精品视频在线观看一区| 色老头在线一区二区三区| 欧美日韩亚洲精品内裤| 日韩亚洲在线视频| 久久日本片精品aaaaa国产| 在线成人av影院| 国产精品偷伦视频免费观看了| 91国内精品| 日韩黄色在线免费观看| 蜜臀久久99精品久久久久久| 四虎成人精品永久免费av九九| 操日韩av在线电影| 久久精品99国产精| 国产视频一区在线观看一区免费| 欧美亚洲视频在线看网址| 日本中文字幕在线观看视频| 久久精品国产一区二区三| 18成人免费观看网站下载| 少妇人妻偷人精品一区二区| 久久日韩精品一区二区五区| 在线观看日韩羞羞视频| 9lporm自拍视频区在线| 91黄色在线观看| 北条麻妃亚洲一区| 宅男在线一区| 最近2019中文字幕一页二页 | 欧美成人一品| 欧美专区在线观看| av在线资源观看| 久久综合狠狠综合| 丰满女人性猛交| 忘忧草在线影院两性视频| 欧美日韩国产三级| 给我免费观看片在线电影的| 日韩一区欧美| 91精品国产电影| 国产又黄又大又爽| 2022国产精品视频| 韩国无码av片在线观看网站| 小黄鸭精品aⅴ导航网站入口| 69久久夜色精品国产69蝌蚪网| 中文字幕精品久久久| 99久久视频| 情事1991在线| 性生活三级视频| 国产精品国产三级国产有无不卡| 国产美女在线一区| 国产精品久一| 中文字幕成人精品久久不卡 | 国产九九精品| 999在线免费观看视频| 国产乱理伦片a级在线观看| 亚洲国产精品人人做人人爽| 天天做天天干天天操| 精品精品99| 97在线精品视频| 精品国产亚洲av麻豆| 亚洲国产精品成人综合| 久久精品香蕉视频| 丝袜久久网站| 午夜精品视频在线| 亚洲老妇色熟女老太| 亚洲人成小说网站色在线| 青青草原av在线播放| 国产精品白浆| 欧美激情视频三区| 国产成人精品一区二区无码呦 | 亚洲欧美久久久久一区二区三区| 亚洲精品动漫| 日韩av在线免费观看| 妺妺窝人体色www婷婷| 国产一区二区福利| 中文字幕在线亚洲精品| 欧美亚洲人成在线| 日韩在线观看视频免费| 最新在线中文字幕| 国产日韩视频一区二区三区| 欧美 日韩精品| 亚洲精品亚洲人成在线| 91wwwcom在线观看| 神马久久精品| 一本久久综合亚洲鲁鲁五月天| 亚洲调教欧美在线| 中日韩男男gay无套| 精品福利影视| 亚洲欧洲自拍| 亚洲色图狂野欧美| 中文字幕人成人乱码亚洲电影| 亚洲国产精品精华液ab| 99视频在线视频| 日韩一区二区三区免费播放| 成人网址在线观看| 日本性爱视频在线观看| 精品国产免费一区二区三区香蕉| 久久高清无码视频| 91亚洲精品久久久蜜桃| 久久精品香蕉视频| 久久精品播放| 96成人在线视频| 国产精品xx| 精品视频—区二区三区免费| 日日夜夜狠狠操| 亚洲欧洲另类国产综合| 久久精品无码专区| 亚洲综合丁香| 亚洲一区尤物| 北条麻妃一区二区三区在线| 777777777亚洲妇女| 黄色av网站在线| 欧美乱妇20p| 国产亚洲精品女人久久久久久| 99这里只有久久精品视频| 免费在线观看毛片网站| 91精品蜜臀一区二区三区在线| 亚洲综合一区二区不卡| 97成人资源| 久久国内精品一国内精品| 丰满少妇被猛烈进入| 色噜噜偷拍精品综合在线| 黄色香蕉视频在线观看| 9人人澡人人爽人人精品| 成人性生生活性生交12| 午夜精品免费| 日本一区二区三不卡| 少妇精品在线| 国产精品高清在线观看| 丝袜美女在线观看| 国产亚洲综合久久| www日本视频| 欧美视频在线不卡| 国产一级二级三级| 中文字幕av一区二区三区| 337p日本欧洲亚洲大胆张筱雨| 久久久久99| 台湾无码一区二区| 第一sis亚洲原创| 国产综合欧美在线看| 日韩一区二区三区四区五区 | 久艹在线免费观看| 日本在线电影一区二区三区| 国产亚洲自拍偷拍| 国产精品美女久久久久| 国产成人av在线| 波多野结衣中文在线| 久久手机精品视频| 高清美女视频一区| 日韩av影院在线观看| 精品女同一区二区三区| 欧美日韩一区二区在线观看| 国产精品视频免费播放| 一区二区三区欧美视频| 成年人网站在线观看视频| 91蜜桃在线免费视频| 国产吃瓜黑料一区二区| 国内外成人在线| www日韩在线观看| 国产精品最新自拍| 国产中文字幕乱人伦在线观看| 手机在线一区二区三区| 神马影院我不卡午夜| 美女网站一区| 久精品国产欧美| 国产精品成人自拍| 成人羞羞视频免费| 欧美国产亚洲精品| 亚洲a一级视频| 国产精品亚洲欧美日韩一区在线| 国产精品三级美女白浆呻吟| 欧美香蕉视频| 日本久久久久久久久久久| 成人精品视频久久久久| 在线免费看黄网站| 在线观看中文字幕亚洲| 黄色av网站在线看| 日韩精品在线看| 三级视频在线| 亚洲男人天堂九九视频| 你懂的在线看| 国产亚洲精品综合一区91| 看电影就来5566av视频在线播放| 日韩精品中文字幕视频在线| 三级在线观看| 亚洲人成电影网| 成年人在线观看视频| 最近2019中文免费高清视频观看www99 | 亚洲色成人www永久在线观看| 中文字幕亚洲综合久久五月天色无吗''| 在线观看国产一区| 亚洲精品久久| 性一交一乱一伧国产女士spa| 影音先锋久久久| 乱妇乱女熟妇熟女网站| 久久久久.com| 57pao国产成永久免费视频| 韩国三级在线一区| 欧美熟妇精品一区二区 | 美国美女黄色片| 国产精品美女久久久久高潮| 老司机成人免费视频| 一区二区高清免费观看影视大全| 国产无套内射又大又猛又粗又爽| 亚洲成av人片在www色猫咪| 天堂网av手机版| 精品动漫一区| 国产精品91一区| 玖玖精品在线| wwwxx欧美| 日本成人7777| 亚洲精品久久区二区三区蜜桃臀| 97久久视频| 亚洲色欲久久久综合网东京热| 亚洲综合国产| 激情图片中文字幕| 91视视频在线观看入口直接观看www | 欧美激情精品久久久久久免费印度| av在线加勒比| 国产精品美乳在线观看| 久久99成人| 欧美人与性禽动交精品| 雨宫琴音一区二区三区| 成人网站免费观看入口| 日本免费新一区视频| 欧美一级片在线免费观看| 久久久久久9999| 国产女片a归国片aa| 色网综合在线观看| 国产福利资源在线| 亚洲欧美成人网| 羞羞网站在线免费观看| 青青久久av北条麻妃黑人| 亚洲精品tv| 久久久久久久久一区| 一本一道久久a久久精品蜜桃 | 久久中文字幕在线观看| 色哟哟在线观看一区二区三区| 国产精品热久久| 亚洲人成电影在线观看天堂色| 亚洲七七久久综合桃花剧情介绍| 日本国产精品视频| 98视频精品全部国产| 亚洲国产aⅴ天堂久久| 四虎永久免费观看| 国产精品不卡一区二区三区| 99精品视频99| 日韩一级成人av| www 日韩| 欧美一级bbbbb性bbbb喷潮片| 国产精品视频一区视频二区| 日本一区二区三区视频免费看| 欧美色一级片| 中国黄色片一级| 国产午夜精品一区二区| 亚洲国产成人精品激情在线| 欧美一区二区三区不卡| 在线日本视频| 国产精品爱啪在线线免费观看| 琪琪久久久久日韩精品| 国产在线无码精品| 精久久久久久久久久久| 欧美亚洲色综久久精品国产| 狠狠色香婷婷久久亚洲精品| 全国男人的天堂网| 欧美激情二区三区| 国产乱码精品一区二区三区亚洲人 | 最新天堂中文在线| 久久美女艺术照精彩视频福利播放| 国产黄色片视频| 精品国产乱码久久久久久1区2区| 老司机精品视频在线观看6| 国产精品美女久久久久久免费| 亚洲资源网你懂的| 91成人在线观看喷潮教学| 成人午夜激情在线| 欧美成人三级在线观看| 91精品国产手机| av超碰免费在线| 成人亚洲激情网| 亚洲蜜桃视频| 国产成人强伦免费视频网站| 综合欧美亚洲日本| 精品国产一级片| 欧美激情在线一区| 国产三级精品三级在线观看国产| 97超碰在线人人| 不卡电影一区二区三区| 91porny在线| 亚洲欧美在线磁力| 亚洲www啪成人一区二区| 丝袜美腿玉足3d专区一区| 日韩电影免费一区| 肉色超薄丝袜脚交69xx图片| 6080yy午夜一二三区久久| 成人在线观看免费网站| 亚洲最大av网站| 精品999日本| 老鸭窝一区二区| 欧美在线影院一区二区| 日韩在线免费电影| 91入口在线观看| 国产精品久久777777毛茸茸| 国产熟妇搡bbbb搡bbbb| 欧美网站一区二区| 午夜影院免费在线| 国产日韩欧美二区| 久久性天堂网| www.av免费| 日韩av在线看| 欧美一级网址| 精品少妇在线视频| 久久久精品天堂| 国产成人久久精品77777综合 | 四虎成人在线观看| 亚洲国产黄色片| 国产另类xxxxhd高清| 先锋影音男人资源| 99久久夜色精品国产网站| 亚洲婷婷久久综合| 欧美第一黄网免费网站| 在线亚洲a色| 伦伦影院午夜理论片| 亚洲高清不卡在线观看| 国产在线一二三区| 99超碰麻豆| 日韩中文欧美在线| 欧洲猛交xxxx乱大交3| 亚洲欧美福利视频| 精品一区二区三区中文字幕 | 天堂网avav| 日韩精品在线视频| 国产视频网站一区二区三区| 成人黄色av片| 亚洲欧洲美洲综合色网| 欧美巨乳在线| 国产精品日韩高清| 久久精品999| 亚洲 欧美 日韩 在线| 久久久久久一区二区三区 |