提交 9f4a6cc7 编写于 作者: S shenglian zhou

merge

要显示的变更太多。

To preserve performance only 1000 of 1000+ files are displayed.
version: 1.0.{build}
image:
- Visual Studio 2015
- macos
environment:
matrix:
- ARCH: amd64
- ARCH: x86
matrix:
exclude:
- image: macos
ARCH: x86
for:
-
matrix:
only:
- image: Visual Studio 2015
clone_folder: c:\dev\TDengine
clone_depth: 1
init:
- call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %ARCH%
before_build:
- cd c:\dev\TDengine
- md build
build_script:
- cd build
- cmake -G "NMake Makefiles" .. -DBUILD_JDBC=false
- nmake install
-
matrix:
only:
- image: macos
clone_depth: 1
build_script:
- mkdir debug
- cd debug
- cmake .. > /dev/null
- make > /dev/null
notifications:
- provider: Email
to:
- sangshuduo@gmail.com
on_build_success: true
on_build_failure: true
on_build_status_changed: true
--- ---
kind: pipeline kind: pipeline
name: test_amd64
platform:
os: linux
arch: amd64
steps:
- name: build
image: gcc
commands:
- apt-get update
- apt-get install -y cmake build-essential
- mkdir debug
- cd debug
- cmake ..
- make -j4
trigger:
event:
- pull_request
when:
branch:
- develop
- master
- 2.0
---
kind: pipeline
name: test_arm64_bionic name: test_arm64_bionic
platform: platform:
...@@ -36,7 +10,11 @@ steps: ...@@ -36,7 +10,11 @@ steps:
image: arm64v8/ubuntu:bionic image: arm64v8/ubuntu:bionic
commands: commands:
- apt-get update - apt-get update
- apt-get install -y cmake build-essential - apt-get install -y cmake git build-essential wget
- wget https://dl.google.com/go/go1.16.9.linux-arm64.tar.gz
- tar -C /usr/local -xzf go1.16.9.linux-arm64.tar.gz
- export PATH=$PATH:/usr/local/go/bin
- git submodule update --init --recursive
- mkdir debug - mkdir debug
- cd debug - cd debug
- cmake .. -DCPUTYPE=aarch64 > /dev/null - cmake .. -DCPUTYPE=aarch64 > /dev/null
...@@ -63,7 +41,11 @@ steps: ...@@ -63,7 +41,11 @@ steps:
commands: commands:
- echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections - echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
- apt-get update - apt-get update
- apt-get install -y -qq cmake build-essential - apt-get install -y -qq git cmake build-essential wget
- wget https://dl.google.com/go/go1.16.9.linux-arm64.tar.gz
- tar -C /usr/local -xzf go1.16.9.linux-arm64.tar.gz
- export PATH=$PATH:/usr/local/go/bin
- git submodule update --init --recursive
- mkdir debug - mkdir debug
- cd debug - cd debug
- cmake .. -DCPUTYPE=aarch64 > /dev/null - cmake .. -DCPUTYPE=aarch64 > /dev/null
...@@ -88,10 +70,17 @@ steps: ...@@ -88,10 +70,17 @@ steps:
- name: build - name: build
image: arm64v8/centos:7 image: arm64v8/centos:7
commands: commands:
- yum install -y gcc gcc-c++ make cmake git - yum install -y epel-release
- yum install -y gcc gcc-c++ make cmake3 wget git
- wget https://dl.google.com/go/go1.16.9.linux-arm64.tar.gz
- tar -C /usr/local -xzf go1.16.9.linux-arm64.tar.gz
- export PATH=$PATH:/usr/local/go/bin
- ln -s /usr/bin/cmake3 /usr/bin/cmake
- go version
- git submodule update --init --recursive
- mkdir debug - mkdir debug
- cd debug - cd debug
- cmake .. -DCPUTYPE=aarch64 > /dev/null - cmake3 .. -DCPUTYPE=aarch64 > /dev/null
- make -j4 - make -j4
trigger: trigger:
event: event:
...@@ -113,7 +102,8 @@ steps: ...@@ -113,7 +102,8 @@ steps:
- name: build - name: build
image: arm64v8/centos:8 image: arm64v8/centos:8
commands: commands:
- dnf install -y gcc gcc-c++ make cmake epel-release git libarchive - dnf install -y gcc gcc-c++ make cmake epel-release git libarchive golang
- git submodule update --init --recursive
- mkdir debug - mkdir debug
- cd debug - cd debug
- cmake .. -DCPUTYPE=aarch64 > /dev/null - cmake .. -DCPUTYPE=aarch64 > /dev/null
...@@ -139,7 +129,8 @@ steps: ...@@ -139,7 +129,8 @@ steps:
image: arm32v7/ubuntu:bionic image: arm32v7/ubuntu:bionic
commands: commands:
- apt-get update - apt-get update
- apt-get install -y cmake build-essential - apt-get install -y cmake build-essential golang-go git
- git submodule update --init --recursive
- mkdir debug - mkdir debug
- cd debug - cd debug
- cmake .. -DCPUTYPE=aarch32 > /dev/null - cmake .. -DCPUTYPE=aarch32 > /dev/null
...@@ -165,8 +156,11 @@ steps: ...@@ -165,8 +156,11 @@ steps:
image: ubuntu:trusty image: ubuntu:trusty
commands: commands:
- apt-get update - apt-get update
- apt-get install -y gcc cmake3 build-essential git binutils-2.26 - apt-get install -y gcc cmake3 build-essential git binutils-2.26 wget
- wget https://dl.google.com/go/go1.16.9.linux-amd64.tar.gz
- tar -C /usr/local -xzf go1.16.9.linux-amd64.tar.gz
- export PATH=$PATH:/usr/local/go/bin
- git submodule update --init --recursive
- mkdir debug - mkdir debug
- cd debug - cd debug
- cmake .. - cmake ..
...@@ -192,7 +186,11 @@ steps: ...@@ -192,7 +186,11 @@ steps:
image: ubuntu:xenial image: ubuntu:xenial
commands: commands:
- apt-get update - apt-get update
- apt-get install -y gcc cmake build-essential - apt-get install -y gcc cmake build-essential git wget
- wget https://dl.google.com/go/go1.16.9.linux-amd64.tar.gz
- tar -C /usr/local -xzf go1.16.9.linux-amd64.tar.gz
- export PATH=$PATH:/usr/local/go/bin
- git submodule update --init --recursive
- mkdir debug - mkdir debug
- cd debug - cd debug
- cmake .. - cmake ..
...@@ -217,7 +215,11 @@ steps: ...@@ -217,7 +215,11 @@ steps:
image: ubuntu:bionic image: ubuntu:bionic
commands: commands:
- apt-get update - apt-get update
- apt-get install -y gcc cmake build-essential - apt-get install -y gcc cmake build-essential git wget
- wget https://dl.google.com/go/go1.16.9.linux-amd64.tar.gz
- tar -C /usr/local -xzf go1.16.9.linux-amd64.tar.gz
- export PATH=$PATH:/usr/local/go/bin
- git submodule update --init --recursive
- mkdir debug - mkdir debug
- cd debug - cd debug
- cmake .. - cmake ..
...@@ -241,10 +243,16 @@ steps: ...@@ -241,10 +243,16 @@ steps:
- name: build - name: build
image: ansible/centos7-ansible image: ansible/centos7-ansible
commands: commands:
- yum install -y gcc gcc-c++ make cmake - yum install -y epel-release
- yum install -y gcc gcc-c++ make cmake3 wget git
- wget https://dl.google.com/go/go1.16.9.linux-amd64.tar.gz
- tar -C /usr/local -xzf go1.16.9.linux-amd64.tar.gz
- export PATH=$PATH:/usr/local/go/bin
- ln -s /usr/bin/cmake3 /usr/bin/cmake
- git submodule update --init --recursive
- mkdir debug - mkdir debug
- cd debug - cd debug
- cmake .. - cmake3 ..
- make -j4 - make -j4
trigger: trigger:
event: event:
......
*.py linguist-detectable=false
build/ build/
.ycm_extra_conf.py
.vscode/ .vscode/
.idea/ .idea/
cmake-build-debug/ cmake-build-debug/
...@@ -85,7 +86,6 @@ tests/script/api/batchprepare ...@@ -85,7 +86,6 @@ tests/script/api/batchprepare
tests/script/api/stmt tests/script/api/stmt
tests/script/api/stmtBatchTest tests/script/api/stmtBatchTest
tests/script/api/stmtTest tests/script/api/stmtTest
# Emacs # Emacs
# -*- mode: gitignore; -*- # -*- mode: gitignore; -*-
*~ *~
......
[submodule "src/connector/go"] [submodule "src/connector/go"]
path = src/connector/go path = src/connector/go
url = git@github.com:taosdata/driver-go.git url = https://github.com/taosdata/driver-go.git
[submodule "src/connector/grafanaplugin"]
path = src/connector/grafanaplugin
url = git@github.com:taosdata/grafanaplugin.git
[submodule "src/connector/hivemq-tdengine-extension"] [submodule "src/connector/hivemq-tdengine-extension"]
path = src/connector/hivemq-tdengine-extension path = src/connector/hivemq-tdengine-extension
url = git@github.com:taosdata/hivemq-tdengine-extension.git url = https://github.com/taosdata/hivemq-tdengine-extension.git
[submodule "tests/examples/rust"]
path = tests/examples/rust
url = https://github.com/songtianyi/tdengine-rust-bindings.git
[submodule "deps/jemalloc"] [submodule "deps/jemalloc"]
path = deps/jemalloc path = deps/jemalloc
url = https://github.com/jemalloc/jemalloc url = https://github.com/jemalloc/jemalloc
[submodule "deps/TSZ"] [submodule "deps/TSZ"]
path = deps/TSZ path = deps/TSZ
url = https://github.com/taosdata/TSZ.git url = https://github.com/taosdata/TSZ.git
branch = master
[submodule "src/kit/taos-tools"]
path = src/kit/taos-tools
url = https://github.com/taosdata/taos-tools
[submodule "src/plugins/taosadapter"]
path = src/plugins/taosadapter
url = https://github.com/taosdata/taosadapter
[submodule "examples/rust"]
path = examples/rust
url = https://github.com/songtianyi/tdengine-rust-bindings.git
...@@ -10,14 +10,35 @@ ELSE () ...@@ -10,14 +10,35 @@ ELSE ()
ENDIF () ENDIF ()
IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
ELSE () ELSE ()
CMAKE_MINIMUM_REQUIRED(VERSION 2.8) CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
ENDIF () ENDIF ()
if(NOT WIN32)
string(ASCII 27 Esc)
set(ColourReset "${Esc}[m")
set(ColourBold "${Esc}[1m")
set(Red "${Esc}[31m")
set(Green "${Esc}[32m")
set(Yellow "${Esc}[33m")
set(Blue "${Esc}[34m")
set(Magenta "${Esc}[35m")
set(Cyan "${Esc}[36m")
set(White "${Esc}[37m")
set(BoldRed "${Esc}[1;31m")
set(BoldGreen "${Esc}[1;32m")
set(BoldYellow "${Esc}[1;33m")
set(BoldBlue "${Esc}[1;34m")
set(BoldMagenta "${Esc}[1;35m")
set(BoldCyan "${Esc}[1;36m")
set(BoldWhite "${Esc}[1;37m")
endif()
SET(TD_ACCOUNT FALSE) SET(TD_ACCOUNT FALSE)
SET(TD_ADMIN FALSE) SET(TD_ADMIN FALSE)
SET(TD_GRANT FALSE) SET(TD_GRANT FALSE)
SET(TD_USB_DONGLE FALSE)
SET(TD_MQTT FALSE) SET(TD_MQTT FALSE)
SET(TD_TSDB_PLUGINS FALSE) SET(TD_TSDB_PLUGINS FALSE)
SET(TD_STORAGE FALSE) SET(TD_STORAGE FALSE)
...@@ -29,7 +50,6 @@ SET(TD_MEM_CHECK FALSE) ...@@ -29,7 +50,6 @@ SET(TD_MEM_CHECK FALSE)
SET(TD_PAGMODE_LITE FALSE) SET(TD_PAGMODE_LITE FALSE)
SET(TD_SOMODE_STATIC FALSE) SET(TD_SOMODE_STATIC FALSE)
SET(TD_POWER FALSE)
SET(TD_GODLL FALSE) SET(TD_GODLL FALSE)
SET(TD_COMMUNITY_DIR ${PROJECT_SOURCE_DIR}) SET(TD_COMMUNITY_DIR ${PROJECT_SOURCE_DIR})
...@@ -50,5 +70,6 @@ INCLUDE(cmake/install.inc) ...@@ -50,5 +70,6 @@ INCLUDE(cmake/install.inc)
ADD_SUBDIRECTORY(deps) ADD_SUBDIRECTORY(deps)
ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(tests) ADD_SUBDIRECTORY(tests)
ADD_SUBDIRECTORY(examples/c)
INCLUDE(CPack) INCLUDE(CPack)
...@@ -4,8 +4,11 @@ import jenkins.model.CauseOfInterruption ...@@ -4,8 +4,11 @@ import jenkins.model.CauseOfInterruption
node { node {
} }
def skipbuild=0 def skipbuild = 0
def win_stop=0 def win_stop = 0
def scope = []
def mod = [0,1,2,3,4]
def sim_mod = [0,1,2,3]
def abortPreviousBuilds() { def abortPreviousBuilds() {
def currentJobName = env.JOB_NAME def currentJobName = env.JOB_NAME
...@@ -38,10 +41,12 @@ def pre_test(){ ...@@ -38,10 +41,12 @@ def pre_test(){
sudo rmtaos || echo "taosd has not installed" sudo rmtaos || echo "taosd has not installed"
''' '''
sh ''' sh '''
killall -9 taosd ||echo "no taosd running" kill -9 $(pidof taosd) ||echo "no taosd running"
kill -9 $(pidof taosadapter) ||echo "no taosadapter running"
killall -9 gdb || echo "no gdb running" killall -9 gdb || echo "no gdb running"
killall -9 python3.8 || echo "no python program running" killall -9 python3.8 || echo "no python program running"
cd ${WKC} cd ${WKC}
[ -f src/connector/grafanaplugin/README.md ] && rm -f src/connector/grafanaplugin/README.md > /dev/null || echo "failed to remove grafanaplugin README.md"
git reset --hard HEAD~10 >/dev/null git reset --hard HEAD~10 >/dev/null
''' '''
script { script {
...@@ -66,11 +71,15 @@ def pre_test(){ ...@@ -66,11 +71,15 @@ def pre_test(){
} }
sh''' sh'''
cd ${WKC} cd ${WKC}
git remote prune origin
[ -f src/connector/grafanaplugin/README.md ] && rm -f src/connector/grafanaplugin/README.md > /dev/null || echo "failed to remove grafanaplugin README.md"
git pull >/dev/null git pull >/dev/null
git fetch origin +refs/pull/${CHANGE_ID}/merge git fetch origin +refs/pull/${CHANGE_ID}/merge
git checkout -qf FETCH_HEAD git checkout -qf FETCH_HEAD
git clean -dfx git clean -dfx
git submodule update --init --recursive git submodule update --init --recursive
cd src/kit/taos-tools/deps/avro
git clean -dfx
cd ${WK} cd ${WK}
git reset --hard HEAD~10 git reset --hard HEAD~10
''' '''
...@@ -103,7 +112,7 @@ def pre_test(){ ...@@ -103,7 +112,7 @@ def pre_test(){
git clean -dfx git clean -dfx
mkdir debug mkdir debug
cd debug cd debug
cmake .. > /dev/null cmake .. -DBUILD_HTTP=false -DBUILD_TOOLS=true > /dev/null
make > /dev/null make > /dev/null
make install > /dev/null make install > /dev/null
cd ${WKC}/tests cd ${WKC}/tests
...@@ -115,6 +124,7 @@ def pre_test_noinstall(){ ...@@ -115,6 +124,7 @@ def pre_test_noinstall(){
sh'hostname' sh'hostname'
sh''' sh'''
cd ${WKC} cd ${WKC}
[ -f src/connector/grafanaplugin/README.md ] && rm -f src/connector/grafanaplugin/README.md > /dev/null || echo "failed to remove grafanaplugin README.md"
git reset --hard HEAD~10 >/dev/null git reset --hard HEAD~10 >/dev/null
''' '''
script { script {
...@@ -139,11 +149,15 @@ def pre_test_noinstall(){ ...@@ -139,11 +149,15 @@ def pre_test_noinstall(){
} }
sh''' sh'''
cd ${WKC} cd ${WKC}
git remote prune origin
[ -f src/connector/grafanaplugin/README.md ] && rm -f src/connector/grafanaplugin/README.md > /dev/null || echo "failed to remove grafanaplugin README.md"
git pull >/dev/null git pull >/dev/null
git fetch origin +refs/pull/${CHANGE_ID}/merge git fetch origin +refs/pull/${CHANGE_ID}/merge
git checkout -qf FETCH_HEAD git checkout -qf FETCH_HEAD
git clean -dfx git clean -dfx
git submodule update --init --recursive git submodule update --init --recursive
cd src/kit/taos-tools/deps/avro
git clean -dfx
cd ${WK} cd ${WK}
git reset --hard HEAD~10 git reset --hard HEAD~10
''' '''
...@@ -176,12 +190,88 @@ def pre_test_noinstall(){ ...@@ -176,12 +190,88 @@ def pre_test_noinstall(){
git clean -dfx git clean -dfx
mkdir debug mkdir debug
cd debug cd debug
cmake .. > /dev/null cmake .. -DBUILD_HTTP=false -DBUILD_TOOLS=true > /dev/null
make make
''' '''
return 1 return 1
} }
def pre_test_mac(){
sh'hostname'
sh'''
cd ${WKC}
[ -f src/connector/grafanaplugin/README.md ] && rm -f src/connector/grafanaplugin/README.md > /dev/null || echo "failed to remove grafanaplugin README.md"
git reset --hard HEAD~10 >/dev/null
'''
script {
if (env.CHANGE_TARGET == 'master') {
sh '''
cd ${WKC}
git checkout master
'''
}
else if(env.CHANGE_TARGET == '2.0'){
sh '''
cd ${WKC}
git checkout 2.0
'''
}
else{
sh '''
cd ${WKC}
git checkout develop
'''
}
}
sh'''
cd ${WKC}
git remote prune origin
[ -f src/connector/grafanaplugin/README.md ] && rm -f src/connector/grafanaplugin/README.md > /dev/null || echo "failed to remove grafanaplugin README.md"
git pull >/dev/null
git fetch origin +refs/pull/${CHANGE_ID}/merge
git checkout -qf FETCH_HEAD
git clean -dfx
git submodule update --init --recursive
cd src/kit/taos-tools/deps/avro
git clean -dfx
cd ${WK}
git reset --hard HEAD~10
'''
script {
if (env.CHANGE_TARGET == 'master') {
sh '''
cd ${WK}
git checkout master
'''
}
else if(env.CHANGE_TARGET == '2.0'){
sh '''
cd ${WK}
git checkout 2.0
'''
}
else{
sh '''
cd ${WK}
git checkout develop
'''
}
}
sh '''
cd ${WK}
git pull >/dev/null
export TZ=Asia/Harbin
date
git clean -dfx
mkdir debug
cd debug
cmake .. -DBUILD_TOOLS=false > /dev/null
go env -w GOPROXY=https://goproxy.cn,direct
go env -w GO111MODULE=on
cmake --build .
'''
return 1
}
def pre_test_win(){ def pre_test_win(){
bat ''' bat '''
taskkill /f /t /im python.exe taskkill /f /t /im python.exe
...@@ -214,6 +304,7 @@ def pre_test_win(){ ...@@ -214,6 +304,7 @@ def pre_test_win(){
} }
bat''' bat'''
cd C:\\workspace\\TDinternal\\community cd C:\\workspace\\TDinternal\\community
git remote prune origin
git pull git pull
git fetch origin +refs/pull/%CHANGE_ID%/merge git fetch origin +refs/pull/%CHANGE_ID%/merge
git checkout -qf FETCH_HEAD git checkout -qf FETCH_HEAD
...@@ -279,44 +370,10 @@ pipeline { ...@@ -279,44 +370,10 @@ pipeline {
script{ script{
abort_previous() abort_previous()
abortPreviousBuilds() abortPreviousBuilds()
scope = ['connector','query','insert','other','tools','taosAdapter']
Collections.shuffle mod
Collections.shuffle sim_mod
} }
// sh'''
// rm -rf ${WORKSPACE}.tes
// cp -r ${WORKSPACE} ${WORKSPACE}.tes
// cd ${WORKSPACE}.tes
// git fetch
// '''
// script {
// if (env.CHANGE_TARGET == 'master') {
// sh '''
// git checkout master
// '''
// }
// else if(env.CHANGE_TARGET == '2.0'){
// sh '''
// git checkout 2.0
// '''
// }
// else{
// sh '''
// git checkout develop
// '''
// }
// }
// sh'''
// git fetch origin +refs/pull/${CHANGE_ID}/merge
// git checkout -qf FETCH_HEAD
// '''
// script{
// skipbuild='2'
// skipbuild=sh(script: "git log -2 --pretty=%B | fgrep -ie '[skip ci]' -e '[ci skip]' && echo 1 || echo 2", returnStdout:true)
// println skipbuild
// }
// sh'''
// rm -rf ${WORKSPACE}.tes
// '''
// }
} }
} }
stage('Parallel test stage') { stage('Parallel test stage') {
...@@ -329,254 +386,246 @@ pipeline { ...@@ -329,254 +386,246 @@ pipeline {
} }
} }
parallel { parallel {
stage('python_1_s1') { stage('python_1') {
agent{label " slave1 || slave11 "} agent{label " slave1 || slave11 "}
steps { steps {
pre_test() pre_test()
timeout(time: 55, unit: 'MINUTES'){ timeout(time: 100, unit: 'MINUTES'){
sh ''' script{
scope.each {
sh """
date date
cd ${WKC}/tests cd ${WKC}/tests
./test-all.sh p1 ./test-CI.sh ${it} 5 ${mod[0]}
date''' date"""
} }
} }
} }
stage('python_2_s5') { }
agent{label " slave5 || slave15 "} }
stage('python_2') {
agent{label " slave2 || slave12 "}
steps { steps {
pre_test() pre_test()
timeout(time: 55, unit: 'MINUTES'){ timeout(time: 100, unit: 'MINUTES'){
sh ''' script{
scope.each {
sh """
date date
cd ${WKC}/tests cd ${WKC}/tests
./test-all.sh p2 ./test-CI.sh ${it} 5 ${mod[1]}
date''' date"""
} }
} }
} }
stage('python_3_s6') { }
agent{label " slave6 || slave16 "} }
stage('python_3') {
agent{label " slave3 || slave13 "}
steps { steps {
timeout(time: 65, unit: 'MINUTES'){ timeout(time: 105, unit: 'MINUTES'){
pre_test() pre_test()
sh ''' script{
scope.each {
sh """
date date
cd ${WKC}/tests cd ${WKC}/tests
./test-all.sh p3 ./test-CI.sh ${it} 5 ${mod[2]}
date''' date"""
} }
} }
} }
stage('test_b1_s2') { }
agent{label " slave2 || slave12 "} }
stage('python_4') {
agent{label " slave4 || slave14 "}
steps { steps {
timeout(time: 55, unit: 'MINUTES'){ timeout(time: 100, unit: 'MINUTES'){
pre_test() pre_test()
sh ''' script{
rm -rf /var/lib/taos/* scope.each {
rm -rf /var/log/taos/* sh """
nohup taosd >/dev/null & date
sleep 10
'''
sh '''
cd ${WKC}/tests/examples/nodejs
npm install td2.0-connector > /dev/null 2>&1
node nodejsChecker.js host=localhost
'''
sh '''
cd ${WKC}/tests/examples/C#/taosdemo
mcs -out:taosdemo *.cs > /dev/null 2>&1
echo '' |./taosdemo -c /etc/taos
'''
sh '''
cd ${WKC}/tests/gotest
bash batchtest.sh
'''
sh '''
cd ${WKC}/tests cd ${WKC}/tests
./test-all.sh b1fq ./test-CI.sh ${it} 5 ${mod[3]}
date''' date"""
} }
} }
}
stage('test_crash_gen_s3') {
agent{label " slave3 || slave13 "}
steps {
pre_test()
timeout(time: 60, unit: 'MINUTES'){
sh '''
cd ${WKC}/tests/pytest
./crash_gen.sh -a -p -t 4 -s 2000
'''
} }
timeout(time: 60, unit: 'MINUTES'){
sh '''
cd ${WKC}/tests/pytest
rm -rf /var/lib/taos/*
rm -rf /var/log/taos/*
./handle_crash_gen_val_log.sh
'''
sh '''
cd ${WKC}/tests/pytest
rm -rf /var/lib/taos/*
rm -rf /var/log/taos/*
./handle_taosd_val_log.sh
'''
} }
timeout(time: 55, unit: 'MINUTES'){ }
sh ''' stage('python_5') {
agent{label " slave5 || slave15 "}
steps {
timeout(time: 100, unit: 'MINUTES'){
pre_test()
script{
scope.each {
sh """
date date
cd ${WKC}/tests cd ${WKC}/tests
./test-all.sh b2fq ./test-CI.sh ${it} 5 ${mod[4]}
date date"""
'''
} }
} }
}
stage('test_valgrind_s4') {
agent{label " slave4 || slave14 "}
}
}
}
stage('sim_1') {
agent{label " slave6 || slave16 "}
steps { steps {
pre_test() pre_test()
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { timeout(time: 100, unit: 'MINUTES'){
sh ''' sh """
cd ${WKC}/tests/pytest
./valgrind-test.sh 2>&1 > mem-error-out.log
./handle_val_log.sh
'''
}
timeout(time: 55, unit: 'MINUTES'){
sh '''
date
cd ${WKC}/tests
./test-all.sh b3fq
date'''
sh '''
date date
cd ${WKC}/tests cd ${WKC}/tests
./test-all.sh full example ./test-CI.sh sim 4 ${sim_mod[0]}
date''' date"""
} }
} }
} }
stage('test_b4_s7') { stage('sim_2') {
agent{label " slave7 || slave17 "} agent{label " slave7 || slave17 "}
steps { steps {
timeout(time: 105, unit: 'MINUTES'){
pre_test() pre_test()
sh ''' timeout(time: 100, unit: 'MINUTES'){
sh """
date date
cd ${WKC}/tests cd ${WKC}/tests
./test-all.sh b4fq ./test-CI.sh sim 4 ${sim_mod[1]}
cd ${WKC}/tests date"""
./test-all.sh p4
cd ${WKC}/tests
./test-all.sh full jdbc
cd ${WKC}/tests
./test-all.sh full unit
date'''
} }
} }
} }
stage('test_b5_s8') { stage('sim_3') {
agent{label " slave8 || slave18 "} agent{label " slave8 || slave18 "}
steps { steps {
timeout(time: 55, unit: 'MINUTES'){ timeout(time: 105, unit: 'MINUTES'){
pre_test() pre_test()
sh ''' sh """
date date
cd ${WKC}/tests cd ${WKC}/tests
./test-all.sh b5fq ./test-CI.sh sim 4 ${sim_mod[2]}
date''' date"""
} }
} }
} }
stage('test_b6_s9') { stage('sim_4') {
agent{label " slave9 || slave19 "} agent{label " slave9 || slave19 "}
steps { steps {
timeout(time: 105, unit: 'MINUTES'){ timeout(time: 100, unit: 'MINUTES'){
pre_test() pre_test()
sh ''' sh """
date date
cd ${WKC}/tests cd ${WKC}/tests
./test-all.sh b6fq ./test-CI.sh sim 4 ${sim_mod[3]}
date''' date"""
} }
} }
} }
stage('test_b7_s10') { stage('other') {
agent{label " slave10 || slave20 "} agent{label " slave10 || slave20 "}
steps { steps {
timeout(time: 55, unit: 'MINUTES'){ timeout(time: 100, unit: 'MINUTES'){
pre_test() pre_test()
timeout(time: 60, unit: 'MINUTES'){
sh '''
cd ${WKC}/tests/pytest
./crash_gen.sh -a -p -t 4 -s 2000
'''
}
timeout(time: 60, unit: 'MINUTES'){
sh '''
cd ${WKC}/tests/pytest
rm -rf /var/lib/taos/*
rm -rf /var/log/taos/*
./handle_crash_gen_val_log.sh
'''
sh '''
cd ${WKC}/tests/pytest
rm -rf /var/lib/taos/*
rm -rf /var/log/taos/*
./handle_taosd_val_log.sh
'''
}
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
sh '''
cd ${WKC}/tests/pytest
./valgrind-test.sh 2>&1 > mem-error-out.log
./handle_val_log.sh
'''
}
sh ''' sh '''
date
cd ${WKC}/tests cd ${WKC}/tests
./test-all.sh b7fq ./test-all.sh full unit
date''' date
'''
} }
} }
} }
stage('arm64centos7') { stage('centos7') {
agent{label " arm64centos7 "} agent{label " centos7 "}
steps { steps {
pre_test_noinstall() pre_test_noinstall()
} }
} }
stage('arm64centos8') { stage('ubuntu:trusty') {
agent{label " arm64centos8 "} agent{label " trusty "}
steps { steps {
pre_test_noinstall() pre_test_noinstall()
} }
} }
stage('arm32bionic') { stage('ubuntu:xenial') {
agent{label " arm32bionic "} agent{label " xenial "}
steps { steps {
pre_test_noinstall() pre_test_noinstall()
} }
} }
stage('arm64bionic') { stage('ubuntu:bionic') {
agent{label " arm64bionic "} agent{label " bionic "}
steps { steps {
pre_test_noinstall() pre_test_noinstall()
} }
} }
stage('arm64focal') { stage('Mac_build') {
agent{label " arm64focal "} agent{label " catalina "}
steps {
pre_test_mac()
}
}
stage('arm64centos7') {
agent{label " arm64centos7 "}
steps { steps {
pre_test_noinstall() pre_test_noinstall()
} }
} }
stage('centos7') { stage('arm64centos8') {
agent{label " centos7 "} agent{label " arm64centos8 "}
steps { steps {
pre_test_noinstall() pre_test_noinstall()
} }
} }
stage('ubuntu:trusty') { stage('arm32bionic') {
agent{label " trusty "} agent{label " arm32bionic "}
steps { steps {
pre_test_noinstall() pre_test_noinstall()
} }
} }
stage('ubuntu:xenial') { stage('arm64bionic') {
agent{label " xenial "} agent{label " arm64bionic "}
steps { steps {
pre_test_noinstall() pre_test_noinstall()
} }
} }
stage('ubuntu:bionic') { stage('arm64focal') {
agent{label " bionic "} agent{label " arm64focal "}
steps { steps {
pre_test_noinstall() pre_test_noinstall()
} }
} }
stage('build'){ stage('build'){
agent{label " wintest "} agent{label " wintest "}
steps { steps {
...@@ -606,8 +655,6 @@ pipeline { ...@@ -606,8 +655,6 @@ pipeline {
} }
} }
} }
} }
} }
} }
...@@ -638,7 +685,6 @@ pipeline { ...@@ -638,7 +685,6 @@ pipeline {
<li>提交信息:${env.CHANGE_TITLE}</li> <li>提交信息:${env.CHANGE_TITLE}</li>
<li>构建地址:<a href=${BUILD_URL}>${BUILD_URL}</a></li> <li>构建地址:<a href=${BUILD_URL}>${BUILD_URL}</a></li>
<li>构建日志:<a href=${BUILD_URL}console>${BUILD_URL}console</a></li> <li>构建日志:<a href=${BUILD_URL}console>${BUILD_URL}console</a></li>
</div> </div>
</ul> </ul>
</td> </td>
...@@ -676,7 +722,6 @@ pipeline { ...@@ -676,7 +722,6 @@ pipeline {
<li>提交信息:${env.CHANGE_TITLE}</li> <li>提交信息:${env.CHANGE_TITLE}</li>
<li>构建地址:<a href=${BUILD_URL}>${BUILD_URL}</a></li> <li>构建地址:<a href=${BUILD_URL}>${BUILD_URL}</a></li>
<li>构建日志:<a href=${BUILD_URL}console>${BUILD_URL}console</a></li> <li>构建日志:<a href=${BUILD_URL}console>${BUILD_URL}console</a></li>
</div> </div>
</ul> </ul>
</td> </td>
...@@ -690,3 +735,4 @@ pipeline { ...@@ -690,3 +735,4 @@ pipeline {
} }
} }
} }
import hudson.model.Result
import hudson.model.*;
import jenkins.model.CauseOfInterruption
node {
}
def sync_source() {
sh '''
hostname
date
'''
sh '''
cd ${WKC}
[ -f src/connector/grafanaplugin/README.md ] && rm -f src/connector/grafanaplugin/README.md > /dev/null || echo "failed to remove grafanaplugin README.md"
git reset --hard
git fetch
cd ${WK}
git reset --hard
git fetch
'''
script {
if (env.CHANGE_TARGET == 'master') {
sh '''
cd ${WKC}
git checkout master
'''
} else if (env.CHANGE_TARGET == '2.0') {
sh '''
cd ${WKC}
git checkout 2.0
'''
} else if (env.CHANGE_TARGET == '2.4') {
sh '''
cd ${WKC}
git checkout 2.4
'''
} else {
sh '''
cd ${WKC}
git checkout develop
'''
}
}
sh '''
export TZ=Asia/Harbin
cd ${WKC}
git remote prune origin
[ -f src/connector/grafanaplugin/README.md ] && rm -f src/connector/grafanaplugin/README.md > /dev/null || echo "failed to remove grafanaplugin README.md"
git pull >/dev/null
git clean -dfx
'''
script {
if (env.CHANGE_TARGET == 'master') {
sh '''
cd ${WK}
git checkout master
'''
} else if (env.CHANGE_TARGET == '2.0') {
sh '''
cd ${WK}
git checkout 2.0
'''
} else if (env.CHANGE_TARGET == '2.4') {
sh '''
cd ${WK}
git checkout 2.4
'''
} else {
sh '''
cd ${WK}
git checkout develop
'''
}
}
sh '''
cd ${WK}
git pull >/dev/null
git clean -dfx
'''
script {
if (env.CHANGE_URL =~ /\/TDengine\//) {
sh '''
echo "match /TDengine/ repository"
cd ${WKC}
git fetch origin +refs/pull/${CHANGE_ID}/merge
git checkout -qf FETCH_HEAD
'''
} else if (env.CHANGE_URL =~ /\/TDinternal\//) {
sh '''
echo "match /TDinternal/ repository"
cd ${WK}
git fetch origin +refs/pull/${CHANGE_ID}/merge
git checkout -qf FETCH_HEAD
'''
} else {
sh '''
echo "unmatched reposiotry ${CHANGE_URL}"
'''
}
}
sh '''
cd ${WKC}
git submodule update --init --recursive
'''
}
def pre_test() {
sync_source()
sh '''
cd ${WK}
mkdir -p debug
cd debug
go env -w GOPROXY=https://goproxy.cn,direct
go env -w GO111MODULE=on
cmake .. -DBUILD_HTTP=false -DBUILD_TOOLS=true > /dev/null
make -j8 >/dev/null
'''
return 1
}
def pre_test_mac() {
sync_source()
sh '''
cd ${WK}
mkdir -p debug
cd debug
go env -w GOPROXY=https://goproxy.cn,direct
go env -w GO111MODULE=on
cmake .. -DBUILD_TOOLS=false > /dev/null
make -j8 >/dev/null
'''
return 1
}
pipeline {
agent {label " dispatcher "}
options { skipDefaultCheckout() }
environment{
WK = '/var/data/jenkins/workspace/TDinternal'
WKC = '/var/data/jenkins/workspace/TDinternal/community'
LOGDIR = '/var/data/jenkins/workspace/log'
}
stages {
stage ('pre_build') {
steps {
sh '''
date
pwd
env
hostname
'''
}
}
stage ('Parallel build stage') {
//only build pr
options { skipDefaultCheckout() }
when {
allOf {
changeRequest()
not { expression { env.CHANGE_BRANCH =~ /docs\// }}
}
}
parallel {
stage ('dispatcher sync source') {
steps {
timeout(time: 20, unit: 'MINUTES') {
sync_source()
script {
sh '''
echo "dispatcher ready"
date
'''
}
}
}
}
stage ('build worker01') {
agent {label " worker01 "}
steps {
timeout(time: 20, unit: 'MINUTES') {
pre_test()
script {
sh '''
echo "worker01 build done"
date
'''
}
}
}
}
stage ('build worker02') {
agent {label " worker02 "}
steps {
timeout(time: 20, unit: 'MINUTES') {
pre_test()
script {
sh '''
echo "worker02 build done"
date
'''
}
}
}
}
stage ('build worker03') {
agent {label " worker03 "}
steps {
timeout(time: 20, unit: 'MINUTES') {
pre_test()
script {
sh '''
echo "worker03 build done"
date
'''
}
}
}
}
stage ('build worker04') {
agent {label " worker04 "}
steps {
timeout(time: 20, unit: 'MINUTES') {
pre_test()
script {
sh '''
echo "worker04 build done"
date
'''
}
}
}
}
stage ('build worker05') {
agent {label " worker05 "}
steps {
timeout(time: 20, unit: 'MINUTES') {
pre_test()
script {
sh '''
echo "worker05 build done"
date
'''
}
}
}
}
}
}
stage('run test') {
options { skipDefaultCheckout() }
when {
allOf {
changeRequest()
not { expression { env.CHANGE_BRANCH =~ /docs\// }}
}
}
parallel {
stage ('build worker08_arm32') {
agent {label " worker08_arm32"}
steps {
timeout(time: 20, unit: 'MINUTES') {
pre_test()
script {
sh '''
echo "worker08_arm32 build done"
date
'''
}
}
}
}
stage ('build worker07_arm64') {
agent {label " worker07_arm64 "}
steps {
timeout(time: 20, unit: 'MINUTES') {
pre_test()
script {
sh '''
echo "worker07_arm64 build done"
date
'''
}
}
}
}
stage ('build Mac_catalina ') {
agent {label " Mac_catalina "}
steps {
timeout(time: 20, unit: 'MINUTES') {
pre_test_mac()
script {
sh '''
echo "Mac_catalina build done"
date
'''
}
}
}
}
stage('run cases') {
steps {
sh '''
date
hostname
'''
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
timeout(time: 20, unit: 'MINUTES') {
sh '''
date
cd ${WKC}/tests/parallel_test
time ./run.sh -m m.json -t cases.task -l ${LOGDIR} -b ${BRANCH_NAME}
date
hostname
'''
}
}
}
}
}
}
}
post {
success {
emailext (
subject: "PR-result: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' SUCCESS",
body: """<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
<table width="95%" cellpadding="0" cellspacing="0" style="font-size: 16pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<tr>
<td>
<br/>
<b><font color="#0B610B"><font size="6">构建信息</font></font></b>
<hr size="2" width="100%" align="center" />
</td>
</tr>
<tr>
<td>
<ul>
<div style="font-size:18px">
<li>构建名称>>分支:${env.BRANCH_NAME}</li>
<li>构建结果:<span style="color:green"> Successful </span></li>
<li>构建编号:${BUILD_NUMBER}</li>
<li>触发用户:${env.CHANGE_AUTHOR}</li>
<li>提交信息:${env.CHANGE_TITLE}</li>
<li>构建地址:<a href=${BUILD_URL}>${BUILD_URL}</a></li>
<li>构建日志:<a href=${BUILD_URL}console>${BUILD_URL}console</a></li>
</div>
</ul>
</td>
</tr>
</table>
</body>
</html>""",
to: "${env.CHANGE_AUTHOR_EMAIL}",
from: "support@taosdata.com"
)
}
failure {
emailext (
subject: "PR-result: Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' FAIL",
body: """<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0">
<table width="95%" cellpadding="0" cellspacing="0" style="font-size: 16pt; font-family: Tahoma, Arial, Helvetica, sans-serif">
<tr>
<td>
<br/>
<b><font color="#0B610B"><font size="6">构建信息</font></font></b>
<hr size="2" width="100%" align="center" />
</td>
</tr>
<tr>
<td>
<ul>
<div style="font-size:18px">
<li>构建名称>>分支:${env.BRANCH_NAME}</li>
<li>构建结果:<span style="color:red"> Failure </span></li>
<li>构建编号:${BUILD_NUMBER}</li>
<li>触发用户:${env.CHANGE_AUTHOR}</li>
<li>提交信息:${env.CHANGE_TITLE}</li>
<li>构建地址:<a href=${BUILD_URL}>${BUILD_URL}</a></li>
<li>构建日志:<a href=${BUILD_URL}console>${BUILD_URL}console</a></li>
</div>
</ul>
</td>
</tr>
</table>
</body>
</html>""",
to: "${env.CHANGE_AUTHOR_EMAIL}",
from: "support@taosdata.com"
)
}
}
}
...@@ -7,25 +7,37 @@ ...@@ -7,25 +7,37 @@
[![TDengine](TDenginelogo.png)](https://www.taosdata.com) [![TDengine](TDenginelogo.png)](https://www.taosdata.com)
简体中文 | [English](./README.md) 简体中文 | [English](./README.md)
很多职位正在热招中,请看[这里](https://www.taosdata.com/cn/careers/)
# TDengine 简介 # TDengine 简介
TDengine是涛思数据专为物联网、车联网、工业互联网、IT运维等设计和优化的大数据平台。除核心的快10倍以上的时序数据库功能外,还提供缓存、数据订阅、流式计算等功能,最大程度减少研发和运维的复杂度,且核心代码,包括集群功能全部开源(开源协议,AGPL v3.0)。 TDengine 是一款高性能、分布式、支持 SQL 的时序数据库。而且除时序数据库功能外,它还提供缓存、数据订阅、流式计算等功能,最大程度减少研发和运维的复杂度,且核心代码,包括集群功能全部开源(开源协议,AGPL v3.0)。与其他时序数据数据库相比,TDengine 有以下特点:
- 10 倍以上性能提升。定义了创新的数据存储结构,单核每秒就能处理至少2万次请求,插入数百万个数据点,读出一千万以上数据点,比现有通用数据库快了十倍以上。 - **高性能**:通过创新的存储引擎设计,无论是数据写入还是查询,TDengine 的性能比通用数据库快 10 倍以上,也远超其他时序数据库,而且存储空间也大为节省。
- 硬件或云服务成本降至1/5。由于超强性能,计算资源不到通用大数据方案的1/5;通过列式存储和先进的压缩算法,存储空间不到通用数据库的1/10。
- 全栈时序数据处理引擎。将数据库、消息队列、缓存、流式计算等功能融合一起,应用无需再集成Kafka/Redis/HBase/Spark等软件,大幅降低应用开发和维护成本。 - **分布式**:通过原生分布式的设计,TDengine 提供了水平扩展的能力,只需要增加节点就能获得更强的数据处理能力,同时通过多副本机制保证了系统的高可用。
- 强大的分析功能。无论是十年前还是一秒钟前的数据,指定时间范围即可查询。数据可在时间轴上或多个设备上进行聚合。即席查询可通过Shell/Python/R/Matlab随时进行。
- 与第三方工具无缝连接。不用一行代码,即可与Telegraf, Grafana, EMQ X, Prometheus, Matlab, R集成。后续还将支持MQTT, OPC, Hadoop,Spark等, BI工具也将无缝连接。 - **支持 SQL**:TDengine 采用 SQL 作为数据查询语言,减少学习和迁移成本,同时提供 SQL 扩展来处理时序数据特有的分析,而且支持方便灵活的 schemaless 数据写入。
- 零运维成本、零学习成本。安装、集群一秒搞定,无需分库分表,实时备份。标准SQL,支持JDBC,RESTful,支持Python/Java/C/C++/Go/Node.JS, 与MySQL相似,零学习成本。
- **All in One**:将数据库、消息队列、缓存、流式计算等功能融合一起,应用无需再集成 Kafka/Redis/HBase/Spark 等软件,大幅降低应用开发和维护成本。
- **零管理**:安装、集群几秒搞定,无任何依赖,不用分库分表,系统运行状态监测能与 Grafana 或其他运维工具无缝集成。
- **零学习成本**:采用 SQL 查询语言,支持 Python、Java、C/C++、Go、Rust、Node.js 等多种编程语言,与 MySQL 相似,零学习成本。
- **无缝集成**:不用一行代码,即可与 Telegraf、Grafana、EMQX、Prometheus、StatsD、collectd、Matlab、R 等第三方工具无缝集成。
- **互动 Console**: 通过命令行 console,不用编程,执行 SQL 语句就能做即席查询、各种数据库的操作、管理以及集群的维护.
TDengine 可以广泛应用于物联网、工业互联网、车联网、IT 运维、能源、金融等领域,让大量设备、数据采集器每天产生的高达 TB 甚至 PB 级的数据能得到高效实时的处理,对业务的运行状态进行实时的监测、预警,从大数据中挖掘出商业价值。
# 文档 # 文档
TDengine是一个高效的存储、查询、分析时序大数据的平台,专为物联网、车联网、工业互联网、运维监测等优化而设计。您可以像使用关系型数据库MySQL一样来使用它,但建议您在使用前仔细阅读一遍下面的文档,特别是 [数据模型](https://www.taosdata.com/cn/documentation/architecture)[数据建模](https://www.taosdata.com/cn/documentation/model)。除本文档之外,欢迎 [下载产品白皮书](https://www.taosdata.com/downloads/TDengine%20White%20Paper.pdf) TDengine 采用传统的关系数据库模型,您可以像使用关系型数据库 MySQL 一样来使用它。但由于引入了超级表,一个采集点一张表的概念,建议您在使用前仔细阅读一遍下面的文档,特别是 [数据模型](https://www.taosdata.com/cn/documentation/architecture)[数据建模](https://www.taosdata.com/cn/documentation/model)。除本文档之外,欢迎 [下载产品白皮书](https://www.taosdata.com/downloads/TDengine%20White%20Paper.pdf)
# 构建 # 构建
TDengine目前2.0版服务器仅能在Linux系统上安装和运行,后续会支持Windows、macOS等系统。客户端可以在Windows或Linux上安装和运行。任何OS的应用也可以选择RESTful接口连接服务器taosd。CPU支持X64/ARM64/MIPS64/Alpha64,后续会支持ARM32、RISC-V等CPU架构。用户可根据需求选择通过[源码](https://www.taosdata.com/cn/getting-started/#通过源码安装)或者[安装包](https://www.taosdata.com/cn/getting-started/#通过安装包安装)来安装。本快速指南仅适用于通过源码安装。 TDengine 目前 2.0 版服务器仅能在 Linux 系统上安装和运行,后续会支持 Windows、macOS 等系统。客户端可以在 Windows 或 Linux 上安装和运行。任何 OS 的应用也可以选择 RESTful 接口连接服务器 taosd。CPU 支持 X64/ARM64/MIPS64/Alpha64,后续会支持 ARM32、RISC-V 等 CPU 架构。用户可根据需求选择通过[源码](https://www.taosdata.com/cn/getting-started/#通过源码安装)或者[安装包](https://www.taosdata.com/cn/getting-started/#通过安装包安装)来安装。本快速指南仅适用于通过源码安装。
## 安装工具 ## 安装工具
...@@ -56,6 +68,18 @@ sudo apt-get install -y openjdk-8-jdk ...@@ -56,6 +68,18 @@ sudo apt-get install -y openjdk-8-jdk
sudo apt-get install -y maven sudo apt-get install -y maven
``` ```
#### 为 taos-tools 安装编译需要的软件
taosTools 是用于 TDengine 的辅助工具软件集合。目前它包含 taosBenchmark(曾命名为 taosdemo)和 taosdump 两个软件。
默认 TDengine 编译不包含 taosTools。您可以在编译 TDengine 时使用`cmake .. -DBUILD_TOOLS=true` 来同时编译 taosTools。
为了在 Ubuntu/Debian 系统上编译 [taos-tools](https://github.com/taosdata/taos-tools) 需要安装如下软件:
```bash
sudo apt install build-essential libjansson-dev libsnappy-dev liblzma-dev libz-dev pkg-config
```
### CentOS 7: ### CentOS 7:
```bash ```bash
...@@ -74,7 +98,7 @@ sudo yum install -y java-1.8.0-openjdk ...@@ -74,7 +98,7 @@ sudo yum install -y java-1.8.0-openjdk
sudo yum install -y maven sudo yum install -y maven
``` ```
### CentOS 8 & Fedora: ### CentOS 8 & Fedora
```bash ```bash
sudo dnf install -y gcc gcc-c++ make cmake epel-release git sudo dnf install -y gcc gcc-c++ make cmake epel-release git
...@@ -92,6 +116,18 @@ sudo dnf install -y java-1.8.0-openjdk ...@@ -92,6 +116,18 @@ sudo dnf install -y java-1.8.0-openjdk
sudo dnf install -y maven sudo dnf install -y maven
``` ```
#### 在 CentOS 上构建 taosTools 安装依赖软件
为了在 CentOS 上构建 [taosTools](https://github.com/taosdata/taos-tools) 需要安装如下依赖软件
```bash
sudo yum install zlib-devel xz-devel snappy-devel jansson-devel pkgconfig libatomic libstdc++-static
```
注意:由于 snappy 缺乏 pkg-config 支持
(参考 [链接](https://github.com/google/snappy/pull/86)),会导致
cmake 提示无法发现 libsnappy,实际上工作正常。
## 获取源码 ## 获取源码
首先,你需要从 GitHub 克隆源码: 首先,你需要从 GitHub 克隆源码:
...@@ -107,6 +143,13 @@ Go 连接器和 Grafana 插件在其他独立仓库,如果安装它们的话 ...@@ -107,6 +143,13 @@ Go 连接器和 Grafana 插件在其他独立仓库,如果安装它们的话
git submodule update --init --recursive git submodule update --init --recursive
``` ```
如果使用 https 协议下载比较慢,可以通过修改 ~/.gitconfig 文件添加以下两行设置使用 ssh 协议下载。需要首先上传 ssh 密钥到 GitHub,详细方法请参考 GitHub 官方文档。
```
[url "git@github.com:"]
insteadOf = https://github.com/
```
## 构建 TDengine ## 构建 TDengine
### Linux 系统 ### Linux 系统
...@@ -116,13 +159,14 @@ mkdir debug && cd debug ...@@ -116,13 +159,14 @@ mkdir debug && cd debug
cmake .. && cmake --build . cmake .. && cmake --build .
``` ```
您可以选择使用 Jemalloc 作为内存分配器,替代默认的 glibc: 您可以选择使用 jemalloc 作为内存分配器,替代默认的 glibc:
```bash ```bash
apt install autoconf apt install autoconf
cmake .. -DJEMALLOC_ENABLED=true cmake .. -DJEMALLOC_ENABLED=true
``` ```
在X86-64、X86、arm64、arm32 和 mips64 平台上,TDengine 生成脚本可以自动检测机器架构。也可以手动配置 CPUTYPE 参数来指定 CPU 类型,如 aarch64 或 aarch32 等。 X86-64、X86、arm64、arm32 和 mips64 平台上,TDengine 生成脚本可以自动检测机器架构。也可以手动配置 CPUTYPE 参数来指定 CPU 类型,如 aarch64 或 aarch32 等。
aarch64: aarch64:
...@@ -157,7 +201,7 @@ nmake ...@@ -157,7 +201,7 @@ nmake
如果你使用的是 Visual Studio 2019 或 2017 版本: 如果你使用的是 Visual Studio 2019 或 2017 版本:
打开cmd.exe,执行 vcvarsall.bat 时,为 64 位操作系统指定“x64”,为 32 位操作系统指定“x86”。 打开 cmd.exe,执行 vcvarsall.bat 时,为 64 位操作系统指定“x64”,为 32 位操作系统指定“x86”。
```bash ```bash
mkdir debug && cd debug mkdir debug && cd debug
...@@ -174,7 +218,7 @@ cmake .. -G "NMake Makefiles" ...@@ -174,7 +218,7 @@ cmake .. -G "NMake Makefiles"
nmake nmake
``` ```
### Mac OS X 系统 ### macOS 系统
安装 Xcode 命令行工具和 cmake. 在 Catalina 和 Big Sur 操作系统上,需要安装 XCode 11.4+ 版本。 安装 Xcode 命令行工具和 cmake. 在 Catalina 和 Big Sur 操作系统上,需要安装 XCode 11.4+ 版本。
...@@ -225,15 +269,15 @@ taos ...@@ -225,15 +269,15 @@ taos
# 体验 TDengine # 体验 TDengine
TDengine终端中,用户可以通过SQL命令来创建/删除数据库、表等,并进行插入查询操作。 TDengine 终端中,用户可以通过 SQL 命令来创建/删除数据库、表等,并进行插入查询操作。
```bash ```bash
create database demo; CREATE DATABASE demo;
use demo; USE demo;
create table t (ts timestamp, speed int); CREATE TABLE t (ts TIMESTAMP, speed INT);
insert into t values ('2019-07-15 00:00:00', 10); INSERT INTO t VALUES('2019-07-15 00:00:00', 10);
insert into t values ('2019-07-15 01:00:00', 20); INSERT INTO t VALUES('2019-07-15 01:00:00', 20);
select * from t; SELECT * FROM t;
ts | speed | ts | speed |
=================================== ===================================
19-07-15 00:00:00.000| 10| 19-07-15 00:00:00.000| 10|
...@@ -245,33 +289,35 @@ Query OK, 2 row(s) in set (0.001700s) ...@@ -245,33 +289,35 @@ Query OK, 2 row(s) in set (0.001700s)
## 官方连接器 ## 官方连接器
TDengine 提供了丰富的应用程序开发接口,其中包括C/C++、Java、Python、Go、Node.js、C# 、RESTful 等,便于用户快速开发应用: TDengine 提供了丰富的应用程序开发接口,其中包括 C/C++、Java、Python、Go、Node.js、C# 、RESTful 等,便于用户快速开发应用:
- [Java](https://www.taosdata.com/cn/documentation/connector/java)
- Java - [C/C++](https://www.taosdata.com/cn/documentation/connector#c-cpp)
- C/C++ - [Python](https://www.taosdata.com/cn/documentation/connector#python)
- Python - [Go](https://www.taosdata.com/cn/documentation/connector#go)
- Go - [RESTful API](https://www.taosdata.com/cn/documentation/connector#restful)
- RESTful API - [Node.js](https://www.taosdata.com/cn/documentation/connector#nodejs)
- Node.js - [Rust](https://www.taosdata.com/cn/documentation/connector/rust)
## 第三方连接器 ## 第三方连接器
TDengine 社区生态中也有一些非常友好的第三方连接器,可以通过以下链接访问它们的源码。 TDengine 社区生态中也有一些非常友好的第三方连接器,可以通过以下链接访问它们的源码。
- [Rust Connector](https://github.com/taosdata/TDengine/tree/master/tests/examples/rust) - [Rust Bindings](https://github.com/songtianyi/tdengine-rust-bindings/tree/master/examples)
- [.Net Core Connector](https://github.com/maikebing/Maikebing.EntityFrameworkCore.Taos) - [.Net Core Connector](https://github.com/maikebing/Maikebing.EntityFrameworkCore.Taos)
- [Lua Connector](https://github.com/taosdata/TDengine/tree/develop/tests/examples/lua) - [Lua Connector](https://github.com/taosdata/TDengine/tree/develop/examples/lua)
# 运行和添加测试例 # 运行和添加测试例
TDengine 的测试框架和所有测试例全部开源。 TDengine 的测试框架和所有测试例全部开源。
点击 [这里](tests/How-To-Run-Test-And-How-To-Add-New-Test-Case.md),了解如何运行测试例和添加新的测试例。 点击 [这里](https://github.com/taosdata/TDengine/blob/develop/tests/How-To-Run-Test-And-How-To-Add-New-Test-Case.md),了解如何运行测试例和添加新的测试例。
# 成为社区贡献者 # 成为社区贡献者
......
...@@ -7,100 +7,164 @@ ...@@ -7,100 +7,164 @@
[![TDengine](TDenginelogo.png)](https://www.taosdata.com) [![TDengine](TDenginelogo.png)](https://www.taosdata.com)
English | [简体中文](./README-CN.md) English | [简体中文](./README-CN.md)
We are hiring, check [here](https://www.taosdata.com/en/careers/)
# What is TDengine? # What is TDengine?
TDengine is an open-sourced big data platform under [GNU AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html), designed and optimized for the Internet of Things (IoT), Connected Cars, Industrial IoT, and IT Infrastructure and Application Monitoring. Besides the 10x faster time-series database, it provides caching, stream computing, message queuing and other functionalities to reduce the complexity and cost of development and operation. TDengine is a high-performance, scalable time-series database with SQL support. Its code including cluster feature is open source under [GNU AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html). Besides the database, it provides caching, stream processing, data data subscription and other functionalities to reduce the complexity and cost of development and operation. TDengine differentiates itself from other TSDBs with the following advantages.
- **10x Faster on Insert/Query Speeds**: Through the innovative design on storage, on a single-core machine, over 20K requests can be processed, millions of data points can be ingested, and over 10 million data points can be retrieved in a second. It is 10 times faster than other databases. - **High Performance**: TDengine outperforms other time series databases in data ingestion and querying while significantly reducing storage cost and compute costs, with an innovatively designed and purpose-built storage engine.
- **1/5 Hardware/Cloud Service Costs**: Compared with typical big data solutions, less than 1/5 of computing resources are required. Via column-based storage and tuned compression algorithms for different data types, less than 1/10 of storage space is needed. - **Scalable**: TDengine provides out-of-box scalability and high-availability through its native distributed design. Nodes can be added through simple configuration to achieve greater data processing power. In addition, this feature is open source.
- **Full Stack for Time-Series Data**: By integrating a database with message queuing, caching, and stream computing features together, it is no longer necessary to integrate Kafka/Redis/HBase/Spark or other software. It makes the system architecture much simpler and more robust. - **SQL Support**: TDengine uses SQL as the query language, thereby reducing learning and migration costs, while adding SQL extensions to handle time-series data better, and supporting convenient and flexible schemaless data ingestion.
- **Powerful Data Analysis**: Whether it is 10 years or one minute ago, data can be queried just by specifying the time range. Data can be aggregated over time, multiple time streams or both. Ad Hoc queries or analyses can be executed via TDengine shell, Python, R or Matlab. - **All in One**: TDengine has built-in caching, stream processing and data subscription functions, it is no longer necessary to integrate Kafka/Redis/HBase/Spark or other software in some scenarios. It makes the system architecture much simpler and easy to maintain.
- **Seamless Integration with Other Tools**: Telegraf, Grafana, Matlab, R, and other tools can be integrated with TDengine without a line of code. MQTT, OPC, Hadoop, Spark, and many others will be integrated soon. - **Seamless Integration**: Without a single line of code, TDengine provide seamless integration with third-party tools such as Telegraf, Grafana, EMQX, Prometheus, StatsD, collectd, etc. More will be integrated.
- **Zero Management, No Learning Curve**: It takes only seconds to download, install, and run it successfully; there are no other dependencies. Automatic partitioning on tables or DBs. Standard SQL is used, with C/C++, Python, JDBC, Go and RESTful connectors. - **Zero Management**: Installation and cluster setup can be done in seconds. Data partitioning and sharding are executed automatically. TDengine’s running status can be monitored via Grafana or other DevOps tools.
- **Zero Learning Cost**: With SQL as the query language, support for ubiquitous tools like Python, Java, C/C++, Go, Rust, Node.js connectors, there is zero learning cost.
- **Interactive Console**: TDengine provides convenient console access to the database to run ad hoc queries, maintain the database, or manage the cluster without any programming.
TDengine can be widely applied to Internet of Things (IoT), Connected Vehicles, Industrial IoT, DevOps, energy, finance and many other scenarios.
# Documentation # Documentation
For user manual, system design and architecture, engineering blogs, refer to [TDengine Documentation](https://www.taosdata.com/en/documentation/)(中文版请点击[这里](https://www.taosdata.com/cn/documentation20/)) For user manual, system design and architecture, engineering blogs, refer to [TDengine Documentation](https://www.taosdata.com/en/documentation/)(中文版请点击[这里](https://www.taosdata.com/cn/documentation20/))
for details. The documentation from our website can also be downloaded locally from *documentation/tdenginedocs-en* or *documentation/tdenginedocs-cn*. for details. The documentation from our website can also be downloaded locally from *documentation/tdenginedocs-en* or *documentation/tdenginedocs-cn*.
# Building # Building
At the moment, TDengine only supports building and running on Linux systems. You can choose to [install from packages](https://www.taosdata.com/en/getting-started/#Install-from-Package) or from the source code. This quick guide is for installation from the source only. At the moment, TDengine only supports building and running on Linux systems. You can choose to [install from packages](https://www.taosdata.com/en/getting-started/#Install-from-Package) or from the source code. This quick guide is for installation from the source only.
To build TDengine, use [CMake](https://cmake.org/) 2.8.12.x or higher versions in the project directory. To build TDengine, use [CMake](https://cmake.org/) 3.0.2 or higher versions in the project directory.
## Install tools ## Install build dependencies
### Ubuntu 16.04 and above or Debian
### Ubuntu 16.04 and above & Debian:
```bash ```bash
sudo apt-get install -y gcc cmake build-essential git sudo apt-get install -y gcc cmake build-essential git
``` ```
### Ubuntu 14.04: ### Ubuntu 14.04
```bash ```bash
sudo apt-get install -y gcc cmake3 build-essential git binutils-2.26 sudo apt-get install -y gcc cmake3 build-essential git binutils-2.26
export PATH=/usr/lib/binutils-2.26/bin:$PATH export PATH=/usr/lib/binutils-2.26/bin:$PATH
``` ```
To compile and package the JDBC driver source code, you should have a Java jdk-8 or higher and Apache Maven 2.7 or higher installed. To compile and package the JDBC driver source code, you should have a Java jdk-8 or higher and Apache Maven 2.7 or higher installed.
To install openjdk-8: To install openjdk-8:
```bash ```bash
sudo apt-get install -y openjdk-8-jdk sudo apt-get install -y openjdk-8-jdk
``` ```
To install Apache Maven: To install Apache Maven:
```bash ```bash
sudo apt-get install -y maven sudo apt-get install -y maven
``` ```
### Centos 7: #### Install build dependencies for taosTools
We provide a few useful tools such as taosBenchmark (was named taosdemo) and taosdump. They were part of TDengine. From TDengine 2.4.0.0, taosBenchmark and taosdump were not released together with TDengine.
By default, TDengine compiling does not include taosTools. You can use 'cmake .. -DBUILD_TOOLS=true' to make them be compiled with TDengine.
To build the [taosTools](https://github.com/taosdata/taos-tools) on Ubuntu/Debian, the following packages need to be installed.
```bash ```bash
sudo yum install -y gcc gcc-c++ make cmake git sudo apt install build-essential libjansson-dev libsnappy-dev liblzma-dev libz-dev pkg-config
```
### CentOS 7
```bash
sudo yum install epel-release
sudo yum update
sudo yum install -y gcc gcc-c++ make cmake3 git
sudo ln -sf /usr/bin/cmake3 /usr/bin/cmake
``` ```
To install openjdk-8: To install openjdk-8:
```bash ```bash
sudo yum install -y java-1.8.0-openjdk sudo yum install -y java-1.8.0-openjdk
``` ```
To install Apache Maven: To install Apache Maven:
```bash ```bash
sudo yum install -y maven sudo yum install -y maven
``` ```
### Centos 8 & Fedora: ### CentOS 8 & Fedora
```bash ```bash
sudo dnf install -y gcc gcc-c++ make cmake epel-release git sudo dnf install -y gcc gcc-c++ make cmake epel-release git
``` ```
To install openjdk-8: To install openjdk-8:
```bash ```bash
sudo dnf install -y java-1.8.0-openjdk sudo dnf install -y java-1.8.0-openjdk
``` ```
To install Apache Maven: To install Apache Maven:
```bash ```bash
sudo dnf install -y maven sudo dnf install -y maven
``` ```
#### Install build dependencies for taosTools on CentOS
To build the [taosTools](https://github.com/taosdata/taos-tools) on CentOS, the following packages need to be installed.
```bash
sudo yum install zlib-devel xz-devel snappy-devel jansson-devel pkgconfig libatomic libstdc++-static
```
Note: Since snappy lacks pkg-config support (refer to [link](https://github.com/google/snappy/pull/86)), it lead a cmake prompt libsnappy not found. But snappy will works well.
### Setup golang environment
TDengine includes few components developed by Go language. Please refer to golang.org official documentation for golang environment setup.
Please use version 1.14+. For the user in China, we recommend using a proxy to accelerate package downloading.
```
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
```
## Get the source codes ## Get the source codes
First of all, you may clone the source codes from github: First of all, you may clone the source codes from github:
```bash ```bash
git clone https://github.com/taosdata/TDengine.git git clone https://github.com/taosdata/TDengine.git
cd TDengine cd TDengine
``` ```
The connectors for go & grafana have been moved to separated repositories, The connectors for go & Grafana and some tools have been moved to separated repositories,
so you should run this command in the TDengine directory to install them: so you should run this command in the TDengine directory to install them:
```bash ```bash
git submodule update --init --recursive git submodule update --init --recursive
``` ```
You can modify the file ~/.gitconfig to use ssh protocol instead of https for better download speed. You need to upload ssh public key to GitHub first. Please refer to GitHub official documentation for detail.
```
[url "git@github.com:"]
insteadOf = https://github.com/
```
## Build TDengine ## Build TDengine
### On Linux platform ### On Linux platform
...@@ -110,7 +174,21 @@ mkdir debug && cd debug ...@@ -110,7 +174,21 @@ mkdir debug && cd debug
cmake .. && cmake --build . cmake .. && cmake --build .
``` ```
Note TDengine 2.3.x.0 and later use a component named 'taosAdapter' to play http daemon role by default instead of the http daemon embedded in the early version of TDengine. The taosAdapter is programmed by go language. If you pull TDengine source code to the latest from an existing codebase, please execute 'git submodule update --init --recursive' to pull taosAdapter source code. Please install go language version 1.14 or above for compiling taosAdapter. If you meet difficulties regarding 'go mod', especially you are from China, you can use a proxy to solve the problem.
```
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
```
The embedded http daemon still be built from TDengine source code by default. Or you can use the following command to choose to build taosAdapter.
```
cmake .. -DBUILD_HTTP=false
```
You can use Jemalloc as memory allocator instead of glibc: You can use Jemalloc as memory allocator instead of glibc:
``` ```
apt install autoconf apt install autoconf
cmake .. -DJEMALLOC_ENABLED=true cmake .. -DJEMALLOC_ENABLED=true
...@@ -120,16 +198,19 @@ TDengine build script can detect the host machine's architecture on X86-64, X86, ...@@ -120,16 +198,19 @@ TDengine build script can detect the host machine's architecture on X86-64, X86,
You can also specify CPUTYPE option like aarch64 or aarch32 too if the detection result is not correct: You can also specify CPUTYPE option like aarch64 or aarch32 too if the detection result is not correct:
aarch64: aarch64:
```bash ```bash
cmake .. -DCPUTYPE=aarch64 && cmake --build . cmake .. -DCPUTYPE=aarch64 && cmake --build .
``` ```
aarch32: aarch32:
```bash ```bash
cmake .. -DCPUTYPE=aarch32 && cmake --build . cmake .. -DCPUTYPE=aarch32 && cmake --build .
``` ```
mips64: mips64:
```bash ```bash
cmake .. -DCPUTYPE=mips64 && cmake --build . cmake .. -DCPUTYPE=mips64 && cmake --build .
``` ```
...@@ -138,6 +219,7 @@ cmake .. -DCPUTYPE=mips64 && cmake --build . ...@@ -138,6 +219,7 @@ cmake .. -DCPUTYPE=mips64 && cmake --build .
If you use the Visual Studio 2013, please open a command window by executing "cmd.exe". If you use the Visual Studio 2013, please open a command window by executing "cmd.exe".
Please specify "amd64" for 64 bits Windows or specify "x86" is for 32 bits Windows when you execute vcvarsall.bat. Please specify "amd64" for 64 bits Windows or specify "x86" is for 32 bits Windows when you execute vcvarsall.bat.
```cmd ```cmd
mkdir debug && cd debug mkdir debug && cd debug
"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" < amd64 | x86 > "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" < amd64 | x86 >
...@@ -158,13 +240,14 @@ nmake ...@@ -158,13 +240,14 @@ nmake
``` ```
Or, you can simply open a command window by clicking Windows Start -> "Visual Studio < 2019 | 2017 >" folder -> "x64 Native Tools Command Prompt for VS < 2019 | 2017 >" or "x86 Native Tools Command Prompt for VS < 2019 | 2017 >" depends what architecture your Windows is, then execute commands as follows: Or, you can simply open a command window by clicking Windows Start -> "Visual Studio < 2019 | 2017 >" folder -> "x64 Native Tools Command Prompt for VS < 2019 | 2017 >" or "x86 Native Tools Command Prompt for VS < 2019 | 2017 >" depends what architecture your Windows is, then execute commands as follows:
```cmd ```cmd
mkdir debug && cd debug mkdir debug && cd debug
cmake .. -G "NMake Makefiles" cmake .. -G "NMake Makefiles"
nmake nmake
``` ```
### On Mac OS X platform ### On macOS platform
Please install XCode command line tools and cmake. Verified with XCode 11.4+ on Catalina and Big Sur. Please install XCode command line tools and cmake. Verified with XCode 11.4+ on Catalina and Big Sur.
...@@ -176,6 +259,7 @@ cmake .. && cmake --build . ...@@ -176,6 +259,7 @@ cmake .. && cmake --build .
# Installing # Installing
After building successfully, TDengine can be installed by: (On Windows platform, the following command should be `nmake install`) After building successfully, TDengine can be installed by: (On Windows platform, the following command should be `nmake install`)
```bash ```bash
sudo make install sudo make install
``` ```
...@@ -184,25 +268,42 @@ Users can find more information about directories installed on the system in the ...@@ -184,25 +268,42 @@ Users can find more information about directories installed on the system in the
Users can also choose to [install from packages](https://www.taosdata.com/en/getting-started/#Install-from-Package) for it. Users can also choose to [install from packages](https://www.taosdata.com/en/getting-started/#Install-from-Package) for it.
To start the service after installation, in a terminal, use: To start the service after installation, in a terminal, use:
```bash ```bash
sudo systemctl start taosd sudo systemctl start taosd
``` ```
Then users can use the [TDengine shell](https://www.taosdata.com/en/getting-started/#TDengine-Shell) to connect the TDengine server. In a terminal, use: Then users can use the [TDengine shell](https://www.taosdata.com/en/getting-started/#TDengine-Shell) to connect the TDengine server. In a terminal, use:
```bash ```bash
taos taos
``` ```
If TDengine shell connects the server successfully, welcome messages and version info are printed. Otherwise, an error message is shown. If TDengine shell connects the server successfully, welcome messages and version info are printed. Otherwise, an error message is shown.
## Install TDengine by apt-get
If you use Debian or Ubuntu system, you can use 'apt-get' command to install TDengine from official repository. Please use following commands to setup:
```
wget -qO - http://repos.taosdata.com/tdengine.key | sudo apt-key add -
echo "deb [arch=amd64] http://repos.taosdata.com/tdengine-stable stable main" | sudo tee /etc/apt/sources.list.d/tdengine-stable.list
[Optional] echo "deb [arch=amd64] http://repos.taosdata.com/tdengine-beta beta main" | sudo tee /etc/apt/sources.list.d/tdengine-beta.list
sudo apt-get update
apt-cache policy tdengine
sudo apt-get install tdengine
```
## Quick Run ## Quick Run
If you don't want to run TDengine as a service, you can run it in current shell. For example, to quickly start a TDengine server after building, run the command below in terminal: (We take Linux as an example, command on Windows will be `taosd.exe`) If you don't want to run TDengine as a service, you can run it in current shell. For example, to quickly start a TDengine server after building, run the command below in terminal: (We take Linux as an example, command on Windows will be `taosd.exe`)
```bash ```bash
./build/bin/taosd -c test/cfg ./build/bin/taosd -c test/cfg
``` ```
In another terminal, use the TDengine shell to connect the server: In another terminal, use the TDengine shell to connect the server:
```bash ```bash
./build/bin/taos -c test/cfg ./build/bin/taos -c test/cfg
``` ```
...@@ -210,7 +311,9 @@ In another terminal, use the TDengine shell to connect the server: ...@@ -210,7 +311,9 @@ In another terminal, use the TDengine shell to connect the server:
option "-c test/cfg" specifies the system configuration file directory. option "-c test/cfg" specifies the system configuration file directory.
# Try TDengine # Try TDengine
It is easy to run SQL commands from TDengine shell which is the same as other SQL databases. It is easy to run SQL commands from TDengine shell which is the same as other SQL databases.
```sql ```sql
create database db; create database db;
use db; use db;
...@@ -222,30 +325,34 @@ drop database db; ...@@ -222,30 +325,34 @@ drop database db;
``` ```
# Developing with TDengine # Developing with TDengine
### Official Connectors
## Official Connectors
TDengine provides abundant developing tools for users to develop on TDengine. Follow the links below to find your desired connectors and relevant documentation. TDengine provides abundant developing tools for users to develop on TDengine. Follow the links below to find your desired connectors and relevant documentation.
- [Java](https://www.taosdata.com/en/documentation/connector/#Java-Connector) - [Java](https://www.taosdata.com/en/documentation/connector/java)
- [C/C++](https://www.taosdata.com/en/documentation/connector/#C/C++-Connector) - [C/C++](https://www.taosdata.com/en/documentation/connector#c-cpp)
- [Python](https://www.taosdata.com/en/documentation/connector/#Python-Connector) - [Python](https://www.taosdata.com/en/documentation/connector#python)
- [Go](https://www.taosdata.com/en/documentation/connector/#Go-Connector) - [Go](https://www.taosdata.com/en/documentation/connector#go)
- [RESTful API](https://www.taosdata.com/en/documentation/connector/#RESTful-Connector) - [RESTful API](https://www.taosdata.com/en/documentation/connector#restful)
- [Node.js](https://www.taosdata.com/en/documentation/connector/#Node.js-Connector) - [Node.js](https://www.taosdata.com/en/documentation/connector#nodejs)
- [Rust](https://www.taosdata.com/en/documentation/connector/rust)
### Third Party Connectors ## Third Party Connectors
The TDengine community has also kindly built some of their own connectors! Follow the links below to find the source code for them. The TDengine community has also kindly built some of their own connectors! Follow the links below to find the source code for them.
- [Rust Connector](https://github.com/taosdata/TDengine/tree/master/tests/examples/rust) - [Rust Bindings](https://github.com/songtianyi/tdengine-rust-bindings/tree/master/examples)
- [.Net Core Connector](https://github.com/maikebing/Maikebing.EntityFrameworkCore.Taos) - [.Net Core Connector](https://github.com/maikebing/Maikebing.EntityFrameworkCore.Taos)
- [Lua Connector](https://github.com/taosdata/TDengine/tree/develop/tests/examples/lua) - [Lua Connector](https://github.com/taosdata/TDengine/tree/develop/tests/examples/lua)
# How to run the test cases and how to add a new test case? # How to run the test cases and how to add a new test case
TDengine's test framework and all test cases are fully open source. TDengine's test framework and all test cases are fully open source.
Please refer to [this document](tests/How-To-Run-Test-And-How-To-Add-New-Test-Case.md) for how to run test and develop new test case. Please refer to [this document](https://github.com/taosdata/TDengine/blob/develop/tests/How-To-Run-Test-And-How-To-Add-New-Test-Case.md) for how to run test and develop new test case.
# TDengine Roadmap # TDengine Roadmap
- Support event-driven stream computing - Support event-driven stream computing
- Support user defined functions - Support user defined functions
- Support MQTT connection - Support MQTT connection
......
TDenginelogo.png

19.2 KB | W: | H:

TDenginelogo.png

9.1 KB | W: | H:

TDenginelogo.png
TDenginelogo.png
TDenginelogo.png
TDenginelogo.png
  • 2-up
  • Swipe
  • Onion skin
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package app package app
import ( import (
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package expr package expr
import ( import (
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package expr package expr
import "testing" import "testing"
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package expr package expr
import ( import (
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package expr package expr
import ( import (
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package app package app
import ( import (
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package app package app
import ( import (
...@@ -81,6 +96,7 @@ func (alert *Alert) doRefresh(firing bool, rule *Rule) bool { ...@@ -81,6 +96,7 @@ func (alert *Alert) doRefresh(firing bool, rule *Rule) bool {
case (!firing) && (alert.State == AlertStateFiring): case (!firing) && (alert.State == AlertStateFiring):
alert.State = AlertStateWaiting alert.State = AlertStateWaiting
alert.EndsAt = time.Now() alert.EndsAt = time.Now()
return false
case firing && (alert.State == AlertStateWaiting): case firing && (alert.State == AlertStateWaiting):
alert.StartsAt = time.Now() alert.StartsAt = time.Now()
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package app package app
import ( import (
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main package main
import ( import (
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package models package models
import ( import (
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package models package models
import "time" import "time"
......
...@@ -52,7 +52,7 @@ echo "cpuType=${cpuType}" ...@@ -52,7 +52,7 @@ echo "cpuType=${cpuType}"
echo "osType=${osType}" echo "osType=${osType}"
echo "version=${version}" echo "version=${version}"
GOOS=${osType} GOARCH=${cpuType} go build -ldflags '-X main.version='${version} GOOS=${osType} GOARCH=${cpuType} go mod tidy && go build -ldflags '-X main.version='${version}
mkdir -p TDengine-alert/driver mkdir -p TDengine-alert/driver
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package utils package utils
import ( import (
......
/*
* Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
*
* This program is free software: you can use, redistribute, and/or modify
* it under the terms of the GNU Affero General Public License, version 3
* or later ("AGPL"), as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package log package log
import ( import (
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
PROJECT(TDengine) PROJECT(TDengine)
IF (TD_ACCOUNT) IF (TD_ACCOUNT)
...@@ -13,6 +13,10 @@ IF (TD_GRANT) ...@@ -13,6 +13,10 @@ IF (TD_GRANT)
ADD_DEFINITIONS(-D_GRANT) ADD_DEFINITIONS(-D_GRANT)
ENDIF () ENDIF ()
IF (TD_USB_DONGLE)
ADD_DEFINITIONS(-D_USB_DONGLE)
ENDIF ()
IF (TD_MQTT) IF (TD_MQTT)
ADD_DEFINITIONS(-D_MQTT) ADD_DEFINITIONS(-D_MQTT)
ENDIF () ENDIF ()
...@@ -37,18 +41,6 @@ IF (TD_GODLL) ...@@ -37,18 +41,6 @@ IF (TD_GODLL)
ADD_DEFINITIONS(-D_TD_GO_DLL_) ADD_DEFINITIONS(-D_TD_GO_DLL_)
ENDIF () ENDIF ()
IF (TD_POWER)
ADD_DEFINITIONS(-D_TD_POWER_)
ENDIF ()
IF (TD_TQ)
ADD_DEFINITIONS(-D_TD_TQ_)
ENDIF ()
IF (TD_PRO)
ADD_DEFINITIONS(-D_TD_PRO_)
ENDIF ()
IF (TD_MEM_CHECK) IF (TD_MEM_CHECK)
ADD_DEFINITIONS(-DTAOS_MEM_CHECK) ADD_DEFINITIONS(-DTAOS_MEM_CHECK)
ENDIF () ENDIF ()
...@@ -118,10 +110,76 @@ IF (TD_MIPS_32) ...@@ -118,10 +110,76 @@ IF (TD_MIPS_32)
ENDIF () ENDIF ()
IF (TD_ALPINE) IF (TD_ALPINE)
SET(COMMON_FLAGS "${COMMON_FLAGS} -largp") SET(COMMON_FLAGS "${COMMON_FLAGS} -Wl,-z,stack-size=2097152")
link_libraries(/usr/lib/libargp.a) link_libraries(argp)
ADD_DEFINITIONS(-D_ALPINE) ADD_DEFINITIONS(-D_ALPINE)
MESSAGE(STATUS "aplhine is defined") MESSAGE(STATUS "alpine is defined")
ENDIF ()
IF ("${BUILD_HTTP}" STREQUAL "")
IF (TD_LINUX)
IF (TD_ARM_32)
SET(TD_BUILD_HTTP TRUE)
ELSE ()
SET(TD_BUILD_HTTP TRUE)
ENDIF ()
ELSEIF (TD_DARWIN)
SET(TD_BUILD_HTTP TRUE)
ELSE ()
SET(TD_BUILD_HTTP TRUE)
ENDIF ()
ELSEIF (${BUILD_HTTP} MATCHES "false")
SET(TD_BUILD_HTTP FALSE)
ELSEIF (${BUILD_HTTP} MATCHES "true")
SET(TD_BUILD_HTTP TRUE)
ELSEIF (${BUILD_HTTP} MATCHES "internal")
SET(TD_BUILD_HTTP FALSE)
SET(TD_BUILD_TAOSA_INTERNAL TRUE)
ELSE ()
SET(TD_BUILD_HTTP TRUE)
ENDIF ()
IF (TD_BUILD_HTTP)
ADD_DEFINITIONS(-DHTTP_EMBEDDED)
ENDIF ()
IF ("${BUILD_TOOLS}" STREQUAL "")
IF (TD_LINUX)
IF (TD_ARM_32)
SET(BUILD_TOOLS "false")
ELSEIF (TD_ARM_64)
SET(BUILD_TOOLS "false")
ELSE ()
SET(BUILD_TOOLS "false")
ENDIF ()
ELSEIF (TD_DARWIN)
SET(BUILD_TOOLS "false")
ELSE ()
SET(BUILD_TOOLS "false")
ENDIF ()
ENDIF ()
IF ("${BUILD_TOOLS}" MATCHES "false")
MESSAGE("${Yellow} Will _not_ build taos_tools! ${ColourReset}")
SET(TD_TAOS_TOOLS FALSE)
ELSE ()
MESSAGE("")
MESSAGE("${Green} Will build taos_tools! ${ColourReset}")
MESSAGE("")
SET(TD_TAOS_TOOLS TRUE)
ENDIF ()
IF (${BUILD_LUA} MATCHES "false")
SET(TD_BUILD_LUA FALSE)
ENDIF ()
IF (TD_BUILD_LUA)
MESSAGE("Enable lua")
ADD_DEFINITIONS(-DLUA_EMBEDDED)
SET(LINK_LUA "lua")
ELSE ()
MESSAGE("Disable lua")
SET(LINK_LUA "")
ENDIF () ENDIF ()
IF (TD_LINUX) IF (TD_LINUX)
...@@ -189,7 +247,7 @@ IF (TD_WINDOWS) ...@@ -189,7 +247,7 @@ IF (TD_WINDOWS)
ADD_DEFINITIONS(-D_MBCS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) ADD_DEFINITIONS(-D_MBCS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE)
SET(CMAKE_GENERATOR "NMake Makefiles" CACHE INTERNAL "" FORCE) SET(CMAKE_GENERATOR "NMake Makefiles" CACHE INTERNAL "" FORCE)
IF (NOT TD_GODLL) IF (NOT TD_GODLL)
SET(COMMON_FLAGS "/nologo /WX /wd4018 /wd2220 /Oi /Oy- /Gm- /EHsc /MT /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Gd /errorReport:prompt /analyze-") SET(COMMON_FLAGS "/nologo /WX /wd4018 /wd4999 /Oi /Oy- /Gm- /EHsc /MT /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Gd /errorReport:prompt /analyze-")
IF (MSVC AND (MSVC_VERSION GREATER_EQUAL 1900)) IF (MSVC AND (MSVC_VERSION GREATER_EQUAL 1900))
SET(COMMON_FLAGS "${COMMON_FLAGS} /Wv:18") SET(COMMON_FLAGS "${COMMON_FLAGS} /Wv:18")
ENDIF () ENDIF ()
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
PROJECT(TDengine) PROJECT(TDengine)
SET(CMAKE_C_STANDARD 11) SET(CMAKE_C_STANDARD 11)
SET(CMAKE_VERBOSE_MAKEFILE ON)
IF (TD_BUILD_VERBOSE)
SET(CMAKE_VERBOSE_MAKEFILE ON)
ELSE ()
SET(CMAKE_VERBOSE_MAKEFILE OFF)
ENDIF ()
#set output directory #set output directory
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/build/lib) SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/build/lib)
...@@ -34,12 +39,22 @@ ENDIF () ...@@ -34,12 +39,22 @@ ENDIF ()
# #
# Set compiler options # Set compiler options
SET(COMMON_C_FLAGS "${COMMON_FLAGS} -std=gnu99") IF (TD_LINUX)
SET(COMMON_C_FLAGS "${COMMON_FLAGS} -std=gnu99")
ELSE ()
SET(COMMON_C_FLAGS "${COMMON_FLAGS} ")
ENDIF ()
SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${COMMON_C_FLAGS} ${DEBUG_FLAGS}") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${COMMON_C_FLAGS} ${DEBUG_FLAGS}")
SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${COMMON_C_FLAGS} ${RELEASE_FLAGS}") SET(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${COMMON_C_FLAGS} ${RELEASE_FLAGS}")
# Set c++ compiler options # Set c++ compiler options
SET(COMMON_CXX_FLAGS "${COMMON_FLAGS} -std=c++11 -Wno-unused-function") IF (TD_WINDOWS)
SET(COMMON_CXX_FLAGS "${COMMON_FLAGS} -std=c++11")
ELSE ()
SET(COMMON_CXX_FLAGS "${COMMON_FLAGS} -std=c++11 -Wno-unused-function")
ENDIF ()
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${COMMON_CXX_FLAGS} ${DEBUG_FLAGS}") SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${COMMON_CXX_FLAGS} ${DEBUG_FLAGS}")
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${COMMON_CXX_FLAGS} ${RELEASE_FLAGS}") SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${COMMON_CXX_FLAGS} ${RELEASE_FLAGS}")
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
PROJECT(TDengine) PROJECT(TDengine)
IF (${ACCOUNT} MATCHES "true") IF (${ACCOUNT} MATCHES "true")
...@@ -43,17 +43,6 @@ IF (${SOMODE} MATCHES "static") ...@@ -43,17 +43,6 @@ IF (${SOMODE} MATCHES "static")
MESSAGE(STATUS "Link so using static mode") MESSAGE(STATUS "Link so using static mode")
ENDIF () ENDIF ()
IF (${DBNAME} MATCHES "power")
SET(TD_POWER TRUE)
MESSAGE(STATUS "power is true")
ELSEIF (${DBNAME} MATCHES "tq")
SET(TD_TQ TRUE)
MESSAGE(STATUS "tq is true")
ELSEIF (${DBNAME} MATCHES "pro")
SET(TD_PRO TRUE)
MESSAGE(STATUS "pro is true")
ENDIF ()
IF (${DLLTYPE} MATCHES "go") IF (${DLLTYPE} MATCHES "go")
SET(TD_GODLL TRUE) SET(TD_GODLL TRUE)
MESSAGE(STATUS "input dll type: " ${DLLTYPE}) MESSAGE(STATUS "input dll type: " ${DLLTYPE})
...@@ -90,16 +79,29 @@ IF (${BUILD_JDBC} MATCHES "false") ...@@ -90,16 +79,29 @@ IF (${BUILD_JDBC} MATCHES "false")
SET(TD_BUILD_JDBC FALSE) SET(TD_BUILD_JDBC FALSE)
ENDIF () ENDIF ()
SET(TD_BUILD_HTTP TRUE)
SET(TD_TAOS_TOOLS TRUE)
SET(TD_BUILD_LUA TRUE)
SET(TD_MEMORY_SANITIZER FALSE) SET(TD_MEMORY_SANITIZER FALSE)
IF (${MEMORY_SANITIZER} MATCHES "true") IF (${MEMORY_SANITIZER} MATCHES "true")
SET(TD_MEMORY_SANITIZER TRUE) SET(TD_MEMORY_SANITIZER TRUE)
ENDIF () ENDIF ()
IF (${TSZ_ENABLED} MATCHES "true") SET(TD_BUILD_VERBOSE FALSE)
IF (${VERBOSE} MATCHES "true")
SET(CMAKE_VERBOSE_MAKEFILE ON)
SET(TD_BUILD_VERBOSE TRUE)
ENDIF ()
# build TSZ by default
IF ("${TSZ_ENABLED}" MATCHES "false")
set(VAR_TSZ "" CACHE INTERNAL "global variant empty" )
ELSE()
# define add # define add
MESSAGE(STATUS "build with TSZ enabled") MESSAGE(STATUS "build with TSZ enabled")
ADD_DEFINITIONS(-DTD_TSZ) ADD_DEFINITIONS(-DTD_TSZ)
set(VAR_TSZ "TSZ" CACHE INTERNAL "global variant tsz" ) set(VAR_TSZ "TSZ" CACHE INTERNAL "global variant tsz" )
ELSE()
set(VAR_TSZ "" CACHE INTERNAL "global variant empty" )
ENDIF() ENDIF()
\ No newline at end of file
...@@ -3,39 +3,24 @@ IF (TD_LINUX) ...@@ -3,39 +3,24 @@ IF (TD_LINUX)
INSTALL(CODE "MESSAGE(\"make install script: ${TD_MAKE_INSTALL_SH}\")") INSTALL(CODE "MESSAGE(\"make install script: ${TD_MAKE_INSTALL_SH}\")")
INSTALL(CODE "execute_process(COMMAND bash ${TD_MAKE_INSTALL_SH} ${TD_COMMUNITY_DIR} ${PROJECT_BINARY_DIR} Linux ${TD_VER_NUMBER})") INSTALL(CODE "execute_process(COMMAND bash ${TD_MAKE_INSTALL_SH} ${TD_COMMUNITY_DIR} ${PROJECT_BINARY_DIR} Linux ${TD_VER_NUMBER})")
ELSEIF (TD_WINDOWS) ELSEIF (TD_WINDOWS)
IF (TD_POWER)
SET(CMAKE_INSTALL_PREFIX C:/PowerDB)
ELSEIF (TD_PRO)
SET(CMAKE_INSTALL_PREFIX C:/ProDB)
ELSE ()
SET(CMAKE_INSTALL_PREFIX C:/TDengine) SET(CMAKE_INSTALL_PREFIX C:/TDengine)
ENDIF ()
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/go DESTINATION connector) INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/go DESTINATION connector)
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/nodejs DESTINATION connector) INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/nodejs DESTINATION connector)
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/python DESTINATION connector) INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/python DESTINATION connector)
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/C\# DESTINATION connector) INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/src/connector/C\# DESTINATION connector)
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/tests/examples DESTINATION .) INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/examples DESTINATION .)
INSTALL(DIRECTORY ${TD_COMMUNITY_DIR}/packaging/cfg DESTINATION .) INSTALL(FILES ${TD_COMMUNITY_DIR}/packaging/cfg/taos.cfg DESTINATION cfg)
INSTALL(FILES ${TD_COMMUNITY_DIR}/src/inc/taos.h DESTINATION include) INSTALL(FILES ${TD_COMMUNITY_DIR}/src/inc/taos.h DESTINATION include)
INSTALL(FILES ${TD_COMMUNITY_DIR}/src/inc/taoserror.h DESTINATION include) INSTALL(FILES ${TD_COMMUNITY_DIR}/src/inc/taoserror.h DESTINATION include)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.lib DESTINATION driver) INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.lib DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos_static.lib DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.exp DESTINATION driver) INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.exp DESTINATION driver)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.dll DESTINATION driver) INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos.dll DESTINATION driver)
IF (TD_POWER)
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/power.exe DESTINATION .)
ELSEIF (TD_PRO)
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/prodbc.exe DESTINATION .)
ELSE ()
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/taos.exe DESTINATION .) INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/taos.exe DESTINATION .)
INSTALL(FILES ${EXECUTABLE_OUTPUT_PATH}/taosdemo.exe DESTINATION .)
ENDIF ()
#INSTALL(TARGETS taos RUNTIME DESTINATION driver)
#INSTALL(TARGETS shell RUNTIME DESTINATION .)
IF (TD_MVN_INSTALLED) IF (TD_MVN_INSTALLED)
INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos-jdbcdriver-2.0.34-dist.jar DESTINATION connector/jdbc) INSTALL(FILES ${LIBRARY_OUTPUT_PATH}/taos-jdbcdriver-2.0.37-dist.jar DESTINATION connector/jdbc)
ENDIF () ENDIF ()
ELSEIF (TD_DARWIN) ELSEIF (TD_DARWIN)
SET(TD_MAKE_INSTALL_SH "${TD_COMMUNITY_DIR}/packaging/tools/make_install.sh") SET(TD_MAKE_INSTALL_SH "${TD_COMMUNITY_DIR}/packaging/tools/make_install.sh")
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
PROJECT(TDengine) PROJECT(TDengine)
# #
...@@ -36,7 +36,13 @@ IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux") ...@@ -36,7 +36,13 @@ IF (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
# Get OS information and store in variable TD_OS_INFO. # Get OS information and store in variable TD_OS_INFO.
# #
execute_process(COMMAND chmod 777 ${TD_COMMUNITY_DIR}/packaging/tools/get_os.sh) execute_process(COMMAND chmod 777 ${TD_COMMUNITY_DIR}/packaging/tools/get_os.sh)
execute_process(COMMAND readlink /bin/sh OUTPUT_VARIABLE SHELL_LINK)
MESSAGE(STATUS "The shell is: " ${SHELL_LINK})
IF (${SHELL_LINK} MATCHES "dash")
execute_process(COMMAND ${TD_COMMUNITY_DIR}/packaging/tools/get_os.sh "" OUTPUT_VARIABLE TD_OS_INFO)
ELSE ()
execute_process(COMMAND sh ${TD_COMMUNITY_DIR}/packaging/tools/get_os.sh "" OUTPUT_VARIABLE TD_OS_INFO) execute_process(COMMAND sh ${TD_COMMUNITY_DIR}/packaging/tools/get_os.sh "" OUTPUT_VARIABLE TD_OS_INFO)
ENDIF()
MESSAGE(STATUS "The current os is " ${TD_OS_INFO}) MESSAGE(STATUS "The current os is " ${TD_OS_INFO})
SET(TD_LINUX TRUE) SET(TD_LINUX TRUE)
...@@ -90,10 +96,12 @@ IF ("${CPUTYPE}" STREQUAL "") ...@@ -90,10 +96,12 @@ IF ("${CPUTYPE}" STREQUAL "")
MESSAGE(STATUS "The current platform is amd64") MESSAGE(STATUS "The current platform is amd64")
MESSAGE(STATUS "Set CPUTYPE to x64") MESSAGE(STATUS "Set CPUTYPE to x64")
SET(CPUTYPE "x64") SET(CPUTYPE "x64")
SET(PLATFORM_ARCH_STR "amd64")
ELSEIF (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)") ELSEIF (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)")
MESSAGE(STATUS "The current platform is x86") MESSAGE(STATUS "The current platform is x86")
MESSAGE(STATUS "Set CPUTYPE to x86") MESSAGE(STATUS "Set CPUTYPE to x86")
SET(CPUTYPE "x32") SET(CPUTYPE "x32")
SET(PLATFORM_ARCH_STR "i386")
ELSEIF (CMAKE_SYSTEM_PROCESSOR MATCHES "armv7l") ELSEIF (CMAKE_SYSTEM_PROCESSOR MATCHES "armv7l")
MESSAGE(STATUS "Set CPUTYPE to aarch32") MESSAGE(STATUS "Set CPUTYPE to aarch32")
SET(CPUTYPE "aarch32") SET(CPUTYPE "aarch32")
...@@ -101,12 +109,14 @@ IF ("${CPUTYPE}" STREQUAL "") ...@@ -101,12 +109,14 @@ IF ("${CPUTYPE}" STREQUAL "")
SET(TD_LINUX TRUE) SET(TD_LINUX TRUE)
SET(TD_LINUX_32 FALSE) SET(TD_LINUX_32 FALSE)
SET(TD_ARM_32 TRUE) SET(TD_ARM_32 TRUE)
SET(PLATFORM_ARCH_STR "arm")
ELSEIF (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") ELSEIF (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
SET(CPUTYPE "aarch64") SET(CPUTYPE "aarch64")
MESSAGE(STATUS "Set CPUTYPE to aarch64") MESSAGE(STATUS "Set CPUTYPE to aarch64")
SET(TD_LINUX TRUE) SET(TD_LINUX TRUE)
SET(TD_LINUX_64 FALSE) SET(TD_LINUX_64 FALSE)
SET(TD_ARM_64 TRUE) SET(TD_ARM_64 TRUE)
SET(PLATFORM_ARCH_STR "arm64")
ELSEIF (CMAKE_SYSTEM_PROCESSOR MATCHES "mips64") ELSEIF (CMAKE_SYSTEM_PROCESSOR MATCHES "mips64")
SET(CPUTYPE "mips64") SET(CPUTYPE "mips64")
MESSAGE(STATUS "Set CPUTYPE to mips64") MESSAGE(STATUS "Set CPUTYPE to mips64")
...@@ -118,7 +128,6 @@ IF ("${CPUTYPE}" STREQUAL "") ...@@ -118,7 +128,6 @@ IF ("${CPUTYPE}" STREQUAL "")
MESSAGE(STATUS "Set CPUTYPE to apple silicon m1") MESSAGE(STATUS "Set CPUTYPE to apple silicon m1")
SET(TD_ARM_64 TRUE) SET(TD_ARM_64 TRUE)
ENDIF () ENDIF ()
ELSE () ELSE ()
# if generate ARM version: # if generate ARM version:
# cmake -DCPUTYPE=aarch32 .. or cmake -DCPUTYPE=aarch64 # cmake -DCPUTYPE=aarch32 .. or cmake -DCPUTYPE=aarch64
...@@ -126,27 +135,33 @@ ELSE () ...@@ -126,27 +135,33 @@ ELSE ()
SET(TD_LINUX TRUE) SET(TD_LINUX TRUE)
SET(TD_LINUX_32 FALSE) SET(TD_LINUX_32 FALSE)
SET(TD_ARM_32 TRUE) SET(TD_ARM_32 TRUE)
SET(PLATFORM_ARCH_STR "arm")
MESSAGE(STATUS "input cpuType: aarch32") MESSAGE(STATUS "input cpuType: aarch32")
ELSEIF (${CPUTYPE} MATCHES "aarch64") ELSEIF (${CPUTYPE} MATCHES "aarch64")
SET(TD_LINUX TRUE) SET(TD_LINUX TRUE)
SET(TD_LINUX_64 FALSE) SET(TD_LINUX_64 FALSE)
SET(TD_ARM_64 TRUE) SET(TD_ARM_64 TRUE)
SET(PLATFORM_ARCH_STR "arm64")
MESSAGE(STATUS "input cpuType: aarch64") MESSAGE(STATUS "input cpuType: aarch64")
ELSEIF (${CPUTYPE} MATCHES "mips64") ELSEIF (${CPUTYPE} MATCHES "mips64")
SET(TD_LINUX TRUE) SET(TD_LINUX TRUE)
SET(TD_LINUX_64 FALSE) SET(TD_LINUX_64 FALSE)
SET(TD_MIPS_64 TRUE) SET(TD_MIPS_64 TRUE)
SET(PLATFORM_ARCH_STR "mips")
MESSAGE(STATUS "input cpuType: mips64") MESSAGE(STATUS "input cpuType: mips64")
ELSEIF (${CPUTYPE} MATCHES "x64") ELSEIF (${CPUTYPE} MATCHES "x64")
SET(PLATFORM_ARCH_STR "amd64")
MESSAGE(STATUS "input cpuType: x64") MESSAGE(STATUS "input cpuType: x64")
ELSEIF (${CPUTYPE} MATCHES "x86") ELSEIF (${CPUTYPE} MATCHES "x86")
SET(PLATFORM_ARCH_STR "i386")
MESSAGE(STATUS "input cpuType: x86") MESSAGE(STATUS "input cpuType: x86")
ELSE () ELSE ()
MESSAGE(STATUS "input cpuType unknown " ${CPUTYPE}) MESSAGE(STATUS "input cpuType unknown " ${CPUTYPE})
ENDIF () ENDIF ()
ENDIF () ENDIF ()
MESSAGE(STATUS "platform arch:" ${PLATFORM_ARCH_STR})
# cmake -DOSTYPE=Ningsi # cmake -DOSTYPE=Ningsi
IF (${OSTYPE} MATCHES "Ningsi60") IF (${OSTYPE} MATCHES "Ningsi60")
SET(TD_NINGSI TRUE) SET(TD_NINGSI TRUE)
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
PROJECT(TDengine) PROJECT(TDengine)
IF (DEFINED VERNUMBER) IF (DEFINED VERNUMBER)
SET(TD_VER_NUMBER ${VERNUMBER}) SET(TD_VER_NUMBER ${VERNUMBER})
ELSE () ELSE ()
SET(TD_VER_NUMBER "2.2.1.3") SET(TD_VER_NUMBER "2.4.0.0")
ENDIF () ENDIF ()
IF (DEFINED VERCOMPATIBLE) IF (DEFINED VERCOMPATIBLE)
...@@ -86,7 +86,7 @@ ENDIF () ...@@ -86,7 +86,7 @@ ENDIF ()
MESSAGE(STATUS "============= compile version parameter information start ============= ") MESSAGE(STATUS "============= compile version parameter information start ============= ")
MESSAGE(STATUS "ver number:" ${TD_VER_NUMBER}) MESSAGE(STATUS "ver number:" ${TD_VER_NUMBER})
MESSAGE(STATUS "compatible ver number:" ${TD_VER_COMPATIBLE}) MESSAGE(STATUS "compatible ver number:" ${TD_VER_COMPATIBLE})
MESSAGE(STATUS "communit commit id:" ${TD_VER_GIT}) MESSAGE(STATUS "community commit id:" ${TD_VER_GIT})
MESSAGE(STATUS "internal commit id:" ${TD_VER_GIT_INTERNAL}) MESSAGE(STATUS "internal commit id:" ${TD_VER_GIT_INTERNAL})
MESSAGE(STATUS "build date:" ${TD_VER_DATE}) MESSAGE(STATUS "build date:" ${TD_VER_DATE})
MESSAGE(STATUS "ver type:" ${TD_VER_VERTYPE}) MESSAGE(STATUS "ver type:" ${TD_VER_VERTYPE})
......
PROJECT(TDengine) PROJECT(TDengine)
IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
ELSE () ELSE ()
CMAKE_MINIMUM_REQUIRED(VERSION 2.8) CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
ENDIF () ENDIF ()
ADD_SUBDIRECTORY(zlib-1.2.11) ADD_SUBDIRECTORY(zlib-1.2.11)
...@@ -15,7 +15,10 @@ ADD_SUBDIRECTORY(cJson) ...@@ -15,7 +15,10 @@ ADD_SUBDIRECTORY(cJson)
ADD_SUBDIRECTORY(wepoll) ADD_SUBDIRECTORY(wepoll)
ADD_SUBDIRECTORY(MsvcLibX) ADD_SUBDIRECTORY(MsvcLibX)
ADD_SUBDIRECTORY(rmonotonic) ADD_SUBDIRECTORY(rmonotonic)
ADD_SUBDIRECTORY(lua)
IF (TD_BUILD_LUA)
ADD_SUBDIRECTORY(lua)
ENDIF ()
IF (TD_LINUX AND TD_MQTT) IF (TD_LINUX AND TD_MQTT)
ADD_SUBDIRECTORY(MQTT-C) ADD_SUBDIRECTORY(MQTT-C)
...@@ -26,6 +29,9 @@ IF (TD_DARWIN AND TD_MQTT) ...@@ -26,6 +29,9 @@ IF (TD_DARWIN AND TD_MQTT)
ENDIF () ENDIF ()
IF (TD_LINUX_64 AND JEMALLOC_ENABLED) IF (TD_LINUX_64 AND JEMALLOC_ENABLED)
MESSAGE("")
MESSAGE("${Green} ENABLE jemalloc ${ColourReset}")
MESSAGE("")
MESSAGE("setup deps/jemalloc, current source dir:" ${CMAKE_CURRENT_SOURCE_DIR}) MESSAGE("setup deps/jemalloc, current source dir:" ${CMAKE_CURRENT_SOURCE_DIR})
MESSAGE("binary dir:" ${CMAKE_BINARY_DIR}) MESSAGE("binary dir:" ${CMAKE_BINARY_DIR})
include(ExternalProject) include(ExternalProject)
...@@ -36,8 +42,9 @@ IF (TD_LINUX_64 AND JEMALLOC_ENABLED) ...@@ -36,8 +42,9 @@ IF (TD_LINUX_64 AND JEMALLOC_ENABLED)
CONFIGURE_COMMAND ./autogen.sh COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/build/ CONFIGURE_COMMAND ./autogen.sh COMMAND ./configure --prefix=${CMAKE_BINARY_DIR}/build/
BUILD_COMMAND ${MAKE} BUILD_COMMAND ${MAKE}
) )
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}/build/include)
ENDIF () ENDIF ()
IF (${TSZ_ENABLED} MATCHES "true") IF (NOT "${TSZ_ENABLED}" MATCHES "false")
ADD_SUBDIRECTORY(TSZ) ADD_SUBDIRECTORY(TSZ)
ENDIF() ENDIF()
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
# MQTT-C build options # MQTT-C build options
option(MQTT_C_OpenSSL_SUPPORT "Build MQTT-C with OpenSSL support?" OFF) option(MQTT_C_OpenSSL_SUPPORT "Build MQTT-C with OpenSSL support?" OFF)
......
PROJECT(TDengine) PROJECT(TDengine)
IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") IF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
ELSE () ELSE ()
CMAKE_MINIMUM_REQUIRED(VERSION 2.8) CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
ENDIF () ENDIF ()
IF (TD_WINDOWS) IF (TD_WINDOWS)
......
Subproject commit ceda5bf9fcd7836509ac97dcc0056b3f1dd48cc5 Subproject commit 11c1060d4f917dd799ae628b131db5d6a5ef6954
...@@ -73,7 +73,7 @@ typedef struct cJSON ...@@ -73,7 +73,7 @@ typedef struct cJSON
char *string; char *string;
//Keep the original string of number //Keep the original string of number
char numberstring[13]; char numberstring[64];
} cJSON; } cJSON;
typedef struct cJSON_Hooks typedef struct cJSON_Hooks
......
...@@ -290,7 +290,7 @@ loop_end: ...@@ -290,7 +290,7 @@ loop_end:
input_buffer->offset += (size_t)(after_end - number_c_string); input_buffer->offset += (size_t)(after_end - number_c_string);
strncpy(item->numberstring, (const char *)number_c_string, 12); strncpy(item->numberstring, (const char *)number_c_string, strlen((const char*)number_c_string));
return true; return true;
} }
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
PROJECT(TDengine) PROJECT(TDengine)
IF (TD_WINDOWS) IF (TD_WINDOWS)
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
PROJECT(TDengine) PROJECT(TDengine)
IF (TD_WINDOWS) IF (TD_WINDOWS)
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
PROJECT(TDengine) PROJECT(TDengine)
IF (TD_WINDOWS) IF (TD_WINDOWS)
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
PROJECT(TDengine) PROJECT(TDengine)
IF (TD_WINDOWS) IF (TD_WINDOWS)
......
CMAKE_MINIMUM_REQUIRED(VERSION 2.8...3.20) CMAKE_MINIMUM_REQUIRED(VERSION 3.0...3.20)
PROJECT(TDengine) PROJECT(TDengine)
IF (TD_WINDOWS) IF (TD_WINDOWS)
......
---
sidebar_label: 产品简介
toc_max_heading_level: 2
---
# 产品简介
## TDengine 简介
TDengine 是一款高性能、分布式、支持 SQL 的时序数据库。而且除时序数据库功能外,它还提供缓存、数据订阅、流式计算等功能,最大程度减少研发和运维的复杂度,且核心代码,包括集群功能全部开源(开源协议,AGPL v3.0)。与其他时序数据数据库相比,TDengine 有以下特点:
- **高性能**:通过创新的存储引擎设计,无论是数据写入还是查询,TDengine 的性能比通用数据库快 10 倍以上,也远超其他时序数据库,而且存储空间也大为节省。
- **分布式**:通过原生分布式的设计,TDengine 提供了水平扩展的能力,只需要增加节点就能获得更强的数据处理能力,同时通过多副本机制保证了系统的高可用。
- **支持 SQL**:TDengine 采用 SQL 作为数据查询语言,减少学习和迁移成本,同时提供 SQL 扩展来处理时序数据特有的分析,而且支持方便灵活的 schemaless 数据写入。
- **All in One**:将数据库、消息队列、缓存、流式计算等功能融合一起,应用无需再集成 Kafka/Redis/HBase/Spark 等软件,大幅降低应用开发和维护成本。
- **零管理**:安装、集群几秒搞定,无任何依赖,不用分库分表,系统运行状态监测能与 Grafana 或其他运维工具无缝集成。
- **零学习成本**:采用 SQL 查询语言,支持 Python, Java, C/C++, Go, Rust, Node.js 等多种编程语言,与 MySQL 相似,零学习成本。
- **无缝集成**:不用一行代码,即可与 Telegraf, Grafana, EMQX, Prometheus, StatsD, collectd, Matlab, R 等第三方工具无缝集成。
- **互动 Console**: 通过命令行 console,不用编程,执行 SQL 语句就能做即席查询、各种数据库的操作、管理以及集群的维护.
采用 TDengine,可将典型的物联网、车联网、工业互联网大数据平台的总拥有成本大幅降低。但需要指出的是,因充分利用了物联网时序数据的特点,它无法用来处理网络爬虫、微博、微信、电商、ERP、CRM 等通用型数据。
![TDengine技术生态图](eco_system.png)
<center>图 1. TDengine技术生态图</center>
## TDengine 总体适用场景
作为一个 IoT 大数据平台,TDengine 的典型适用场景是在 IoT 范畴,而且用户有一定的数据量。本文后续的介绍主要针对这个范畴里面的系统。范畴之外的系统,比如 CRM,ERP 等,不在本文讨论范围内。
### 数据源特点和需求
从数据源角度,设计人员可以从下面几个角度分析 TDengine 在目标应用系统里面的适用性。
| 数据源特点和需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 |
| ---------------------------- | ------ | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| 总体数据量巨大 | | | √ | TDengine 在容量方面提供出色的水平扩展功能,并且具备匹配高压缩的存储结构,达到业界最优的存储效率。 |
| 数据输入速度偶尔或者持续巨大 | | | √ | TDengine 的性能大大超过同类产品,可以在同样的硬件环境下持续处理大量的输入数据,并且提供很容易在用户环境里面运行的性能评估工具。 |
| 数据源数目巨大 | | | √ | TDengine 设计中包含专门针对大量数据源的优化,包括数据的写入和查询,尤其适合高效处理海量(千万或者更多量级)的数据源。 |
### 系统架构要求
| 系统架构要求 | 不适用 | 可能适用 | 非常适用 | 简单说明 |
| ---------------------- | ------ | -------- | -------- | ----------------------------------------------------------------------------------------------------- |
| 要求简单可靠的系统架构 | | | √ | TDengine 的系统架构非常简单可靠,自带消息队列,缓存,流式计算,监控等功能,无需集成额外的第三方产品。 |
| 要求容错和高可靠 | | | √ | TDengine 的集群功能,自动提供容错灾备等高可靠功能。 |
| 标准化规范 | | | √ | TDengine 使用标准的 SQL 语言提供主要功能,遵守标准化规范。 |
### 系统功能需求
| 系统功能需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 |
| -------------------------- | ------ | -------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
| 要求完整的内置数据处理算法 | | √ | | TDengine 的实现了通用的数据处理算法,但是还没有做到妥善处理各行各业的所有要求,因此特殊类型的处理还需要应用层面处理。 |
| 需要大量的交叉查询处理 | | √ | | 这种类型的处理更多应该用关系型数据系统处理,或者应该考虑 TDengine 和关系型数据系统配合实现系统功能。 |
### 系统性能需求
| 系统性能需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 |
| ---------------------- | ------ | -------- | -------- | ------------------------------------------------------------------------------------------------------ |
| 要求较大的总体处理能力 | | | √ | TDengine 的集群功能可以轻松地让多服务器配合达成处理能力的提升。 |
| 要求高速处理数据 | | | √ | TDengine 的专门为 IoT 优化的存储和数据处理的设计,一般可以让系统得到超出同类产品多倍数的处理速度提升。 |
| 要求快速处理小粒度数据 | | | √ | 这方面 TDengine 性能可以完全对标关系型和 NoSQL 型数据处理系统。 |
### 系统维护需求
| 系统维护需求 | 不适用 | 可能适用 | 非常适用 | 简单说明 |
| ---------------------- | ------ | -------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
| 要求系统可靠运行 | | | √ | TDengine 的系统架构非常稳定可靠,日常维护也简单便捷,对维护人员的要求简洁明了,最大程度上杜绝人为错误和事故。 |
| 要求运维学习成本可控 | | | √ | 同上。 |
| 要求市场有大量人才储备 | √ | | | TDengine 作为新一代产品,目前人才市场里面有经验的人员还有限。但是学习成本低,我们作为厂家也提供运维的培训和辅助服务。 |
## TDengine 与其他数据库的对比测试
- [用 InfluxDB 开源的性能测试工具对比 InfluxDB 和 TDengine](https://www.taosdata.com/blog/2020/01/13/1105.html)
- [TDengine 与 OpenTSDB 对比测试](https://www.taosdata.com/blog/2019/08/21/621.html)
- [TDengine 与 Cassandra 对比测试](https://www.taosdata.com/blog/2019/08/14/573.html)
- [TDengine 与 InfluxDB 对比测试](https://www.taosdata.com/blog/2019/07/19/419.html)
- [TDengine VS InfluxDB ,写入性能大 PK !](https://www.taosdata.com/2021/11/05/3248.html)
- [TDengine 和 InfluxDB 查询性能对比测试报告](https://www.taosdata.com/2022/02/22/5969.html)
- [TDengine 与 InfluxDB、OpenTSDB、Cassandra、MySQL、ClickHouse 等数据库的对比测试报告](https://www.taosdata.com/downloads/TDengine_Testing_Report_cn.pdf)
# 基本概念
## 物联网典型场景
在典型的物联网、车联网、运维监测场景中,往往有多种不同类型的数据采集设备,采集一个到多个不同的物理量。而同一种采集设备类型,往往又有多个具体的采集设备分布在不同的地点。大数据处理系统就是要将各种采集的数据汇总,然后进行计算和分析。对于同一类设备,其采集的数据类似如下的表格:
| Device ID | Time Stamp | Value 1 | Value 2 | Value 3 | Tag 1 | Tag 2 |
| :-------: | :-----------: | :-----: | :-----: | :-----: | :---: | :---: |
| D1001 | 1538548685000 | 10.3 | 219 | 0.31 | Red | Tesla |
| D1002 | 1538548684000 | 10.2 | 220 | 0.23 | Blue | BMW |
| D1003 | 1538548686500 | 11.5 | 221 | 0.35 | Black | Honda |
| D1004 | 1538548685500 | 13.4 | 223 | 0.29 | Red | Volvo |
| D1001 | 1538548695000 | 12.6 | 218 | 0.33 | Red | Tesla |
| D1004 | 1538548696600 | 11.8 | 221 | 0.28 | Black | Honda |
每一条记录都有设备 ID,时间戳,采集的物理量,还有与每个设备相关的静态标签。每个设备是受外界的触发,或按照设定的周期采集数据。采集的数据点是时序的,是一个数据流。
**数据特征**
除时序特征外,仔细研究发现,物联网、车联网、运维监测类数据还具有很多其他明显的特征。
1. 数据是结构化的;
2. 数据极少有更新或删除操作;
3. 无需传统数据库的事务处理;
4. 相对互联网应用,写多读少;
5. 流量平稳,根据设备数量和采集频次,可以预测出来;
6. 用户关注的是一段时间的趋势,而不是某一特点时间点的值;
7. 数据是有保留期限的;
8. 数据的查询分析一定是基于时间段和地理区域的;
9. 除存储查询外,还往往需要各种统计和实时计算操作;
10. 数据量巨大,一天采集的数据就可以超过 100 亿条。
充分利用上述特征,TDengine 采取了一特殊的优化的存储和计算设计来处理时序数据,能将系统处理能力显著提高。
## 数据采集点
持续按照预设频率生成数据的软件或硬件设备称为数据采集点。TDengine中推荐一个数据采集点生成的数据存储在对应的一个或若干个表中。采集点产生的数据通常是包括时间戳、测量值、标签等必要信息构成的元组。
## 采集量
数据采集点生成的具有时间、测量值、标签的元组信息。除了时间戳、标签信息以外的测量值称为采集量。
## 标签
表的结构化描述信息,以一维数组形式存在。标签模式需要在创建超级表的时候指定,后续可动态调整。只有基于超级表创建的子表才具有标签。标签信息的内容首次创建子表的时候指定,并可按需调整。可以看到,TDengine中标签信息是表级别,而不是记录级别。
## 数据库
TDengine中数据库与普通数据库管理系统中的数据库语义和行为相同,但是结合应用需求增加了若干配置参数用以控制其行为。
## 超级表
超级表(Super Table, STable)是TDengine中一个重要的概念。超级表是面向相同数据模式的数据表,提供(数据+标签)模式管理和查询处理的逻辑抽象。通常将基于超级表创建的表称为该超级表的子表,并在系统内部建立逻辑关联。超级表与(基于其创建的)子表的关系体现在以下几个方面:
超级表的子表共享其数据模式和标签模式。因此,不能通过子表调整数据或标签的模式。对于超级表的数据模式修改立即对所有的子表生效。
超级表自身不能存储任何数据或标签信息。因此,不能向一个超级表写入数据,只能将数据写入子表中。
针对超级表的查询,将所有子表中的数据视为一个整体数据集进行处理。但是可以直接发起针对某个或若干个子表的查询,此时查询请求将该子表视为一个普通的表进行处理。
## 表
一系列二维数组的集合,用来代表和储存数据对象之间的关系,由纵向的列和横向的行组成。TDengine中的表与普通数据库中的表没有差别。
但是,对于通过超级表创建的子表,还具有额外的标签数据信息。不能针对某个子表调整数据模式。每个子表具有与其关联的标签数据,并可按需调整内容。但是模式调整必须要通过超级表模式调整来完成。
使用TDengine存储和管理物联网数据的时候,推荐使用一个子表存储一个数据采集点生成的数据,而使用超级表用来代表一组相同类型的数据采集点的集合。
label: 基本概念
\ No newline at end of file
# 立即开始
## 从 Docker 快速开始
如果已经安装了 docker, 只需执行下面的命令。
```shell
docker run -d -p 6030-6049:6030-6049 -p 6030-6049:6030-6049/udp tdengine/tdengine
```
确定该容器已经启动并且在正常运行
```shell
docker ps
```
进入该容器并执行 bash
```shell
docker exec -it <containrid> bash
```
然后就可以执行相关的 Linux 命令操作和访问 TDengine
详细操作方法请参照 [通过 Docker 快速体验 TDengine](/train-fqa/docker)
:::info
从 2.4.0.10 开始,除 taosd 以外,Docker 镜像还包含:taos、taosAdapter、taosdump、taosBenchmark、TDinsight 安装脚本和示例代码。启动 Docker 容器时,将同时启动 taosAdapter 和 taosd,实现对 RESTful 的支持。
:::
:::note
暂时不建议生产环境采用 Docker 来部署 TDengine 的客户端或服务端,但在开发环境下或初次尝试时,使用 Docker 方式部署是十分方便的。特别是,利用 Docker,可以方便地在 macOS 和 Windows 环境下尝试 TDengine。
:::
## 安装
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import PkgInstall from "./\_pkg_install.mdx";
import AptGetInstall from "./\_apt_get_install.mdx";
import SrcInstall from "./\_src_install.mdx";
TDengine 包括服务端、客户端和周边生态工具软件,目前 2.0 版服务端仅在 Linux 系统上安装和运行,后续将支持 Windows、macOS 等系统。客户端可以在 Windows 或 Linux 上安装和运行。在任何操作系统上的应用都可以使用 RESTful 接口连接服务端程序 taosd,其中 2.4 之后版本默认使用单独运行的独立组件 taosAdapter 提供 http 服务和更多数据写入方式。taosAdapter 需要手动启动。
之前版本 TDengine 服务端,以及所有服务端 lite 版,均使用内置 http 服务。
TDengine 支持 X64/ARM64/MIPS64/Alpha64 硬件平台,后续将支持 ARM32、RISC-V 等 CPU 架构。
<Tabs defaultValue="apt-get">
<TabItem value="apt-get" label="apt-get">
<AptGetInstall />
</TabItem>
<TabItem value="pkg" label="安装包">
<PkgInstall />
</TabItem>
<TabItem value="src" label="源码">
<SrcInstall />
</TabItem>
</Tabs>
## 启动
使用 `systemctl` 命令来启动 TDengine 的服务进程。
```bash
systemctl start taosd
```
检查服务是否正常工作:
```bash
systemctl status taosd
```
如果 TDengine 服务正常工作,那么您可以通过 TDengine 的命令行程序 `taos` 来访问并体验 TDengine。
:::info
- systemctl 命令需要 _root_ 权限来运行,如果您非 _root_ 用户,请在命令前添加 sudo 。
- 为更好的获得产品反馈,改善产品,TDengine 会采集基本的使用信息,但您可以修改系统配置文件 taos.cfg 里的配置参数 telemetryReporting,将其设为 0,就可将其关闭。
- TDengine 采用 FQDN(一般就是 hostname)作为节点的 ID,为保证正常运行,需要给运行 taosd 的服务器配置好 hostname,在客户端应用运行的机器配置好 DNS 服务或 hosts 文件,保证 FQDN 能够解析。
- `systemctl stop taosd` 指令在执行后并不会马上停止 TDengine 服务,而是会等待系统中必要的落盘工作正常完成。在数据量很大的情况下,这可能会消耗较长时间。
TDengine 支持在使用 [`systemd`](https://en.wikipedia.org/wiki/Systemd) 做进程服务管理的 Linux 系统上安装,用 `which systemctl` 命令来检测系统中是否存在 `systemd` 包:
```bash
which systemctl
```
如果系统中不支持 `systemd`,也可以用手动运行 `/usr/local/taos/bin/taosd` 方式启动 TDengine 服务。
:::note
## 进入命令行
执行 TDengine 客户端程序,您只要在 Linux 终端执行 `taos` 即可。
```bash
taos
```
如果连接服务成功,将会打印出欢迎消息和版本信息。如果失败,则会打印错误消息出来(请参考 [FAQ](/train-fqa/faq) 来解决终端连接服务端失败的问题)。客户端的提示符号如下:
```cmd
taos>
```
在 TDengine 客户端中,用户可以通过 SQL 命令来创建/删除数据库、表等,并进行插入查询操作。在终端中运行的 SQL 语句需要以分号结束来运行。示例:
```mysql
create database demo;
use demo;
create table t (ts timestamp, speed int);
insert into t values ('2019-07-15 00:00:00', 10);
insert into t values ('2019-07-15 01:00:00', 20);
select * from t;
ts | speed |
========================================
2019-07-15 00:00:00.000 | 10 |
2019-07-15 01:00:00.000 | 20 |
Query OK, 2 row(s) in set (0.003128s)
```
除执行 SQL 语句外,系统管理员还可以从 TDengine 客户端进行检查系统运行状态、添加删除用户账号等操作。
## 命令行参数
您可通过配置命令行参数来改变 TDengine 客户端的行为。以下为常用的几个命令行参数:
- -c, --config-dir: 指定配置文件目录,默认为 `/etc/taos`
- -h, --host: 指定服务的 FQDN 地址或 IP 地址,默认为连接本地服务
- -s, --commands: 在不进入终端的情况下运行 TDengine 命令
- -u, --user: 连接 TDengine 服务端的用户名,缺省为 root
- -p, --password: 连接 TDengine 服务端的密码,缺省为 taosdata
- -?, --help: 打印出所有命令行参数
示例:
```bash
taos -h h1.taos.com -s "use db; show tables;"
```
## 运行 SQL 命令脚本
TDengine 终端可以通过 `source` 命令来运行 SQL 命令脚本。
```mysql
taos> source <filename>;
```
## taos shell 小技巧
- 可以使用上下光标键查看历史输入的指令
- 修改用户密码:在 shell 中使用 `alter user` 命令,缺省密码为 taosdata
- ctrl+c 中止正在进行中的查询
- 执行 `RESET QUERY CACHE` 可清除本地缓存的表 schema
- 批量执行 SQL 语句。可以将一系列的 shell 命令(以英文 ; 结尾,每个 SQL 语句为一行)按行存放在文件里,在 shell 里执行命令 `source <file-name>` 自动执行该文件里所有的 SQL 语句
- 输入 q 回车,退出 taos shell
## 使用 taosBenchmark 体验写入速度
启动 TDengine 的服务,在 Linux 终端执行 `taosBenchmark` (曾命名为 taosdemo):
```bash
taosBenchmark
```
该命令将在数据库 test 下面自动创建一张超级表 meters,该超级表下有 1 万张表,表名为 "d0" 到 "d9999",每张表有 1 万条记录,每条记录有 (ts, current, voltage, phase) 四个字段,时间戳从 "2017-07-14 10:40:00 000" 到 "2017-07-14 10:40:09 999",每张表带有标签 location 和 groupId,groupId 被设置为 1 到 10, location 被设置为 "beijing" 或者 "shanghai"。
这条命令很快完成 1 亿条记录的插入。具体时间取决于硬件性能,即使在一台普通的 PC 服务器往往也仅需十几秒。
## taosBenchmark 详细功能列表
taosBenchmark 命令本身带有很多选项,配置表的数目、记录条数等等,请执行 `taosBenchmark --help` 详细列出。您可以设置不同参数进行体验。
taosBenchmark 详细使用方法请参照 [如何使用 taosBenchmark 对 TDengine 进行性能测试](https://www.taosdata.com/2021/10/09/3111.html)
## 使用 taos shell 体验查询速度
在 TDengine 客户端输入查询命令,体验查询速度。
查询超级表下记录总条数:
```mysql
taos> select count(*) from test.meters;
```
查询 1 亿条记录的平均值、最大值、最小值等:
```mysql
taos> select avg(current), max(voltage), min(phase) from test.meters;
```
查询 location="beijing" 的记录总条数:
```mysql
taos> select count(*) from test.meters where location="beijing";
```
查询 groupId=10 的所有记录的平均值、最大值、最小值等:
```mysql
taos> select avg(current), max(voltage), min(phase) from test.meters where groupId=10;
```
对表 d10 按 10s 进行平均值、最大值和最小值聚合统计:
```mysql
taos> select avg(current), max(voltage), min(phase) from test.d10 interval(10s);
```
如果,可以使用 apt-get 工具从官方仓库安装。
**安装包仓库**
```
wget -qO - http://repos.taosdata.com/tdengine.key | sudo apt-key add -
echo "deb [arch=amd64] http://repos.taosdata.com/tdengine-stable stable main" | sudo tee /etc/apt/sources.list.d/tdengine-stable.list
```
如果安装 Beta 版需要安装包仓库
```
echo "deb [arch=amd64] http://repos.taosdata.com/tdengine-beta beta main" | sudo tee /etc/apt/sources.list.d/tdengine-beta.list
```
**使用 apt-get 命令安装**
```
sudo apt-get update
apt-cache policy tdengine
sudo apt-get install tdengine
```
:::tip
apt-get 方式只适用于 Debian 或 Ubuntu 系统
::::
import PkgList from "./_pkg_list.mdx";
TDengine 的安装非常简单,从下载到安装成功仅仅只要几秒钟。
为方便使用,从 2.4.0.10 开始,标准的服务端安装包包含了 taos、taosd、taosAdapter、taosdump、taosBenchmark、TDinsight 安装脚本和示例代码;如果您只需要用到服务端程序和客户端连接的 C/C++ 语言支持,也可以仅下载 lite 版本的安装包。
在安装包格式上,我们提供 tar.gz, rpm 和 deb 格式,为企业客户提供 tar.gz 格式安装包,以方便在特定操作系统上使用。需要注意的是,rpm 和 deb 包不含 taosdump、taosBenchmark 和 TDinsight 安装脚本,这些工具需要通过安装 taosTool 包获得。
发布版本包括稳定版和 Beta 版,Beta 版含有更多新功能。正式上线或测试建议安装稳定版。您可以根据需要选择下载:
<PkgList />
具体的安装方法,请参见 [安装包的安装和卸载](/operation/pkg-install) 以及 [视频教程](https://www.taosdata.com/blog/2020/11/11/1941.html)。
**最新安装包**
<ul id="server-packageList" className="package-list">
<li>
<a id="tdengine_rpm" name="TDengine RPM">
TDengine-server-2.4.0.12-Linux-x64.rpm (14.5 M)
</a>
</li>
<li>
<a id="tdengine_deb" name="TDengine DEB">
TDengine-server-2.4.0.12-Linux-x64.deb (12.8 M)
</a>
</li>
<li>
<a id="tdengine_tar" name="TDengine Tarball">
TDengine-server-2.4.0.12-Linux-x64.tar.gz (15.5 M)
</a>
</li>
<li>
<a id="tdengine_Lite_tar" name="undefined">
TDengine-server-2.4.0.12-Linux-x64-Lite.tar.gz (3.4 M)
</a>
</li>
<li>
<a id="tdengine_Lite_beta_tar" name="TDengine Lite Beta Tarball">
TDengine-server-2.3.5.0-beta-Linux-x64-Lite.tar.gz (3 M)
</a>
</li>
<li>
<a id="tdengine_beta_rpm" name="TDengine Beta RPM">
TDengine-server-2.3.5.0-beta-Linux-x64.rpm (18.4 M)
</a>
</li>
<li>
<a id="tdengine_beta_deb" name="TDengine Beta DEB">
TDengine-server-2.3.5.0-beta-Linux-x64.deb (16.8 M)
</a>
</li>
<li>
<a id="tdengine_beta_tar" name="TDengine Beta Tarball">
TDengine-server-2.3.5.0-beta-Linux-x64.tar.gz (18.8 M)
</a>
</li>
</ul>
**所有下载**
https://www.taosdata.com/all-downloads
如果您希望对 TDengine 贡献代码或对内部实现感兴趣,请参考我们的 [TDengine GitHub 主页](https://github.com/taosdata/TDengine) 下载源码构建和安装.
下载其他组件、最新 Beta 版及之前版本的安装包,请点击[这里](https://www.taosdata.com/cn/all-downloads/)。
---
sidebar_label: 建立连接
sidebar_position: 4
---
# 建立连接
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import ConnRest from "./_connect_restful.mdx";
import ConnJava from "./_connect_java.mdx";
import ConnGo from "./_connect_go.mdx";
import ConnRust from "./_connect_rust.mdx";
import ConnNode from "./_connect_node.mdx";
import ConnJavaNative from "./_connect_java_native.mdx";
import ConnPythonNative from "./_connect_python_native.mdx";
import ConnGoNative from "./_connect_go_native.mdx";
import ConnRustNative from "./_connect_rust_native.mdx";
import ConnNodeNative from "./_connect_node_native.mdx";
import ConnCSNative from "./_connect_cs_native.mdx";
import ConnC from "./_connect_c.mdx";
## 连接器介绍
TDengine 提供了丰富的应用程序开发接口,其中包括 C/C++、Java、Python、Go、Node.js、C# 、RESTful 等,便于用户快速开发应用。
![image-connecotr](/img/connector.png)
<center>(图:连接器工作原理)</center>
TDengine 提供了 RESTful 接口,应用程序无需安装任何依赖,只需发送 HTTP 请求就可以连接到 TDengine。同时也提供了各语言的连接器,使用驱动程序 (taosc) 连接 TDengine。通过驱动程序可以使用 TDengine 更丰富的功能。
## 前提条件
在执行这一步之前,请确保有一个正在运行的,且可以访问到的 TDengine。以下所有示例代码,都假设 TDengine 安装在本机,且 fqdn(默认 localhost) 和 serverPort(默认 6030) 都使用默认配置。
## 建立连接
### RESTful
如果你安装的是 TDengine 2.4 之后的版本,使用 RESTful 方式连接时还需启动 taosAdaper。
<Tabs groupId="lang">
<TabItem label="curl" value="">
<ConnRest />
</TabItem>
<TabItem label="Java" value="java">
<ConnJava />
</TabItem>
<TabItem label="Go" value="go">
<ConnGo />
</TabItem>
<TabItem label="Rust" value="rust">
<ConnRust />
</TabItem>
<TabItem label="Node.js" value="node">
<ConnNode />
</TabItem>
</Tabs>
### 本地驱动
在没有安装 TDengine 服务端软件的系统上使用本地驱动连接 TDengine, 需要提前[安装客户端驱动程序](/reference/connector/#安装客户端驱动)。
:::note
需要单独安装客户端时,为避免客户端驱动和服务端不兼容,请尽量使用一致的版本。
:::
<Tabs groupId="lang" defaultValue="java">
<TabItem label="Java" value="java">
<ConnJavaNative />
</TabItem>
<TabItem label="Python" value="Python">
<ConnPythonNative />
</TabItem>
<TabItem label="Go" value="go">
<ConnGoNative />
</TabItem>
<TabItem label="Rust" value="rust">
<ConnRustNative />
</TabItem>
<TabItem label="Node.js" value="node">
<ConnNodeNative />
</TabItem>
<TabItem label="C#" value="csharp">
<ConnCSNative />
</TabItem>
<TabItem label="C" value="c">
<ConnC />
</TabItem>
</Tabs>
```c
{{#include docs-examples/c/connect_example.c}}
```
首先添加 [TDengine.Connector](https://www.nuget.org/packages/TDengine.Connector/) 的引用:
```bash
dotnet add package TDengine.Connector
```
```csharp
{{#include docs-examples/csharp/ConnectExample.cs}}
```
```go-mod title=go.mod
module goexample
go 1.17
require github.com/taosdata/driver-go/v2 develop
```
```go
{{#include docs-examples/go/connect/restexample/main.go}}
```
:::note
driver-go 使用 cgo 封装了 taosc 的 API。cgo 需要使用 gcc 编译 C 的源码。因此需要确保你的系统上有 gcc。
:::
```go-mod title=go.mod
module goexample
go 1.17
require github.com/taosdata/driver-go/v2 develop
```
```go
{{#include docs-examples/go/connect/cgoexample/main.go}}
```
```xml title=pom.xml
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>2.0.37</version>
</dependency>
```
```java
{{#include docs-examples/java/src/main/java/com/taos/example/RESTConnectExample.java}}
```
```xml title=pom.xml
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>2.0.37</version>
</dependency>
```
```java
{{#include docs-examples/java/src/main/java/com/taos/example/JNIConnectExample.java}}
```
首先需要安装 node.js 的连接器 `td2.0-connector`:
```bash
npm install td2.0-connector
```
```js
{{#include docs-examples/node/nativeexample/connect.js}}
```
推荐使用 `python >= 3.4`。首先需要安装 python 连接器 `taospy`。执行以下命令即可:
```
pip3 install taospy
```
```python
{{#include docs-examples/python/connect_exmaple.py}}
```
下面用 curl 工具来演示访问 RESTful 接口。在命令行执行下面的命令:
```bash
curl http://localhost:6041/rest/sql -u root:taosdata -d "select server_version()"
```
- `-u` 用于指定访问数据库的用户名和密码。
- `-d` 用于指定发送要执行的 SQL 语句。它会被放入 POST 请求的 Body 中。
如果正常,会返回一个 JSON 字符串,里面包含了服务端的版本。例如:
```json
{
"status": "succ",
"head": ["server_version()"],
"column_meta": [["server_version()", 8, 7]],
"data": [["2.4.0.0"]],
"rows": 1
}
```
了解更多:[RESTful API](/reference/restful-api)。
```toml title=Cargo.toml
[dependencies]
libtaos = { version = "*", features = ["rest"] }
```
```rust
{{#include docs-examples/rust/restexample/examples/connect.rs}}
```
```toml title=Cargo.toml
[dependencies]
libtaos = { version = "0.4.2"}
```
```rust
{{#include docs-examples/rust/nativeexample/examples/connect.rs}}
```
# TDengine 数据建模
TDengine 采用关系型数据模型,需要建库、建表。因此对于一个具体的应用场景,需要考虑库、超级表和普通表的设计。本节不讨论细致的语法规则,只介绍概念。
关于数据建模请参考[视频教程](https://www.taosdata.com/blog/2020/11/11/1945.html)。
## 创建库
不同类型的数据采集点往往具有不同的数据特征,包括数据采集频率的高低,数据保留时间的长短,副本的数目,数据块的大小,是否允许更新数据等等。为了在各种场景下 TDengine 都能最大效率的工作,TDengine 建议将不同数据特征的表创建在不同的库里,因为每个库可以配置不同的存储策略。创建一个库时,除 SQL 标准的选项外,应用还可以指定保留时长、副本数、内存块个数、时间精度、文件块里最大最小记录条数、是否压缩、一个数据文件覆盖的天数等多种参数。比如:
```sql
CREATE DATABASE power KEEP 365 DAYS 10 BLOCKS 6 UPDATE 1;
```
上述语句将创建一个名为 power 的库,这个库的数据将保留 365 天(超过 365 天将被自动删除),每 10 天一个数据文件,内存块数为 6,允许更新数据。详细的语法及参数请见 [数据库管理](/reference/taos-sql/database) 章节。
创建库之后,需要使用 SQL 命令 `USE` 将当前库切换过来,例如:
```sql
USE power;
```
将当前连接里操作的库换为 power,否则对具体表操作前,需要使用“库名.表名”来指定库的名字。
:::note
- 任何一张表或超级表是属于一个库的,在创建表之前,必须先创建库。
- 处于两个不同库的表是不能进行 JOIN 操作的。
- 创建并插入记录、查询历史记录的时候,均需要指定时间戳。
:::
## 创建超级表
一个物联网系统,往往存在多种类型的设备,比如对于电网,存在智能电表、变压器、母线、开关等等。为便于多表之间的聚合,使用 TDengine, 需要对每个类型的数据采集点创建一个超级表。以[表 1](/tdinternal/arch#model_table1) 中的智能电表为例,可以使用如下的 SQL 命令创建超级表:
```sql
CREATE STABLE meters (ts timestamp, current float, voltage int, phase float) TAGS (location binary(64), groupId int);
```
:::note
这一指令中的 STABLE 关键字,在 2.0.15 之前的版本中需写作 TABLE 。
:::
与创建普通表一样,创建表时,需要提供表名(示例中为 meters),表结构 Schema,即数据列的定义。第一列必须为时间戳(示例中为 ts),其他列为采集的物理量(示例中为 current, voltage, phase),数据类型可以为整型、浮点型、字符串等。除此之外,还需要提供标签的 schema (示例中为 location, groupId),标签的数据类型可以为整型、浮点型、字符串等。采集点的静态属性往往可以作为标签,比如采集点的地理位置、设备型号、设备组 ID、管理员 ID 等等。标签的 schema 可以事后增加、删除、修改。具体定义以及细节请见 [TAOS SQL 的超级表管理](/reference/taos-sql/stable) 章节。
每一种类型的数据采集点需要建立一个超级表,因此一个物联网系统,往往会有多个超级表。对于电网,我们就需要对智能电表、变压器、母线、开关等都建立一个超级表。在物联网中,一个设备就可能有多个数据采集点(比如一台风力发电的风机,有的采集点采集电流、电压等电参数,有的采集点采集温度、湿度、风向等环境参数),这个时候,对这一类型的设备,需要建立多张超级表。一张超级表里包含的采集物理量必须是同时采集的(时间戳是一致的)。
一张超级表最多容许 1024 列,如果一个采集点采集的物理量个数超过 1024,需要建多张超级表来处理。一个系统可以有多个 DB,一个 DB 里可以有一到多个超级表。(从 2.1.7.0 版本开始,列数限制由 1024 列放宽到了 4096 列。)
## 创建表
TDengine 对每个数据采集点需要独立建表。与标准的关系型数据库一样,一张表有表名,Schema,但除此之外,还可以带有一到多个标签。创建时,需要使用超级表做模板,同时指定标签的具体值。以[表 1](/tdinternal/arch#model_table1)中的智能电表为例,可以使用如下的 SQL 命令建表:
```sql
CREATE TABLE d1001 USING meters TAGS ("Beijing.Chaoyang", 2);
```
其中 d1001 是表名,meters 是超级表的表名,后面紧跟标签 Location 的具体标签值 ”Beijing.Chaoyang",标签 groupId 的具体标签值 2。虽然在创建表时,需要指定标签值,但可以事后修改。详细细则请见 [TAOS SQL 的表管理](/reference/taos-sql/table) 章节。
:::warning
目前 TDengine 没有从技术层面限制使用一个 database (dbA)的超级表作为模板建立另一个 database (dbB)的子表,后续会禁止这种用法,不建议使用这种方法建表。
:::
TDengine 建议将数据采集点的全局唯一 ID 作为表名(比如设备序列号)。但对于有的场景,并没有唯一的 ID,可以将多个 ID 组合成一个唯一的 ID。不建议将具有唯一性的 ID 作为标签值。
### 自动建表
在某些特殊场景中,用户在写数据时并不确定某个数据采集点的表是否存在,此时可在写入数据时使用自动建表语法来创建不存在的表,若该表已存在则不会建立新表。比如:
```sql
INSERT INTO d1001 USING meters TAGS ("Beijng.Chaoyang", 2) VALUES (now, 10.2, 219, 0.32);
```
上述 SQL 语句将记录`(now, 10.2, 219, 0.32)`插入表 d1001。如果表 d1001 还未创建,则使用超级表 meters 做模板自动创建,同时打上标签值 `"Beijing.Chaoyang", 2`。
关于自动建表的详细语法请参见 [插入记录时自动建表](reference/taos-sql/insert#插入记录时自动建表) 章节。
## 多列模型 vs 单列模型
TDengine 支持多列模型,只要物理量是一个数据采集点同时采集的(时间戳一致),这些量就可以作为不同列放在一张超级表里。但还有一种极限的设计,单列模型,每个采集的物理量都单独建表,因此每种类型的物理量都单独建立一超级表。比如电流、电压、相位,就建三张超级表。
TDengine 建议尽可能采用多列模型,因为插入效率以及存储效率更高。但对于有些场景,一个采集点的采集量的种类经常变化,这个时候,如果采用多列模型,就需要频繁修改超级表的结构定义,让应用变的复杂,这个时候,采用单列模型会显得更简单。
# SQL 写入
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
## SQL 写入
应用通过 C/C++, Java, Go, C#, Python, Node.js 连接器执行 SQL insert 语句来插入数据,用户还可以通过 TAOS Shell,手动输入 SQL insert 语句插入数据。比如下面这条 insert 就将一条记录写入到表 d1001 中:
```mysql
INSERT INTO d1001 VALUES (1538548685000, 10.3, 219, 0.31);
```
TDengine 支持一次写入多条记录,比如下面这条命令就将两条记录写入到表 d1001 中:
```mysql
INSERT INTO d1001 VALUES (1538548684000, 10.2, 220, 0.23) (1538548696650, 10.3, 218, 0.25);
```
TDengine 也支持一次向多个表写入数据,比如下面这条命令就向 d1001 写入两条记录,向 d1002 写入一条记录:
```mysql
INSERT INTO d1001 VALUES (1538548685000, 10.3, 219, 0.31) (1538548695000, 12.6, 218, 0.33) d1002 VALUES (1538548696800, 12.3, 221, 0.31);
```
详细的 SQL INSERT 语法规则请见 [TAOS SQL 的数据写入](https://www.taosdata.com/cn/documentation/taos-sql#insert) 章节。
:::tip
- 要提高写入效率,需要批量写入。一批写入的记录条数越多,插入效率就越高。但一条记录不能超过 16K,一条 SQL 语句总长度不能超过 1M 。
- TDengine 支持多线程同时写入,要进一步提高写入速度,一个客户端需要打开 20 个以上的线程同时写。但线程数达到一定数量后,无法再提高,甚至还会下降,因为线程频繁切换,带来额外开销。
- 对同一张表,如果新插入记录的时间戳已经存在,默认情形下(UPDATE=0)新记录将被直接抛弃,也就是说,在一张表里,时间戳必须是唯一的。如果应用自动生成记录,很有可能生成的时间戳是一样的,这样,成功插入的记录条数会小于应用插入的记录条数。如果在创建数据库时使用了 UPDATE 1 选项,插入相同时间戳的新记录将覆盖原有记录。
- 写入的数据的时间戳必须大于当前时间减去配置参数 keep 的时间。如果 keep 配置为 3650 天,那么无法写入比 3650 天还早的数据。写入数据的时间戳也不能大于当前时间加配置参数 days。如果 days 为 2,那么无法写入比当前时间还晚 2 天的数据。
:::
## 示例程序
### 普通 SQL 写入
<Tabs defaultValue="java">
<TabItem label="Java" value="java"></TabItem>
<TabItem label="Python" value="Python"></TabItem>
<TabItem label="Go" value="go"></TabItem>
<TabItem label="C" value="c"></TabItem>
<TabItem label="Rust" value="rust"></TabItem>
<TabItem label="Node.js" value="nodejs"></TabItem>
<TabItem label="C#" value="csharp"></TabItem>
</Tabs>
### 动态绑定写入
(补充介绍)
### 示例代码
<Tabs defaultValue="java">
<TabItem label="Java" value="java"></TabItem>
<TabItem label="Python" value="Python"></TabItem>
<TabItem label="Go" value="go"></TabItem>
<TabItem label="C" value="c"></TabItem>
<TabItem label="Rust" value="rust"></TabItem>
<TabItem label="Node.js" value="nodejs"></TabItem>
<TabItem label="C#" value="csharp"></TabItem>
</Tabs>
---
sidebar_label: InfluxDB Line 协议
---
# InfluxDB Line 协议
## 示例代码
---
sidebar_label: OpentsDB Telnet 协议
---
# OpentsDB Telnet 协议
## 示例代码
\ No newline at end of file
---
sidebar_label: OpentsDB JSON 格式协议
---
# OpentsDB JSON 格式协议
## 示例代码
label: 写入数据
link:
type: generated-index
description: "TDengine 支持多种接口写入数据,包括 SQL,Prometheus,Telegraf,collectd,StatsD,EMQ MQTT Broker,HiveMQ Broker,CSV 文件等,后续还将提供 Kafka,OPC 等接口。数据可以单条插入,也可以批量插入,可以插入一个数据采集点的数据,也可以同时插入多个数据采集点的数据。支持多线程插入,支持时间乱序数据插入,也支持历史数据插入。"
keywords:
[
SQL,
Prometheus,
Telegraf,
collectd,
StatsD,
EMQ MQTT Broker,
HiveMQ Broker,
CSV,
]
# 查询数据
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
## 主要查询功能
TDengine 采用 SQL 作为查询语言。应用程序可以通过 C/C++, Java, Go, C#, Python, Node.js 连接器发送 SQL 语句,用户可以通过 TDengine 提供的命令行(Command Line Interface, CLI)工具 TAOS Shell 手动执行 SQL 即席查询(Ad-Hoc Query)。TDengine 支持如下查询功能:
- 单列、多列数据查询
- 标签和数值的多种过滤条件:>, <, =, <\>, like 等
- 聚合结果的分组(Group by)、排序(Order by)、约束输出(Limit/Offset)
- 数值列及聚合结果的四则运算
- 时间戳对齐的连接查询(Join Query: 隐式连接)操作
- 多种聚合/计算函数: count, max, min, avg, sum, twa, stddev, leastsquares, top, bottom, first, last, percentile, apercentile, last_row, spread, diff 等
例如:在 TAOS Shell 中,从表 d1001 中查询出 voltage > 215 的记录,按时间降序排列,仅仅输出 2 条。
```mysql
taos> select * from d1001 where voltage > 215 order by ts desc limit 2;
ts | current | voltage | phase |
======================================================================================
2018-10-03 14:38:16.800 | 12.30000 | 221 | 0.31000 |
2018-10-03 14:38:15.000 | 12.60000 | 218 | 0.33000 |
Query OK, 2 row(s) in set (0.001100s)
```
为满足物联网场景的需求,TDengine 支持几个特殊的函数,比如 twa(时间加权平均),spread (最大值与最小值的差),last_row(最后一条记录)等,更多与物联网场景相关的函数将添加进来。TDengine 还支持连续查询。
具体的查询语法请看 [TAOS SQL 的数据查询](https://www.taosdata.com/cn/documentation/taos-sql#select) 章节。
## 多表聚合查询
物联网场景中,往往同一个类型的数据采集点有多个。TDengine 采用超级表(STable)的概念来描述某一个类型的数据采集点,一张普通的表来描述一个具体的数据采集点。同时 TDengine 使用标签来描述数据采集点的静态属性,一个具体的数据采集点有具体的标签值。通过指定标签的过滤条件,TDengine 提供了一高效的方法将超级表(某一类型的数据采集点)所属的子表进行聚合查询。对普通表的聚合函数以及绝大部分操作都适用于超级表,语法完全一样。
**示例 1**:在 TAOS Shell,查找北京所有智能电表采集的电压平均值,并按照 location 分组
```
taos> SELECT AVG(voltage) FROM meters GROUP BY location;
avg(voltage) | location |
=============================================================
222.000000000 | Beijing.Haidian |
219.200000000 | Beijing.Chaoyang |
Query OK, 2 row(s) in set (0.002136s)
```
**示例 2**:在 TAOS shell, 查找 groupId 为 2 的所有智能电表过去 24 小时的记录条数,电流的最大值
```
taos> SELECT count(*), max(current) FROM meters where groupId = 2 and ts > now - 24h;
cunt(*) | max(current) |
==================================
5 | 13.4 |
Query OK, 1 row(s) in set (0.002136s)
```
TDengine 仅容许对属于同一个超级表的表之间进行聚合查询,不同超级表之间的聚合查询不支持。在 [TAOS SQL 的数据查询](https://www.taosdata.com/cn/documentation/taos-sql#select) 一章,查询类操作都会注明是否支持超级表。
## 降采样查询、插值
物联网场景里,经常需要通过降采样(down sampling)将采集的数据按时间段进行聚合。TDengine 提供了一个简便的关键词 interval 让按照时间窗口的查询操作变得极为简单。比如,将智能电表 d1001 采集的电流值每 10 秒钟求和
```
taos> SELECT sum(current) FROM d1001 INTERVAL(10s);
ts | sum(current) |
======================================================
2018-10-03 14:38:00.000 | 10.300000191 |
2018-10-03 14:38:10.000 | 24.900000572 |
Query OK, 2 row(s) in set (0.000883s)
```
降采样操作也适用于超级表,比如:将北京所有智能电表采集的电流值每秒钟求和
```
taos> SELECT SUM(current) FROM meters where location like "Beijing%" INTERVAL(1s);
ts | sum(current) |
======================================================
2018-10-03 14:38:04.000 | 10.199999809 |
2018-10-03 14:38:05.000 | 32.900000572 |
2018-10-03 14:38:06.000 | 11.500000000 |
2018-10-03 14:38:15.000 | 12.600000381 |
2018-10-03 14:38:16.000 | 36.000000000 |
Query OK, 5 row(s) in set (0.001538s)
```
降采样操作也支持时间偏移,比如:将所有智能电表采集的电流值每秒钟求和,但要求每个时间窗口从 500 毫秒开始
```
taos> SELECT SUM(current) FROM meters INTERVAL(1s, 500a);
ts | sum(current) |
======================================================
2018-10-03 14:38:04.500 | 11.189999809 |
2018-10-03 14:38:05.500 | 31.900000572 |
2018-10-03 14:38:06.500 | 11.600000000 |
2018-10-03 14:38:15.500 | 12.300000381 |
2018-10-03 14:38:16.500 | 35.000000000 |
Query OK, 5 row(s) in set (0.001521s)
```
物联网场景里,每个数据采集点采集数据的时间是难同步的,但很多分析算法(比如 FFT)需要把采集的数据严格按照时间等间隔的对齐,在很多系统里,需要应用自己写程序来处理,但使用 TDengine 的降采样操作就轻松解决。如果一个时间间隔里,没有采集的数据,TDengine 还提供插值计算的功能。
语法规则细节请见 [TAOS SQL 的时间维度聚合](https://www.taosdata.com/cn/documentation/taos-sql#aggregation) 章节。
## 示例代码
### 同步查询
<Tabs defaultValue="java">
<TabItem label="Java" value="java"></TabItem>
<TabItem label="Python" value="Python"></TabItem>
<TabItem label="Go" value="go"></TabItem>
<TabItem label="C" value="c"></TabItem>
<TabItem label="Rust" value="rust"></TabItem>
<TabItem label="Node.js" value="nodejs"></TabItem>
<TabItem label="C#" value="csharp"></TabItem>
</Tabs>
### 异步查询
<Tabs defaultValue="go">
<TabItem label="Python" value="Python"></TabItem>
<TabItem label="Go" value="go"></TabItem>
<TabItem label="C" value="c"></TabItem>
<TabItem label="Rust" value="rust"></TabItem>
<TabItem label="Node.js" value="nodejs"></TabItem>
<TabItem label="C#" value="csharp"></TabItem>
</Tabs>
---
sidebar_label: 连续查询
---
# 连续查询(Continuous Query)
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
连续查询是 TDengine 定期自动执行的查询,采用滑动窗口的方式进行计算,是一种简化的时间驱动的流式计算。针对库中的表或超级表,TDengine 可提供定期自动执行的连续查询,用户可让 TDengine 推送查询的结果,也可以将结果再写回到 TDengine 中。每次执行的查询是一个时间窗口,时间窗口随着时间流动向前滑动。在定义连续查询的时候需要指定时间窗口(time window, 参数 interval)大小和每次前向增量时间(forward sliding times, 参数 sliding)。
TDengine 的连续查询采用时间驱动模式,可以直接使用 TAOS SQL 进行定义,不需要额外的操作。使用连续查询,可以方便快捷地按照时间窗口生成结果,从而对原始采集数据进行降采样(down sampling)。用户通过 TAOS SQL 定义连续查询以后,TDengine 自动在最后的一个完整的时间周期末端拉起查询,并将计算获得的结果推送给用户或者写回 TDengine。
TDengine 提供的连续查询与普通流计算中的时间窗口计算具有以下区别:
- 不同于流计算的实时反馈计算结果,连续查询只在时间窗口关闭以后才开始计算。例如时间周期是 1 天,那么当天的结果只会在 23:59:59 以后才会生成。
- 如果有历史记录写入到已经计算完成的时间区间,连续查询并不会重新进行计算,也不会重新将结果推送给用户。对于写回 TDengine 的模式,也不会更新已经存在的计算结果。
- 使用连续查询推送结果的模式,服务端并不缓存客户端计算状态,也不提供 Exactly-Once 的语意保证。如果用户的应用端崩溃,再次拉起的连续查询将只会从再次拉起的时间开始重新计算最近的一个完整的时间窗口。如果使用写回模式,TDengine 可确保数据写回的有效性和连续性。
## 使用连续查询
下面以智能电表场景为例介绍连续查询的具体使用方法。假设我们通过下列 SQL 语句创建了超级表和子表:
```sql
create table meters (ts timestamp, current float, voltage int, phase float) tags (location binary(64), groupId int);
create table D1001 using meters tags ("Beijing.Chaoyang", 2);
create table D1002 using meters tags ("Beijing.Haidian", 2);
...
```
我们已经知道,可以通过下面这条 SQL 语句以一分钟为时间窗口、30 秒为前向增量统计这些电表的平均电压。
```sql
select avg(voltage) from meters interval(1m) sliding(30s);
```
每次执行这条语句,都会重新计算所有数据。 如果需要每隔 30 秒执行一次来增量计算最近一分钟的数据,可以把上面的语句改进成下面的样子,每次使用不同的 `startTime` 并定期执行:
```sql
select avg(voltage) from meters where ts > {startTime} interval(1m) sliding(30s);
```
这样做没有问题,但 TDengine 提供了更简单的方法,只要在最初的查询语句前面加上 `create table {tableName} as` 就可以了,例如:
```sql
create table avg_vol as select avg(voltage) from meters interval(1m) sliding(30s);
```
会自动创建一个名为 `avg_vol` 的新表,然后每隔 30 秒,TDengine 会增量执行 `as` 后面的 SQL 语句,并将查询结果写入这个表中,用户程序后续只要从 `avg_vol` 中查询数据即可。例如:
```mysql
taos> select * from avg_vol;
ts | avg_voltage_ |
===================================================
2020-07-29 13:37:30.000 | 222.0000000 |
2020-07-29 13:38:00.000 | 221.3500000 |
2020-07-29 13:38:30.000 | 220.1700000 |
2020-07-29 13:39:00.000 | 223.0800000 |
```
需要注意,查询时间窗口的最小值是 10 毫秒,没有时间窗口范围的上限。
此外,TDengine 还支持用户指定连续查询的起止时间。如果不输入开始时间,连续查询将从第一条原始数据所在的时间窗口开始;如果没有输入结束时间,连续查询将永久运行;如果用户指定了结束时间,连续查询在系统时间达到指定的时间以后停止运行。比如使用下面的 SQL 创建的连续查询将运行一小时,之后会自动停止。
```mysql
create table avg_vol as select avg(voltage) from meters where ts > now and ts <= now + 1h interval(1m) sliding(30s);
```
需要说明的是,上面例子中的 `now` 是指创建连续查询的时间,而不是查询执行的时间,否则,查询就无法自动停止了。另外,为了尽量避免原始数据延迟写入导致的问题,TDengine 中连续查询的计算有一定的延迟。也就是说,一个时间窗口过去后,TDengine 并不会立即计算这个窗口的数据,所以要稍等一会(一般不会超过 1 分钟)才能查到计算结果。
## 管理连续查询
用户可在控制台中通过 `show streams` 命令来查看系统中全部运行的连续查询,并可以通过 `kill stream` 命令杀掉对应的连续查询。后续版本会提供更细粒度和便捷的连续查询管理命令。
## 示例代码
<Tabs defaultValue="java">
<TabItem label="Java" value="java"></TabItem>
<TabItem label="Python" value="Python"></TabItem>
<TabItem label="Go" value="go"></TabItem>
<TabItem label="C" value="c"></TabItem>
<TabItem label="Rust" value="rust"></TabItem>
<TabItem label="Node.js" value="nodejs"></TabItem>
<TabItem label="C#" value="csharp"></TabItem>
</Tabs>
\ No newline at end of file
---
sidebar_position: 2
sidebar_label: 订阅
---
# 数据订阅(Publisher/Subscriber)
基于数据天然的时间序列特性,TDengine 的数据写入(insert)与消息系统的数据发布(pub)逻辑上一致,均可视为系统中插入一条带时间戳的新记录。同时,TDengine 在内部严格按照数据时间序列单调递增的方式保存数据。本质上来说,TDengine 中里每一张表均可视为一个标准的消息队列。
TDengine 内嵌支持轻量级的消息订阅与推送服务。使用系统提供的 API,用户可使用普通查询语句订阅数据库中的一张或多张表。订阅的逻辑和操作状态的维护均是由客户端完成,客户端定时轮询服务器是否有新的记录到达,有新的记录到达就会将结果反馈到客户。
TDengine 的订阅与推送服务的状态是客户端维持,TDengine 服务器并不维持。因此如果应用重启,从哪个时间点开始获取最新数据,由应用决定。
TDengine 的 API 中,与订阅相关的主要有以下三个:
```c
taos_subscribe
taos_consume
taos_unsubscribe
```
这些 API 的文档请见 [C/C++ Connector](https://www.taosdata.com/cn/documentation/connector#c-cpp),下面仍以智能电表场景为例介绍一下它们的具体用法(超级表和子表结构请参考上一节“连续查询”),完整的示例代码可以在 [这里](https://github.com/taosdata/TDengine/blob/master/examples/c/subscribe.c) 找到。
如果我们希望当某个电表的电流超过一定限制(比如 10A)后能得到通知并进行一些处理, 有两种方法:一是分别对每张子表进行查询,每次查询后记录最后一条数据的时间戳,后续只查询这个时间戳之后的数据:
```sql
select * from D1001 where ts > {last_timestamp1} and current > 10;
select * from D1002 where ts > {last_timestamp2} and current > 10;
...
```
这确实可行,但随着电表数量的增加,查询数量也会增加,客户端和服务端的性能都会受到影响,当电表数增长到一定的程度,系统就无法承受了。
另一种方法是对超级表进行查询。这样,无论有多少电表,都只需一次查询:
```sql
select * from meters where ts > {last_timestamp} and current > 10;
```
但是,如何选择 `last_timestamp` 就成了一个新的问题。因为,一方面数据的产生时间(也就是数据时间戳)和数据入库的时间一般并不相同,有时偏差还很大;另一方面,不同电表的数据到达 TDengine 的时间也会有差异。所以,如果我们在查询中使用最慢的那台电表的数据的时间戳作为 `last_timestamp`,就可能重复读入其它电表的数据;如果使用最快的电表的时间戳,其它电表的数据就可能被漏掉。
TDengine 的订阅功能为上面这个问题提供了一个彻底的解决方案。
首先是使用 `taos_subscribe` 创建订阅:
```c
TAOS_SUB* tsub = NULL;
if (async) {
  // create an asynchronized subscription, the callback function will be called every 1s
  tsub = taos_subscribe(taos, restart, topic, sql, subscribe_callback, &blockFetch, 1000);
} else {
  // create an synchronized subscription, need to call 'taos_consume' manually
  tsub = taos_subscribe(taos, restart, topic, sql, NULL, NULL, 0);
}
```
TDengine 中的订阅既可以是同步的,也可以是异步的,上面的代码会根据从命令行获取的参数 `async` 的值来决定使用哪种方式。这里,同步的意思是用户程序要直接调用 `taos_consume` 来拉取数据,而异步则由 API 在内部的另一个线程中调用 `taos_consume`,然后把拉取到的数据交给回调函数 `subscribe_callback`去处理。(注意,`subscribe_callback` 中不宜做较为耗时的操作,否则有可能导致客户端阻塞等不可控的问题。)
参数 `taos` 是一个已经建立好的数据库连接,在同步模式下无特殊要求。但在异步模式下,需要注意它不会被其它线程使用,否则可能导致不可预计的错误,因为回调函数在 API 的内部线程中被调用,而 TDengine 的部分 API 不是线程安全的。
参数 `sql` 是查询语句,可以在其中使用 where 子句指定过滤条件。在我们的例子中,如果只想订阅电流超过 10A 时的数据,可以这样写:
```sql
select * from meters where current > 10;
```
注意,这里没有指定起始时间,所以会读到所有时间的数据。如果只想从一天前的数据开始订阅,而不需要更早的历史数据,可以再加上一个时间条件:
```sql
select * from meters where ts > now - 1d and current > 10;
```
订阅的 `topic` 实际上是它的名字,因为订阅功能是在客户端 API 中实现的,所以没必要保证它全局唯一,但需要它在一台客户端机器上唯一。
如果名为 `topic` 的订阅不存在,参数 `restart` 没有意义;但如果用户程序创建这个订阅后退出,当它再次启动并重新使用这个 `topic` 时,`restart` 就会被用于决定是从头开始读取数据,还是接续上次的位置进行读取。本例中,如果 `restart` 是 **true**(非零值),用户程序肯定会读到所有数据。但如果这个订阅之前就存在了,并且已经读取了一部分数据,且 `restart` 是 **false**(**0**),用户程序就不会读到之前已经读取的数据了。
`taos_subscribe`的最后一个参数是以毫秒为单位的轮询周期。在同步模式下,如果前后两次调用 `taos_consume` 的时间间隔小于此时间,`taos_consume` 会阻塞,直到间隔超过此时间。异步模式下,这个时间是两次调用回调函数的最小时间间隔。
`taos_subscribe` 的倒数第二个参数用于用户程序向回调函数传递附加参数,订阅 API 不对其做任何处理,只原样传递给回调函数。此参数在同步模式下无意义。
订阅创建以后,就可以消费其数据了,同步模式下,示例代码是下面的 else 部分:
```c
if (async) {
  getchar();
} else while(1) {
  TAOS_RES* res = taos_consume(tsub);
  if (res == NULL) {
    printf("failed to consume data.");
    break;
  } else {
    print_result(res, blockFetch);
    getchar();
  }
}
```
这里是一个 **while** 循环,用户每按一次回车键就调用一次 `taos_consume`,而 `taos_consume` 的返回值是查询到的结果集,与 `taos_use_result` 完全相同,例子中使用这个结果集的代码是函数 `print_result`:
```c
void print_result(TAOS_RES* res, int blockFetch) {
  TAOS_ROW row = NULL;
  int num_fields = taos_num_fields(res);
  TAOS_FIELD* fields = taos_fetch_fields(res);
  int nRows = 0;
  if (blockFetch) {
    nRows = taos_fetch_block(res, &row);
    for (int i = 0; i < nRows; i++) {
      char temp[256];
      taos_print_row(temp, row + i, fields, num_fields);
      puts(temp);
    }
  } else {
    while ((row = taos_fetch_row(res))) {
      char temp[256];
      taos_print_row(temp, row, fields, num_fields);
      puts(temp);
      nRows++;
    }
  }
  printf("%d rows consumed.\n", nRows);
}
```
其中的 `taos_print_row` 用于处理订阅到数据,在我们的例子中,它会打印出所有符合条件的记录。而异步模式下,消费订阅到的数据则显得更为简单:
```c
void subscribe_callback(TAOS_SUB* tsub, TAOS_RES *res, void* param, int code) {
  print_result(res, *(int*)param);
}
```
当要结束一次数据订阅时,需要调用 `taos_unsubscribe`:
```c
taos_unsubscribe(tsub, keep);
```
其第二个参数,用于决定是否在客户端保留订阅的进度信息。如果这个参数是**false**(**0**),那无论下次调用 `taos_subscribe` 时的 `restart` 参数是什么,订阅都只能重新开始。另外,进度信息的保存位置是 _{DataDir}/subscribe/_ 这个目录下,每个订阅有一个与其 `topic` 同名的文件,删掉某个文件,同样会导致下次创建其对应的订阅时只能重新开始。
代码介绍完毕,我们来看一下实际的运行效果。假设:
- 示例代码已经下载到本地
- TDengine 也已经在同一台机器上安装好
- 示例所需的数据库、超级表、子表已经全部创建好
则可以在示例代码所在目录执行以下命令来编译并启动示例程序:
```bash
make
./subscribe -sql='select * from meters where current > 10;'
```
示例程序启动后,打开另一个终端窗口,启动 TDengine 的 shell 向 **D1001** 插入一条电流为 12A 的数据:
```sql
$ taos
> use test;
> insert into D1001 values(now, 12, 220, 1);
```
这时,因为电流超过了 10A,您应该可以看到示例程序将它输出到了屏幕上。您可以继续插入一些数据观察示例程序的输出。
## Java 使用数据订阅功能
订阅功能也提供了 Java 开发接口,相关说明请见 [Java Connector](https://www.taosdata.com/cn/documentation/connector/java#subscribe)。需要注意的是,目前 Java 接口没有提供异步订阅模式,但用户程序可以通过创建 `TimerTask` 等方式达到同样的效果。
下面以一个示例程序介绍其具体使用方法。它所完成的功能与前面介绍的 C 语言示例基本相同,也是订阅数据库中所有电流超过 10A 的记录。
### 准备数据
```sql
# 创建 power 库
taos> create database power;
# 切换库
taos> use power;
# 创建超级表
taos> create table meters(ts timestamp, current float, voltage int, phase int) tags(location binary(64), groupId int);
# 创建表
taos> create table d1001 using meters tags ("Beijing.Chaoyang", 2);
taos> create table d1002 using meters tags ("Beijing.Haidian", 2);
# 插入测试数据
taos> insert into d1001 values("2020-08-15 12:00:00.000", 12, 220, 1),("2020-08-15 12:10:00.000", 12.3, 220, 2),("2020-08-15 12:20:00.000", 12.2, 220, 1);
taos> insert into d1002 values("2020-08-15 12:00:00.000", 9.9, 220, 1),("2020-08-15 12:10:00.000", 10.3, 220, 1),("2020-08-15 12:20:00.000", 11.2, 220, 1);
# 从超级表 meters 查询电流大于 10A 的记录
taos> select * from meters where current > 10;
ts | current | voltage | phase | location | groupid |
===========================================================================================================
2020-08-15 12:10:00.000 | 10.30000 | 220 | 1 | Beijing.Haidian | 2 |
2020-08-15 12:20:00.000 | 11.20000 | 220 | 1 | Beijing.Haidian | 2 |
2020-08-15 12:00:00.000 | 12.00000 | 220 | 1 | Beijing.Chaoyang | 2 |
2020-08-15 12:10:00.000 | 12.30000 | 220 | 2 | Beijing.Chaoyang | 2 |
2020-08-15 12:20:00.000 | 12.20000 | 220 | 1 | Beijing.Chaoyang | 2 |
Query OK, 5 row(s) in set (0.004896s)
```
### 示例程序
```java
public class SubscribeDemo {
private static final String topic = "topic-meter-current-bg-10";
private static final String sql = "select * from meters where current > 10";
public static void main(String[] args) {
Connection connection = null;
TSDBSubscribe subscribe = null;
try {
Class.forName("com.taosdata.jdbc.TSDBDriver");
Properties properties = new Properties();
properties.setProperty(TSDBDriver.PROPERTY_KEY_CHARSET, "UTF-8");
properties.setProperty(TSDBDriver.PROPERTY_KEY_TIME_ZONE, "UTC-8");
String jdbcUrl = "jdbc:TAOS://127.0.0.1:6030/power?user=root&password=taosdata";
connection = DriverManager.getConnection(jdbcUrl, properties);
subscribe = ((TSDBConnection) connection).subscribe(topic, sql, true); // 创建订阅
int count = 0;
while (count < 10) {
TimeUnit.SECONDS.sleep(1); // 等待1秒,避免频繁调用 consume,给服务端造成压力
TSDBResultSet resultSet = subscribe.consume(); // 消费数据
if (resultSet == null) {
continue;
}
ResultSetMetaData metaData = resultSet.getMetaData();
while (resultSet.next()) {
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
System.out.print(metaData.getColumnLabel(i) + ": " + resultSet.getString(i) + "\t");
}
System.out.println();
count++;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != subscribe)
subscribe.close(true); // 关闭订阅
if (connection != null)
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
```
运行示例程序,首先,它会消费符合查询条件的所有历史数据:
```bash
# java -jar subscribe.jar
ts: 1597464000000 current: 12.0 voltage: 220 phase: 1 location: Beijing.Chaoyang groupid : 2
ts: 1597464600000 current: 12.3 voltage: 220 phase: 2 location: Beijing.Chaoyang groupid : 2
ts: 1597465200000 current: 12.2 voltage: 220 phase: 1 location: Beijing.Chaoyang groupid : 2
ts: 1597464600000 current: 10.3 voltage: 220 phase: 1 location: Beijing.Haidian groupid : 2
ts: 1597465200000 current: 11.2 voltage: 220 phase: 1 location: Beijing.Haidian groupid : 2
```
接着,使用 taos 客户端向表中新增一条数据:
```sql
# taos
taos> use power;
taos> insert into d1001 values("2020-08-15 12:40:00.000", 12.4, 220, 1);
```
因为这条数据的电流大于 10A,示例程序会将其消费:
```
ts: 1597466400000 current: 12.4 voltage: 220 phase: 1 location: Beijing.Chaoyang groupid: 2
```
---
sidebar_label: 缓存
---
# 缓存(Cache)
TDengine 采用时间驱动缓存管理策略(First-In-First-Out,FIFO),又称为写驱动的缓存管理机制。这种策略有别于读驱动的数据缓存模式(Least-Recent-Used,LRU),直接将最近写入的数据保存在系统的缓存中。当缓存达到临界值的时候,将最早的数据批量写入磁盘。一般意义上来说,对于物联网数据的使用,用户最为关心最近产生的数据,即当前状态。TDengine 充分利用了这一特性,将最近到达的(当前状态)数据保存在缓存中。
TDengine 通过查询函数向用户提供毫秒级的数据获取能力。直接将最近到达的数据保存在缓存中,可以更加快速地响应用户针对最近一条或一批数据的查询分析,整体上提供更快的数据库查询响应能力。从这个意义上来说,可通过设置合适的配置参数将 TDengine 作为数据缓存来使用,而不需要再部署额外的缓存系统,可有效地简化系统架构,降低运维的成本。需要注意的是,TDengine 重启以后系统的缓存将被清空,之前缓存的数据均会被批量写入磁盘,缓存的数据将不会像专门的 key-value 缓存系统再将之前缓存的数据重新加载到缓存中。
TDengine 分配固定大小的内存空间作为缓存空间,缓存空间可根据应用的需求和硬件资源配置。通过适当的设置缓存空间,TDengine 可以提供极高性能的写入和查询的支持。TDengine 中每个虚拟节点(virtual node)创建时分配独立的缓存池。每个虚拟节点管理自己的缓存池,不同虚拟节点间不共享缓存池。每个虚拟节点内部所属的全部表共享该虚拟节点的缓存池。
TDengine 将内存池按块划分进行管理,数据在内存块里是以行(row)的形式存储。一个 vnode 的内存池是在 vnode 创建时按块分配好,而且每个内存块按照先进先出的原则进行管理。在创建内存池时,块的大小由系统配置参数 cache 决定;每个 vnode 中内存块的数目则由配置参数 blocks 决定。因此对于一个 vnode,总的内存大小为:`cache * blocks`。一个 cache block 需要保证每张表能存储至少几十条以上记录,才会有效率。
你可以通过函数 last_row() 快速获取一张表或一张超级表的最后一条记录,这样很便于在大屏显示各设备的实时状态或采集值。例如:
```mysql
select last_row(voltage) from meters where location='Beijing.Chaoyang';
```
该 SQL 语句将获取所有位于北京朝阳区的电表最后记录的电压值。
---
sidebar_label: UDF
---
# UDF(用户定义函数)
在有些应用场景中,应用逻辑需要的查询无法直接使用系统内置的函数来表示。利用 UDF 功能,TDengine 可以插入用户编写的处理代码并在查询中使用它们,就能够很方便地解决特殊应用场景中的使用需求。 UDF 通常以数据表中的一列数据做为输入,同时支持以嵌套子查询的结果作为输入。
从 2.2.0.0 版本开始,TDengine 支持通过 C/C++ 语言进行 UDF 定义。接下来结合示例讲解 UDF 的使用方法。
## 用 C/C++ 语言来定义 UDF
TDengine 提供 3 个 UDF 的源代码示例,分别为:
- [add_one.c](#add_one.c)
- [abs_max.c](#abs_max.c)
- [demo.c](#demo.c)
### 标量函数
[add_one.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/add_one.c) 是结构最简单的 UDF 实现。其功能为:对传入的一个数据列(可能因 WHERE 子句进行了筛选)中的每一项,都输出 +1 之后的值,并且要求输入的列数据类型为 INT。
这一具体的处理逻辑在函数 `void add_one(char* data, short itype, short ibytes, int numOfRows, long long* ts, char* dataOutput, char* interBuf, char* tsOutput, int* numOfOutput, short otype, short obytes, SUdfInit* buf)` 中定义。这类用于实现 UDF 的基础计算逻辑的函数,我们称为 udfNormalFunc,也就是对行数据块的标量计算函数。需要注意的是,udfNormalFunc 的参数项是固定的,用于按照约束完成与引擎之间的数据交换。
- udfNormalFunc 中各参数的具体含义是:
- data:输入数据。
- itype:输入数据的类型。这里采用的是短整型表示法,与各种数据类型对应的值可以参见 [column_meta 中的列类型说明](https://www.taosdata.com/cn/documentation/connector#column_meta)。例如 4 用于表示 INT 型。
- iBytes:输入数据中每个值会占用的字节数。
- numOfRows:输入数据的总行数。
- ts:主键时间戳在输入中的列数据(只读)。
- dataOutput:输出数据的缓冲区,缓冲区大小为用户指定的输出类型大小 \* numOfRows。
- interBuf:中间计算结果的缓冲区,大小为用户在创建 UDF 时指定的 BUFSIZE 大小。通常用于计算中间结果与最终结果不一致时使用,由引擎负责分配与释放。
- tsOutput:主键时间戳在输出时的列数据,如果非空可用于输出结果对应的时间戳。
- numOfOutput:输出结果的个数(行数)。
- oType:输出数据的类型。取值含义与 itype 参数一致。
- oBytes:输出数据中每个值占用的字节数。
- buf:用于在 UDF 与引擎间的状态控制信息传递块。
### 聚合函数
[abs_max.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/abs_max.c) 实现的是一个聚合函数,功能是对一组数据按绝对值取最大值。
其计算过程为:与所在查询语句相关的数据会被分为多个行数据块,对每个行数据块调用 udfNormalFunc(在本例的实现代码中,实际函数名是 `abs_max`)来生成每个子表的中间结果,再将子表的中间结果调用 udfMergeFunc(本例中,其实际的函数名是 `abs_max_merge`)进行聚合,生成超级表的最终聚合结果或中间结果。聚合查询最后还会通过 udfFinalizeFunc(本例中,其实际的函数名是 `abs_max_finalize`)再把超级表的中间结果处理为最终结果,最终结果只能含 0 或 1 条结果数据。
值得注意的是,udfNormalFunc、udfMergeFunc、udfFinalizeFunc 之间,函数名约定使用相同的前缀,此前缀即 udfNormalFunc 的实际函数名。udfMergeFunc 的函数名后缀 `_merge`、udfFinalizeFunc 的函数名后缀 `_finalize`,是 UDF 实现规则的一部分,系统会按照这些函数名后缀来调用相应功能。
- udfMergeFunc 用于对计算中间结果进行聚合,只有针对超级表的聚合查询才需要调用该函数。本例中 udfMergeFunc 对应的实现函数为 `void abs_max_merge(char* data, int32_t numOfRows, char* dataOutput, int32_t* numOfOutput, SUdfInit* buf)`,其中各参数的具体含义是:
- data:udfNormalFunc 的输出数据数组,如果使用了 interBuf 那么 data 就是 interBuf 的数组。
- numOfRows:data 中数据的行数。
- dataOutput:输出数据的缓冲区,大小等于一条最终结果的大小。如果此时输出还不是最终结果,可以选择输出到 interBuf 中即 data 中。
- numOfOutput:输出结果的个数(行数)。
- buf:用于在 UDF 与引擎间的状态控制信息传递块。
- udfFinalizeFunc 用于对计算结果进行最终计算,通常用于有 interBuf 使用的场景。本例中 udfFinalizeFunc 对应的实现函数为 `void abs_max_finalize(char* dataOutput, char* interBuf, int* numOfOutput, SUdfInit* buf)`,其中各参数的具体含义是:
- dataOutput:输出数据的缓冲区。
- interBuf:中间结算结果缓冲区,可作为输入。
- numOfOutput:输出数据的个数,对聚合函数来说只能是 0 或者 1。
- buf:用于在 UDF 与引擎间的状态控制信息传递块。
其他典型场景,如协方差的计算,即可通过定义聚合 UDF 的方式实现。
### 其他 UDF 函数
用户 UDF 程序除了需要实现上面几个函数外,还有两个用于初始化和释放 UDF 与引擎间的状态控制信息传递块的函数。具体来说,也即对应 udfInitFunc 和 udfDestroyFunc。其函数名命名规则同样是采取以 udfNormalFunc 的实际函数名为前缀,以 `_init``_destroy` 为后缀。系统会在初始化和资源释放时调用对应名称的函数。
- udfInitFunc 用于初始化状态控制信息传递块。上例中 udfInitFunc 对应的实现函数为 `int abs_max_init(SUdfInit* buf)`,其中各参数的具体含义是:
- buf:用于在 UDF 与引擎间的状态控制信息传递块。
- udfDestroyFunc 用于释放状态控制信息传递块。上例中 udfDestroyFunc 对应的实现函数为 `void abs_max_destroy(SUdfInit* buf)`,其中各参数的具体含义是:
- buf:用于在 UDF 与引擎间的状态控制信息传递块。
目前该功能暂时没有实际意义,待后续扩展使用。
### UDF 实现方式的规则总结
根据 UDF 函数类型的不同,用户所要实现的功能函数也不同:
- 标量函数:UDF 中需实现 udfNormalFunc。
- 聚合函数:UDF 中需实现 udfNormalFunc、udfMergeFunc(对超级表查询)、udfFinalizeFunc。
需要注意的是,如果对应的函数不需要具体的功能,也需要实现一个空函数。
## 编译 UDF
用户定义函数的 C 语言源代码无法直接被 TDengine 系统使用,而是需要先编译为 .so 链接库,之后才能载入 TDengine 系统。
例如,按照上一章节描述的规则准备好了用户定义函数的源代码 add_one.c,那么可以执行如下指令编译得到动态链接库文件:
```bash
gcc -g -O0 -fPIC -shared add_one.c -o add_one.so
```
这样就准备好了动态链接库 add_one.so 文件,可以供后文创建 UDF 时使用了。为了保证可靠的系统运行,编译器 GCC 推荐使用 7.5 及以上版本。
## 在系统中管理和使用 UDF
### 创建 UDF
用户可以通过 SQL 指令在系统中加载客户端所在主机上的 UDF 函数库(不能通过 RESTful 接口或 HTTP 管理界面来进行这一过程)。一旦创建成功,则当前 TDengine 集群的所有用户都可以在 SQL 指令中使用这些函数。UDF 存储在系统的 MNode 节点上,因此即使重启 TDengine 系统,已经创建的 UDF 也仍然可用。
在创建 UDF 时,需要区分标量函数和聚合函数。如果创建时声明了错误的函数类别,则可能导致通过 SQL 指令调用函数时出错。此外, UDF 支持输入与输出类型不一致,用户需要保证输入数据类型与 UDF 程序匹配,UDF 输出数据类型与 OUTPUTTYPE 匹配。
- 创建标量函数:`CREATE FUNCTION ids(X) AS ids(Y) OUTPUTTYPE typename(Z) [ BUFSIZE B ];`
- ids(X):标量函数未来在 SQL 指令中被调用时的函数名,必须与函数实现中 udfNormalFunc 的实际名称一致;
- ids(Y):包含 UDF 函数实现的动态链接库的库文件绝对路径(指的是库文件在当前客户端所在主机上的保存路径,通常是指向一个 .so 文件),这个路径需要用英文单引号或英文双引号括起来;
- typename(Z):此函数计算结果的数据类型,与上文中 udfNormalFunc 的 itype 参数不同,这里不是使用数字表示法,而是直接写类型名称即可;
- B:中间计算结果的缓冲区大小,单位是字节,最小 0,最大 512,如果不使用可以不设置。
例如,如下语句可以把 add_one.so 创建为系统中可用的 UDF:
```sql
CREATE FUNCTION add_one AS "/home/taos/udf_example/add_one.so" OUTPUTTYPE INT;
```
- 创建聚合函数:`CREATE AGGREGATE FUNCTION ids(X) AS ids(Y) OUTPUTTYPE typename(Z) [ BUFSIZE B ];`
- ids(X):聚合函数未来在 SQL 指令中被调用时的函数名,必须与函数实现中 udfNormalFunc 的实际名称一致;
- ids(Y):包含 UDF 函数实现的动态链接库的库文件绝对路径(指的是库文件在当前客户端所在主机上的保存路径,通常是指向一个 .so 文件),这个路径需要用英文单引号或英文双引号括起来;
- typename(Z):此函数计算结果的数据类型,与上文中 udfNormalFunc 的 itype 参数不同,这里不是使用数字表示法,而是直接写类型名称即可;
- B:中间计算结果的缓冲区大小,单位是字节,最小 0,最大 512,如果不使用可以不设置。
关于中间计算结果的使用,可以参考示例程序[demo.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/demo.c)
例如,如下语句可以把 demo.so 创建为系统中可用的 UDF:
```sql
CREATE AGGREGATE FUNCTION demo AS "/home/taos/udf_example/demo.so" OUTPUTTYPE DOUBLE bufsize 14;
```
### 管理 UDF
- 删除指定名称的用户定义函数:`DROP FUNCTION ids(X);`
- ids(X):此参数的含义与 CREATE 指令中的 ids(X) 参数一致,也即要删除的函数的名字,例如 `DROP FUNCTION add_one;`
- 显示系统中当前可用的所有 UDF:`SHOW FUNCTIONS;`
### 调用 UDF
在 SQL 指令中,可以直接以在系统中创建 UDF 时赋予的函数名来调用用户定义函数。例如:
```sql
SELECT X(c) FROM table/stable;
```
表示对名为 c 的数据列调用名为 X 的用户定义函数。SQL 指令中用户定义函数可以配合 WHERE 等查询特性来使用。
## UDF 的一些使用限制
在当前版本下,使用 UDF 存在如下这些限制:
1. 在创建和调用 UDF 时,服务端和客户端都只支持 Linux 操作系统;
2. UDF 不能与系统内建的 SQL 函数混合使用,暂不支持在一条 SQL 语句中使用多个不同名的 UDF ;
3. UDF 只支持以单个数据列作为输入;
4. UDF 只要创建成功,就会被持久化存储到 MNode 节点中;
5. 无法通过 RESTful 接口来创建 UDF;
6. UDF 在 SQL 中定义的函数名,必须与 .so 库文件实现中的接口函数名前缀保持一致,也即必须是 udfNormalFunc 的名称,而且不可与 TDengine 中已有的内建 SQL 函数重名。
## 代码附件
### [add_one.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/add_one.c)
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct SUdfInit{
int maybe_null; /* 1 if function can return NULL */
int decimals; /* for real functions */
long long length; /* For string functions */
char *ptr; /* free pointer for function data */
int const_item; /* 0 if result is independent of arguments */
} SUdfInit;
void add_one(char* data, short itype, short ibytes, int numOfRows, long long* ts, char* dataOutput, char* interBUf, char* tsOutput,
int* numOfOutput, short otype, short obytes, SUdfInit* buf) {
int i;
int r = 0;
// printf("add_one input data:%p, type:%d, rows:%d, ts:%p,%lld, dataoutput:%p, tsOutput:%p, numOfOutput:%p, buf:%p\n", data, itype, numOfRows, ts, *ts, dataOutput, tsOutput, numOfOutput, buf);
if (itype == 4) {
for(i=0;i<numOfRows;++i) {
// printf("input %d - %d", i, *((int *)data + i));
*((int *)dataOutput+i)=*((int *)data + i) + 1;
// printf(", output %d\n", *((int *)dataOutput+i));
if (tsOutput) {
*(long long*)tsOutput=1000000;
}
}
*numOfOutput=numOfRows;
// printf("add_one out, numOfOutput:%d\n", *numOfOutput);
}
}
```
### [abs_max.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/abs_max.c)
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
typedef struct SUdfInit{
int maybe_null; /* 1 if function can return NULL */
int decimals; /* for real functions */
int64_t length; /* For string functions */
char *ptr; /* free pointer for function data */
int const_item; /* 0 if result is independent of arguments */
} SUdfInit;
#define TSDB_DATA_INT_NULL 0x80000000L
#define TSDB_DATA_BIGINT_NULL 0x8000000000000000L
void abs_max(char* data, short itype, short ibytes, int numOfRows, int64_t* ts, char* dataOutput, char* interBuf, char* tsOutput,
int* numOfOutput, short otype, short obytes, SUdfInit* buf) {
int i;
int64_t r = 0;
// printf("abs_max input data:%p, type:%d, rows:%d, ts:%p, %" PRId64 ", dataoutput:%p, tsOutput:%p, numOfOutput:%p, buf:%p\n", data, itype, numOfRows, ts, *ts, dataOutput, tsOutput, numOfOutput, buf);
if (itype == 5) {
r=*(int64_t *)dataOutput;
*numOfOutput=0;
for(i=0;i<numOfRows;++i) {
if (*((int64_t *)data + i) == TSDB_DATA_BIGINT_NULL) {
continue;
}
*numOfOutput=1;
//int64_t v = abs(*((int64_t *)data + i));
int64_t v = *((int64_t *)data + i);
if (v < 0) {
v = 0 - v;
}
if (v > r) {
r = v;
}
}
*(int64_t *)dataOutput=r;
// printf("abs_max out, dataoutput:%" PRId64", numOfOutput:%d\n", *(int64_t *)dataOutput, *numOfOutput);
}else {
*numOfOutput=0;
}
}
void abs_max_finalize(char* dataOutput, char* interBuf, int* numOfOutput, SUdfInit* buf) {
int i;
//int64_t r = 0;
// printf("abs_max_finalize dataoutput:%p:%d, numOfOutput:%d, buf:%p\n", dataOutput, *dataOutput, *numOfOutput, buf);
// *numOfOutput=1;
// printf("abs_max finalize, dataoutput:%" PRId64", numOfOutput:%d\n", *(int64_t *)dataOutput, *numOfOutput);
}
void abs_max_merge(char* data, int32_t numOfRows, char* dataOutput, int32_t* numOfOutput, SUdfInit* buf) {
int64_t r = 0;
if (numOfRows > 0) {
r = *((int64_t *)data);
}
// printf("abs_max_merge numOfRows:%d, dataoutput:%p, buf:%p\n", numOfRows, dataOutput, buf);
for (int i = 1; i < numOfRows; ++i) {
// printf("abs_max_merge %d - %" PRId64"\n", i, *((int64_t *)data + i));
if (*((int64_t*)data + i) > r) {
r= *((int64_t*)data + i);
}
}
*(int64_t*)dataOutput=r;
if (numOfRows > 0) {
*numOfOutput=1;
} else {
*numOfOutput=0;
}
// printf("abs_max_merge, dataoutput:%" PRId64", numOfOutput:%d\n", *(int64_t *)dataOutput, *numOfOutput);
}
int abs_max_init(SUdfInit* buf) {
// printf("abs_max init\n");
return 0;
}
void abs_max_destroy(SUdfInit* buf) {
// printf("abs_max destroy\n");
}
```
### [demo.c](https://github.com/taosdata/TDengine/blob/develop/tests/script/sh/demo.c)
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct SUdfInit{
int maybe_null; /* 1 if function can return NULL */
int decimals; /* for real functions */
long long length; /* For string functions */
char *ptr; /* free pointer for function data */
int const_item; /* 0 if result is independent of arguments */
} SUdfInit;
typedef struct SDemo{
double sum;
int num;
short otype;
}SDemo;
#define FLOAT_NULL 0x7FF00000 // it is an NAN
#define DOUBLE_NULL 0x7FFFFF0000000000L // it is an NAN
void demo(char* data, short itype, short ibytes, int numOfRows, long long* ts, char* dataOutput, char* interBuf, char* tsOutput,
int* numOfOutput, short otype, short obytes, SUdfInit* buf) {
int i;
double r = 0;
SDemo *p = (SDemo *)interBuf;
SDemo *q = (SDemo *)dataOutput;
printf("demo input data:%p, type:%d, rows:%d, ts:%p,%lld, dataoutput:%p, interBUf:%p, tsOutput:%p, numOfOutput:%p, buf:%p\n", data, itype, numOfRows, ts, *ts, dataOutput, interBuf, tsOutput, numOfOutput, buf);
for(i=0;i<numOfRows;++i) {
if (itype == 4) {
r=*((int *)data+i);
} else if (itype == 6) {
r=*((float *)data+i);
} else if (itype == 7) {
r=*((double *)data+i);
}
p->sum += r*r;
}
p->otype = otype;
p->num += numOfRows;
q->sum = p->sum;
q->num = p->num;
q->otype = p->otype;
*numOfOutput=1;
printf("demo out, sum:%f, num:%d, numOfOutput:%d\n", p->sum, p->num, *numOfOutput);
}
void demo_merge(char* data, int32_t numOfRows, char* dataOutput, int32_t* numOfOutput, SUdfInit* buf) {
int i;
SDemo *p = (SDemo *)data;
SDemo res = {0};
printf("demo_merge input data:%p, rows:%d, dataoutput:%p, numOfOutput:%p, buf:%p\n", data, numOfRows, dataOutput, numOfOutput, buf);
for(i=0;i<numOfRows;++i) {
res.sum += p->sum * p->sum;
res.num += p->num;
p++;
}
p->sum = res.sum;
p->num = res.num;
*numOfOutput=1;
printf("demo out, sum:%f, num:%d, numOfOutput:%d\n", p->sum, p->num, *numOfOutput);
}
void demo_finalize(char* dataOutput, char* interBuf, int* numOfOutput, SUdfInit* buf) {
SDemo *p = (SDemo *)interBuf;
printf("demo_finalize interbuf:%p, numOfOutput:%p, buf:%p, sum:%f, num:%d\n", interBuf, numOfOutput, buf, p->sum, p->num);
if (p->otype == 6) {
if (p->num != 30000) {
*(unsigned int *)dataOutput = FLOAT_NULL;
} else {
*(float *)dataOutput = (float)(p->sum / p->num);
}
printf("finalize values:%f\n", *(float *)dataOutput);
} else if (p->otype == 7) {
if (p->num != 30000) {
*(unsigned long long *)dataOutput = DOUBLE_NULL;
} else {
*(double *)dataOutput = (double)(p->sum / p->num);
}
printf("finalize values:%f\n", *(double *)dataOutput);
}
*numOfOutput=1;
printf("demo finalize, numOfOutput:%d\n", *numOfOutput);
}
int demo_init(SUdfInit* buf) {
printf("demo init\n");
return 0;
}
void demo_destroy(SUdfInit* buf) {
printf("demo destroy\n");
}
```
# 高级功能
本章介绍以下TDengine中的高级功能。
## 连续查询
连续查询是一个按照预设频率自动执行的查询功能,提供按照时间窗口的聚合查询能力,是一种简化的时间驱动流式计算。
## 订阅
轻量级的数据订阅与推送服务。连续写入到TDengine中时序数据均能够推动到订阅客户端。
## 缓存
提供写驱动的缓存管理机制,将每个表最近的一条写入记录数据持续保存在缓存中,可以提供给高性能的最近状态查询。
## 用户定义函数
支持用户编码的聚合函数和标量函数,在查询中嵌入并使用用户定义函数,拓展查询的能力和功能。
---
sidebar_label: 流计算
---
# 流计算
label: 高级功能
\ No newline at end of file
---
sidebar_label: Grafana
---
# Grafana
TDengine 能够与开源数据可视化系统 [Grafana](https://www.grafana.com/) 快速集成搭建数据监测报警系统,整个过程无需任何代码开发,TDengine 中数据表中内容可以在仪表盘(DashBoard)上进行可视化展现。关于 TDengine 插件的使用您可以在[GitHub](https://github.com/taosdata/grafanaplugin/blob/master/README.md)中了解更多。
## 安装 Grafana
目前 TDengine 支持 Grafana 7.0 以上的版本。用户可以根据当前的操作系统,到 Grafana 官网下载安装包,并执行安装。下载地址如下:<https://grafana.com/grafana/download>。
## 配置 Grafana
TDengine 的 Grafana 插件托管在 GitHub,可从 <https://github.com/taosdata/grafanaplugin/releases/latest> 下载,当前最新版本为 3.1.3。
推荐使用 [`grafana-cli` 命令行工具](https://grafana.com/docs/grafana/latest/administration/cli/) 进行插件安装。
```bash
sudo -u grafana grafana-cli \
--pluginUrl https://github.com/taosdata/grafanaplugin/releases/download/v3.1.3/tdengine-datasource-3.1.3.zip \
plugins install tdengine-datasource
```
或者下载到本地并解压到 Grafana 插件目录。
```bash
GF_VERSION=3.1.3
wget https://github.com/taosdata/grafanaplugin/releases/download/v$GF_VERSION/tdengine-datasource-$GF_VERSION.zip
```
以 CentOS 7.2 操作系统为例,将插件包解压到 /var/lib/grafana/plugins 目录下,重新启动 grafana 即可。
```bash
sudo unzip tdengine-datasource-$GF_VERSION.zip -d /var/lib/grafana/plugins/
```
Grafana 7.3+ / 8.x 版本会对插件进行签名检查,因此还需要在 grafana.ini 文件中增加如下行,才能正确使用插件:
```ini
[plugins]
allow_loading_unsigned_plugins = tdengine-datasource
```
在 Docker 环境下,可以使用如下的环境变量设置自动安装并设置 TDengine 插件:
```bash
GF_INSTALL_PLUGINS=https://github.com/taosdata/grafanaplugin/releases/download/v3.1.3/tdengine-datasource-3.1.3.zip;tdengine-datasource
GF_PLUGINS_ALLOW_LOADING_UNSIGNED_PLUGINS=tdengine-datasource
```
## 使用 Grafana
### 配置数据源
用户可以直接通过 http://localhost:3000 的网址,登录 Grafana 服务器(用户名/密码:admin/admin),通过左侧 `Configuration -> Data Sources` 可以添加数据源,如下图所示:
![img](/img/connections/add_datasource1.jpg)
点击 `Add data source` 可进入新增数据源页面,在查询框中输入 TDengine 可选择添加,如下图所示:
![img](/img/connections/add_datasource2.jpg)
进入数据源配置页面,按照默认提示修改相应配置即可:
![img](/img/connections/add_datasource3.jpg)
- Host: TDengine 集群的中任意一台服务器的 IP 地址与 TDengine RESTful 接口的端口号(6041),默认 http://localhost:6041。注意:从 2.4 版本开始 RESTful 服务默认使用独立组件 taosAdapter 提供,请参考相关文档配置部署。
- User:TDengine 用户名。
- Password:TDengine 用户密码。
点击 `Save & Test` 进行测试,成功会有如下提示:
![img](/img/connections/add_datasource4.jpg)
### 创建 Dashboard
回到主界面创建 Dashboard,点击 Add Query 进入面板查询页面:
![img](/img/connections/create_dashboard1.jpg)
如上图所示,在 Query 中选中 `TDengine` 数据源,在下方查询框可输入相应 sql 进行查询,具体说明如下:
- INPUT SQL:输入要查询的语句(该 SQL 语句的结果集应为两列多行),例如:`select avg(mem_system) from log.dn where ts >= $from and ts < $to interval($interval)` ,其中,from、to 和 interval 为 TDengine 插件的内置变量,表示从 Grafana 插件面板获取的查询范围和时间间隔。除了内置变量外,`也支持可以使用自定义模板变量`。
- ALIAS BY:可设置当前查询别名。
- GENERATE SQL: 点击该按钮会自动替换相应变量,并生成最终执行的语句。
按照默认提示查询当前 TDengine 部署所在服务器指定间隔系统内存平均使用量如下:
![img](/img/connections/create_dashboard2.jpg)
> 关于如何使用 Grafana 创建相应的监测界面以及更多有关使用 Grafana 的信息,请参考 Grafana 官方的[文档](https://grafana.com/docs/)。
### 导入 Dashboard
在 2.3.3.0 及以上版本,您可以导入 TDinsight Dashboard (Grafana Dashboard ID: [15167](https://grafana.com/grafana/dashboards/15167)) 作为 TDengine 集群的监控可视化工具。安装和使用说明请见 [TDinsight 用户手册](https://www.taosdata.com/cn/documentation/tools/insight)。
---
sidebar_label: Prometheus
---
# Prometheus 写入
remote_read 和 remote_write 是 Prometheus 数据读写分离的集群方案。
只需要将 remote_read 和 remote_write url 指向 taosAdapter 对应的 url 同时设置 Basic 验证即可使用。
- remote_read url : `http://host_to_taosAdapter:port(default 6041)/prometheus/v1/remote_read/:db`
- remote_write url : `http://host_to_taosAdapter:port(default 6041)/prometheus/v1/remote_write/:db`
Basic 验证:
- username: TDengine 连接用户名
- password: TDengine 连接密码
示例 prometheus.yml 如下:
```yaml
remote_write:
- url: "http://localhost:6041/prometheus/v1/remote_write/prometheus_data"
basic_auth:
username: root
password: taosdata
remote_read:
- url: "http://localhost:6041/prometheus/v1/remote_read/prometheus_data"
basic_auth:
username: root
password: taosdata
remote_timeout: 10s
read_recent: true
```
---
sidebar_label: Telegraf
---
# Telegraf 写入
安装 Telegraf 请参考[官方文档](https://portal.influxdata.com/downloads/)
TDengine 新版本(2.3.0.0+)包含一个 taosAdapter 独立程序,负责接收包括 Telegraf 的多种应用的数据写入。
配置方法,在 /etc/telegraf/telegraf.conf 增加如下文字,其中 database name 请填写希望在 TDengine 保存 Telegraf 数据的数据库名,TDengine server/cluster host、username 和 password 填写 TDengine 实际值:
```
[[outputs.http]]
url = "http://<TDengine server/cluster host>:6041/influxdb/v1/write?db=<database name>"
method = "POST"
timeout = "5s"
username = "<TDengine's username>"
password = "<TDengine's password>"
data_format = "influx"
influx_max_line_bytes = 250
```
然后重启 telegraf:
```
sudo systemctl start telegraf
```
即可在 TDengine 中查询 metrics 数据库中 Telegraf 写入的数据。
taosAdapter 相关配置参数请参考 taosadapter --help 命令输出以及相关文档。
---
sidebar_label: collectd
---
# collectd 写入
安装 collectd,请参考[官方文档](https://collectd.org/download.shtml)
TDengine 新版本(2.3.0.0+)包含一个 taosAdapter 独立程序,负责接收包括 collectd 的多种应用的数据写入。
在 /etc/collectd/collectd.conf 文件中增加如下内容,其中 host 和 port 请填写 TDengine 和 taosAdapter 配置的实际值:
```
LoadPlugin network
<Plugin network>
Server "<TDengine cluster/server host>" "<port for collectd>"
</Plugin>
```
重启 collectd
```
sudo systemctl start collectd
```
taosAdapter 相关配置参数请参考 taosadapter --help 命令输出以及相关文档。
---
sidebar_label: StatsD
---
# StatsD 直接写入
安装 StatsD
请参考[官方文档](https://github.com/statsd/statsd)
TDengine 新版本(2.3.0.0+)包含一个 taosAdapter 独立程序,负责接收包括 StatsD 的多种应用的数据写入。
在 config.js 文件中增加如下内容后启动 StatsD,其中 host 和 port 请填写 TDengine 和 taosAdapter 配置的实际值:
```
backends 部分添加 "./backends/repeater"
repeater 部分添加 { host:'<TDengine server/cluster host>', port: <port for StatsD>}
```
示例配置文件:
```
{
port: 8125
, backends: ["./backends/repeater"]
, repeater: [{ host: '127.0.0.1', port: 6044}]
}
```
taosAdapter 相关配置参数请参考 taosadapter --help 命令输出以及相关文档。
icinga2 可以收集监控和性能数据并写入 OpenTSDB,taosAdapter 可以支持接收 icinga2 的数据并写入到 TDengine 中。
---
sidebar_label: icinga2
---
# icinga2 写入
- 参考链接 `https://icinga.com/docs/icinga-2/latest/doc/14-features/#opentsdb-writer` 使能 opentsdb-writer
- 使能 taosAdapter 配置项 opentsdb_telnet.enable
- 修改配置文件 /etc/icinga2/features-enabled/opentsdb.conf
```
object OpenTsdbWriter "opentsdb" {
host = "host to taosAdapter"
port = 6048
}
```
taosAdapter 相关配置参数请参考 taosadapter --help 命令输出以及相关文档。
\ No newline at end of file
---
sidebar_label: TCollector
---
# TCollector 写入
TCollector 是一个在客户侧收集本地收集器并发送数据到 OpenTSDB 的进程,taosAdaapter 可以支持接收 TCollector 的数据并写入到 TDengine 中。
使能 taosAdapter 配置项 opentsdb_telnet.enable
修改 TCollector 配置文件,修改 OpenTSDB 宿主机地址为 taosAdapter 被部署的地址,并修改端口号为 taosAdapter 使用的端口(默认 6049)。
taosAdapter 相关配置参数请参考 taosadapter --help 命令输出以及相关文档。
---
sidebar_label: EMQ Broker
---
# EMQ Broker 写入
MQTT 是流行的物联网数据传输协议,[EMQ](https://github.com/emqx/emqx)是一开源的 MQTT Broker 软件,无需任何代码,只需要在 EMQ Dashboard 里使用“规则”做简单配置,即可将 MQTT 的数据直接写入 TDengine。EMQ X 支持通过 发送到 Web 服务的方式保存数据到 TDEngine,也在企业版上提供原生的 TDEngine 驱动实现直接保存。详细使用方法请参考 [EMQ 官方文档](https://docs.emqx.io/broker/latest/cn/rule/rule-example.html#%E4%BF%9D%E5%AD%98%E6%95%B0%E6%8D%AE%E5%88%B0-tdengine)
# 第三方工具
TDengine 通过对标准 SQL 命令、常用数据库连接器标准(例如 JDBC)、ORM 以及其他流行时序数据库写入协议(例如 InfluxDB Line Protocol、OpenTSDB JSON、OpenTSDB Telnet 等)的支持可以使 TDengine 非常容易和第三方工具共同使用。
---
sidebar_label: HiveMQ Broker
---
# HiveMQ Broker 写入
[HiveMQ](https://www.hivemq.com/) 是一个提供免费个人版和企业版的 MQTT 代理,主要用于企业和新兴的机器到机器 M2M 通讯和内部传输,满足可伸缩性、易管理和安全特性。HiveMQ 提供了开源的插件开发包。可以通过 HiveMQ extension - TDengine 保存数据到 TDengine。详细使用方法请参考 [HiveMQ extension - TDengine 说明文档](https://github.com/huskar-t/hivemq-tdengine-extension/blob/b62a26ecc164a310104df57691691b237e091c89/README.md)
# 准备工作
## 第零步
规划集群所有物理节点的 FQDN,将规划好的 FQDN 分别添加到每个物理节点的/etc/hostname;修改每个物理节点的/etc/hosts,将所有集群物理节点的 IP 与 FQDN 的对应添加好。【如部署了 DNS,请联系网络管理员在 DNS 上做好相关配置】
## 第一步
如果搭建集群的物理节点中,存有之前的测试数据、装过 1.X 的版本,或者装过其他版本的 TDengine,请先将其删除,并清空所有数据(如果需要保留原有数据,请联系涛思交付团队进行旧版本升级、数据迁移),具体步骤请参考博客《TDengine 多种安装包的安装和卸载》。
:::note
因为 FQDN 的信息会写进文件,如果之前没有配置或者更改 FQDN,且启动了 TDengine。请一定在确保数据无用或者备份的前提下,清理一下之前的数据(rm -rf /var/lib/taos/*);
:::
:::note
客户端也需要配置,确保它可以正确解析每个节点的 FQDN 配置,不管是通过 DNS 服务,还是修改 hosts 文件。
:::
## 第二步
建议关闭所有物理节点的防火墙,至少保证端口:6030 - 6042 的 TCP 和 UDP 端口都是开放的。强烈建议先关闭防火墙,集群搭建完毕之后,再来配置端口;
## 第三步
在所有物理节点安装 TDengine,且版本必须是一致的,但不要启动 taosd。安装时,提示输入是否要加入一个已经存在的 TDengine 集群时,第一个物理节点直接回车创建新集群,后续物理节点则输入该集群任何一个在线的物理节点的 FQDN:端口号(默认 6030);
## 第四步
检查所有数据节点,以及应用程序所在物理节点的网络设置:
每个物理节点上执行命令hostname -f,查看和确认所有节点的 hostname 是不相同的(应用驱动所在节点无需做此项检查);
每个物理节点上执行ping host,其中 host 是其他物理节点的 hostname,看能否 ping 通其它物理节点;如果不能 ping 通,需要检查网络设置,或/etc/hosts 文件(Windows 系统默认路径为 C:\Windows\system32\drivers\etc\hosts),或 DNS 的配置。如果无法 ping 通,是无法组成集群的;
从应用运行的物理节点,ping taosd 运行的数据节点,如果无法 ping 通,应用是无法连接 taosd 的,请检查应用所在物理节点的 DNS 设置或 hosts 文件;
每个数据节点的 End Point 就是输出的 hostname 外加端口号,比如h1.taosdata.com:6030。
## 第五步
修改 TDengine 的配置文件(所有节点的文件/etc/taos/taos.cfg 都需要修改)。假设准备启动的第一个数据节点 End Point 为 h1.taosdata.com:6030,其与集群配置相关参数如下:
```
// firstEp 是每个数据节点首次启动后连接的第一个数据节点
firstEp h1.taosdata.com:6030
// 必须配置为本数据节点的FQDN,如果本机只有一个hostname, 可注释掉本项
fqdn h1.taosdata.com
// 配置本数据节点的端口号,缺省是6030
serverPort 6030
// 副本数为偶数的时候,需要配置,请参考《Arbitrator的使用》的部分
arbitrator ha.taosdata.com:6042
```
一定要修改的参数是 firstEp 和 fqdn。在每个数据节点,firstEp 需全部配置成一样,但 fqdn 一定要配置成其所在数据节点的值。其他参数可不做任何修改,除非你很清楚为什么要修改。
加入到集群中的数据节点 dnode,涉及集群相关的下表 9 项参数必须完全相同,否则不能成功加入到集群中。
| **#** | **配置参数名称** | **含义** |
| ----- | ------------------ | ------------------------------------------- |
| 1 | numOfMnodes | 系统中管理节点个数 |
| 2 | mnodeEqualVnodeNum | 一个 mnode 等同于 vnode 消耗的个数 |
| 3 | offlineThreshold | dnode 离线阈值,超过该时间将导致 Dnode 离线 |
| 4 | statusInterval | dnode 向 mnode 报告状态时长 |
| 5 | arbitrator | 系统中裁决器的 End Point |
| 6 | timezone | 时区 |
| 7 | balance | 是否启动负载均衡 |
| 8 | maxTablesPerVnode | 每个 vnode 中能够创建的最大表个数 |
| 9 | maxVgroupsPerDb | 每个 DB 中能够使用的最大 vgroup 个数 |
:::note
在 2.0.19.0 及更早的版本中,除以上 9 项参数外,dnode 加入集群时,还会要求 locale 和 charset 参数的取值也一致。
:::
# 集群部署
## 启动第一个数据节点
按照《立即开始》里的指示,启动第一个数据节点,例如 h1.taosdata.com,然后执行 taos, 启动 taos shell,从 shell 里执行命令"show dnodes;",如下所示:
```
Welcome to the TDengine shell from Linux, Client Version:2.0.0.0
Copyright (c) 2017 by TAOS Data, Inc. All rights reserved.
taos> show dnodes;
id | end_point | vnodes | cores | status | role | create_time |
=====================================================================================
1 | h1.taos.com:6030 | 0 | 2 | ready | any | 2020-07-31 03:49:29.202 |
Query OK, 1 row(s) in set (0.006385s)
taos>
```
上述命令里,可以看到这个刚启动的这个数据节点的 End Point 是:h1.taos.com:6030,就是这个新集群的 firstEp。
## 启动后续数据节点
将后续的数据节点添加到现有集群,具体有以下几步:
按照《立即开始》一章的方法在每个物理节点启动 taosd;(注意:每个物理节点都需要在 taos.cfg 文件中将 firstEp 参数配置为新集群首个节点的 End Point——在本例中是 h1.taos.com:6030)
在第一个数据节点,使用 CLI 程序 taos,登录进 TDengine 系统,执行命令:
```mysql
CREATE DNODE "h2.taos.com:6030";
```
将新数据节点的 End Point (准备工作中第四步获知的) 添加进集群的 EP 列表。"fqdn:port"需要用双引号引起来,否则出错。请注意将示例的“h2.taos.com:6030" 替换为这个新数据节点的 End Point。
然后执行命令
```mysql
SHOW DNODES;
```
查看新节点是否被成功加入。如果该被加入的数据节点处于离线状态,请做两个检查:
查看该数据节点的 taosd 是否正常工作,如果没有正常运行,需要先检查为什么
查看该数据节点 taosd 日志文件 taosdlog.0 里前面几行日志(一般在/var/log/taos 目录),看日志里输出的该数据节点 fqdn 以及端口号是否为刚添加的 End Point。如果不一致,需要将正确的 End Point 添加进去。
按照上述步骤可以源源不断的将新的数据节点加入到集群。
:::tip
任何已经加入集群在线的数据节点,都可以作为后续待加入节点的 firstEp。
firstEp 这个参数仅仅在该数据节点首次加入集群时有作用,加入集群后,该数据节点会保存最新的 mnode 的 End Point 列表,不再依赖这个参数。
接下来,配置文件中的 firstEp 参数就主要在客户端连接的时候使用了,例如 taos shell 如果不加参数,会默认连接由 firstEp 指定的节点。
两个没有配置 firstEp 参数的数据节点 dnode 启动后,会独立运行起来。这个时候,无法将其中一个数据节点加入到另外一个数据节点,形成集群。无法将两个独立的集群合并成为新的集群。
:::
## 数据节点管理
上面已经介绍如何从零开始搭建集群。集群组建完后,还可以随时添加新的数据节点进行扩容,或删除数据节点,并检查集群当前状态。
提示:
以下所有执行命令的操作需要先登陆进 TDengine 系统,必要时请使用 root 权限。
添加数据节点
执行 CLI 程序 taos,执行:
```mysql
CREATE DNODE "fqdn:port";
```
将新数据节点的 End Point 添加进集群的 EP 列表。"fqdn:port"需要用双引号引起来,否则出错。一个数据节点对外服务的 fqdn 和 port 可以通过配置文件 taos.cfg 进行配置,缺省是自动获取。【强烈不建议用自动获取方式来配置 FQDN,可能导致生成的数据节点的 End Point 不是所期望的】
## 删除数据节点
执行 CLI 程序 taos,执行:
```mysql
DROP DNODE "fqdn:port | dnodeID";
```
通过"fqdn:port"或"dnodeID"来指定一个具体的节点都是可以的。其中 fqdn 是被删除的节点的 FQDN,port 是其对外服务器的端口号;dnodeID 可以通过 SHOW DNODES 获得。
:::warning
一个数据节点一旦被 drop 之后,不能重新加入集群。需要将此节点重新部署(清空数据文件夹)。集群在完成 drop dnode 操作之前,会将该 dnode 的数据迁移走。
请注意 drop dnode 和 停止 taosd 进程是两个不同的概念,不要混淆:因为删除 dnode 之前要执行迁移数据的操作,因此被删除的 dnode 必须保持在线状态。待删除操作结束之后,才能停止 taosd 进程。
一个数据节点被 drop 之后,其他节点都会感知到这个 dnodeID 的删除操作,任何集群中的节点都不会再接收此 dnodeID 的请求。
dnodeID 是集群自动分配的,不得人工指定。它在生成时是递增的,不会重复。
:::
## 手动迁移数据节点
手动将某个 vnode 迁移到指定的 dnode。
执行 CLI 程序 taos,执行:
```mysql
ALTER DNODE <source-dnodeId> BALANCE "VNODE:<vgId>-DNODE:<dest-dnodeId>";
```
其中:source-dnodeId 是源 dnodeId,也就是待迁移的 vnode 所在的 dnodeID;vgId 可以通过 SHOW VGROUPS 获得,列表的第一列;dest-dnodeId 是目标 dnodeId。
:::warning
只有在集群的自动负载均衡选项关闭时(balance 设置为 0),才允许手动迁移。
只有处于正常工作状态的 vnode 才能被迁移:master/slave,当处于 offline/unsynced/syncing 状态时,是不能迁移的。
迁移前,务必核实目标 dnode 的资源足够:CPU、内存、硬盘。
:::
## 查看数据节点
执行 CLI 程序 taos,执行:
```mysql
SHOW DNODES;
```
它将列出集群中所有的 dnode,每个 dnode 的 ID,end_point(fqdn:port),状态(ready, offline 等),vnode 数目,还未使用的 vnode 数目等信息。在添加或删除一个数据节点后,可以使用该命令查看。
## 查看虚拟节点组
为充分利用多核技术,并提供 scalability,数据需要分片处理。因此 TDengine 会将一个 DB 的数据切分成多份,存放在多个 vnode 里。这些 vnode 可能分布在多个数据节点 dnode 里,这样就实现了水平扩展。一个 vnode 仅仅属于一个 DB,但一个 DB 可以有多个 vnode。vnode 的是 mnode 根据当前系统资源的情况,自动进行分配的,无需任何人工干预。
执行 CLI 程序 taos,执行:
```mysql
USE SOME_DATABASE;
SHOW VGROUPS;
```
# 高可用与负载均衡
## vnode 的高可用性
TDengine 通过多副本的机制来提供系统的高可用性,包括 vnode 和 mnode 的高可用性。
vnode 的副本数是与 DB 关联的,一个集群里可以有多个 DB,根据运营的需求,每个 DB 可以配置不同的副本数。创建数据库时,通过参数 replica 指定副本数(缺省为 1)。如果副本数为 1,系统的可靠性无法保证,只要数据所在的节点宕机,就将无法提供服务。集群的节点数必须大于等于副本数,否则创建表时将返回错误"more dnodes are needed"。比如下面的命令将创建副本数为 3 的数据库 demo:
```mysql
CREATE DATABASE demo replica 3;
```
一个 DB 里的数据会被切片分到多个 vnode group,vnode group 里的 vnode 数目就是 DB 的副本数,同一个 vnode group 里各 vnode 的数据是完全一致的。为保证高可用性,vnode group 里的 vnode 一定要分布在不同的数据节点 dnode 里(实际部署时,需要在不同的物理机上),只要一个 vgroup 里超过半数的 vnode 处于工作状态,这个 vgroup 就能正常的对外服务。
一个数据节点 dnode 里可能有多个 DB 的数据,因此一个 dnode 离线时,可能会影响到多个 DB。如果一个 vnode group 里的一半或一半以上的 vnode 不工作,那么该 vnode group 就无法对外服务,无法插入或读取数据,这样会影响到它所属的 DB 的一部分表的读写操作。
因为 vnode 的引入,无法简单地给出结论:“集群中过半数据节点 dnode 工作,集群就应该工作”。但是对于简单的情形,很好下结论。比如副本数为 3,只有三个 dnode,那如果仅有一个节点不工作,整个集群还是可以正常工作的,但如果有两个数据节点不工作,那整个集群就无法正常工作了。
## Mnode 的高可用性
TDengine 集群是由 mnode (taosd 的一个模块,管理节点) 负责管理的,为保证 mnode 的高可用,可以配置多个 mnode 副本,副本数由系统配置参数 numOfMnodes 决定,有效范围为 1-3。为保证元数据的强一致性,mnode 副本之间是通过同步的方式进行数据复制的。
一个集群有多个数据节点 dnode,但一个 dnode 至多运行一个 mnode 实例。多个 dnode 情况下,哪个 dnode 可以作为 mnode 呢?这是完全由系统根据整个系统资源情况,自动指定的。用户可通过 CLI 程序 taos,在 TDengine 的 console 里,执行如下命令:
```mysql
SHOW MNODES;
```
来查看 mnode 列表,该列表将列出 mnode 所处的 dnode 的 End Point 和角色(master, slave, unsynced 或 offline)。当集群中第一个数据节点启动时,该数据节点一定会运行一个 mnode 实例,否则该数据节点 dnode 无法正常工作,因为一个系统是必须有至少一个 mnode 的。如果 numOfMnodes 配置为 2,启动第二个 dnode 时,该 dnode 也将运行一个 mnode 实例。
为保证 mnode 服务的高可用性,numOfMnodes 必须设置为 2 或更大。因为 mnode 保存的元数据必须是强一致的,如果 numOfMnodes 大于 2,复制参数 quorum 自动设为 2,也就是说,至少要保证有两个副本写入数据成功,才通知客户端应用写入成功。
:::note
一个 TDengine 高可用系统,无论是 vnode 还是 mnode, 都必须配置多个副本。
:::
## 负载均衡
有三种情况,将触发负载均衡,而且都无需人工干预。
当一个新数据节点添加进集群时,系统将自动触发负载均衡,一些节点上的数据将被自动转移到新数据节点上,无需任何人工干预。
当一个数据节点从集群中移除时,系统将自动把该数据节点上的数据转移到其他数据节点,无需任何人工干预。
如果一个数据节点过热(数据量过大),系统将自动进行负载均衡,将该数据节点的一些 vnode 自动挪到其他节点。
当上述三种情况发生时,系统将启动各个数据节点的负载计算,从而决定如何挪动。
:::tip
负载均衡由参数 balance 控制,它决定是否启动自动负载均衡, 0 表示禁用, 1 表示启用自动负载均衡。**
:::
## 数据节点离线处理
如果一个数据节点离线,TDengine 集群将自动检测到。有如下两种情况:
该数据节点离线超过一定时间(taos.cfg 里配置参数 offlineThreshold 控制时长),系统将自动把该数据节点删除,产生系统报警信息,触发负载均衡流程。如果该被删除的数据节点重新上线时,它将无法加入集群,需要系统管理员重新将其添加进集群才会开始工作。
离线后,在 offlineThreshold 的时长内重新上线,系统将自动启动数据恢复流程,等数据完全恢复后,该节点将开始正常工作。
:::note
如果一个虚拟节点组(包括 mnode 组)里所归属的每个数据节点都处于离线或 unsynced 状态,必须等该虚拟节点组里的所有数据节点都上线、都能交换状态信息后,才能选出 Master,该虚拟节点组才能对外提供服务。比如整个集群有 3 个数据节点,副本数为 3,如果 3 个数据节点都宕机,然后 2 个数据节点重启,是无法工作的,只有等 3 个数据节点都重启成功,才能对外服务。
:::
## Arbitrator 的使用
如果副本数为偶数,当一个 vnode group 里一半或超过一半的 vnode 不工作时,是无法从中选出 master 的。同理,一半或超过一半的 mnode 不工作时,是无法选出 mnode 的 master 的,因为存在“split brain”问题。
为解决这个问题,TDengine 引入了 Arbitrator 的概念。Arbitrator 模拟一个 vnode 或 mnode 在工作,但只简单的负责网络连接,不处理任何数据插入或访问。只要包含 Arbitrator 在内,超过半数的 vnode 或 mnode 工作,那么该 vnode group 或 mnode 组就可以正常的提供数据插入或查询服务。比如对于副本数为 2 的情形,如果一个节点 A 离线,但另外一个节点 B 正常,而且能连接到 Arbitrator,那么节点 B 就能正常工作。
总之,在目前版本下,TDengine 建议在双副本环境要配置 Arbitrator,以提升系统的可用性。
Arbitrator 的执行程序名为 tarbitrator。该程序对系统资源几乎没有要求,只需要保证有网络连接,找任何一台 Linux 服务器运行它即可。以下简要描述安装配置的步骤:
请点击 安装包下载,在 TDengine Arbitrator Linux 一节中,选择合适的版本下载并安装。
该应用的命令行参数 -p 可以指定其对外服务的端口号,缺省是 6042。
修改每个 taosd 实例的配置文件,在 taos.cfg 里将参数 arbitrator 设置为 tarbitrator 程序所对应的 End Point。(如果该参数配置了,当副本数为偶数时,系统将自动连接配置的 Arbitrator。如果副本数为奇数,即使配置了 Arbitrator,系统也不会去建立连接。)
在配置文件中配置了的 Arbitrator,会出现在 SHOW DNODES; 指令的返回结果中,对应的 role 列的值会是“arb”。
查看集群 Arbitrator 的状态【2.0.14.0 以后支持】
```mysql
SHOW DNODES;
```
label: 集群管理
link:
type: generated-index
description: "TDengine支持以集群方式部署,以提升系统的处理能力和高可用性。TDengine集群支持任意数据的多副本从而提升高可用性,并自动实现负载均衡。同时TDengine集群具有很好的横向扩展能力以处理更多的数据采集点和更大的数据量。"
keywords:
[
集群,
高可用,
负载均衡,
横向扩展
]
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册