TiDB 在華泰證券的探索與實踐

文章轉載自公衆號華泰證券數字科技。前端

原文連接:mp.weixin.qq.com/s/Hp-ZJLdvd…mysql

做者介紹 華泰證券數字科技分佈式數據庫項目組,主要負責華泰證券分佈式數據庫系統建設工做,項目組成員均擁有多年數據庫從業經歷,對數據庫運做原理具備較深的研究,並積累了大量實戰經驗。sql

傳統數據庫存儲能力有限、擴容成本高、服務器壓力大、響應時間長等問題逐漸凸顯,分佈式數據庫應運而生。2016年末,華泰證券就已經開始着手調研分佈式數據庫產品。近年來,國家不斷提升對信息技術自主可控的戰略要求,發展和支持國產數據庫事業,不只能夠提高自主掌控能力,還能夠不斷下降企業經營成本。通過多方比較,本文將從 TiDB 技術特色、開發注意事項以及 TiDB 在華泰證券的實踐進展等方面進行介紹。數據庫

1. TiDB 技術特色

1.1 TiDB 簡介

TiDB 是一款開源分佈式 NewSQL 數據庫,結合了傳統 RDBMS 和 NoSQL 的最佳特性,其設計靈感來源於 Google Spanner 和 F1。TiDB 的設計目標是覆蓋 100% 的 OLTP 場景和 80% 的 OLAP 場景,更復雜的 OLAP 分析則經過 TiSpark 來完成。TiDB 屏蔽了分庫分表等 Sharding 方案對業務的侵入性,開發人員再也不須要關注數據如何分片等細節問題,專一於業務開發,極大地提高研發的生產力。bash

1.2 總體架構

TiDB 採用 Shared-Nothing、計算存儲分離的分佈式集羣架構,主要包括三個核心組件:TiDB Server、PD Server 和 TiKV Server。此外,還有用於解決用戶複雜 OLAP 需求的 TiSpark 組件。總體架構以下圖所示:服務器

  • TiDB Server

負責接收 SQL 請求,處理 SQL 相關的邏輯,並經過 PD 找到存儲計算所需數據的 TiKV 地址,與 TiKV 交互獲取數據,最終返回結果。TiDB Server 是無狀態的,其自己並不存儲數據,只負責計算,能夠無限水平擴展,能夠經過負載均衡組件(如 LVS、HAProxy 或 F5)對外提供統一的接入地址。網絡

  • PD(Placement Driver)Server

PD Server 是整個集羣的管理模塊,經過 Raft 協議實現多副本集羣架構,保證數據的一致性和高可用。其主要工做有三個:一是存儲集羣的元數據信息(某個 Key 存儲在哪一個 TiKV 節點);二是對 TiKV 集羣進行調度和負載均衡(如數據的遷移、Raft group leader 的遷移等);三是分配全局惟一且遞增的事務 ID。架構

  • TiKV Server

TiKV Server 負責存儲數據,從外部看 TiKV 是一個支持事務的分佈式 Key-Value 存儲引擎。存儲數據的基本單位是 Region,每一個 Region 負責存儲一個 Key Range(從 StartKey 到 EndKey 的左閉右開區間)的數據,每一個 TiKV 節點會負責多個 Region。TiKV 使用 Raft 協議作複製,保持數據的一致性和高可用。副本以 Region 爲單位進行管理,不一樣節點上的多個 Region 構成一個 Raft Group,互爲副本。數據在多個 TiKV 之間的負載均衡由 PD 以 Region 爲單位進行調度。併發

  • TiSpark

TiSpark 做爲 TiDB 中解決用戶複雜 OLAP 需求的主要組件,將 Spark SQL 直接運行在 TiDB 存儲層 TiKV 上,同時融合 TiKV 分佈式集羣的優點,並融入大數據社區生態。至此,TiDB 能夠經過一套系統,同時支持 OLTP 與 OLAP,免除用戶數據同步的煩惱。負載均衡

1.3 核心特性

TiDB 具有以下核心特性:

  • 高度兼容 MySQL

