一. TiDB的核心特性 高度相容 MySQL 大多數情況下,無需修改代碼即可從 MySQL 輕鬆遷移至 TiDB,分庫分表後的 MySQL 集群亦可通過 TiDB 工具進行實時遷移。 水平彈性擴展 通過簡單地增加新節點即可實現 TiDB 的水平擴展,按需擴展吞吐或存儲,輕鬆應對高併發、海量數據場景 ...
一. TiDB的核心特性
高度相容 MySQL
大多數情況下,無需修改代碼即可從 MySQL 輕鬆遷移至 TiDB,分庫分表後的 MySQL 集群亦可通過 TiDB 工具進行實時遷移。
水平彈性擴展
通過簡單地增加新節點即可實現 TiDB 的水平擴展,按需擴展吞吐或存儲,輕鬆應對高併發、海量數據場景。
分散式事務
TiDB 100% 支持標準的 ACID 事務。
高可用
相比於傳統主從 (M-S) 複製方案,基於 Raft 的多數派選舉協議可以提供金融級的 100% 數據強一致性保證,且在不丟失大多數副本的前提下,可以實現故障的自動恢復 (auto-failover),無需人工介入。
一站式 HTAP 解決方案
TiDB 作為典型的 OLTP 行存資料庫,同時兼具強大的 OLAP 性能,配合 TiSpark,可提供一站式 HTAP 解決方案,一份存儲同時處理 OLTP & OLAP,無需傳統繁瑣的 ETL 過程。
雲原生 SQL 資料庫
TiDB 是為雲而設計的資料庫,同 Kubernetes 深度耦合,支持公有雲、私有雲和混合雲,使部署、配置和維護變得十分簡單。
二.TiDB 整體架構
TiDB Server
TiDB Server 負責接收SQL請求,處理SQL相關的邏輯,並通過PD找到存儲計算所需數據的TiKV地址,與TiKV交互獲取數據,最終返回結果。TiDB Server 是無狀態的,其本身並不存儲數據,只負責計算,可以無限水平擴展,可以通過負載均衡組件(LVS、HAProxy或F5)對外提供統一的接入地址。
PD Server
Placement Driver(簡稱PD)是整個集群的管理模塊,其主要工作有三個:一是存儲集群的元信息(某個Key存儲在那個TiKV節點);二是對TiKV集群進行調度和負載均衡(如數據的遷移、Raft group leader的遷移等);三是分配全局唯一且遞增的事務ID。
PD 是一個集群,需要部署奇數個節點,一般線上推薦至少部署3個節點。PD在選舉的過程中無法對外提供服務,這個時間大約是3秒。
TiKV Server
TiKV Server 負責存儲數據,從外部看TiKV是一個分散式的提供事務的Key-Value存儲引擎。存儲數據的基本單位是Region,每個Region負責存儲一個Key Range(從StartKey到EndKey的左閉右開區間)的數據,每個TiKV節點會負責多個Region。TiKV使用Raft協議做複製,保持數據的一致性和容災。副本以Region為單位進行管理,不同節點上的多個Region構成一個Raft Group,互為副本。數據在多個TiKV之間的負載均衡由PD調度,這裡也就是以Region為單位進行調度
三. 存儲結構
一個 Region 的多個 Replica 會保存在不同的節點上,構成一個 Raft Group。其中一個 Replica 會作為這個 Group 的 Leader,其他的 Replica 作為 Follower。所有的讀和寫都是通過 Leader 進行,再由 Leader 複製給 Follower。
Key-Value 模型
TiDB對每個表分配一個TableID,每一個索引都會分配一個IndexID,每一行分配一個RowID(如果表有整形的Primary Key,那麼會用Primary Key的值當做RowID),其中TableID在整個集群內唯一,IndexID/RowID 在表內唯一,這些ID都是int64類型。每行數據按照如下規則進行編碼成Key-Value pair:
Key: tablePrefix_rowPrefix_tableID_rowID
Value: [col1, col2, col3, col4]
其中Key的tablePrefix/rowPrefix都是特定的字元串常量,用於在KV空間內區分其他數據。對於Index數據,會按照如下規則編碼成Key-Value pair
Key: tablePrefix_idxPrefix_tableID_indexID_indexColumnsValue
Value: rowID
Index 數據還需要考慮Unique Index 和 非 Unique Index兩種情況,對於Unique Index,可以按照上述編碼規則。但是對於非Unique Index,通常這種編碼並不能構造出唯一的Key,因為同一個Index的tablePrefix_idxPrefix_tableID_indexID_都一樣,可能有多行數據的ColumnsValue都是一樣的,所以對於非Unique Index的編碼做了一點調整:
Key: tablePrefix_idxPrefix_tableID_indexID_ColumnsValue_rowID
Value:null
這樣能夠對索引中的每行數據構造出唯一的Key。註意上述編碼規則中的Key裡面的各種xxPrefix都是字元串常量,作用都是用來區分命名空間,以免不同類型的數據之間互相衝突,定義如下:
var(
tablePrefix = []byte{'t'}
recordPrefixSep = []byte("_r")
indexPrefixSep = []byte("_i")
)
舉個簡單的例子,假設表中有3行數據:
1,“TiDB”, “SQL Layer”, 10
2,“TiKV”, “KV Engine”, 20
3,“PD”, “Manager”, 30
那麼首先每行數據都會映射為一個Key-Value pair,註意,這個表有一個Int類型的Primary Key,所以RowID的值即為這個Primary Key的值。假設這個表的Table ID 為10,其中Row的數據為:
t_r_10_1 --> ["TiDB", "SQL Layer", 10]
t_r_10_2 --> ["TiKV", "KV Engine", 20]
t_r_10_3 --> ["PD", "Manager", 30]
除了Primary Key之外,這個表還有一個Index,假設這個Index的ID為1,其數據為:
t_i_10_1_10_1 --> null
t_i_10_1_20_2 --> null
t_i_10_1_30_3 --> null
Database/Table 都有元信息,也就是其定義以及各項屬性,這些信息也需要持久化,我們也將這些信息存儲在TiKV中。每個Database/Table都被分配了一個唯一的ID,這個ID作為唯一標識,並且在編碼為Key-Value時,這個ID都會編碼到Key中,再加上m_首碼。這樣可以構造出一個Key,Value中存儲的是序列化後的元數據。除此之外,還有一個專門的Key-Value存儲當前Schema信息的版本。TiDB使用Google F1的Online Schema變更演算法,有一個後臺線程在不斷的檢查TiKV上面存儲的Schema版本是否發生變化,並且保證在一定時間內一定能夠獲取版本的變化(如果確實發生了變化)。
四. SQL 運算
用戶的 SQL 請求會直接或者通過 Load Balancer 發送到 tidb-server,tidb-server 會解析 MySQL Protocol Packet,獲取請求內容,然後做語法解析、查詢計劃制定和優化、執行查詢計劃獲取和處理數據。數據全部存儲在 TiKV 集群中,所以在這個過程中 tidb-server 需要和 tikv-server 交互,獲取數據。最後 tidb-server 需要將查詢結果返回給用戶。
五. 調 度
調度的流程
PD 不斷的通過 Store 或者 Leader 的心跳包收集信息,獲得整個集群的詳細數據,並且根據這些信息以及調度策略生成調度操作序列,每次收到 Region Leader 發來的心跳包時,PD 都會檢查是否有對這個 Region 待進行的操作,通過心跳包的回覆消息,將需要進行的操作返回給 Region Leader,併在後面的心跳包中監測執行結果。
註意這裡的操作只是給 Region Leader 的建議,並不保證一定能得到執行,具體是否會執行以及什麼時候執行,由 Region Leader 自己根據當前自身狀態來定。
信息收集
調度依賴於整個集群信息的收集,需要知道每個TiKV節點的狀態以及每個Region的狀態。TiKV集群會向PD彙報兩類信息:
(1)每個TiKV節點會定期向PD彙報節點的整體信息。
TiKV節點(Store)與PD之間存在心跳包,一方面PD通過心跳包檢測每個Store是否存活,以及是否有新加入的Store;另一方面,心跳包中也會攜帶這個Store的狀態信息,主要包括:
a) 總磁碟容量
b) 可用磁碟容量
c) 承載的Region數量
d) 數據寫入速度
e) 發送/接受的Snapshot數量(Replica之間可能會通過Snapshot同步數據)
f) 是否過載
g) 標簽信息(標簽是否具備層級關係的一系列Tag)
(2)每個 Raft Group 的 Leader 會定期向 PD 彙報Region信息
每個Raft Group 的 Leader 和 PD 之間存在心跳包,用於彙報這個Region的狀態,主要包括下麵幾點信息:
a) Leader的位置
b) Followers的位置
c) 掉線Replica的個數
d) 數據寫入/讀取的速度
PD 不斷的通過這兩類心跳消息收集整個集群的信息,再以這些信息作為決策的依據。
除此之外,PD 還可以通過管理介面接受額外的信息,用來做更準確的決策。比如當某個 Store 的心跳包中斷的時候,PD 並不能判斷這個節點是臨時失效還是永久失效,只能經過一段時間的等待(預設是 30 分鐘),如果一直沒有心跳包,就認為是 Store 已經下線,再決定需要將這個 Store 上面的 Region 都調度走。但是有的時候,是運維人員主動將某台機器下線,這個時候,可以通過 PD 的管理介面通知 PD 該 Store 不可用,PD 就可以馬上判斷需要將這個 Store 上面的 Region 都調度走。
調度策略
PD 收集以上信息後,還需要一些策略來制定具體的調度計劃。
一個Region的Replica數量正確
當PD通過某個Region Leader的心跳包發現這個Region的Replica的數量不滿足要求時,需要通過Add/Remove Replica操作調整Replica數量。出現這種情況的可能原因是:
A.某個節點掉線,上面的數據全部丟失,導致一些Region的Replica數量不足
B.某個掉線節點又恢復服務,自動接入集群,這樣之前已經彌補了Replica的Region的Replica數量過多,需要刪除某個Replica
C.管理員調整了副本策略,修改了max-replicas的配置
訪問熱點數量在 Store 之間均勻分配
每個Store以及Region Leader 在上報信息時攜帶了當前訪問負載的信息,比如Key的讀取/寫入速度。PD會檢測出訪問熱點,且將其在節點之間分散開。
各個 Store 的存儲空間占用大致相等
每個 Store 啟動的時候都會指定一個 Capacity 參數,表明這個 Store 的存儲空間上限,PD 在做調度的時候,會考慮節點的存儲空間剩餘量。
控制調度速度,避免影響線上服務
調度操作需要耗費 CPU、記憶體、磁碟 IO 以及網路帶寬,我們需要避免對線上服務造成太大影響。PD 會對當前正在進行的操作數量進行控制,預設的速度控制是比較保守的,如果希望加快調度(比如已經停服務升級,增加新節點,希望儘快調度),那麼可以通過 pd-ctl 手動加快調度速度。
支持手動下線節點
當通過 pd-ctl 手動下線節點後,PD 會在一定的速率控制下,將節點上的數據調度走。當調度完成後,就會將這個節點置為下線狀態。
一個 Raft Group 中的多個 Replica 不在同一個位置
以上內容為個人梳理總結於TiDB官網 https://www.pingcap.com/docs-cn/