據(jù)存儲利器TokuDB安裝配置與踩坑全記錄)
這臺 MySQL 單機 8 萬的寫入量全庫爆掉之后我才正兒八經(jīng)去研究 TokuDB。當(dāng)時生產(chǎn)環(huán)境里一堆日志類數(shù)據(jù)動輒幾百 GBInnoDB 吃磁盤吃得肉疼。換到 TokuDB 之后數(shù)據(jù)文件直接縮到原來的四分之一寫入也不再被索引折騰得死去活來。今天我不講那些花里胡哨的理論直接把這套安裝流程里真正會踩的坑、必須配的參數(shù)、按下去就要命的選項全部拆開來寫一遍。你要是之前只在 MySQL 官網(wǎng)下載過默認安裝包那大概率裝 TokuDB 直接會在加載插件那一步卡住。這個引擎的安裝牽扯到版本選型、底層依賴、內(nèi)核參數(shù)缺一個環(huán)節(jié)都跑不起來。下文按我從零裝的順序來適合數(shù)據(jù)庫運維、后端開發(fā)、以及準備把冷數(shù)據(jù)從 InnoDB 遷出來的兄弟。1. TokuDB 是什么值不值得花精力裝1.1 先說清楚它和 InnoDB 的核心差異TokuDB 最核心的東西叫 Fractal Tree分形樹索引跟 InnoDB 的 B 樹是完全不同的數(shù)據(jù)結(jié)構(gòu)。B 樹寫入的時候最怕索引頁頻繁分裂和隨機寫數(shù)據(jù)一多、索引一多寫放大非常明顯。TokuDB 的做法是在樹節(jié)點里預(yù)留一塊消息緩沖寫入操作先以消息形式掛在節(jié)點上攢到一定量再批量往下層合并。這個機制帶來的直接結(jié)果就是高并發(fā)隨機寫入時索引維護成本遠低于 B 樹。說人話就是同樣是一張大表、多個二級索引TokuDB 在寫入密集型場景下能把磁盤 I/O 壓得很低。再配合天然的頁壓縮能力數(shù)據(jù)文件體積通常能比 InnoDB 小好幾倍。另外還有一點必須提TokuDB 做 DDL 時不需要重建整張表。在 InnoDB 里給一張幾千萬行的大表加索引往往要折騰半天甚至更久TokuDB 加索引基本秒級完成因為 Fractal Tree 對局部重構(gòu)的需求很小。這一點在做歸檔表結(jié)構(gòu)變更時非常爽。1.2 適合你的場景才值得裝我的建議很直接別把它當(dāng)默認引擎也別把它神化。這玩意兒是為特定場景生的。場景推薦程度原因日志流水、埋點數(shù)據(jù)、IoT 傳感器數(shù)據(jù)強烈推薦寫入量大、索引多、對壓縮比敏感歷史歸檔、冷數(shù)據(jù)存儲強烈推薦文件小、省磁盤檢索頻率低需要頻繁在線加索引的表推薦DDL 不鎖不重建體驗接近秒改金融交易類強一致核心庫不推薦社區(qū)維護力度有限不建議賭核心數(shù)據(jù)MySQL 8.0 新項目完全不推薦官方 8.0 已不再內(nèi)置該引擎支持我見過有人把用戶主表和訂單表也遷到 TokuDB結(jié)果后續(xù)遇到一些復(fù)雜關(guān)聯(lián)查詢和在線 DDL 兼容問題時來回折騰成本反而更高。所以判斷標準很簡單如果你的表接近“寫入多、讀取少、數(shù)據(jù)量大、不常修改、允許壓縮存儲”那 TokuDB 是真香。核心交易鏈路還是老老實實 InnoDB 加合理拆分。2. 動手前的版本選型與環(huán)境準備2.1 版本選型是第一道坎TokuDB 不是裝個插件就能在所有 MySQL 里跑的。官方社區(qū)版默認發(fā)布的二進制里壓根就沒有包含 ha_tokudb.so 這類組件你拿官方包去搞INSTALL PLUGIN大概率直接報錯。實際可用的方案基本集中在 Percona Server 5.6、5.7 的分支里這一階段 TokuDB 集成度和穩(wěn)定性最高。5.7 之后的 8.0 時代TokuDB 就逐漸被官方邊緣化相關(guān)組件的更新幾乎停滯我不建議你在 8.0 上做任何嘗試。選版本的時候我建議直接選 5.7 的最新維護版本理由是它跟現(xiàn)有主流業(yè)務(wù)兼容性最好備份工具、監(jiān)控體系也都成熟。另一個細節(jié)是安裝方式如果你用的是 RPM 或者 DEB 包記得看有沒有單獨的 tokudb 子包。比如下載 Percona Server 的包列表時能看到 percona-server-tokudb-57 這樣的組件包單獨裝上才會把 TokuDB 相關(guān)的庫文件和配置模板一起放好。2.2 內(nèi)核參數(shù)和依賴庫準備這一步是最容易被忽略的。TokuDB 對運行環(huán)境有兩個硬性要求交代不清楚后面全是淚。第一jemalloc。TokuDB 的內(nèi)存管理深度依賴 jemalloc官方文檔里的說法是它需要 jemalloc 來避免內(nèi)存碎片化問題。我實測下來不配 jemalloc 直接啟動實例輕則一堆警告重則 TokuDB 初始化失敗。安裝方式很簡單絕大多數(shù) Linux 發(fā)行版都有現(xiàn)成包包名一般是 jemalloc 開頭具體叫 libjemalloc1、libjemalloc2 還是 jemalloc看你系統(tǒng)版本直接yum install jemalloc或apt install libjemalloc-dev就行。第二透明大頁THP必須關(guān)。Linux 的 THP 機制對 InnoDB 影響不大對 TokuDB 卻很容易引發(fā)嚴重的性能抖動我甚至遇到過因為 THP 導(dǎo)致 TokuDB 啟動時直接掛掉的情況。操作也很粗暴echo never /sys/kernel/mm/transparent_hugepage/enabled echo never /sys/kernel/mm/transparent_hugepage/defrag這兩個路徑在某些發(fā)行版上略有差異可以用ls /sys/kernel/mm/看實際目錄。只執(zhí)行一次是不會重啟保持的建議寫到/etc/rc.local或者通過 systemd 的 tmpfiles 配置做持久化。除此之外我還習(xí)慣把vm.swappiness調(diào)到較小值比如 1減少內(nèi)存交換帶來的抖動。如果底層是機械硬盤順便把磁盤 IO 調(diào)度器改成 deadlineSSD 則改成 none老內(nèi)核可能是 noop。2.3 內(nèi)存規(guī)劃先算清楚再加參數(shù)TokuDB 不像 InnoDB 使用 buffer pool 那一套它自己維護了一塊叫 cachetable 的內(nèi)存區(qū)域?qū)iT用來緩存索引節(jié)點和數(shù)據(jù)節(jié)點。你在 my.cnf 里看到的tokudb_cache_size就是這塊的上限。如果同一臺機器上還跑著 InnoDB內(nèi)存分配一定要先加總再配置。舉個例子物理內(nèi)存 32GInnoDB buffer_pool 給了 16G那 TokuDB cache_size 就不要再給 16G 了我一般會控制在 6G 到 8G留出操作系統(tǒng)和其他進程的空間。之前見過有人隨手設(shè)成 24G結(jié)果實例啟動后直接被系統(tǒng) OOM kill數(shù)據(jù)沒丟但業(yè)務(wù)中斷了一陣子非常不值。3. 一步步安裝 TokuDB 引擎3.1 安裝插件前的目錄準備在 Percona Server 的分支版本里TokuDB 的插件文件一般會放到 MySQL 的 plugin 目錄下。你可以在 MySQL 里執(zhí)行下面這條 SQL 確認插件目錄位置SHOW VARIABLES LIKE plugin_dir;拿到實際路徑后去目錄下找ha_tokudb.so是否存在。如果用的是打包安裝方式一般會自動放好無需手動拷貝。手動編譯或者遷移二進制時這一步容易漏。文件必須跟隨 MySQL 的 plugin 目錄權(quán)限屬主要一致否則啟動加載時會因為權(quán)限問題失敗。同時要確認數(shù)據(jù)目錄周邊有沒有_tokudb目錄規(guī)劃。沒創(chuàng)建也不要緊初始化階段引擎會自己建但你要知道 TokuDB 的數(shù)據(jù)文件并不是傳統(tǒng)的ibd文件結(jié)構(gòu)它有一套獨立的目錄組織方式后面看表文件大小的時候不能沿用 InnoDB 的習(xí)慣。3.2 配置 my.cnf 并啟動安裝依賴完成后我先在 my.cnf 里寫好基礎(chǔ)配置再啟動實例避免插件加載時參數(shù)缺失。一個相對穩(wěn)妥的起始配置[mysqld_safe] malloc-lib/usr/lib64/libjemalloc.so.1 [mysqld] tokudb_cache_size8G tokudb_commit_syncON tokudb_directioON tokudb_read_block_size128K tokudb_read_buf_size128K tokudb_write_buf_size4M tokudb_row_formattokudb_zlib注意malloc-lib的路徑不同的發(fā)行版、不同的 jemalloc 版本文件后綴和路徑差別很大。你可以執(zhí)行下面的命令確認find /usr -name libjemalloc.so* 2/dev/null把找到的真實路徑填進去。如果路徑寫錯TokuDB 啟動時同樣會報 jemalloc 相關(guān)錯誤。如果用的是 systemd 方式托管 MySQLmysqld_safe段可能不生效這種情況建議直接在 Environment 變量里追加LD_PRELOAD指向 libjemalloc 的絕對路徑。啟動實例之后第一步是確認引擎插件能不能被識別SHOW PLUGINS;看到輸出里存在 tokudb 行就說明組件已經(jīng)加載進來了。如果列表里沒有再看錯誤日志通常會有明確提示下一節(jié)我會介紹最常見的幾個。3.3 創(chuàng)建第一張 TokuDB 表插件加載成功后我們直接建表驗證。CREATE DATABASE test; USE test; CREATE TABLE tokudb_demo ( id BIGINT NOT NULL AUTO_INCREMENT, device_id INT NOT NULL, payload TEXT, created_at DATETIME NOT NULL, PRIMARY KEY (id), KEY idx_device_time (device_id, created_at) ) ENGINETokuDB ROW_FORMATtokudb_zlib;建表成功只是第一步還要養(yǎng)成習(xí)慣去確認這張表真的用上了 TokuDBSELECT TABLE_NAME, ENGINE, ROW_FORMAT, TABLE_ROWS FROM information_schema.TABLES WHERE TABLE_SCHEMAtest AND TABLE_NAMEtokub_demo;我這里把表名寫成了 tokub_demo實際你根據(jù)自己剛建的表名查詢。如果 ENGINE 列顯示的是 TokuDB就成功了。此時去數(shù)據(jù)目錄看一眼_tokudb_開頭的目錄會發(fā)現(xiàn)出現(xiàn)了一組文件這些就是 TokuDB 實際持有的數(shù)據(jù)節(jié)點和索引節(jié)點。驗證通過后你還可以做一次從 InnoDB 遷移的完整演練CREATE TABLE tbl_moved LIKE tbl_original; ALTER TABLE tbl_moved ENGINETokuDB ROW_FORMATtokudb_lzma; INSERT INTO tbl_moved SELECT * FROM tbl_original;這種先建結(jié)構(gòu)再插入的方式比直接ALTER TABLE ... ENGINETokuDB更容易控制進度適合大表遷移時不希望鎖住原表的場景。4. 安裝成功后的參數(shù)調(diào)優(yōu)與壓縮策略4.1 核心運行參數(shù)一次講全安裝跑通只是開始參數(shù)調(diào)不好TokuDB 跟普通引擎比也占不到便宜。tokudb_cache_size是最關(guān)鍵的一個。它決定 cachetable 的上限不宜設(shè)小太小會導(dǎo)致節(jié)點頻繁換入換出也不宜設(shè)大太大容易拖垮整機。我的參考策略是單機只跑 TokuDB 實例給物理內(nèi)存的 50%~60%和其他引擎混跑則根據(jù)內(nèi)存總量減掉 InnoDB 部分后再分配。tokudb_commit_sync控制提交時是否同步刷盤。默認是 ON數(shù)據(jù)安全優(yōu)先。如果你做的是大量非關(guān)鍵日志寫入可以改成 OFF 換取更高吞吐但代價是崩潰時可能丟最近一小段已提交事務(wù)這類場景需要業(yè)務(wù)上能容忍否則別動。tokudb_directio簡單理解是讓 TokuDB 繞過操作系統(tǒng)頁緩存直接讀寫磁盤。開頭我建議設(shè)置為 ON如果你的存儲本身是高性能 SSD這樣能少一層內(nèi)存拷貝。它和某些文件系統(tǒng)、虛擬化環(huán)境的兼容性需要實測遇到性能波動可以試著關(guān)掉對比。還有一個容易誤解的參數(shù)tokudb_read_block_size它決定從磁盤讀取節(jié)點時每次拉取多大的塊。機械盤可以設(shè)大一點以減少尋道SSD 保持 128K 一般夠了。4.2 壓縮算法怎么選TokuDB 提供好幾種壓縮算法用ROW_FORMAT控制。在建表和修改表時都可以指定ROW_FORMAT壓縮比寫入速度適用場景tokudb_lzma最高較慢冷數(shù)據(jù)歸檔幾乎不更新tokudb_zlib較高中等通用壓縮需求我主力推薦tokudb_quicklz中等較快寫入頻率高、需要一定壓縮tokudb_snappy較低最快對壓縮比無要求、只想要寫入快tokudb_uncompressed無壓縮最快不壓縮只想要 Fractal Tree我踩過的坑是剛開始圖壓縮比全表選了 lzma結(jié)果跑批量導(dǎo)入時 CPU 直接被打滿導(dǎo)入速度慢到懷疑人生。后來把實時寫入的表換成 snappy把只讀歸檔表保留為 lzma整體效果好了很多。建議你按數(shù)據(jù)熱度分層選擇而不是全庫一把梭。如果想在建表后修改可以執(zhí)行ALTER TABLE tokub_demo ROW_FORMATtokudb_lzma;這里有一點要注意TokuDB 的 DDL 雖然不需要重建表但切換壓縮格式本質(zhì)上是重寫節(jié)點該操作仍然會在后臺做大量 I/O。小表無所謂大表請安排在業(yè)務(wù)低峰期。4.3 用監(jiān)控指標確認引擎狀態(tài)經(jīng)常有人裝了 TokuDB 之后發(fā)現(xiàn)數(shù)據(jù)量是降了但說不清楚它到底工作正不正常。我在實踐中主要看幾個信息第一看SHOW ENGINE TOKUDB STATUS。輸出里能看到 cachetable 的當(dāng)前使用量、未刷盤節(jié)點數(shù)量、文件系統(tǒng)讀寫統(tǒng)計等。如果節(jié)點數(shù)量持續(xù)異常增長說明 cache_size 可能偏小。第二看information_schema.TOKUDB_FRACTAL_TREE_INFO這個在 Percona Server 5.7 里會暴露每張 TokuDB 表的 Fractal Tree 節(jié)點信息包括消息緩沖的使用情況。消息緩沖如果長期高水位說明寫入壓力大可以考慮調(diào)大寫入緩沖區(qū)。第三比較實際文件大小和邏輯數(shù)據(jù)量。系統(tǒng)表里查到的是邏輯行數(shù)實際占用空間去看數(shù)據(jù)目錄下的_tokudb_文件大小。兩者差距越大說明壓縮效果越好。我一般都是隔一段時間統(tǒng)計一次最后匯成一個表去評估成本節(jié)省情況。5. 常見安裝報錯與避坑實錄5.1 加載插件時最常見的報錯很多朋友跑INSTALL PLUGIN tokudb SONAME ha_tokudb.so;會直接得到一句ERROR 1123 (HY000): Cant initialize function tokudb或者ERROR 1126 (HY000): Cant open shared library .../ha_tokudb.so看到這類報錯先別慌按順序查三件事。第一插件目錄對不對確認ha_tokudb.so真實存在。第二有沒有裝 jemalloc并用 ldd 檢查它的依賴ldd /usr/local/mysql/lib/plugin/ha_tokudb.so如果輸出里有 not found 字樣說明某個共享庫缺失。第三把 MySQL 錯誤日志拖到最前面看有沒有關(guān)于內(nèi)存分配器初始化失敗的記錄。我遇到過的最典型組合是libjemalloc.so 路徑寫錯插件加載時報 undefined symbol一改路徑立刻就好了。5.2 內(nèi)核參數(shù)沒關(guān)導(dǎo)致的坑透明大頁沒關(guān)掉的一種典型表現(xiàn)是TokuDB 能啟動但長時間跑下來性能越來越差或者壓測時出現(xiàn)詭異的周期性抖動。排查方法很直接cat /sys/kernel/mm/transparent_hugepage/enabled如果輸出不是always [never]說明還沒關(guān)徹底。另外有些云主機重啟后內(nèi)核參數(shù)會被重置別只寫入一次就以為萬事大吉必須確認持久化生效。另一個被忽視的參數(shù)是文件系統(tǒng) mount 選項。TokuDB 對直接 I/O 的兼容性要求較高如果你在某個虛擬文件系統(tǒng)上開tokudb_directioON遇到校驗錯誤可以考慮先換成 OFF 驗證。我實際遇到過一次某個內(nèi)網(wǎng)低版本內(nèi)核加特定文件系統(tǒng)組合directio 開啟后直接 O_DIRECT 校驗失敗關(guān)閉后恢復(fù)。5.3 問題排查速查表現(xiàn)象大概率原因建議動作插件加載報 undefined symboljemalloc 未安裝或路徑錯誤確認 LD_PRELOAD 或 malloc-lib 路徑引擎啟動后自動退出透明大頁未關(guān)閉立即設(shè)置 never 并復(fù)查持久化寫入時 CPU 異常高壓縮算法選擇了 lzma考慮 zlib 或 snappy內(nèi)存被吃滿后 OOMcache_size 設(shè)得過大重新核算物理內(nèi)存和其他引擎占用數(shù)據(jù)文件沒有變小row_format 沒生效確認表當(dāng)前 row_format 是 tokudb_ 開頭備份腳本不識別 TokuDB 表用了只支持 InnoDB 的備份工具改用支持 TokuDB 的備份方式5.4 還有幾個容易忽略的細節(jié)TokuDB 對部分高級特性的支持不如 InnoDB 完整。全文索引這類功能就不用想了空間索引也基本別指望。外鍵支持層面存在兼容性差異我習(xí)慣在遷移前先用開發(fā)環(huán)境跑一遍外鍵相關(guān)鏈路不盲目信任文檔里的一兩句話。如果你做了主從復(fù)制TokuDB 表產(chǎn)生的 binlog 事件和 InnoDB 是完全兼容的復(fù)制本身沒問題。但災(zāi)難恢復(fù)測試要做足TokuDB 的崩潰恢復(fù)在極大數(shù)據(jù)量下比 InnoDB 慢這一點要在容災(zāi)預(yù)案里提前想到別真的宕機了才發(fā)現(xiàn)恢復(fù)時間不可控。備份方案也有講究。TokuDB 自己的備份插件提供的是熱備能力安裝方式類似引擎插件。如果你還在用傳統(tǒng)物理拷貝務(wù)必確認表空間文件沒有被占用的進程寫壞。我現(xiàn)在的習(xí)慣是保留一個周期性的邏輯導(dǎo)出畢竟邏輯導(dǎo)出雖然慢但可控性最強。我個人實際操作中還有一個體會千萬別在生產(chǎn)環(huán)境里直接改tokudb_row_format或tokudb_cache_size這種重磅參數(shù)。每次調(diào)整前先用一個小表測試再在準生產(chǎn)環(huán)境跑一輪壓測最后才輪到生產(chǎn)。TokuDB 的生態(tài)沒有 InnoDB 那么龐大的社區(qū)兜底寧可慢一點也別拿線上穩(wěn)定性去賭參數(shù)效果。