對於沒有事務衝突場景的業務系統,在大多數狀況下無需修改代碼便可從 MySQL 輕鬆遷移至 TiDB,分庫分表後的 MySQL 集羣亦可經過 TiDB 工具進行實時遷移。

  • 水平彈性擴展

這裏說的水平擴展包括兩方面:計算能力和存儲能力。TiDB Server 負責處理 SQL 請求,隨着業務的增加,能夠簡單的添加 TiDB Server 節點,提升總體的處理能力。TiKV 負責存儲數據,隨着數據量的增加,能夠部署更多的 TiKV Server 節點解決數據容量的問題。PD 會在 TiKV 節點之間以 Region 爲單位作調度,將部分數據遷移到新加的節點上。因此在業務的早期,能夠只部署少許的服務實例,隨着業務量的增加,按照需求添加 TiKV 或者 TiDB 實例。

  • 支持分佈式事務

TiDB 支持標準的 ACID 事務,經過兩階段提交和樂觀鎖實現分佈式事務。

  • 高可用

TiDB/TiKV/PD 這三個組件都能容忍部分實例失效,不影響整個集羣的可用性。TiDB 是無狀態的,能夠部署多個實例,前端經過負載均衡組件對外提供服務。當單個實例失效時,會影響正在這個實例上進行的會話,從應用的角度看,會出現單次請求失敗的狀況,從新鏈接後便可繼續得到服務。PD 是一個集羣,經過 Raft 協議保持數據的一致性,單個實例失效時,若是這個實例不是 Raft 的 leader,那麼服務徹底不受影響;若是這個實例是 Raft 的 leader,會從新選出新的 Raft leader,自動恢復服務。PD 在選舉的過程當中沒法對外提供服務,這個時間大約是 3 秒鐘。TiKV 是一個集羣,經過 Raft 協議保持數據的一致性,並經過 PD 作負載均衡調度。單個節點失效時,會影響這個節點上存儲的全部 Region。對於 Region 中的 Leader 節點,會中斷服務,等待從新選舉;對於 Region 中的 Follower 節點,不會影響服務。當某個 TiKV 節點失效,而且在一段時間內沒法恢復,PD 會將其上的數據遷移到其餘的 TiKV 節點。

  • 一站式 HTAP 解決方案

TiDB 做爲典型的 OLTP 行存數據庫,同時兼具強大的 OLAP 性能,配合 TiSpark,可提供一站式 HTAP(Hybrid Transactional and Analytical Processing)解決方案,一份存儲同時處理 OLTP & OLAP,無需傳統繁瑣的 ETL 過程。

  • 雲原生 SQL 數據庫

TiDB 是爲雲而設計的數據庫,支持公有云、私有云和混合雲。

2. TiDB 開發注意事項

做爲一款新興的 NewSQL 數據庫,TiDB 在許多方面取得了使人矚目的成績。尤爲在兼容性方面,TiDB 能夠說兼容 MySQL 90% 以上的行爲,這爲業務系統平滑遷移奠基了良好的基礎。但咱們依舊須要對剩下的 10% 的不兼容行爲保持嚴謹的態度,避免給業務系統帶來風險。

2.1 事務

(1)TiDB 的隔離級別

與不少傳統數據庫不一樣,TiDB 支持的隔離級別是 Snapshot Isolation(SI,快照隔離級別),採用「樂觀鎖+MVCC」的實現方式。它和 Repeatable Read(RR)隔離級別基本等價但也有必定的差別,詳細狀況以下:

①TiDB 的 SI 隔離級別能夠避免幻讀(Phantom Reads),但 ANSI/ISO SQL 標準中的 RR 隔離級別不能避免。

所謂幻讀是指:事務 A 首先根據條件查詢獲得 n 條記錄,而後事務 B 改變了這 n 條記錄以外的 m 條記錄或者增添了 m 條符合事務 A 查詢條件的記錄,致使事務 A 再次發起請求時發現有 n+m 條符合條件記錄,就產生了幻讀。

②TiDB 的 SI 隔離級別不能避免寫偏斜(Write Skew),須要使用 select for update 語法來避免寫偏斜。

