【转】TDengine踩坑随记(最后一次更新:2021-4-7 20:30)
原文:https://www.cnblogs.com/quchunhui/p/13731825.html
------------------------------------------------------------------------
==背景==
在搭建工業(yè)物聯(lián)網(wǎng)的數(shù)據(jù)平臺(tái),用來完成設(shè)備連接以及數(shù)據(jù)存儲(chǔ)等,
以支持上層的應(yīng)用及數(shù)據(jù)分析等。目前使用的是mq→flink→influxdb的基本套路。
雖然說InfluxDB的性能和功能都非常的棒,特別是連續(xù)查詢對(duì)性能的支撐,
不過由于influxdb使用的是社區(qū)版,存在單點(diǎn)故障的問題,總之不是長(zhǎng)久直接。
于是打算嘗試驗(yàn)證一下陶老師的TDEngine。祝自己一切順利。
==環(huán)境==
服務(wù)器節(jié)點(diǎn)數(shù):3個(gè)(VMware虛擬機(jī))
操作系統(tǒng):Linux Centos8 64位
TDengine版本:2.0.4(2020年10月16日,使用版本換成了2.0.5.1)
JDK:1.8.0_77
JDBC客戶端版本建議:
tdengine:2.0.10.0,jdbc版本:2.0.15
tdengine:2.0.15.0,jdbc版本:2.0.18
==下載==
https://www.taosdata.com/cn/getting-started/
TDengine-server-2.0.4.0-Linux-x64.rpm TDengine-client-2.0.4.0-Linux-x64.tar.gz TDengine-alert-2.0.4.0-Linux-x64.tar.gz
==測(cè)試代碼==
所有程序的測(cè)試代碼放到了git上,需要的自取。
https://github.com/quchunhui/java-demo/tree/master/tdengine
==集群部署==
參考官網(wǎng):https://www.taosdata.com/cn/documentation/cluster/
PS:中文文檔是真的很爽,比看英文省心多了。
1、安裝server
上傳到服務(wù)器,并復(fù)制到各個(gè)節(jié)點(diǎn),在各個(gè)節(jié)點(diǎn)執(zhí)行rpm安裝命令
命令:rpm -ivh TDengine-server-2.0.4.0-Linux-x64.rpm
【小插曲】
在另一個(gè)集群上安裝新的版本(2.0.7.0)的時(shí)候,發(fā)現(xiàn)如論如何也無法使新增加的節(jié)點(diǎn)上線,
后來看了新版本的文檔,上面提示:在增加新的節(jié)點(diǎn)時(shí),先不要啟動(dòng)taosd,安裝時(shí),提示輸入是否要加入一個(gè)已經(jīng)存在的TDengine集群時(shí),第一個(gè)物理節(jié)點(diǎn)直接回車創(chuàng)建新集群,后續(xù)物理節(jié)點(diǎn)則輸入該集群任何一個(gè)在線的物理節(jié)點(diǎn)的FQDN:端口號(hào)(默認(rèn)6030);
2、修改配置
配置文件路徑:/etc/taos/taos.cfg
配置文件說明:TDengine的運(yùn)營(yíng)與維護(hù)
按照官網(wǎng)的提示,修改了幾個(gè)配置:firstEp、secondEp、fqdn、serverPort、logDir、dataDir、replica。
根據(jù)官網(wǎng)的說明,我羅列了配置的中文,供參考。沒有寫的是在這個(gè)版本的官網(wǎng)上沒有找到的了。
######################################################## # # # TDengine Configuration # # Any questions, please email support@taosdata.com # # # ######################################################## # first fully qualified domain name (FQDN) for TDengine system # taosd啟動(dòng)時(shí),主動(dòng)連接的集群中第一個(gè)dnode的end point, 默認(rèn)值為localhost:6030。 firstEp vm1:6030 # second fully qualified domain name (FQDN) for TDengine system, for cluster only # taosd啟動(dòng)時(shí),如果first連接不上,嘗試連接集群中第二個(gè)dnode的end point, 默認(rèn)值為空。 secondEp vm2:6030 # local fully qualified domain name (FQDN) # 數(shù)據(jù)節(jié)點(diǎn)的FQDN,缺省為操作系統(tǒng)配置的第一個(gè)hostname。如果習(xí)慣IP地址訪問,可設(shè)置為該節(jié)點(diǎn)的IP地址。 fqdn vm1 # first port number for the connection (12 continuous UDP/TCP port number are used) # taosd啟動(dòng)后,對(duì)外服務(wù)的端口號(hào),默認(rèn)值為6030。 serverPort 6030 # log file's directory # 日志文件目錄,客戶端和服務(wù)器的運(yùn)行日志文件將寫入該目錄。默認(rèn)值:/var/log/taos。 logDir /home/radmin/data/tdengine/log # data file's directory # 數(shù)據(jù)文件目錄,所有的數(shù)據(jù)文件都將寫入該目錄。默認(rèn)值:/var/lib/taos。 dataDir /home/radmin/data/tdengine/data # the arbitrator's fully qualified domain name (FQDN) for TDengine system, for cluster only # 系統(tǒng)中裁決器的end point, 缺省值為空。 # arbitrator arbitrator_hostname:6042 # number of threads per CPU core # numOfThreadsPerCore 1.0 # number of management nodes in the system # 系統(tǒng)中管理節(jié)點(diǎn)個(gè)數(shù)。默認(rèn)值:3。 # numOfMnodes 3 # enable/disable backuping vnode directory when removing dnode # vnodeBak 1 # enable/disable load balancing # 是否啟動(dòng)負(fù)載均衡。0:否,1:是。默認(rèn)值:1。 # balance 1 # role for dnode. 0 - any, 1 - mnode, 2 - dnode # dnode的可選角色。0-any; 既可作為mnode,也可分配vnode;1-mgmt;只能作為mnode,不能分配vnode;2-dnode;不能作為mnode,只能分配vnode # role 0 # max timer control blocks # maxTmrCtrl 512 # time interval of system monitor, seconds # monitorInterval 30 # number of seconds allowed for a dnode to be offline, for cluster only # dnode離線閾值,超過該時(shí)間將導(dǎo)致該dnode從集群中刪除。單位為秒,默認(rèn)值:86400*10(即10天)。 # offlineThreshold 8640000 # RPC re-try timer, millisecond # rpcTimer 300 # RPC maximum time for ack, seconds. # rpcMaxTime 600 # time interval of dnode status reporting to mnode, seconds, for cluster only # statusInterval 1 # time interval of heart beat from shell to dnode, seconds # shellActivityTimer 3 # time of keeping table meta data in cache, seconds # tableMetaKeepTimer 7200 # minimum sliding window time, milli-second # minSlidingTime 10 # minimum time window, milli-second # minIntervalTime 10 # maximum delay before launching a stream compution, milli-second # maxStreamCompDelay 20000 # maximum delay before launching a stream computation for the first time, milli-second # maxFirstStreamCompDelay 10000 # retry delay when a stream computation fails, milli-second # retryStreamCompDelay 10 # the delayed time for launching a stream computation, from 0.1(default, 10% of whole computing time window) to 0.9 # streamCompDelayRatio 0.1 # max number of vgroups per db, 0 means configured automatically # 每個(gè)數(shù)據(jù)庫(kù)中能夠使用的最大vnode個(gè)數(shù)。 # maxVgroupsPerDb 0 # max number of tables per vnode # 每個(gè)vnode中能夠創(chuàng)建的最大表個(gè)數(shù)。默認(rèn)值:1000000。 # maxTablesPerVnode 1000000 # step size of increasing table number in a vnode # tableIncStepPerVnode 1000 # cache block size (Mbyte) # cache 16 # number of cache blocks per vnode # blocks 6 # number of days per DB file # 一個(gè)數(shù)據(jù)文件存儲(chǔ)數(shù)據(jù)的時(shí)間跨度,單位為天,默認(rèn)值:10。 # days 10 # number of days to keep DB file # 數(shù)據(jù)庫(kù)中數(shù)據(jù)保留的天數(shù),單位為天,默認(rèn)值:3650。 # keep 3650 # minimum rows of records in file block # 文件塊中記錄的最小條數(shù),單位為條,默認(rèn)值:100。 # minRows 100 # maximum rows of records in file block # 文件塊中記錄的最大條數(shù),單位為條,默認(rèn)值:4096。 # maxRows 4096 # enable/disable compression # 文件壓縮標(biāo)志位,0:關(guān)閉,1:一階段壓縮,2:兩階段壓縮。默認(rèn)值:2。 # comp 2 # write ahead log (WAL) level, 0: no wal; 1: write wal, but no fysnc; 2: write wal, and call fsync # WAL級(jí)別。1:寫wal, 但不執(zhí)行fsync; 2:寫wal, 而且執(zhí)行fsync。默認(rèn)值:1。 # walLevel 1 # if walLevel is set to 2, the cycle of fsync being executed, if set to 0, fsync is called right away # 當(dāng)wal設(shè)置為2時(shí),執(zhí)行fsync的周期。設(shè)置為0,表示每次寫入,立即執(zhí)行fsync。單位為毫秒,默認(rèn)值:3000。 # fsync 3000 # number of replications, for cluster only # 副本個(gè)數(shù),取值范圍:1-3。單位為個(gè),默認(rèn)值:1 replica 3 # mqtt hostname # mqttHostName test.mosquitto.org # mqtt port # mqttPort 1883 # mqtt topic # mqttTopic /test # the compressed rpc message, option: # -1 (no compression) # 0 (all message compressed), # > 0 (rpc message body which larger than this value will be compressed) # compressMsgSize -1 # max length of an SQL # 單條SQL語(yǔ)句允許最長(zhǎng)限制。默認(rèn)值:65380字節(jié)。 maxSQLLength 1048576 # the maximum number of records allowed for super table time sorting # maxNumOfOrderedRes 100000 # system time zone # 默認(rèn)值:從系統(tǒng)中動(dòng)態(tài)獲取當(dāng)前的時(shí)區(qū)設(shè)置 # timezone Asia/Shanghai (CST, +0800) # system locale # 默認(rèn)值:系統(tǒng)中動(dòng)態(tài)獲取,如果自動(dòng)獲取失敗,需要用戶在配置文件設(shè)置或通過API設(shè)置 # locale en_US.UTF-8 # default system charset # 默認(rèn)值:系統(tǒng)中動(dòng)態(tài)獲取,如果自動(dòng)獲取失敗,需要用戶在配置文件設(shè)置或通過API設(shè)置 # charset UTF-8 # max number of connections allowed in dnode # maxShellConns 5000 # max numerber of connections allowed in client # maxConnections 5000 # stop writing logs when the disk size of the log folder is less than this value # minimalLogDirGB 0.1 # stop writing temporary files when the disk size of the log folder is less than this value # minimalTmpDirGB 0.1 # stop writing data when the disk size of the log folder is less than this value # minimalDataDirGB 0.1 # enbale/disable http service # http 1 # enable/disable muqq service # mqtt 0 # enable/disable system monitor # monitor 1 # enable/disable recording the SQL statements via restful interface # httpEnableRecordSql 0 # number of threads used to process http requests # httpMaxThreads 2 # maximum number of rows returned by the restful interface # restfulRowLimit 10240 # The following parameter is used to limit the maximum number of lines in log files. # max number of rows per log filters # 單個(gè)日志文件允許的最大行數(shù)。默認(rèn)值:10,000,000行。 # numOfLogLines 10000000 # time of keeping log files, days # 日志文件的最長(zhǎng)保存時(shí)間。大于0時(shí),日志文件會(huì)被重命名為taosdlog.xxx,其中xxx為日志文件最后修改的時(shí)間戳,單位為秒。默認(rèn)值:0天。 # logKeepDays 0 # enable/disable async log # asyncLog 1 # The following parameters are used for debug purpose only. # debugFlag 8 bits mask: FILE-SCREEN-UNUSED-HeartBeat-DUMP-TRACE_WARN-ERROR # 131: output warning and error, 135: output debug, warning and error, 143 : output trace, debug, warning and error to log. # 199: output debug, warning and error to both screen and file # 207: output trace, debug, warning and error to both screen and file # debug flag for all log type, take effect when non-zero value # debugFlag 0 # debug flag for meta management messages # mDebugFlag 135 # debug flag for dnode messages # dDebugFlag 135 # debug flag for sync module # sDebugFlag 135 # debug flag for WAL # wDebugFlag 135 # debug flag for SDB # sdbDebugFlag 135 # debug flag for RPC # rpcDebugFlag 131 # debug flag for TAOS TIMER # tmrDebugFlag 131 # debug flag for TDengine client # cDebugFlag 131 # debug flag for JNI # jniDebugflag 131 # debug flag for ODBC # odbcDebugflag 131 # debug flag for storage # uDebugflag 131 # debug flag for http server # httpDebugFlag 131 # debug flag for mqtt # mqttDebugFlag 131 # debug flag for monitor # monitorDebugFlag 131 # debug flag for query # qDebugflag 131 # debug flag for vnode # vDebugflag 131 # debug flag for http server # tsdbDebugFlag 131 # enable/disable recording the SQL in taos client # tscEnableRecordSql 0 # generate core file when service crash # enableCoreFile 1 # maximum display width of binary and nchar fields in the shell. The parts exceeding this limit will be hidden # maxBinaryDisplayWidth 30
PS:到此的感受:
總體感覺TDengine的安裝非常容易,配置文件也簡(jiǎn)單易懂,到此體驗(yàn)良好。
==啟動(dòng)==
1、啟動(dòng)第1個(gè)節(jié)點(diǎn)
命令:systemctl start taosd
2、驗(yàn)證第1個(gè)節(jié)點(diǎn)是否啟動(dòng)成功
命令:taos
命令:show dnodes;
3、啟動(dòng)后續(xù)數(shù)據(jù)節(jié)點(diǎn)
先將后兩個(gè)節(jié)點(diǎn)的服務(wù)啟動(dòng)起來。
在每個(gè)節(jié)點(diǎn)執(zhí)行:systemctl start taosd
確認(rèn)服務(wù)狀態(tài):systemctl status taosd
如下圖,第2個(gè)節(jié)點(diǎn)的服務(wù)正常:
第3個(gè)節(jié)點(diǎn)的服務(wù)正常:
【小插曲】
在啟動(dòng)第2個(gè)節(jié)點(diǎn)的時(shí)候報(bào)錯(cuò),
[root@vm2 ~]# systemctl status taosd ● taosd.service - TDengine server service Loaded: loaded (/etc/systemd/system/taosd.service; enabled; vendor preset: disabled) Active: failed (Result: start-limit) since Tue 2020-09-29 20:38:20 UTC; 2s ago Process: 5095 ExecStart=/usr/bin/taosd (code=exited, status=1/FAILURE) Main PID: 5095 (code=exited, status=1/FAILURE) Sep 29 20:38:20 vm2 systemd[1]: taosd.service: main process exited, code=exited, status=1/FAILURE Sep 29 20:38:20 vm2 systemd[1]: Unit taosd.service entered failed state. Sep 29 20:38:20 vm2 systemd[1]: taosd.service failed. Sep 29 20:38:20 vm2 systemd[1]: taosd.service holdoff time over, scheduling restart. Sep 29 20:38:20 vm2 systemd[1]: Stopped TDengine server service. Sep 29 20:38:20 vm2 systemd[1]: start request repeated too quickly for taosd.service Sep 29 20:38:20 vm2 systemd[1]: Failed to start TDengine server service. Sep 29 20:38:20 vm2 systemd[1]: Unit taosd.service entered failed state. Sep 29 20:38:20 vm2 systemd[1]: taosd.service failed.
【原因】
沒有創(chuàng)建log和data的文件路徑,在第2個(gè)和第3個(gè)節(jié)點(diǎn)上分別創(chuàng)建兩個(gè)路徑
mkdir -p /home/radmin/data/tdengine/log
mkdir -p /home/radmin/data/tdengine/data
4、添加節(jié)點(diǎn)
在第一個(gè)數(shù)據(jù)節(jié)點(diǎn),使用CLI程序taos, 登錄進(jìn)TDengine系統(tǒng), 執(zhí)行命令:
CREATE DNODE "vm2:6030"; CREATE DNODE "vm3:6030";
查看新的節(jié)點(diǎn)是否加入進(jìn)來。
【小插曲】
執(zhí)行show dnodes;之后,發(fā)現(xiàn)vm2這個(gè)節(jié)點(diǎn)處于offline狀態(tài)。按照官網(wǎng)提示的方法:
查看日志中提示如下錯(cuò)誤:
09/29 20:59:55.055274 0x7fcb0aae7700 DND ERROR status rsp is received, error:Cluster cfg inconsistent 09/29 20:59:56.060418 0x7fcb0aae7700 DND ERROR status rsp is received, error:Cluster cfg inconsistent 09/29 20:59:57.065157 0x7fcb0aae7700 DND ERROR status rsp is received, error:Cluster cfg inconsistent
【原因】
經(jīng)過群里的咨詢,有朋友建議排查一下時(shí)區(qū)是否一致,經(jīng)排查第2個(gè)節(jié)點(diǎn)的時(shí)區(qū)確實(shí)與另外兩個(gè)不一致。
修改了一下時(shí)區(qū)之后,從新啟動(dòng)第2個(gè)節(jié)點(diǎn),發(fā)現(xiàn)狀態(tài)恢復(fù)正常。修改時(shí)區(qū)命令:
mv /etc/localtime /etc/localtime.bak ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/locaktime
【小插曲2】
解決了上面的小插曲,非常的開心,合上筆記本電腦回家。
回家之后,重新連接上服務(wù)器,查看狀態(tài),結(jié)果發(fā)現(xiàn)第2個(gè)節(jié)點(diǎn)的狀態(tài)又變成offline了,真是開心不過3分鐘啊。
【原因】
發(fā)現(xiàn)時(shí)間貌似不同步。看來是時(shí)候加上NTP時(shí)間同步了。按照以下步驟嘗試了一下
1)停掉taos服務(wù)
2)配置ntp同步(參考我的另一篇博客:Linux配置ntp時(shí)間服務(wù)器)
3)重啟taos服務(wù)
重啟之后,第2個(gè)節(jié)點(diǎn)的狀態(tài)恢復(fù)為ready,開森。
為了以防萬一,將系統(tǒng)放置了一整夜,第2天早上起來查看集群狀態(tài),依然是正常的,這下才算放心。
【感謝道友】
5、數(shù)據(jù)節(jié)點(diǎn)管理命令
添加數(shù)據(jù)節(jié)點(diǎn):CREATE DNODE "fqdn:port"; 刪除數(shù)據(jù)節(jié)點(diǎn):DROP DNODE "fqdn:port"; 查看數(shù)據(jù)節(jié)點(diǎn):SHOW DNODES;
PS:到此的感受
TDengine的集群部署也非常的簡(jiǎn)單,比傳統(tǒng)的HBase,MongoDB之類的簡(jiǎn)單的多,感受不錯(cuò)。
唯一比較大的遺憾是,目前的社區(qū)版不支持多級(jí)存儲(chǔ),我們實(shí)際項(xiàng)目中磁盤是插滿的,
如果要利用磁盤空間,就必須要加上lvm,但是lvm會(huì)影響讀寫速度,對(duì)已經(jīng)存在的hdfs也有一些影響。
6、集群卸載
因?yàn)槭怯胷pm的格式安裝的,目前用的是yum remove來卸載,不清楚是否為最佳卸載方式。
systemctl stop taosd
rpm -qa | grep TD
yum removeTDengine-2.0.5.0-3.x86_64
2020年11月10日
卸載了2.0.5.0版本,重新安裝了2.0.7.0版本,和上面的步驟一樣
1、停止集群
2、卸載td
3、沖洗安裝
【小插曲】
在這次重新安裝的時(shí)候,遇到了下面的問題。在Git上提了issue:https://github.com/taosdata/TDengine/issues/4168
說是需要安裝python環(huán)境,可是我又沒有用到python,而且2.0.5.0的時(shí)候還沒有這個(gè)問題。
所以,tdengine的版本更新是不是太快了,導(dǎo)致一些問題沒能充分測(cè)試呢?
error: Failed dependencies:
/usr/bin/python is needed by tdengine-2.0.7.0-3.x86_64
解決過程:
按照提示,安裝了3.8.6版本的python,并確認(rèn)了python安裝沒有問題,嘗試重新安裝tdengine,仍然報(bào)了同樣的錯(cuò)誤,
后來和官方的人員確認(rèn)過了,應(yīng)該是官網(wǎng)放的release包有點(diǎn)問題,他們重新更新了一個(gè)包之后就可以了,
而且不需要安裝python,不需要安裝python,不需要安裝python。
==TAOS SQL==
官網(wǎng)提供了完整的文檔,官網(wǎng)地址:https://www.taosdata.com/cn/documentation20/taos-sql/
我個(gè)人習(xí)慣了逐個(gè)嘗試驗(yàn)證一遍,以加深印象,好記性不如爛筆頭。
另外,可以通過官方提供的樣例數(shù)據(jù)創(chuàng)建一些表供驗(yàn)證。
命令:taosdemo(注意,需要預(yù)留大約2.1GB的存儲(chǔ)空間)
1、數(shù)據(jù)庫(kù)管理
#創(chuàng)建庫(kù):
#COMP參數(shù)是指修改數(shù)據(jù)庫(kù)文件壓縮標(biāo)志位,取值范圍為[0, 2]. 0表示不壓縮,1表示一階段壓縮,2表示兩階段壓縮。
#REPLICA參數(shù)是指修改數(shù)據(jù)庫(kù)副本數(shù),取值范圍[1, 3]。在集群中使用,副本數(shù)必須小于dnode的數(shù)目。
#KEEP參數(shù)是指修改數(shù)據(jù)文件保存的天數(shù),缺省值為3650,取值范圍[days, 365000],必須大于或等于days參數(shù)值。
#QUORUM參數(shù)是指數(shù)據(jù)寫入成功所需要的確認(rèn)數(shù)。取值范圍[1, 3]。對(duì)于異步復(fù)制,quorum設(shè)為1,具有master角色的虛擬節(jié)點(diǎn)自己確認(rèn)即可。對(duì)于同步復(fù)制,需要至少大于等于2。原則上,Quorum >=1 并且 Quorum <= replica(副本數(shù)),這個(gè)參數(shù)在啟動(dòng)一個(gè)同步模塊實(shí)例時(shí)需要提供。
#BLOCKS參數(shù)是每個(gè)VNODE (TSDB) 中有多少cache大小的內(nèi)存塊,因此一個(gè)VNODE的用的內(nèi)存大小粗略為(cache * blocks)。取值范圍[3, 1000]。
#DAYS一個(gè)數(shù)據(jù)文件存儲(chǔ)數(shù)據(jù)的時(shí)間跨度,單位為天,默認(rèn)值:10。
create database mydb keep 365 days 10 blocks 4; #創(chuàng)建庫(kù)(如果不存在): create database if not exists mydb keep 365 days 10 blocks 4; #使用庫(kù): use mydb; #刪除庫(kù): drop database mydb; #刪除庫(kù)(如果存在): drop database if exists mydb; #顯示所有數(shù)據(jù)庫(kù): show databases; #修改數(shù)據(jù)庫(kù)文件壓縮標(biāo)志位: alter database mydb comp 2; #修改數(shù)據(jù)庫(kù)副本數(shù): alter database mydb replica 2; #修改數(shù)據(jù)文件保存的天數(shù): alter database mydb keep 365; #修改數(shù)據(jù)寫入成功所需要的確認(rèn)數(shù): alter database mydb quorum 2; #修改每個(gè)VNODE (TSDB) 中有多少cache大小的內(nèi)存塊: alter database mydb blocks 100;
2、表管理
#創(chuàng)建表(搞了個(gè)包含所有數(shù)據(jù)類型的表): create table if not exists mytable(time timestamp, intfield int, bigintfield bigint, floatfield float, doublefield double, binaryfield binary(20), smallintfield smallint, tinyintfield tinyint, boolfield bool, ncharfiel d nchar(50)); #刪除數(shù)據(jù)表 drop table if exists mytable; #顯示當(dāng)前數(shù)據(jù)庫(kù)下的所有數(shù)據(jù)表信息 show tables; #顯示當(dāng)前數(shù)據(jù)庫(kù)下的所有數(shù)據(jù)表信息 #可在like中使用通配符進(jìn)行名稱的匹配。通配符匹配:1)’%’ (百分號(hào))匹配0到任意個(gè)字符;2)’_’下劃線匹配一個(gè)字符。 show tables like "%my%"; #獲取表的結(jié)構(gòu)信息 describe mytable; #表增加列 alter table mytable add column addfield int; #表刪除列 alter table mytable drop column addfield;
注意:表名一定不要用數(shù)字開頭,官方文檔明確提醒了。
3、超級(jí)表管理
#創(chuàng)建超級(jí)表 #創(chuàng)建STable, 與創(chuàng)建表的SQL語(yǔ)法相似,但需指定TAGS字段的名稱和類型。說明: #1) TAGS 列的數(shù)據(jù)類型不能是timestamp類型; #2) TAGS 列名不能與其他列名相同; #3) TAGS 列名不能為預(yù)留關(guān)鍵字; #4) TAGS 最多允許128個(gè),可以0個(gè),總長(zhǎng)度不超過16k個(gè)字符 create table if not exists mysupertable (time timestamp, intfield int, bigintfield bigint, floatfield float, doublefield double, binaryfield binary(20), smallintfield smallint, tinyintfield tinyint, boolfield bool, nch arfield nchar(50)) TAGS (product nchar(50), device nchar(100)); #刪除超級(jí)表 drop table if exists mysupertable; #顯示當(dāng)前數(shù)據(jù)庫(kù)下的所有超級(jí)表信息 show stables like "%super%"; #獲取超級(jí)表的結(jié)構(gòu)信息 describe mysupertable; #超級(jí)表增加列 alter table mysupertable add column addfield int; #超級(jí)表刪除列 alter table mysupertable drop column addfield; #添加標(biāo)簽 alter table mysupertable add tag devicetype nchar(60); #刪除標(biāo)簽 alter table mysupertable drop tag devicetype; #修改標(biāo)簽名 alter table mysupertable change tag product productKey; #修改子表標(biāo)簽值 #說明:除了更新標(biāo)簽的值的操作是針對(duì)子表進(jìn)行,其他所有的標(biāo)簽操作(添加標(biāo)簽、刪除標(biāo)簽等)均只能作用于STable,不能對(duì)單個(gè)子表操作。對(duì)STable添加標(biāo)簽以后,依托于該STable建立的所有表將自動(dòng)增加了一個(gè)標(biāo)簽,所有新增標(biāo)簽的默認(rèn)值都是NULL。 alter table mysupertable set tag productkey="abc";
【小插曲】
執(zhí)行了一個(gè)下面的創(chuàng)建超級(jí)表的語(yǔ)句(通過自己寫的程序生成的),結(jié)果報(bào)錯(cuò)
SQL語(yǔ)句:create table if not exists AI_PICK_UDATA ( time timestamp, data double ) tags ( productKey binary(256),deviceName binary(256),pointId binary(256),name binary(256),dataType binary(256),min binary(256),max binary(256),step binary(256),unit binary(256),description binary(256) );
提示錯(cuò)誤:DB error: invalid SQL: invalid tag name
【問題原因】
創(chuàng)建表的語(yǔ)句中有tdengine的保留字段,如min,max。
但是官方文檔上并沒有對(duì)此進(jìn)行相關(guān)介紹,吐槽一下文檔。
4、數(shù)據(jù)插入
#插入一條數(shù)據(jù) insert into mytable values(now, 1, 2, 3, 4, 0, 6, 7, 1, "s"); #插入一條記錄,數(shù)據(jù)對(duì)應(yīng)到指定的列 insert into mytable(time, intfield, bigintfield, floatfield, doublefield, binaryfield, smallintfield, tinyintfield, boolfield, ncharfield) values(now, 1, 2, 3, 4, 0, 6, 7, 1, "s"); #插入多條記錄 insert into mytable values(now, 1, 2, 3, 4, 0, 6, 7, 1, "s") (now, 2, 3, 4, 5, 6, 7, 8, 0, "t"); #按指定的列插入多條記錄 insert into mytable(time, intfield, bigintfield, floatfield, doublefield, binaryfield, smallintfield, tinyintfield, boolfield, ncharfield) values(now, 1, 2, 3, 4, 0, 6, 7, 1, "s") (now, 2, 3, 4, 5, 6, 7, 8, 0, "t"); #向多個(gè)表插入多條記錄(本人沒有驗(yàn)證此語(yǔ)句) INSERT INTO tb1_name VALUES (field1_value1, ...)(field1_value2, ...) tb2_name VALUES (field1_value1, ...)(field1_value2, ...); #同時(shí)向多個(gè)表按列插入多條記錄(本人沒有驗(yàn)證此語(yǔ)句) INSERT INTO tb1_name (tb1_field1_name, ...) VALUES (field1_value1, ...) (field1_value2, ...) tb2_name (tb2_field1_name, ...) VALUES (field1_value1, ...) (field1_value2, ...);
【小插入】
插入多條記錄的時(shí)候,語(yǔ)句中寫的是兩條數(shù)據(jù),實(shí)際上只插入了一條
插入語(yǔ)句:
insert into mytable(time, intfield, bigintfield, floatfield, doublefield, binaryfield, smallintfield, tinyintfield, boolfield, ncharfield) values(now, 1, 2, 3, 4, 0, 6, 7, 1, "s") (now, 2, 3, 4, 5, 6, 7, 8, 0, "t");
【原因】
需要使用不同的時(shí)間戳,如果兩條語(yǔ)句都使用now,時(shí)間戳一樣,最終只能插入一條。
【感謝道友】
5、數(shù)據(jù)查詢
查詢語(yǔ)法:
SELECT select_expr [, select_expr ...]
FROM {tb_name_list}
[WHERE where_condition]
[INTERVAL (interval_val [, interval_offset])]
[FILL fill_val]
[SLIDING fill_val]
[GROUP BY col_list]
[ORDER BY col_list { DESC | ASC }]
[SLIMIT limit_val [, SOFFSET offset_val]]
[LIMIT limit_val [, OFFSET offset_val]]
[>> export_file]
==查詢語(yǔ)句==
查詢語(yǔ)法:
SELECT select_expr [, select_expr ...]
FROM {tb_name_list}
[WHERE where_condition]
[INTERVAL (interval_val [, interval_offset])]
[FILL fill_val]
[SLIDING fill_val]
[GROUP BY col_list]
[ORDER BY col_list { DESC | ASC }]
[SLIMIT limit_val [, SOFFSET offset_val]]
[LIMIT limit_val [, OFFSET offset_val]]
[>> export_file]
常用查詢語(yǔ)句樣例:
#查詢表中的所有字段 select * from t_znsllj001; #按照時(shí)間戳查詢表中的所有字段 select * from t_znsllj001 where time > "2020-10-10 22:23:08.728"; #按照時(shí)間戳查詢超級(jí)表中的所有字段 select * from st_znsllj where time > "2020-10-10 22:23:08.728"; #查詢超級(jí)表中的指定字段 select time, forwardintegratedflow, product from st_znsllj; #按照標(biāo)簽值查詢超級(jí)表中的指定字段 select time, forwardintegratedflow, product from st_znsllj where product = "product1"; #查詢結(jié)果按照時(shí)間倒序排序 select time, forwardintegratedflow, product from st_znsllj where product = "product1" order by time desc; #結(jié)果集列名重命名 select time, forwardintegratedflow as ff, product from st_znsllj; #查詢超級(jí)表數(shù)據(jù)并附帶表名(TBNAME: 在超級(jí)表查詢中可視為一個(gè)特殊的標(biāo)簽,代表查詢涉及的子表名,不區(qū)分大小寫) select tbname, * from st_znsllj; #查詢超級(jí)表的表名及第一列 select tbname, _c0 from st_znsllj; #獲取當(dāng)前所在的數(shù)據(jù)庫(kù) select database(); #獲取客戶端版本號(hào) select client_version() #獲取服務(wù)器版本號(hào) select server_version(); #服務(wù)器狀態(tài)檢測(cè)語(yǔ)句 select server_status() #統(tǒng)計(jì)超級(jí)表下轄子表數(shù)量 select count(tbname) from st_znsllj;
==用戶管理==
#創(chuàng)建用戶,并指定用戶名和密碼,密碼需要用單引號(hào)引起來,單引號(hào)為英文半角 create user admin pass 'admin123'; #刪除用戶,限r(nóng)oot用戶使用 drop user admin; #修改用戶密碼, 為避免被轉(zhuǎn)換為小寫,密碼需要用單引號(hào)引用,單引號(hào)為英文半角 alter user admin pass 'admin1234'; #修改用戶權(quán)限為:super/write/read。 為避免被轉(zhuǎn)換為小寫,密碼需要用單引號(hào)引用,單引號(hào)為英文半角 #語(yǔ)法:ALTER USER <user_name> PRIVILEGE <super|write|read>; alter user admin privilege read;
如果修改了root密碼之后,進(jìn)入命令行的命令就需要加上密碼的參數(shù)了。
可以通過taos --usage查看命令行的參數(shù)格式:
如:修改了密碼之后,可以通過以下命令來登錄命令行
taos --user=root -p'RexelRoot!@#'
==Java連接==
官網(wǎng)地址:https://www.taosdata.com/cn/documentation/connector-java/
1、maven配置
<dependency> <groupId>com.taosdata.jdbc</groupId> <artifactId>taos-jdbcdriver</artifactId> <version>2.0.4</version> </dependency>
2、JDBC連接樣例
【TdUtils.java】
單例工具類,實(shí)現(xiàn)創(chuàng)建連接等通用方法。
package com.rexel.tdengine.utils;
import com.taosdata.jdbc.TSDBDriver;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
/**
* @ClassName TdUtils
* @Description TDengine共通類
* @Author: chunhui.qu
* @Date: 2020/9/30
*/
public class TdUtils {
private Connection connection = null;
/**
* 構(gòu)造函數(shù)
*/
private TdUtils() {
// do nothing
}
/**
* 單例模式
*/
private static class SingletonInstance {
private static final TdUtils INSTANCE = new TdUtils();
}
/**
* 獲取對(duì)象句柄
*/
public static TdUtils getInstance() {
return SingletonInstance.INSTANCE;
}
public Connection getConnection() {
if (connection != null) {
return connection;
}
try {
Class.forName("com.taosdata.jdbc.TSDBDriver");
String jdbcUrl = "jdbc:TAOS://rexel-ids001:6030/qch_test?user=root&password=taosdata";
Properties connProps = new Properties();
connProps.setProperty(TSDBDriver.PROPERTY_KEY_USER, "root");
connProps.setProperty(TSDBDriver.PROPERTY_KEY_PASSWORD, "taosdata");
connProps.setProperty(TSDBDriver.PROPERTY_KEY_CONFIG_DIR, "C:\TDengine\cfg");
connProps.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
connProps.setProperty(TSDBDriver.PROPERTY_KEY_LOCALE, "en_US.UTF-8");
connProps.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "Asia/Shanghai");
connection = DriverManager.getConnection(jdbcUrl, connProps);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return connection;
}
}
【CreateDatabase.java】
創(chuàng)建數(shù)據(jù)的樣例程序。
package com.rexel.tdengine.api;
import com.rexel.tdengine.utils.TdUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @ClassName CreateDatabase
* @Description CreateDatabase
* @Author: chunhui.qu
* @Date: 2020/9/30
*/
public class CreateDatabase {
public static void main(String[] args) throws SQLException {
TdUtils tdUtils = TdUtils.getInstance();
Connection conn = tdUtils.getConnection();
if (conn == null) {
return;
}
System.out.println("get connection");
Statement stmt = conn.createStatement();
if (stmt == null) {
return;
}
stmt.executeUpdate("create database if not exists javatestdb");
System.out.println("create database");
stmt.executeUpdate("use javatestdb");
System.out.println("use database");
stmt.executeUpdate("create table if not exists javatesttable (ts timestamp, temperature int, humidity float)");
System.out.println("create table");
}
}
【小插曲1】
寫完了測(cè)試代碼,在執(zhí)行的時(shí)候報(bào)錯(cuò):
Exception in thread "main" java.lang.UnsatisfiedLinkError: no taos in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1867)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at com.taosdata.jdbc.TSDBJNIConnector.<clinit>(TSDBJNIConnector.java:25)
at com.taosdata.jdbc.TSDBDriver.connect(TSDBDriver.java:133)
at java.sql.DriverManager.getConnection(DriverManager.java:664)
at java.sql.DriverManager.getConnection(DriverManager.java:208)
at com.rexel.tdengine.utils.TdUtils.getConnection(TdUtils.java:54)
at com.rexel.tdengine.api.CreateDatabase.main(CreateDatabase.java:17)
Disconnected from the target VM, address: '127.0.0.1:54954', transport: 'socket'
【解決過程】
重新看了一下官方文檔,懷疑是沒有安裝Window客戶端,嘗試安裝客戶端程序。
安裝文件:TDengine-client-2.0.4.0-Windows-x64.exe
重新執(zhí)行程序之后,報(bào)了另一個(gè)錯(cuò)誤:
java.sql.SQLException: TDengine Error: Invalid timestamp
at com.taosdata.jdbc.TSDBJNIConnector.connect(TSDBJNIConnector.java:100)
at com.taosdata.jdbc.TSDBConnection.connect(TSDBConnection.java:64)
at com.taosdata.jdbc.TSDBConnection.<init>(TSDBConnection.java:56)
at com.taosdata.jdbc.TSDBDriver.connect(TSDBDriver.java:135)
at java.sql.DriverManager.getConnection(DriverManager.java:664)
at java.sql.DriverManager.getConnection(DriverManager.java:208)
at com.rexel.tdengine.utils.TdUtils.getConnection(TdUtils.java:54)
at com.rexel.tdengine.api.CreateDatabase.main(CreateDatabase.java:17)
網(wǎng)友說可能是因?yàn)榭蛻舳说臅r(shí)間與服務(wù)器時(shí)間相差比較多的原因,
于是調(diào)整了服務(wù)器的時(shí)間。命令如下,調(diào)整之后,重啟了一下服務(wù)器
修改系統(tǒng)時(shí)間:date --set "09/30/20 14:15" 系統(tǒng)時(shí)間向硬件同步:clock --show
重新運(yùn)行程序,正確執(zhí)行。感謝網(wǎng)友的幫助。
【感謝道友】
【小插曲2】
本來運(yùn)行的好好的,debug也都沒有問題,但是下午去酒店再debug測(cè)試,就提示“Unable to establish connection”
按照官網(wǎng)的FAQ提示,一頓檢查,沒有發(fā)現(xiàn)什么問題,非常迷惑。
后來想到由于服務(wù)器用的是阿里云ECS,需要配置網(wǎng)絡(luò)安全組的IP地址白名單,
我配置的時(shí)候只配置了TCP,沒有配置UDP,配置上了UCP的權(quán)限之后,debug就恢復(fù)正常了。
3、插入數(shù)據(jù)樣例
我這里寫了一個(gè)JSON字符串轉(zhuǎn)換為SQL,并持續(xù)插入的樣例。
package com.rexel.tdengine.api;
import com.alibaba.fastjson.JSONObject;
import com.rexel.tdengine.utils.TdUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
/**
* @ClassName CreateDatabase
* @Description CreateDatabase
* @Author: chunhui.qu
* @Date: 2020/10/12
*/
public class InsertJson {
public static void main(String[] args) throws SQLException {
TdUtils tdUtils = TdUtils.getInstance();
Connection conn = tdUtils.getConnection();
if (conn == null) {
return;
}
System.out.println("get connection");
Statement stmt = conn.createStatement();
if (stmt == null) {
return;
}
List<String> aiList = new ArrayList<String>(){{
add("AI_PICK_UDATA");
add("AI_PROD_NPER");
add("AI_PROD_NPICK");
add("AI_PRODUCT_EFF");
add("AI_PRODUCT_QUANTITY");
add("AI_PRODUCT_TIME_MIN");
add("AI_PROD_NPRODING");
}};
List<String> diList = new ArrayList<String>(){{
add("DI_BELOWCLOSTS");
add("DI_BELOWOPNSTS");
add("DI_CANDY_OUTBEELINEA");
add("DI_CANDY_OUTBEELINEB");
add("DI_CANDY_OUTBEELINEC");
add("DI_CHUCKCLOSTS");
add("DI_CHUCKOPNSTS");
add("DI_COVERCLOSTS");
add("DI_COVEROPNSTS");
add("DI_CTNCLO_BSTART");
add("DI_CTNOPN_BSTART");
add("DI_DENSO_BAUTOST");
add("DI_DENSO_BERROR");
add("DI_DENSO_BMOTORONST");
add("DI_DENSO_BREADY");
add("DI_DENSO_BRUNNING");
add("DI_EPSON_BERROR");
add("DI_EPSON_BESTOPON");
add("DI_EPSON_BMOTORONST");
add("DI_EPSON_BREADY");
add("DI_EPSON_BRUNNING");
add("DI_LEFTPUSHCLOSTS");
add("DI_LEFTPUSHOPNSTS");
add("DI_PRINT_BRUNNING");
add("DI_RIGHTCLOSTS");
add("DI_RIGHTOPNSTS");
add("DI_RIGHTPUSHCLOSTS");
add("DI_RIGHTPUSHLOPN");
add("DI_RLCLOSTS");
add("DI_RLOPNSTS");
add("DI_ROBOT1_BLINKSTS");
add("DI_ROBOT1_BSTART");
add("DI_ROBOT2_BLINKSTS");
add("DI_ROBOT2TOSTOR");
add("DI_ROBOT2TOWAI");
add("DI_SYS_BRUNNING");
add("DI_TOPCLOSTS");
add("DI_TOPOPNSTS");
add("DI_UPDOWNCLOSTS");
add("DI_UPDOWNOPNSTS");
add("DI_BBELTSENSOROVT");
add("DI_BCTNCLOCOOPNOVT");
add("DI_BCTNCLORCLOOVT");
add("DI_BCTNCLOROPNOVT");
add("DI_BCTNCLOTCLOOVT");
add("DI_BCTNCLOTOPNOVT");
add("DI_BCTNOVT");
add("DI_BELOWVALCLOOVT");
add("DI_BELOWVALOPNOVT");
add("DI_BROBOT2VALCLOVT");
add("DI_BROBOT2VALOPOVT");
add("DI_BRTSUCKEROVT");
add("DI_BVALCOVERCLOOVT");
add("DI_CHUCJVALCLOOVT");
add("DI_CHUCJVALOPOVT");
add("DI_CHUCKVALOPNOVT");
add("DI_DENSOBERROR");
add("DI_EPSONBERROR");
add("DI_INSENCTNALARM");
add("DI_LEFTNEWVALCLOVT");
add("DI_RLNEWOPVALOPOVT");
add("DI_RLNEWVALCLOOVT");
add("DI_UPDOWNNVALCLOVT");
add("DI_UPDOWNNVALOPOVT");
}};
int count = 0;
while(true) {
Random intRandom = new Random();
JSONObject demoJson = new JSONObject();
demoJson.put("table", "device_data_up");
demoJson.put("time", System.currentTimeMillis());
JSONObject tagJson = new JSONObject();
tagJson.put("productKey", "a1B6t6ZG6oR");
tagJson.put("deviceName", "QCHTestDevice1");
demoJson.put("tags", tagJson);
JSONObject dataJson = new JSONObject();
aiList.forEach(field -> dataJson.put(field, intRandom.nextInt(10000)));
diList.forEach(field -> dataJson.put(field, intRandom.nextInt(2)));
demoJson.put("datas", dataJson);
String sql = jsonToSql(demoJson);
stmt.executeUpdate(sql);
count ++;
System.out.println("executeUpdate. count=" + count + ", sql=" + sql);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static String jsonToSql(JSONObject jsonObject) {
String sql = "insert into {table} ({fields}) values ({values});";
sql = sql.replace("{table}", getTable(jsonObject));
sql = sql.replace("{fields}", getFields(jsonObject));
return sql.replace("{values}", getValues(jsonObject));
}
private static String getTable(JSONObject jsonObject) {
return "t_" + jsonObject.getString("table");
}
private static String getTime(JSONObject jsonObject) {
if (jsonObject.containsKey("time")) {
return timeLongToStr(jsonObject.getLong("time"));
} else {
return timeLongToStr(System.currentTimeMillis());
}
}
private static String getFields(JSONObject jsonObject) {
JSONObject dataJson = jsonObject.getJSONObject("datas");
StringBuilder sb = new StringBuilder();
sb.append("time").append(",");
dataJson.forEach((key, value) -> sb.append(key).append(","));
return sb.substring(0, sb.length() - 1);
}
private static String getValues(JSONObject jsonObject) {
JSONObject dataJson = jsonObject.getJSONObject("datas");
StringBuilder sb = new StringBuilder();
sb.append(""").append(getTime(jsonObject)).append(""").append(",");
dataJson.forEach((key, value) -> sb.append(value).append(","));
return sb.substring(0, sb.length() - 1);
}
private static String timeLongToStr(long time) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
return sdf.format(new Date(time));
}
}
【小插曲】
測(cè)試過程中,發(fā)現(xiàn)了一個(gè)問題,詳細(xì)請(qǐng)參考issue:https://github.com/taosdata/TDengine/issues/3818
==數(shù)據(jù)模型==
官網(wǎng)地址:https://www.taosdata.com/cn/documentation/architecture/
官網(wǎng)上面寫了TD的數(shù)據(jù)模型,不過沒有圖例進(jìn)行直觀的說明,所以介紹的多少有些抽象。希望文檔上可以提升一下逼格。
【物理量】
也叫“測(cè)點(diǎn)”、“點(diǎn)位””。就像做心電圖時(shí),粘在身上的幾個(gè)吸盤。一吸盤就是一個(gè)測(cè)點(diǎn),也就是一個(gè)物理量。
我這里以智能水流量計(jì)為例,包括以下測(cè)點(diǎn)。
【數(shù)據(jù)采集點(diǎn)】
每個(gè)數(shù)據(jù)采集點(diǎn)可以采集多個(gè)物理量。
我覺得實(shí)際場(chǎng)景中的PLC應(yīng)該算是一個(gè)數(shù)據(jù)采集點(diǎn)了。
【表】
一個(gè)數(shù)據(jù)采集點(diǎn)一張表。
【超級(jí)表】
同一類型數(shù)據(jù)采集點(diǎn)的集合。
我這里做了一寫想到哪里就試到哪里的嘗試。
1、先創(chuàng)建一個(gè)超級(jí)表
create table if not exists znsllj (time timestamp, ForwardIntegratedFlow double, BackwardIntegratedFlow double, InstantaneousFlow double, Velocity double, Density double, Temperature double, Pressure double, Error bool) tags(product nchar(256), device nchar(256));
2、基于超級(jí)表創(chuàng)建表
create table znsllj001 using znsllj tags("product1", "device1");
3、向表中插入一條數(shù)據(jù)
insert into znsllj001(time, ForwardIntegratedFlow, BackwardIntegratedFlow, InstantaneousFlow, Velocity, Density, Temperature, Pressure, Error) values(now, 10, 11, 12, 13, 14, 15, 16, 0);
4、如果修改表結(jié)構(gòu)可以嗎?
alter table znsllj001 add column AddField double;
發(fā)現(xiàn)直接修改表的結(jié)構(gòu)是失敗的,提示只能修改超級(jí)表
5、嘗試修改超級(jí)表結(jié)構(gòu)
alter table znsllj add column AddField double;
發(fā)現(xiàn)修改成功,并且表的結(jié)構(gòu)也跟著變化了。
6、分別查看一下表和超級(jí)表的結(jié)構(gòu)
describe znsllj001;
describe znsllj;
7、刪除超級(jí)表中一個(gè)列(有數(shù)據(jù)的列)
alter table znsllj drop column ForwardIntegratedFlow;
看到的是可以成功刪除,同時(shí)表結(jié)構(gòu)也發(fā)生了改變。
8、此時(shí)如果刪除超級(jí)表會(huì)如何?
drop table if exists znsllj;
看到可以正常刪除超級(jí)表,而且表也被連帶著一起刪掉了。
9、重新創(chuàng)建超級(jí)表、表及插入一條數(shù)據(jù)(修改了一下超級(jí)表和表的的前綴,便于區(qū)分)
create table if not exists st_znsllj (time timestamp, ForwardIntegratedFlow double, BackwardIntegratedFlow double, InstantaneousFlow double, Velocity double, Density double, Temperature double, Pressure double, Error bool) tags (product nchar(256), device nchar(256));
create table t_znsllj001 using st_znsllj tags("product1", "device1");
insert into t_znsllj001(time, ForwardIntegratedFlow, BackwardIntegratedFlow, InstantaneousFlow, Velocity, Density, Temperature, Pressure, Error) values(now, 10, 11, 12, 13, 14, 15, 16, 0);
10、繼續(xù)創(chuàng)建幾個(gè)表(同一個(gè)超級(jí)表)
兩個(gè)標(biāo)簽值重復(fù):create table t_znsllj002 using st_znsllj tags("product1", "device1");
標(biāo)簽值都不重復(fù):create table t_znsllj003 using st_znsllj tags("product3", "device3");
少一個(gè)標(biāo)簽:create table t_znsllj004 using st_znsllj tags("product4");
無標(biāo)簽:create table t_znsllj005 using st_znsllj;
多一個(gè)標(biāo)簽:create table t_znsllj006 using st_znsllj tags("product6", "device6", "type6");
一個(gè)標(biāo)簽值重復(fù):create table t_znsllj007 using st_znsllj tags("product1", "device7");
結(jié)論如下:
表的標(biāo)簽數(shù)量必須與超級(jí)表一致。
表的標(biāo)簽值可以重復(fù)。
11、分別向幾個(gè)成功的表中插入一條數(shù)據(jù)
insert into t_znsllj001(time, ForwardIntegratedFlow, BackwardIntegratedFlow, InstantaneousFlow, Velocity, Density, Temperature, Pressure, Error) values(now, 10, 11, 12, 13, 14, 15, 16, 0);
insert into t_znsllj002(time, ForwardIntegratedFlow, BackwardIntegratedFlow, InstantaneousFlow, Velocity, Density, Temperature, Pressure, Error) values(now, 20, 21, 22, 23, 24, 25, 26, 0);
insert into t_znsllj003(time, ForwardIntegratedFlow, BackwardIntegratedFlow, InstantaneousFlow, Velocity, Density, Temperature, Pressure, Error) values(now, 30, 31, 32, 33, 34, 35, 36, 0);
insert into t_znsllj007(time, ForwardIntegratedFlow, BackwardIntegratedFlow, InstantaneousFlow, Velocity, Density, Temperature, Pressure, Error) values(now, 70, 71, 72, 73, 74, 75, 76, 0);
12、查看一下幾個(gè)表的數(shù)據(jù)(僅時(shí)間戳,正向累計(jì)流量)
select time, forwardintegratedflow from t_znsllj001 order by time;
select time, forwardintegratedflow from t_znsllj002 order by time;
select time, forwardintegratedflow from t_znsllj003 order by time;
select time, forwardintegratedflow from t_znsllj007 order by time;
13、檢索超級(jí)表的所有數(shù)據(jù)(時(shí)間戳,一個(gè)物理量(正向累計(jì)流量)、兩個(gè)TAG(產(chǎn)品、設(shè)備))
select time, forwardintegratedflow, product, device from st_znsllj order by time;
可以看到,通過超級(jí)表的結(jié)果會(huì)帶有標(biāo)簽,普通表是么有標(biāo)簽的。另外超級(jí)表只看標(biāo)簽值,結(jié)果集中看不到具體是從哪個(gè)表中數(shù)據(jù)。
==聚合語(yǔ)句==
根據(jù)官方文檔進(jìn)行了一堆驗(yàn)證,感覺這些函數(shù)還不錯(cuò)。但是在大量數(shù)據(jù)的情況下,結(jié)果是否正確沒有驗(yàn)證過。僅進(jìn)行了功能上的驗(yàn)證,基本可以代替InfluxDB。
另外,對(duì)于“適用于”這一列,實(shí)際上與官方文檔有出入。至少在這個(gè)版本,我的驗(yàn)證結(jié)果如下。
Excel版本的驗(yàn)證結(jié)果,我共享到百度網(wǎng)盤了,有需要的自提。
鏈接:https://pan.baidu.com/s/1AOgDeGCqoOsvhGWu27Vrtg
提取碼:vxpb
==滾動(dòng)升級(jí)測(cè)試==
目前版本的td尚屬于更新迭代很快的階段,所以難免會(huì)有一些bug發(fā)生,所以在實(shí)際使用中,會(huì)頻繁的進(jìn)行版本升級(jí)。
這一點(diǎn)也會(huì)影響td是否能夠順利落地,以及長(zhǎng)久使用的一個(gè)考量指標(biāo)。我按照以下的方式進(jìn)行了一次嘗試。
【驗(yàn)證思路】
因?yàn)槟壳凹喊鎟elease的就這一個(gè)版本,所以沒有辦法測(cè)試版本升級(jí),只能測(cè)試卸載之后,在重新安裝。
【驗(yàn)證結(jié)論】
卸載并重新安裝之后,集群可以正常使用。
【驗(yàn)證過程】
1、先備份配置文件
分別將3個(gè)節(jié)點(diǎn)的配置文件下載并備份。
2、停止td集群
在3個(gè)節(jié)點(diǎn)分別執(zhí)行命令:systemctl stop taosd
3、卸載tdengine
在3個(gè)節(jié)點(diǎn)分別執(zhí)行命令:
#查看tdengine rpm -qa | grep TDengine #卸載tdengine yum remove TDengine-2.0.4.0-3.x86_64
4、查看卸載后的狀態(tài)
配置文件保留,沒有被刪除。
數(shù)據(jù)文件保留,沒有被刪除。
5、重新安裝集群
在3個(gè)節(jié)點(diǎn)分別執(zhí)行:rpm -ivh /home/radmin/soft/TDengine-server-2.0.4.0-Linux-x64.rpm
6、觀察配置文件
配置文件沒有被覆蓋,保留了卸載之前的配置,這樣挺好的,省著重新配置了。
7、啟動(dòng)集群
在3個(gè)節(jié)點(diǎn)分別執(zhí)行命令:systemctl start taosd
8、進(jìn)入命令行
在任意節(jié)點(diǎn)運(yùn)行taos,確認(rèn)可以進(jìn)入命令行。
9、查看集群狀態(tài)
show dnodes;
查看數(shù)據(jù)庫(kù)show databases;
切換數(shù)據(jù)庫(kù),并查看超級(jí)表
查詢?nèi)我獗?/p>
2020年10月12日 13:38 追記
看到官方剛剛發(fā)布了2.0.5.0版本,按照下面的順序重新嘗試了一遍,結(jié)果一樣,順利升級(jí)成功。
2020年10月12日 14:00 追記
集群使用了一會(huì)之后,發(fā)現(xiàn)第2個(gè)節(jié)點(diǎn)宕掉了,
沒有辦法,由于著急測(cè)試,將tdengine卸載,然后刪除數(shù)據(jù)目錄,重新安裝之后才得以解決。
2020年10月12日 19:37 追記
服務(wù)器版本升級(jí)到2.0.5.0之后,taos-jdbcdriver的版本需要使用2.0.8,使用2.0.5的時(shí)候會(huì)報(bào)錯(cuò)。感謝網(wǎng)友
2020年12月21日 9:24 追記
服務(wù)器從2.0.8.0升級(jí)到2.0.10.0,出現(xiàn)了一個(gè)問題。升級(jí)之后,通過shell查詢數(shù)據(jù)提示錯(cuò)誤:DB error: Invalid message
在群里咨詢之后,最終問題得到了解決,不過不敢保證是以下哪個(gè)方式解決的,猜測(cè)是第1個(gè)原因
1、在升級(jí)taosd的時(shí)候,taos進(jìn)程一直沒有關(guān)閉,升級(jí)之后也是用的2.0.8.0版本的taos shell檢索的。所以報(bào)錯(cuò)
2、java客戶端版本不正確,java客戶端版本從2.0.12升級(jí)到2.0.15之后,重新執(zhí)行程序確實(shí)解決了。
另外,從群里咨詢得到了一下幾個(gè)結(jié)論,后續(xù)升級(jí)需要注意:
濤思的版本升級(jí)規(guī)則是,前三位一致是版本兼容的,如不一致需要同步升級(jí)客戶端和服務(wù)端
==連續(xù)查詢==
在InfluxDB中有連續(xù)查詢的功能,可以按照一定的時(shí)間頻率對(duì)原始數(shù)據(jù)進(jìn)行聚合,并將聚合結(jié)果放入到新的數(shù)據(jù)庫(kù)中,
具體可以參考我InfluxDB連續(xù)查詢的另一篇博客:使用InfluxDB的連續(xù)查詢解決聚合性能問題
同樣,在TDengine中也提供了連續(xù)查詢的功能,我這里需要驗(yàn)證一下TDengine的連續(xù)查詢與InfluxDB的區(qū)別,
用來評(píng)估替換InfluxDB之后,對(duì)應(yīng)用端的代碼改動(dòng)的影響大小。
【語(yǔ)法嘗試】
1、創(chuàng)建連續(xù)查詢
create table avg_velocity as select avg(velocity) from st_znsllj interval(1m) sliding(30s);
在定義連續(xù)查詢的時(shí)候需要指定時(shí)間窗口大小(time window, 參數(shù)interval)和每次前向增量時(shí)間(forward sliding times, 參數(shù)sliding)。
2、查看連續(xù)查詢
show streams;
3、殺掉指定連續(xù)查詢
kill stream 3:1;
【小插曲】
在超級(jí)表的查詢語(yǔ)句中加上sliding之后,提示Redirect錯(cuò)誤。Issue為:https://github.com/taosdata/TDengine/issues/3801
官方上是說在連續(xù)查詢上不建議使用sliding
4、嘗試將連續(xù)查詢的結(jié)果放入另一個(gè)庫(kù)
需要在查詢的表以及目標(biāo)表上加上數(shù)據(jù)的名字即可。
create tableqch_cq.device_data_up_sum as select sum(AI_PICK_UDATA) fromqch_test.st_device_data_up interval(1m) sliding(30s) order by time desc;
【小插曲】
配置上連續(xù)查詢,并且程序段在一直的寫入數(shù)據(jù),發(fā)現(xiàn)了幾個(gè)奇怪的現(xiàn)象
現(xiàn)象1:streamId會(huì)一直變化
==寫入性能測(cè)試==
我寫了一個(gè)單列模式的測(cè)試樣例,
1、測(cè)試服務(wù)器:3個(gè)節(jié)點(diǎn)(阿里云ECS服務(wù)器,8核16G內(nèi)存)
2、單client
3、單列模式
4、自動(dòng)建表
5、分別測(cè)試5千,10萬,100萬測(cè)點(diǎn),按照不同粒度批量寫入(每批100、1000、5000等共10個(gè)粒度)
6、1個(gè)超級(jí)表
在本地運(yùn)行環(huán)境中跑,由于服務(wù)器在阿里云上,受網(wǎng)絡(luò)的影響,速度時(shí)非常的慢的,
10萬點(diǎn)的數(shù)據(jù),一共跑了3次,每次都差不多要1個(gè)小時(shí)才能跑完(一共3個(gè)小時(shí))
如果把程序打成jar包,扔到阿里云ECS服務(wù)器上跑,就快了太多太多了,
一樣的程序,每次10萬,一共3次,每次差多也就2分鐘就可以插入完成。
但是,感覺還是有一些慢,等找到一些優(yōu)化的方法,在繼續(xù)更新。。。。
2020年11月10日追記:
嘗試了批量寫入的方式,比如每次寫入500條(由于是單列模式,相當(dāng)于每次向500個(gè)表同時(shí)寫入數(shù)據(jù))
從結(jié)果上看,速度確實(shí)要快了很多很多,是一個(gè)好的結(jié)果。
【小插曲】
由于是單列模式的批量插入,所以sql語(yǔ)句會(huì)非常的長(zhǎng),大概是下面的這種感覺,
當(dāng)sql超過一定長(zhǎng)度的時(shí)候,程序就報(bào)錯(cuò)TDengine Error: Invalid SQL statement。
嘗試調(diào)整maxSQLLength參數(shù),將限制放大到10倍,也就是654800,問題解決。
注意:
maxSQLLength是客戶端參數(shù),是調(diào)用JDBC Connector的應(yīng)用程序所在機(jī)器上的配置文件,
Linux OS 上默認(rèn)值 /etc/taos/taos.cfg ,Windows OS 上默認(rèn)值 C://TDengine/cfg/taos.cfg。
【測(cè)試結(jié)論】
測(cè)試結(jié)論:
1、單client,單列模式,自動(dòng)建表模式,每秒寫入性能約每秒2萬條。
2、按照我目前的機(jī)器配置,批量寫入的時(shí)候,建議每批500條。
下圖是測(cè)試的統(tǒng)計(jì)圖。
==可靠性測(cè)試==
==數(shù)據(jù)備份==
==聚合性能驗(yàn)證==
==實(shí)際落地姿勢(shì)==
筆記:2021年1月19日
目前在一個(gè)項(xiàng)目中,準(zhǔn)備上TDengine,但是考慮到TDengine的一些實(shí)際情況
1、TD在快速的版本迭代過程中,難免會(huì)有一些問題不能第一時(shí)間解決(需要提Issue)
2、社區(qū)版的解決速度也會(huì)比預(yù)期的要慢一些 (實(shí)際上還是比較不錯(cuò)的了)
3、TD本身是用C語(yǔ)言開發(fā)的,能獨(dú)立解決TD問題的人不多(年輕人,會(huì)C的人不是很多)
綜合考慮之后,沒有獨(dú)立的使用TDengine來支撐項(xiàng)目,而是采用了以下方案
1、使用InfluxDB(社區(qū)版)來支撐實(shí)時(shí)業(yè)務(wù),數(shù)據(jù)存儲(chǔ)時(shí)間不超過3個(gè)月。并使用連續(xù)查詢來支撐實(shí)時(shí)聚合。
2、使用TDengine(社區(qū)版)來支撐分析業(yè)務(wù),數(shù)據(jù)存儲(chǔ)時(shí)間為默認(rèn)的10年。使用單列模式,來支撐測(cè)點(diǎn)的增減場(chǎng)景。
這樣的好處是,可以充分使用InfluxDB的穩(wěn)定,來確保實(shí)時(shí)類業(yè)務(wù)的可靠性。
同時(shí)有可以引入TDengine的新物種,帶來聚合性能的提升,給業(yè)務(wù)帶來一些成長(zhǎng)因素。
筆記:2021年4月7日 20:30
我們?cè)贗nfluxDB和TDengine之外包裝了一層SDK,對(duì)應(yīng)用層暴露SDK,這樣應(yīng)用層就不需要關(guān)心到底用的哪個(gè)存儲(chǔ)技術(shù)了。
在SDK中需要解決以下問題:
1、檢查InfluxDB及TDengine的連接狀態(tài),如果其中一個(gè)宕了,使用另一個(gè)。
2、判斷查詢數(shù)據(jù)的起始時(shí)間,如果在時(shí)間范圍內(nèi)則查詢InfluxDB,如果超過一定時(shí)間則使用TDengine。
3、翻譯應(yīng)用端的查詢語(yǔ)句以及查詢的數(shù)據(jù)結(jié)果。統(tǒng)一到InfluxDB的數(shù)據(jù)格式。
實(shí)際場(chǎng)景中的數(shù)據(jù)備份方案還沒有最終確定,后續(xù)更新。
--END--
總結(jié)
以上是生活随笔為你收集整理的【转】TDengine踩坑随记(最后一次更新:2021-4-7 20:30)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 原型设计工具比较及实践
- 下一篇: 文件系统是什么 NTFS是什么