在2.1及之前的 TiDB 版本中浇冰,對于事務(wù)的限制是和其他關(guān)系型數(shù)據(jù)庫而言比較特殊的地方贬媒,很多用戶在使用過程中總是會感覺比較困惑,本文針對事務(wù)限制做一些詳細(xì)的說明肘习,希望能夠幫助大家理解际乘。
官方定義
由于 TiDB 分布式兩階段提交的要求,修改數(shù)據(jù)的大事務(wù)可能會出現(xiàn)一些問題漂佩。因此脖含,TiDB 特意對事務(wù)大小設(shè)置了一些限制以減少這種影響:
每個鍵值對不超過 6MB
鍵值對的總數(shù)不超過 300,000
鍵值對的總大小不超過 100MB
詳見PingCAP 官方文檔罪塔,https://pingcap.com/docs-cn/sql/mysql-compatibility/
相信鍵值對應(yīng)該比較容易理解,畢竟 TiDB 底層存儲選用的是 rocksdb 引擎养葵,一種基于 key-value 的存儲結(jié)構(gòu)征堪。而每個鍵值對的大小和總大小限制分別是6MB 和100MB,這個應(yīng)該也比較容易理解关拒。關(guān)鍵在于每個事物包含鍵值對的總數(shù)不超過30W 這個經(jīng)常會引起一些誤解佃蚜,下面做一些詳細(xì)說明。
如何理解30W
很多人第一眼看上去着绊,以為是一個事務(wù)涉及的行數(shù)不能超過30W谐算,但其實不是這樣的,首先需要了解 TiKV 對于結(jié)構(gòu)化數(shù)據(jù)是如何轉(zhuǎn)化為 key-value 結(jié)構(gòu)存儲的归露。
對于 key-value 結(jié)構(gòu)的數(shù)據(jù)洲脂,可以認(rèn)為結(jié)構(gòu)如下
當(dāng)插入一條數(shù)據(jù)時,tikv 是如何記錄這條數(shù)據(jù)呢剧包,包含以下幾個步驟:
1恐锦、插入數(shù)據(jù)本身
2、插入唯一索引
3疆液、插入普通索引
綜上踩蔚,當(dāng)執(zhí)行 insert 事務(wù)時,30W 限制需要除以所有索引的數(shù)量(包含主鍵和唯一索引)枚粘。
下面考慮當(dāng)刪除一條數(shù)據(jù)時馅闽,tikv 是如何處理的。首先需要明確馍迄,rocksdb 引擎所有的操作都是新增福也,所以刪除也是插入,只是插入了一條 flag = del 的記錄攀圈,具體情況如下:
1暴凑、插入數(shù)據(jù)本身的刪除標(biāo)記
2、插入唯一索引的刪除標(biāo)記
3赘来、插入普通索引的刪除標(biāo)記
綜上现喳,當(dāng)執(zhí)行delete 事務(wù)時,30W 限制需要除以所有索引的數(shù)量(包含主鍵和唯一索引)犬辰。
更新比較復(fù)雜嗦篱,放到最后來說明。首先來看幌缝,更新的是非主鍵且無索引字段的情況灸促。
這種情況,只需要修改記錄本身的內(nèi)容即可,也就是下面一步:
1浴栽、插入數(shù)據(jù)本身即可
綜上荒叼,非主鍵且無索引字段更新,30W 限制就是30W典鸡。
其次被廓,來看更新的是非主鍵,但包含索引的字段情況萝玷。
1伊者、數(shù)據(jù)本身
2、如果更新字段上有唯一索引
3间护、如果更新字段上有普通索引
綜上亦渗,非主鍵但索引相關(guān)字段的更新,30W 限制需要除以(1 + 字段涉及索引數(shù)量 * 2)汁尺。
最后來看當(dāng)更新的是主鍵字段的情況法精。從上面插入的描述中可以看出,無論是數(shù)據(jù)本身痴突,還是索引搂蜓,都包含了 pk,所以主鍵更新會觸發(fā)所有的key 更新辽装,具體如下:
1帮碰、數(shù)據(jù)本身
2、所有的唯一索引
3拾积、所有的普通索引
綜上殉挽,主鍵字段的更新,30W 限制需要除以(所有索引的數(shù)量 * 2)拓巧。
30W 鍵值對的轉(zhuǎn)換
總結(jié)如下:
具體案例:
CREATE TABLE `t1` (
? `id` int(11) NOT NULL AUTO_INCREMENT,
? `name` char(10) CHARSET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
? `age` int(11) DEFAULT NULL,
? PRIMARY KEY (`id`),
? KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
以上面的簡單表結(jié)構(gòu)為例斯碌,該表有自增主鍵,外加1個普通索引肛度,那么上面的事務(wù)限制對應(yīng)的記錄數(shù)為
事務(wù)的其他限制
除了上面 rocksdb 層的限制意外傻唾,tidb 中對于事務(wù)還有另外一個限制
1、參數(shù) stmt-count-limit承耿,默認(rèn)值是5000冠骄。
StmtCountLimit limits the max count of statement inside a transaction.
也就是一個事務(wù)里面,默認(rèn)最多包含5000條 SQL statement加袋,在不超過上面 rocksdb 層的幾個限制的前提下凛辣,這個參數(shù)可以修改 tidb 的配置文件進(jìn)行調(diào)整。
2锁荔、另外在某些場景下蟀给,例如執(zhí)行insert into? select 的時候蝙砌,可能會遇到下面的報錯
ERROR 1105 (HY000): BatchInsert failed with error: [try again later]: con:3877 txn takes too much time, start: 405023027269206017, commit: 405023312534306817????
這個主要是有一個隱藏參數(shù)阳堕,max-txn-time-use跋理,默認(rèn)值是 gc_life_time - 10s,也就是590
具體參考 PingCAP GitHub 上的文檔:https://github.com/pingcap/tidb/blob/master/config/config.toml.example#L240
# The max time a Txn may use (in seconds) from its startTS to commitTS.# We use it to guarantee GC worker will not influence any active txn. Please make sure that this# value is less than gc_life_time - 10s.
所以我們要盡量保證一個事務(wù)在這個gc_life_time - 10s 的時間內(nèi)完成恬总,也可以通過調(diào)整 gc 時間 + 修改這個參數(shù)來避免這個問題前普,可能 tidb 的配置文件中沒有放出這個參數(shù),可以手動編輯壹堰,加入這個值拭卿。當(dāng)然了,更好的辦法應(yīng)該是開啟 tidb_batch_insert 參數(shù)來規(guī)避單個事務(wù)過大的問題贱纠。
如何繞開大事務(wù)的限制
官方提供內(nèi)部 batch 的方法峻厚,來繞過大事務(wù)的限制,分別由三個參數(shù)來控制:
tidb_batch_insert
作用域: SESSION默認(rèn)值: 0這個變量用來設(shè)置是否自動切分插入數(shù)據(jù)谆焊。僅在 autocommit 開啟時有效惠桃。 當(dāng)插入大量數(shù)據(jù)時,可以將其設(shè)置為 true辖试,這樣插入數(shù)據(jù)會被自動切分為多個 batch辜王,每個 batch 使用一個單獨的事務(wù)進(jìn)行插入。
tidb_batch_delete
作用域: SESSION默認(rèn)值: 0這個變量用來設(shè)置是否自動切分待刪除的數(shù)據(jù)罐孝。僅在 autocommit 開啟時有效呐馆。 當(dāng)刪除大量數(shù)據(jù)時,可以將其設(shè)置為 true莲兢,這樣待刪除數(shù)據(jù)會被自動切分為多個 batch汹来,每個 batch 使用一個單獨的事務(wù)進(jìn)行刪除。
tidb_dml_batch_size
作用域: SESSION默認(rèn)值: 20000這個變量用來設(shè)置自動切分插入/待刪除數(shù)據(jù)的的 batch 大小改艇。僅在 tidb_batch_insert 或 tidb_batch_delete 開啟時有效俗慈。 需要注意的是,當(dāng)單行總數(shù)據(jù)大小很大時遣耍,20k 行總數(shù)據(jù)量數(shù)據(jù)會超過單個事務(wù)大小限制闺阱。因此在這種情況下,用戶應(yīng)當(dāng)將其設(shè)置為一個較小的值舵变。
針對 update 場景酣溃,官方還是建議通過 limit 的方式來循環(huán)操作,目前并未提供內(nèi)部 batch update 的參數(shù)開關(guān)纪隙。
需要注意的是赊豌,開啟了 batch 功能之后,大事務(wù)的完整性就沒法保證了绵咱,只能保證每個批次的事務(wù)完整性碘饼。當(dāng)然,數(shù)據(jù)庫的最佳實踐依然是由程序或 DBA 來控制事務(wù)的大小,尤其是針對分布式數(shù)據(jù)庫艾恼,建議每個batch 控制在100條左右住涉,高并發(fā)的寫入,同時避免熱點現(xiàn)象钠绍,才能發(fā)揮TiDB? 分布式的優(yōu)勢舆声。