寫偏斜是指:兩個併發的事務讀取了兩行不一樣但相關的記錄,接着這兩個事務各自更新了本身讀到的那行數據,並最終都提交了事務,若是這兩行相關的記錄之間存在着某種約束,那麼最終結果多是違反約束的。下圖的「黑白球」經常被用來講明寫偏斜問題:

TiDB 在默認配置下不能避免丟失更新(Lost Updates)。

所謂丟失更新是指:兩個事務 A、B 讀取相同記錄並更新同一列的值,若 A 先於 B 提交事務,當 B 事務提交後 A 再次查詢時發現本身的更新丟失了。

TiDB 在默認配置下不能避免丟失更新是因爲在事務衝突中提交較晚的事務被自動重試致使的(重試時會獲取最新的 tso,至關於從新開啓了一個事務),能夠將參數 tidb_disable_txn_auto_retry 設成 1 來避免丟失更新,可是修改後發生衝突的事務將會失敗並回滾,而不進行自動重試。

(2)顯式事務中 DML 語句返回的 affected rows 不可信

與全部使用了樂觀鎖機制的分佈式數據庫同樣,在顯式執行的事務中(設置爲非自動提交 autocommit=0,或使用 begin 語句顯式聲明事務開始),DML 操做所返回的 affected rows 並不保證與最終提交事務時所影響的數據行數一致。

以下案例,事務 B 在併發中丟失了它的更新,它的 affected rows 並不可靠。

這是因爲在顯式執行的事務中 DML 操做與提交操做分開被執行,在事務提交過程當中,若是因爲事務衝突、找不到 TiKV、網絡不穩定等緣由而發生了重試,TiDB 將獲取新的時間戳從新執行本事務中的 DML 操做,本來的 SI 隔離級別在重試後會產生相似 RC(Read Committed)隔離級別的不可重複讀與幻讀異常現象。因爲重試機制在內部完成,若是最終事務提交成功,用戶通常是沒法感知到是否發生了重試的,所以不能經過 affected rows 來做爲程序執行邏輯的判斷條件。而隱式事務中(以單條 SQL 爲單位進行提交),語句的返回是提交以後的結果,所以隱式事務中的 affected rows 是可信的。

( 3)不支持 Spring 的 PROPAGATION_NESTED 傳播行爲

Spring 支持的 PROPAGATION_NESTED 傳播行爲會啓動一個嵌套的事務,它是當前事務之上獨立啓動的一個子事務。嵌套事務開始時會記錄一個 savepoint,若是嵌套事務執行失敗,事務將會回滾到 savepoint 的狀態,嵌套事務是外層事務的一部分,它將會在外層事務提交時一塊兒被提交。下面案例展現了 savepoint 機制:

mysql> BEGIN;
mysql> INSERT INTO T2 VALUES(100);
mysql> SAVEPOINT svp1;
mysql> INSERT INTO T2 VALUES(200);
mysql> ROLLBACK TO SAVEPOINT svp1;
mysql> RELEASE SAVEPOINT svp1;
mysql> COMMIT;
mysql> SELECT * FROM T2;
+------+
| ID |
+------+
| 100 |
+------+
複製代碼

TiDB 不支持 savepoint 機制,所以也不支持 PROPAGATION_NESTED 傳播行爲。基於 Java Spring 框架的應用若是使用了 PROPAGATION_NESTED 傳播行爲,須要在應用端作出調整,將嵌套事務的邏輯移除。

(4)對大事務的限制

基於日誌的數據庫在面對大事務時,須要手動調大可用日誌的容量,以免日誌被單一事務佔滿。因爲 TiDB 分佈式兩階段提交的要求,修改數據的大事務可能會出現一些問題。所以,TiDB 對事務大小設置了一些限制以減小這種影響:

  • 每一個鍵值對不超過 6MB

  • 鍵值對的總數不超過 300000

  • 鍵值對的總大小不超過 100MB

一行數據是一個鍵值對,一行索引也是一個鍵值對,當一張表只有 2 個索引時,每 insert 一行數據會寫入 3 個鍵值對。據此,涉及大量數據增刪改的事務(如批量的對帳事務等),須要進行縮減事務量的改造,最佳實踐是將大事務改寫爲分頁 SQL,分段提交,TiDB 中能夠利用 order by 配合 limit 的 offset 實現分頁功能,寫法以下:

