1. 程式人生 > >TiKV 是如何存取資料的(下)

TiKV 是如何存取資料的(下)

上篇文章中,我們介紹了與 TiKV 處理讀寫請求相關的基礎知識,下面將開始詳細的介紹 TiKV 的讀寫流程。Enjoy~

作者:唐劉 @siddontang

RawKV

TiKV 提供兩套 API,一套叫做 RawKV,另一套叫做 TxnKV。TxnKV 對應的就是上面提到的 Percolator,而 RawKV 則不會對事務做任何保證,而且比 TxnKV 簡單很多,這裡我們先討論 RawKV。

Write

當進行寫入,譬如 Write a = 1,會進行如下步驟:

  1. Client 找 PD 問 a 所在的 Region

  2. PD 告訴 Region 相關資訊,主要是 Leader 所在的 TiKV

  3. Client 將命令傳送給 Leader 所在的 TiKV

  4. Leader 接受請求之後執行 Raft 流程

  5. Leader 將 a = 1 Apply 到 KV RocksDB 然後給 Client 返回寫入成功

Read

對於 Read 來說,也是一樣的操作,唯一不同在於 Leader 可以直接提供 Read,不需要走 Raft。

TxnKV

Write

對於 TxnKV 來說,情況就要複雜的多,不過大部分流程已經在 Percolator 章節進行說明了。這裡需要注意的是,因為我們要快速的 seek 到最新的 commit,所以在 RocksDB 裡面,我們會先將 TS 使用 bigendian 生成 8 位元組的 bytes,然後將這個 bytes 逐位取反,在跟原始的 key 組合儲存到 RocksDB 裡面,這樣就能保證最新的提交存放到前面,seek 的時候就能直接定位了,當然 seek 的時候,也同樣會將對應的 TS 按照相同的方式編碼處理。

譬如,假設一個 key 現在有兩次提交,commitTS 分別為 10 和 12,startTS 則是 9 和 11,那麼在 RocksDB 裡面,key 的存放順序則是:

Write CF:

a_12 -> 11
a_10 -> 9

Data CF:

a_11 -> data_11
a_9 -> data_9
複製程式碼

另外,還需要注意的是,對於 value 比較小的情況,TiKV 會直接將 value 存放到 Write CF 裡面,這樣 Read 的時候只要走 Write CF 就行了。在寫入的時候,流程如下:

PreWrite:

Lock CF: W a -> Lock + Data

Commit:
Lock CF: R a -> Lock + 10 + Data
Lock CF: D a

Write CF: W a_11 -> 10 + Data
複製程式碼

對於 TiKV 來說,在 Commit 階段無論怎樣都會讀取 Lock 來判斷事務衝突,所以我們可以從 Lock 拿到資料,然後再寫入到 Write CF 裡面。

Read

Read 的流程之前的 Percolator 已經有說明了,這裡就不詳細解釋了。

SQL Key Mapping

我們在 TiKV 上面構建了一個分散式資料庫 TiDB,它是一個關係型資料庫,所以大家需要關注的是一個關係型的 table 是如何對映到 key-value 上面的。假設我們有如下的表結構:

CREATE TABLE t1 {
	id BIGINT PRIMARY KEY,
	name VARCHAR(1024),
	age BIGINT,
	content BLOB,
	UNIQUE(name),
	INDEX(age),
}
複製程式碼

上面我們建立了一張表 t1,裡面有四個欄位,id 是主鍵,name 是唯一索引,age 是一個索引。那麼這個表裡面的資料是如何對應到 TiKV 的呢?

在 TiDB 裡面,任何一張表都有一個唯一的 ID,譬如這裡是 11,任何的索引也有唯一的 ID,上面 name 就是 12,age 就是 13。我們使用字首 t 和 i 來區分表裡面的 data 和 index。對於上面表 t1 來說,假設現在它有兩行資料,分別是 (1, “a”, 10, “hello”) 和 (2, “b”, 12, “world”),在 TiKV 裡面,每一行資料會有不同的 key-value 對應。如下:

PK
t_11_1 -> (1, “a”, 10, “hello”)
t_11_2 -> (2, “b”, 12, “world”)

Unique Name
i_12_a -> 1
i_12_b -> 2

Index Age
i_13_10_1 -> nil
i_13_12_2 -> nil
複製程式碼

因為 PK 具有唯一性,所以我們可以用 t + Table ID + PK 來唯一表示一行資料,value 就是這行資料。對於 Unique 來說,也是具有唯一性的,所以我們用 i + Index ID + name 來表示,而 value 則是對應的 PK。如果兩個 name 相同,就會破壞唯一性約束。當我們使用 Unique 來查詢的時候,會先找到對應的 PK,然後再通過 PK 找到對應的資料。

對於普通的 Index 來說,不需要唯一性約束,所以我們使用 i + Index ID + age + PK,而 value 為空。因為 PK 一定是唯一的,所以兩行資料即使 age 一樣,也不會衝突。當我們使用 Index 來查詢的時候,會先 seek 到第一個大於等於 i + Index ID + age 這個 key 的資料,然後看字首是否匹配,如果匹配,則解碼出對應的 PK,再從 PK 拿到實際的資料。

TiDB 在操作 TiKV 的時候需要保證操作 keys 的一致性,所以需要使用 TxnKV 模式。

結語

上面簡單的介紹了下 TiKV 讀寫資料的流程,還有很多東西並沒有覆蓋到,譬如錯誤處理,Percolator 的效能優化這些,如果你對這些感興趣,可以參與到 TiKV 的開發,歡迎聯絡我 [email protected]