五分鐘技術(shù)趣談 | 時(shí)序數(shù)據(jù)庫TDengine在和家親上的應(yīng)用實(shí)踐
Part 01
業(yè)務(wù)背景
和家親APP作為中國移動智慧家庭業(yè)務(wù)入口,承載了大量智能設(shè)備告警推送和家庭業(yè)務(wù)推送。到目前為止,平臺每天產(chǎn)生將近30億的推送量,如何將這些推送數(shù)據(jù)進(jìn)行存儲、查詢和分析是一個(gè)比較棘手的問題。最初我們使用的是mysql集群分庫分表方案,但隨著數(shù)據(jù)量的增加,遇到了數(shù)據(jù)寫入和查詢的瓶頸,而且運(yùn)維復(fù)雜且不便管理。結(jié)合業(yè)務(wù)特點(diǎn),同時(shí)也是響應(yīng)集團(tuán)去IOE的要求,我們開始調(diào)研新的存儲方案--國產(chǎn)開源時(shí)序數(shù)據(jù)庫(Time Series Database)。
圖1 和家親業(yè)務(wù)數(shù)據(jù)存儲方案演進(jìn)
目前國產(chǎn)時(shí)序數(shù)據(jù)庫中比較有影響力的就IoTDB和TDengine,經(jīng)過我們多維度的選型測試,最終確定選擇TDengine作為新的數(shù)據(jù)存儲引擎,我們發(fā)現(xiàn)業(yè)務(wù)數(shù)據(jù)某些特點(diǎn)非常契合TDengine:
- 高頻寫入,峰值寫入高達(dá)7W條/秒
- 數(shù)據(jù)很少更新且查詢簡單低頻
- 數(shù)據(jù)存儲周期自動調(diào)整
- 數(shù)據(jù)帶有時(shí)間戳
Part 02
數(shù)據(jù)建模
區(qū)別于傳統(tǒng)關(guān)系型數(shù)據(jù)庫,數(shù)據(jù)寫入之前需要提前建表,TDengine有超級表的概念,具備自動建表的功能。這樣業(yè)務(wù)數(shù)據(jù)入庫只需要建一個(gè)庫和一張與業(yè)務(wù)需求相關(guān)的超級表,就可以在數(shù)據(jù)第一次入庫的時(shí)候自動創(chuàng)建子表。自動創(chuàng)建子表在第一次入庫時(shí)會有性能折損,但是經(jīng)過測試TDengine的自動建表效率非常高,幾乎可以忽略不計(jì)。在我們的業(yè)務(wù)場景中,我們把每個(gè)用戶對應(yīng)成一個(gè)設(shè)備一張表,用戶每天產(chǎn)生的告警寫入到自己的表中。
建庫語句如下:
CREATE DATABASE `hjq_push` BUFFER 900 CACHESIZE 1 CACHEMODEL 'none' COMP 2 DURATION 180m WAL_FSYNC_PERIOD 3000 MAXROWS 4096 MINROWS 10 STT_TRIGGER 16 KEEP 10080m,10080m,10080m PAGES 160 PAGESIZE 128 PRECISION 'ms' REPLICA 3 WAL_LEVEL 1 VGROUPS 200 SINGLE_STABLE 0;
超級表語句如下:
CREATE STABLE s_push (ts TIMESTAMP, guid BIGINT, source NCHAR(30),msgName NCHAR(64), msgContent NCHAR(1024), status SMALLINT, updateTime TIMESTAMP) TAGS(flag TINYINT);
利用超級表寫入語句:
INSERT INTO u_#{phone} USING s_push TAGS #{tag} (ts, guid, source, msgName, msgContent, status,updateTime) VALUES(#{ts},#{guid}, #{source}, #{msgName}, #{msgContent}, #{status},#{updateTime});
Part 03
性能表現(xiàn)
3.1 高效寫入
采用時(shí)序數(shù)據(jù)庫的一個(gè)重要原因就是支持高頻寫入。TDengine寫入速度極高,寫接近硬盤的連續(xù)寫入性能。經(jīng)過業(yè)務(wù)實(shí)際測試,峰值寫入7W/s完全沒有壓力。
圖2 業(yè)務(wù)實(shí)測寫入
當(dāng)然要達(dá)到高效的寫入性能,需要客戶端、數(shù)據(jù)源和服務(wù)端配合調(diào)試才能達(dá)到最優(yōu)狀態(tài)。
從客戶端角度:
- 盡量在一條寫入sql中拼接更多數(shù)據(jù)。
- 寫入方式:參數(shù)綁定>sql寫入(不自動建表)>sql寫入(自動建表)>無模式寫入。
從數(shù)據(jù)源角度:
- 通過隊(duì)列(kafka)方式來提升數(shù)據(jù)并發(fā)寫入。
- 盡量將同一張子表的數(shù)據(jù)提前匯聚到一起,提高寫入時(shí)數(shù)據(jù)的相鄰性。
從服務(wù)器配置角度:
需要根據(jù)系統(tǒng)磁盤的數(shù)量、I/O 能力及處理器性能在創(chuàng)建數(shù)據(jù)庫時(shí)設(shè)置適當(dāng)?shù)膙groups數(shù)量以充分發(fā)揮系統(tǒng)性能。如果vgroups過少,則系統(tǒng)性能無法發(fā)揮;如果vgroups過多,會造成無謂的資源競爭。常規(guī)推薦vgroups數(shù)量為CPU核數(shù)的2倍,但仍然要結(jié)合具體的系統(tǒng)資源配置進(jìn)行調(diào)優(yōu)。
3.2 系統(tǒng)及存儲性能
TDengine已經(jīng)在我們線上業(yè)務(wù)平穩(wěn)運(yùn)行一段時(shí)間,通過系統(tǒng)監(jiān)控CPU使用率平常不到15%,內(nèi)存使用率穩(wěn)定在10%。另外由于其高效的壓縮算法,可以節(jié)省大量存儲空間,相比于之前MySQL集群存儲只有1/7。下圖為集群中一臺DNode機(jī)器的監(jiān)控?cái)?shù)據(jù):
圖片
3.3 查詢性能
TDengine對常見ORM框架和數(shù)據(jù)庫連接池的支持較好,采用SQL作為查詢語言,開發(fā)簡單方便,對于之前使用關(guān)系型數(shù)據(jù)庫的開發(fā)者可以無縫切換。經(jīng)過業(yè)務(wù)測試,在單個(gè)子表查詢都能達(dá)到ms級別。
圖3 業(yè)務(wù)查詢測試
Part 04
遇到的一些問題
在使用TDengine的業(yè)務(wù)實(shí)踐中,也遇到一些問題(可能有些問題在最新的版本更新迭代):
- 空子表無法自動清理,由于我們數(shù)據(jù)存儲有周期性,但目前的TTL策略只是針對數(shù)據(jù)而不是子表本身,針對空的子表淘汰還需要腳本介入。
- 缺少簡潔易操作的可視化界面。
- 數(shù)據(jù)更新會影響count()函數(shù)性能(商業(yè)版有碎片整理功能,沒試過)
- LAST_ROW函數(shù)返回的數(shù)據(jù)集字段帶有l(wèi)ast_row(row),需要單獨(dú)解析
Part 05
結(jié)語
和家親推送業(yè)務(wù)對時(shí)序數(shù)據(jù)庫的使用目前也只是小試牛刀,相信隨著業(yè)務(wù)的發(fā)展,會使用到更多時(shí)序數(shù)據(jù)庫的功能特點(diǎn)。當(dāng)然同樣的業(yè)務(wù)數(shù)據(jù)可能還存在更優(yōu)的存儲方案, 無論是MySQL還是TDengine,都是優(yōu)秀的數(shù)據(jù)庫產(chǎn)品,最終還是業(yè)務(wù)場景為王,只有適配業(yè)務(wù)數(shù)據(jù)才是好產(chǎn)品。