update tab set value=’new_value’ where id in (select id from tab order by id limit 0,10000);
commit;
update tab set value=’new_value’ where id in (select id from tab order by id limit 10000,10000);
commit;
update tab set value=’new_value’ where id in (select id from tab order by id limit 20000,10000);
commit;
... ...
複製代碼

2.2 自增 ID

TiDB 的自增 ID(auto_increment)只保證自增且惟一,並不保證連續分配。TiDB 目前採用批量分配的方式,因此若是在多臺 TiDB 上同時插入數據,分配的自增 ID 會不連續。當多個線程併發往不一樣的 tidb-server 插入數據的時候,有可能會出現後插入的數據自增 ID 小的狀況。此外,TiDB 容許給整型類型的列指定 auto_increment,且一個表只容許一個屬性爲 auto_increment的列。

2.3 惟一性約束

和其餘數據庫同樣,TiDB 中的主鍵和惟一索引都是表中數據的惟一性約束,可是有以下不一樣點:

  • TiDB 中的主鍵必須在建表時聲明,目前版本(v2.1.0)還不能爲已有的表添加、修改或刪除主鍵;惟一索引沒有此限制

  • Drop Column 操做不支持刪除主鍵列

TiDB 不支持外鍵,要去掉全部表結構中建立外鍵的相關語句。外鍵的級聯操做多表數據的功能須要在應用中完成。

2.4 索引

和表中的數據同樣,TiDB 中表的索引在存儲引擎中也被做爲 KV 來存儲,一行索引是一個 KV 對。例如一張有 2 個索引的表,每插入一行數據的時候,會寫入 3 個 KV 對。

TiDB 支持主鍵索引、惟一索引,也支持二級索引,構成以上索引的能夠是單一列,也能夠是多個列(複合索引)。TiDB 目前(v2.1.0)還不支持雙向索引、全文索引、分區表的全局索引。

TiDB 中在查詢的謂詞是 =,>,<,>=,<=,like ‘...%’,not like ‘...%’,in,not in,<>,!=,is null,is not null 時可以使用索引,使用與否由優化器來決策。TiDB 中在查詢的謂詞是 like ‘%...’,like ‘%...%’,not like ‘%...’,not like ‘%...%’,<=>時沒法使用索引。

TiDB 目前(v2.1.0)對於一張表的查詢還不能同時利用到這張表上的兩個索引。

TiDB 中的複合索引與其餘數據庫同樣,設計的通常原則是儘量的把數據值區分度高的列排在前面,這樣就可讓 SQL 在執行時儘快篩選出更少的數據行。在當前版本(v2.1.0 及如下的所有版本)使用中須要特別注意,複合索引中前一列的範圍查詢會停止後續索引列的使用,能夠經過下面的案例來理解這個特性。在以下的查詢中:

select a,b,c from tablename where a<predicate>’<value1>’ and b<predicate>’<value2>’and c<predicate>’<value3>’;
複製代碼

若是 a 條件的謂詞是 =in,那麼在 b 的查詢條件上就能夠利用到組合索引(a,b,c)。例:select a,b,c from tablename where a = 1 and b<5 and c=’abc’

一樣的,若是 a 條件和 b 條件的謂詞都是 =in,那麼在 c 上的查詢就能夠利用到組合索引(a,b,c)。例:select a,b,c from tablename where a in (1,2,3) and b = 5 and c=’abc’

若是 a 條件的謂詞不是 = 也不是 in,那麼 b 上的查詢就沒法利用到組合索引(a,b,c)。此時 b 條件將在 a 條件篩選後的數據中進行無索引的數據掃描。例:select a,b,c from tablename where a > 1 and b<5 and c=’abc’

這是因爲在 TiDB 中,複合索引中排在前面的列若是被用於範圍查詢,那麼後續列的查詢就會在前一列篩選後的數據範圍中進行非索引的掃描。

綜上,在 TiDB 中進行復合索引設計時,須要儘量的將區分度高的列排在前面,將常常進行範圍查詢的列排在後面。

2.5 寫入熱點

TiKV 是一個按 range 切分的 KV 系統,KV 的 Key 決定了寫入位置在哪一個 region。對於主鍵爲非整數或沒有主鍵的表,TiDB 會使用一個隱式的自增 rowid,大量 INSERT 時會把數據集中寫入單個 region,形成寫入熱點。經過設置表級別選項 SHARD_ROW_ID_BITS(以下所示)能夠把 rowid 打散寫入多個不一樣的 region,緩解寫入熱點問題。可是設置的過大會形成 RPC 請求數放大,增長 CPU 和網絡開銷。

SHARD_ROW_ID_BITS = 4 表示 2^4=16 個分片

SHARD_ROW_ID_BITS = 6 表示 2^6=64 個分片

SHARD_ROW_ID_BITS = 0 表示 2^0,就是默認值 1 個分片

CREATE TABLE 語句示例:

CREATE TABLE t (c int) SHARD_ROW_ID_BITS = 4;

ALTER TABLE 語句示例:

ALTER TABLE t SHARD_ROW_ID_BITS = 4;

分區表能夠將一張表的數據分散到多張物理表中,經過合理的設計分區規則,能夠進一步避免寫入熱點問題。

2.6 暫不支持的特性

TiDB 在大部分狀況下能保證與 MySQL 的兼容,不過一些特性因爲在分佈式環境下無法很好的實現,目前暫時不支持,好比:

  • 存儲過程

  • 視圖

  • 觸發器

  • 自定義函數

  • 外鍵約束

  • 全文索引

  • 空間索引

  • 非 UTF8 字符集

  • CREATE TABLE tblName AS SELECT stmt 語法

  • … …

3. 實踐機器

咱們從 2017 年初就開始了 TiDB 的調研與測試工做,到目前爲止,已經在多個業務系統測試了 TiDB 的功能與性能。TiDB 也從最初運行不穩定、性能很差、周邊工具缺失的年輕產品,慢慢成長爲了產品穩定、性能可隨節點數目線性擴展、周邊工具豐富、社區火熱的金融級分佈式 NewSQL 數據庫。

2019 年 4 月下旬,咱們上線了第一套 TiDB 生產集羣,採用 6 臺服務器構成「3 TiDB Server + 3 TiKV Server」的架構。PD Server 與 TiDB Server 共享服務器。每臺服務器配置 128 GB 內存,2 路共 12 核 CPU,6 塊 960GB SSD 盤作成 RAID 10。

目前接入生產 TiDB 集羣的業務系統分爲如下幾個階段進行實施:

1)經過 TiDB Lightning 工具將當月之前的歷史存量數據以文件的方式導入 TiDB 集羣

2)上線當日,經過 TiDB Lightning 工具將當月的數據以文件的方式導入 TiDB 集羣

3)上線以後,業務端進行雙寫,利用 kafka 將新的數據同步到 TiDB 生產集羣

4)穩定運行幾個月後,將查詢流量逐步切到 TiDB

5)繼續穩定運行幾個月後,將查詢流量和寫入流量所有切到 TiDB,經過業務雙寫將新的數據同步到原 Mycat+MySQL 環境

6)完全下線原 Mycat+MySQL 環境

當前處於第三個階段。自上線以來,TiDB 集羣運行穩定,最高 QPS 達到每秒 3.4 萬筆。寫入速度與原 MySQL 環境至關,kafka 端未出現數據積壓,系統資源使用均衡,而且尚有餘量。

總結

從咱們實踐的結果來看,TiDB 這種 NewSQL 數據庫確實展示了不錯的技術優點。其提供的 MySQL 兼容性讓業務系統的改造代價大大下降,對分佈式事務的支持讓業務系統能夠像訪問單個 MySQL 庫同樣訪問 TiDB。基於 raft 協議的多副本機制,極大的保證了數據的一致性和可用性。其雲原生的設計理念,讓擴容縮容變得很是方便,大大解放了運維人員的時間。

可是咱們也須要看到它的缺點,TiDB 最大的缺點是還比較年輕,很多功能還沒有完善,所以咱們的思路是先小範圍試用,選擇非交易類系統進行推廣,待穩定運行後再擴大推廣範圍。

相關文章
相關標籤/搜索