diff --git a/documentation/webdocs/markdowndocs/administrator-ch.md b/documentation/webdocs/markdowndocs/administrator-ch.md index b48cf23995950cb8cf084c6db3e1cdd86afa9c93..45a3501a0f8e1f86a452fde5a1c9d099f4bf0350 100644 --- a/documentation/webdocs/markdowndocs/administrator-ch.md +++ b/documentation/webdocs/markdowndocs/administrator-ch.md @@ -1,349 +1,236 @@ -#系统管理 +# TDengine的运营与维护 -## 文件目录结构 - -安装TDengine的过程中,安装程序将在操作系统中创建以下目录或文件: - -| 目录/文件 | 说明 | -| ---------------------- | :------------------------------------------------| -| /etc/taos/taos.cfg | 默认[配置文件] | -| /usr/local/taos/driver | 动态链接库目录 | -| /var/lib/taos | 默认数据文件目录,可通过[配置文件]修改位置. | -| /var/log/taos | 默认日志文件目录,可通过[配置文件]修改位置 | -| /usr/local/taos/bin | 可执行文件目录 | - -### 可执行文件 - -TDengine的所有可执行文件默认存放在 _/usr/local/taos/bin_ 目录下。其中包括: +## 容量规划 -- _taosd_:TDengine服务端可执行文件。 -- _taos_: TDengine Shell可执行文件。 -- _taosdump_:数据导出工具。 -- *rmtaos*: 卸载TDengine的脚本, 该脚本会删除全部的程序和数据文件。请务必谨慎执行,如非必须不建议使用。 +使用TDengine来搭建一个物联网大数据平台,计算资源、存储资源需要根据业务场景进行规划。下面分别讨论系统运行所需要的内存、CPU以及硬盘空间。 -您可以通过修改系统配置文件taos.cfg来配置不同的数据目录和日志目录 +### 内存需求 -## 服务端配置 +每个DB可以创建固定数目的vnode,默认与CPU核数相同,可通过maxVgroupsPerDb配置;每个vnode会占用固定大小的内存(大小与数据库的配置参数blocks和cache有关);每个Table会占用与标签总长度有关的内存;此外,系统会有一些固定的内存开销。因此,每个DB需要的系统内存可通过如下公式计算: -TDengine系统后台服务程序是`taosd`,其启动时候读取的配置文件缺省目录是`/etc/taos`。可以通过命令行执行参数-c指定配置文件目录,比如 ``` -taosd -c /home/user +Memory Size = maxVgroupsPerDb * (blocks * cache + 10Mb) + numOfTables * (tagSizePerTable + 0.5Kb) ``` -指定`taosd`启动的时候读取`/home/user`目录下的配置文件taos.cfg。 - -下面仅仅列出一些重要的配置参数,更多的参数请看配置文件里的说明。各个参数的详细介绍及作用请看前述章节。**注意:配置修改后,需要重启*taosd*服务才能生效。** - -**privateIp** -- 默认值:物理节点IP地址列表中的第一个IP地址 - -对外提供服务的IP地址。 - -**publicIp** -- 默认值:与privateIp相同 - -对于阿里等云平台,此为公网IP地址,publicIp在内部映射为对应的privateIP地址,仅对企业版有效。 -**masterIp** -- 默认值:与privateIp相同 +示例:假设是4核机器,cache是缺省大小16M, blocks是缺省值6,假设有10万张表,标签总长度是256字节,则总的内存需求为:4\*(16\*6+10) + 100000*(0.25+0.5)/1000 = 499M。 -集群内第一个物理节点的privateIp地址,仅对企业版有效。 +实际运行的系统往往会根据数据特点的不同,将数据存放在不同的DB里。因此做规划时,也需要考虑。 -**secondIp** -- 默认值:与privateIp相同 +如果内存充裕,可以加大Blocks的配置,这样更多数据将保存在内存里,提高查询速度。 -集群内第二个物理节点的privateIp地址,仅对企业版有效。 +### CPU需求 -**mgmtShellPort** -- 默认值: _6030_ +CPU的需求取决于如下两方面: -数据库服务中管理节点与客户端通信使用的TCP/UDP端口号。 -> 端口范围 _6030_ - _6034_ 均用于UDP通讯。此外,还使用端口 _6030_ 用于TCP通讯。 +* __数据插入__ TDengine单核每秒能至少处理一万个插入请求。每个插入请求可以带多条记录,一次插入一条记录与插入10条记录,消耗的计算资源差别很小。因此每次插入,条数越大,插入效率越高。如果一个插入请求带200条以上记录,单核就能达到每秒插入100万条记录的速度。但对前端数据采集的要求越高,因为需要缓存记录,然后一批插入。 +* __查询需求__ TDengine提供高效的查询,但是每个场景的查询差异很大,查询频次变化也很大,难以给出客观数字。需要用户针对自己的场景,写一些查询语句,才能确定。 -**vnodeShellPort** -- 默认值: _6035_ +因此仅对数据插入而言,CPU是可以估算出来的,但查询所耗的计算资源无法估算。在实际运营过程中,不建议CPU使用率超过50%,超过后,需要增加新的节点,以获得更多计算资源。 -数据节点与客户端通信使用的TCP/UDP端口号。 -> 端口范围 _6035_ - _6039_ 的5个端口用于UDP通信。此外,还使用端口 _6035_ 用于TCP通讯。 +### 存储需求 -**mgmtVnodePort** -- 默认值: _6040_ +TDengine相对于通用数据库,有超高的压缩比,在绝大多数场景下,TDengine的压缩比不会低于5倍,有的场合,压缩比可达到10倍以上,取决于实际场景的数据特征。压缩前的原始数据大小可通过如下方式计算: -管理节点与数据节点通信使用的TCP/UDP端口号,仅对企业版有效。 -> 端口范围 _6040_ - _6044_ 的5个端口用于UDP通信。此外,还使用端口 _6040_ 用于TCP通讯。 - -**vnodeVnodePort** -- 默认值: _6045_ - -数据节点与数据节点通信使用的TCP/UDP端口号,仅对企业版有效。 -> 端口范围 _6045_ - _6049_ 的5个端口用于UDP通信。此外,还使用端口 _6045_ 用于TCP通讯。 +``` +Raw DataSize = numOfTables * rowSizePerTable * rowsPerTable +``` -**mgmtMgmtPort** -- 默认值: _6050_ +示例:1000万台智能电表,每台电表每15分钟采集一次数据,每次采集的数据128字节,那么一年的原始数据量是:10000000\*128\*24\*60/15*365 = 44851T。TDengine大概需要消耗44851/5=8970T, 8.9P空间。 -管理节点与管理节点通信使用的UDP端口号,仅对企业版有效。 +用户可以通过参数keep,设置数据在磁盘中的最大保存时长。为进一步减少存储成本,TDengine还提供多级存储,最冷的数据可以存放在最廉价的存储介质上,应用的访问不用做任何调整,只是读取速度降低了。 -**mgmtSyncPort** -- 默认值: _6050_ +为提高速度,可以配置多快硬盘,这样可以并发写入或读取数据。需要提醒的是,TDengine采取多副本的方式提供数据的高可靠,因此不再需要采用昂贵的磁盘阵列。 -管理节点与管理节点同步使用的TCP端口号,仅对企业版有效。 +### 物理机或虚拟机台数 -**httpPort** -- 默认值: _6020_ +根据上面的内存、CPU、存储的预估,就可以知道整个系统需要多少核、多少内存、多少存储空间。如果数据副本数不为1,总需求量需要再乘以副本数。 -RESTful服务使用的端口号,所有的HTTP请求(TCP)都需要向该接口发起查询/写入请求。 +因为TDengine具有很好的水平扩展能力,根据总量,再根据单个物理机或虚拟机的资源,就可以轻松决定需要购置多少台物理机或虚拟机了。 -**dataDir** -- 默认值:/var/lib/taos +## 容错和灾备 -数据文件目录,所有的数据文件都将写入该目录。 +### 容错 -**logDir** -- 默认值:/var/log/taos +TDengine支持**WAL**(Write Ahead Log)机制,实现数据的容错能力,保证数据的高可用。 -日志文件目录,客户端和服务器的运行日志将写入该目录。 +TDengine接收到应用的请求数据包时,先将请求的原始数据包写入数据库日志文件,等数据成功写入数据库数据文件后,再删除相应的WAL。这样保证了TDengine能够在断电等因素导致的服务重启时从数据库日志文件中恢复数据,避免数据的丢失。 -**shellActivityTimer** -- 默认值:3 +涉及的系统配置参数有两个: -系统在服务端保持结果集的最长时间,单位:秒,范围[1-120]。 +- walLevel:WAL级别,0:不写wal; 1:写wal, 但不执行fsync; 2:写wal, 而且执行fsync。 +- fsync:当walLevel设置为2时,执行fsync的周期。设置为0,表示每次写入,立即执行fsync。 -**maxUsers** -- 默认值:10,000 +如果要100%的保证数据不丢失,需要将walLevel设置为2,fsync设置为0。这时写入速度将会下降。但如果应用侧启动的写数据的线程数达到一定的数量(超过50),那么写入数据的性能也会很不错,只会比fsync设置为3000毫秒下降30%左右。 -系统允许创建用户数量的上限 +### 灾备 -**maxDbs** -- 默认值:1,000 +TDengine的集群通过多个副本的机制,来提供系统的高可用性,实现灾备能力。 -系统允许的创建数据库的上限 +TDengine集群是由mnode负责管理的,为保证mnode的高可靠,可以配置多个mnode副本,副本数由系统配置参数numOfMnodes决定,为了支持高可靠,需要设置大于1。为保证元数据的强一致性,mnode副本之间通过同步方式进行数据复制,保证了元数据的强一致性。 -**maxTables** -- 默认值:650,000 +TDengine集群中的时序数据的副本数是与数据库关联的,一个集群里可以有多个数据库,每个数据库可以配置不同的副本数。创建数据库时,通过参数replica 指定副本数。为了支持高可靠,需要设置副本数大于1。 -系统允许创建数据表的上限。 ->系统能够创建的表受到多种因素的限制,单纯地增大该参数并不能直接增加系统能够创建的表数量。例如,由于每个表创建均需要消耗一定量的缓存空间,系统可用内存一定的情况下,创建表的总数的上限是一个固定的值。 +TDengine集群的节点数必须大于等于副本数,否则创建表时将报错。 -**monitor** -- 默认值:1(激活状态) +当TDengine集群中的节点部署在不同的物理机上,并设置多个副本数时,就实现了系统的高可靠性,无需再使用其他软件或工具。TDengine企业版还可以将副本部署在不同机房,从而实现异地容灾。 -服务器内部的系统监控开关。监控主要负责收集物理节点的负载状况,包括CPU、内存、硬盘、网络带宽、HTTP请求量的监控记录,记录信息存储在`LOG`库中。0表示关闭监控服务,1表示激活监控服务。 +## 服务端配置 -**numOfLogLines** -- 默认值:10,000,000 +TDengine系统后台服务由taosd提供,可以在配置文件taos.cfg里修改配置参数,以满足不同场景的需求。配置文件的缺省位置在/etc/taos目录,可以通过taosd命令行执行参数-c指定配置文件目录。比如taosd -c /home/user来指定配置文件位于/home/user这个目录。 -单个日志文件允许的最大行数(10,000,000行)。 +下面仅仅列出一些重要的配置参数,更多的参数请看配置文件里的说明。各个参数的详细介绍及作用请看前述章节,而且这些参数的缺省配置都是工作的,一般无需设置。**注意:配置修改后,需要重启*taosd*服务才能生效。** -**debugFlag** -- 默认值:131(仅输出错误和警告信息) +- firstEp: taosd启动时,主动连接的集群中第一个dnode的end point, 缺省值为 localhost:6030。 +- secondEp: taosd启动时,如果first连接不上,尝试连接集群中第二个dnode的end point, 缺省值为空。 +- fqdn:数据节点的FQDN。如果为空,将自动获取操作系统配置的第一个, 缺省值为空。 +- serverPort:taosd启动后,对外服务的端口号,默认值为6030。 +- httpPort: RESTful服务使用的端口号,所有的HTTP请求(TCP)都需要向该接口发起查询/写入请求。 +- dataDir: 数据文件目录,所有的数据文件都将写入该目录。默认值:/var/lib/taos。 +- logDir:日志文件目录,客户端和服务器的运行日志文件将写入该目录。默认值:/var/log/taos。 +- arbitrator:系统中裁决器的end point, 缺省值为空。 +- role:dnode的可选角色。0-any; 既可作为mnode,也可分配vnode;1-mgmt;只能作为mnode,不能分配vnode;2-dnode;不能作为mnode,只能分配vnode +- debugFlag:运行日志开关。131(输出错误和警告日志),135( 输出错误、警告和调试日志),143( 输出错误、警告、调试和跟踪日志)。默认值:131或135(不同模块有不同的默认值)。 +- numOfLogLines:单个日志文件允许的最大行数。默认值:10,000,000行。 +- maxSQLLength:单条SQL语句允许最长限制。默认值:65380字节。 +- maxBinaryDisplayWidth:Shell中binary 和 nchar字段的显示宽度上限,超过此限制的部分将被隐藏。默认值:30。可在 shell 中通过命令 set max_binary_display_width nn动态修改此选项。 -系统(服务端和客户端)运行日志开关: -- 131 仅输出错误和警告信息 -- 135 输出错误(ERROR)、警告(WARN)、信息(Info) +**注意:**对于端口,TDengine会使用从serverPort起11个连续的TCP和UDP端口号,请务必在防火墙打开。因此如果是缺省配置,需要打开从6030都6040共11个端口,而且必须TCP和UDP都打开。 不同应用场景的数据往往具有不同的数据特征,比如保留天数、副本数、采集频次、记录大小、采集点的数量、压缩等都可完全不同。为获得在存储上的最高效率,TDengine提供如下存储相关的系统配置参数: -- days:数据文件存储数据的时间跨度,单位为天 -- keep:数据保留的天数 -- rows: 文件块中记录条数 -- comp: 文件压缩标志位,0:关闭,1:一阶段压缩,2:两阶段压缩 -- ctime:数据从写入内存到写入硬盘的最长时间间隔,单位为秒 -- clog:数据提交日志(WAL)的标志位,0为关闭,1为打开 -- tables:每个vnode允许创建表的最大数目 -- cache: 内存块的大小(字节数) -- tblocks: 每张表最大的内存块数 -- ablocks: 每张表平均的内存块数 -- precision:时间戳为微秒的标志位,ms表示毫秒,us表示微秒 +- days:一个数据文件存储数据的时间跨度,单位为天,默认值:10。 +- keep:数据库中数据保留的天数,单位为天,默认值:3650。 +- minRows: 文件块中记录的最小条数,单位为条,默认值:100。 +- maxRows: 文件块中记录的最大条数,单位为条,默认值:4096。 +- comp: 文件压缩标志位,0:关闭,1:一阶段压缩,2:两阶段压缩。默认值:2。 +- walLevel:WAL级别。1:写wal, 但不执行fsync; 2:写wal, 而且执行fsync。默认值:1。 +- fsync:当wal设置为2时,执行fsync的周期。设置为0,表示每次写入,立即执行fsync。单位为毫秒,默认值:3000。 +- cache: 内存块的大小,单位为兆字节(MB),默认值:16。 +- blocks: 每个VNODE(TSDB)中有多少cache大小的内存块。因此一个VNODE的用的内存大小粗略为(cache * blocks)。单位为块,默认值:4。 +- replica:副本个数,取值范围:1-3。单位为个,默认值:1 +- precision:时间戳精度标识,ms表示毫秒,us表示微秒。默认值:ms -对于一个应用场景,可能有多种数据特征的数据并存,最佳的设计是将具有相同数据特征的表放在一个库里,这样一个应用有多个库,而每个库可以配置不同的存储参数,从而保证系统有最优的性能。TDengine容许应用在创建库时指定上述存储参数,如果指定,该参数就将覆盖对应的系统配置参数。举例,有下述SQL: +对于一个应用场景,可能有多种数据特征的数据并存,最佳的设计是将具有相同数据特征的表放在一个库里,这样一个应用有多个库,而每个库可以配置不同的存储参数,从而保证系统有最优的性能。TDengine允许应用在创建库时指定上述存储参数,如果指定,该参数就将覆盖对应的系统配置参数。举例,有下述SQL: ``` - create database demo days 10 cache 16000 ablocks 4 + create database demo days 10 cache 32 blocks 8 replica 3; ``` -该SQL创建了一个库demo, 每个数据文件保留10天数据,内存块为16000字节,每个表平均占用4个内存块,而其他参数与系统配置完全一致。 - -## 客户端配置 - -TDengine系统的前台交互客户端应用程序为taos(Windows平台上为taos.exe)。与服务端程序一样,也可以通过设置taos.cfg来配置`taos`启动和运行的配置项。启动的时候如果不指定taos加载配置文件路径,默认读取`/etc/taos/`路径下的`taos.cfg`文件。指定配置文件来启动`taos`的命令如下: - -``` -taos -c /home/cfg/ -``` -**注意:启动设置的是配置文件所在目录,而不是配置文件本身** +该SQL创建了一个库demo, 每个数据文件存储10天数据,内存块为32兆字节,每个VNODE占用8个内存块,副本数为3,而其他参数与系统配置完全一致。 -如果`/home/cfg/`目录下没有配置文件,程序会继续启动并打印如下告警信息: -```plaintext -Welcome to the TDengine shell from linux, client version:1.6.4.0 -option file:/home/cfg/taos.cfg not found, all options are set to system default -``` -更多taos的使用方法请见[Shell命令行程序](#_TDengine_Shell命令行程序)。本节主要讲解taos客户端应用在配置文件taos.cfg文件中使用到的参数。 +TDengine集群中加入一个新的dnode时,涉及集群相关的一些参数必须与已有集群的配置相同,否则不能成功加入到集群中。会进行校验的参数如下: -客户端配置参数说明 +- numOfMnodes:系统中管理节点个数。默认值:3。 +- balance:是否启动负载均衡。0:否,1:是。默认值:1。 +- mnodeEqualVnodeNum: 一个mnode等同于vnode消耗的个数。默认值:4。 +- offlineThreshold: dnode离线阈值,超过该时间将导致该dnode从集群中删除。单位为秒,默认值:86400*10(即10天)。 +- statusInterval: dnode向mnode报告状态时长。单位为秒,默认值:1。 +- maxTablesPerVnode: 每个vnode中能够创建的最大表个数。默认值:1000000。 +- maxVgroupsPerDb: 每个数据库中能够使用的最大vnode个数。 +- arbitrator: 系统中裁决器的end point,缺省为空 +- timezone:时区。从系统中动态获取当前的时区设置。 +- locale:系统区位信息及编码格式。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。 +- charset:字符集编码。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。 -**masterIP** -- 默认值:127.0.0.1 - -客户端连接的TDengine服务器IP地址,如果不设置默认连接127.0.0.1的节点。以下两个命令等效: -``` -taos -taos -h 127.0.0.1 -``` -其中的IP地址是从配置文件中读取的masterIP的值。 +## 客户端配置 -**locale** -- 默认值:系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置 +TDengine系统的前台交互客户端应用程序为taos,它与taosd共享同一个配置文件taos.cfg。运行taos时,使用参数-c指定配置文件目录,如taos -c /home/cfg,表示使用/home/cfg/目录下的taos.cfg配置文件中的参数,缺省目录是/etc/taos。更多taos的使用方法请见[Shell命令行程序](#_TDengine_Shell命令行程序)。本节主要讲解taos客户端应用在配置文件taos.cfg文件中使用到的参数。 -TDengine为存储中文、日文、韩文等非ASCII编码的宽字符,提供一种专门的字段类型`nchar`。写入`nchar`字段的数据将统一采用`UCS4-LE`格式进行编码并发送到服务器。需要注意的是,**编码正确性**是客户端来保证。因此,如果用户想要正常使用`nchar`字段来存储诸如中文、日文、韩文等非ASCII字符,需要正确设置客户端的编码格式。 +客户端配置参数列表及解释 -客户端的输入的字符均采用操作系统当前默认的编码格式,在Linux系统上多为`UTF-8`,部分中文系统编码则可能是`GB18030`或`GBK`等。在docker环境中默认的编码是`POSIX`。在中文版Windows系统中,编码则是`CP936`。客户端需要确保正确设置自己所使用的字符集,即客户端运行的操作系统当前编码字符集,才能保证`nchar`中的数据正确转换为`UCS4-LE`编码格式。 +- firstEp: taos启动时,主动连接的集群中第一个taosd实例的end point, 缺省值为 localhost:6030。 +- secondEp: taos启动时,如果first连接不上,尝试连接集群中第二个taosd实例的end point, 缺省值为空。 +- charset:字符集编码。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。 +- locale:系统区位信息及编码格式。系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置。 -在 Linux 中 locale 的命名规则为: -`<语言>_<地区>.<字符集编码>` -如:`zh_CN.UTF-8`,zh代表中文,CN代表大陆地区,UTF-8表示字符集。字符集编码为客户端正确解析本地字符串提供编码转换的说明。Linux系统与Mac OSX系统可以通过设置locale来确定系统的字符编码,由于Windows使用的locale中不是POSIX标准的locale格式,因此在Windows下需要采用另一个配置参数`charset`来指定字符编码。在Linux系统中也可以使用charset来指定字符编码。 +日志的配置参数,与server的配置参数完全一样。 -**charset** -- 默认值:系统中动态获取,如果自动获取失败,需要用户在配置文件设置或通过API设置 +启动taos时,也可以从命令行指定一个taosd实例的end point,否则就从taos.cfg读取。 -如果配置文件中不设置`charset`,在Linux系统中,taos在启动时候,自动读取系统当前的locale信息,并从locale信息中解析提取charset编码格式。如果自动读取locale信息失败,则尝试读取charset配置,如果读取charset配置也失败,**则中断启动过程**。 +## 用户管理 -在Linux系统中,locale信息包含了字符编码信息,因此正确设置了Linux系统locale以后可以不用再单独设置charset。例如: -``` -locale zh_CN.UTF-8 -``` -在Windows系统中,无法从locale获取系统当前编码。如果无法从配置文件中读取字符串编码信息,`taos`默认设置为字符编码为`CP936`。其等效在配置文件中添加如下配置: -``` -charset CP936 -``` -如果需要调整字符编码,请查阅当前操作系统使用的编码,并在配置文件中正确设置。 +系统管理员可以在CLI界面里添加、删除用户,也可以修改密码。CLI里SQL语法如下: -在Linux系统中,如果用户同时设置了locale和字符集编码charset,并且locale和charset的不一致,后设置的值将覆盖前面设置的值。 -``` -locale zh_CN.UTF-8 -charset GBK -``` -则`charset`的有效值是`GBK`。 ``` -charset GBK -locale zh_CN.UTF-8 +CREATE USER PASS <‘password’>; ``` -`charset`的有效值是`UTF-8`。 -**sockettype** -- 默认值:UDP +创建用户,并指定用户名和密码,密码需要用单引号引起来 -客户端连接服务端的套接字的方式,可以使用`UDP`和`TCP`两种配置。 -在客户端和服务端之间的通讯需要经过恶劣的网络环境下(如公共网络、互联网)、客户端与数据库服务端连接不稳定(由于MTU的问题导致UDP丢包)的情况下,可以将连接的套接字类型调整为`TCP` - ->注意:客户端套接字的类型需要和服务端的套接字类型相同,否则无法连接数据库。 - -**compressMsgSize** -- 默认值:-1(不压缩) - -客户端与服务器之间进行消息通讯过程中,对通讯的消息进行压缩的阈值,默认值为-1(不压缩)。如果要压缩消息,建议设置为64330字节,即大于64330字节的消息体才进行压缩。在配置文件中增加如下配置项即可: ``` -compressMsgSize 64330 +DROP USER ; ``` -如果配置项设置为0,`compressMsgSize 0`表示对所有的消息均进行压缩。 -**timezone** -- 默认值:从系统中动态获取当前的时区设置 - -客户端运行系统所在的时区。为应对多时区的数据写入和查询问题,TDengine采用Unix时间戳([Unix Timestamp](https://en.wikipedia.org/wiki/Unix_time))来记录和存储时间戳。Unix时间戳的特点决定了任一时刻不论在任何时区,产生的时间戳均一致。需要注意的是,Unix时间戳是在客户端完成转换和记录。为了确保客户端其他形式的时间转换为正确的Unix时间戳,需要设置正确的时区。 +删除用户,限root用户使用 -在Linux系统中,客户端会自动读取系统设置的时区信息。用户也可以采用多种方式在配置文件设置时区。例如: ``` -timezone UTC-8 -timezone GMT-8 -timezone Asia/Shanghai +ALTER USER PASS <‘password’>; ``` -均是合法的设置东八区时区的格式。 +修改用户密码, 为避免被转换为小写,密码需要用单引号引用 -时区的设置对于查询和写入SQL语句中非Unix时间戳的内容(时间戳字符串、关键词`now`的解析)产生影响。例如: ``` -SELECT count(*) FROM table_name WHERE TS<'2019-04-11 12:01:08'; +SHOW USERS; ``` -在东八区,SQL语句等效于 -``` -SELECT count(*) FROM table_name WHERE TS<1554955268000; -``` -在UTC时区,SQL语句等效于 -``` -SELECT count(*) FROM table_name WHERE TS<1554984068000; -``` -为了避免使用字符串时间格式带来的不确定性,也可以直接使用Unix时间戳。此外,还可以在SQL语句中使用带有时区的时间戳字符串,例如:RFC3339格式的时间戳字符串,`2013-04-12T15:52:01.123+08:00`或者ISO-8601格式时间戳字符串`2013-04-12T15:52:01.123+0800`。上述两个字符串转化为Unix时间戳不受系统所在时区的影响。 - -**defaultUser** -- 默认值:root -登录用户名,客户端登录的时候,如果不指定用户名,则自动使用该用户名登录。默认情况下,以下的两个命令等效 -``` -taos -taos -u root -``` -用户名为从配置中读取的`defaultUser`配置项。如果更改`defaultUser abc`,则以下两个命令等效: -``` -taos -taos -u abc -``` +显示所有用户 -**defaultPass** -- 默认值:taosdata +**注意:**SQL 语法中,< >表示需要用户输入的部分,但请不要输入< >本身 -登录用户名,客户端登录的时候,如果不指定密码,则自动使用该密码登录。默认情况下,以下的两个命令等效 -``` -taos -taos -ptaosdata -``` +## 数据导入 -TCP/UDP端口,以及日志的配置参数,与server的配置参数完全一样。使用命令`taos -?` 可查看`taos`允许的可选项。 +TDengine提供多种方便的数据导入功能,一种按脚本文件导入,一种按数据文件导入,一种是taosdump工具导入本身导出的文件。 -## 用户管理 +**按脚本文件导入** -系统管理员可以在CLI界面里添加、删除用户,也可以修改密码。CLI里SQL语法如下: +TDengine的shell支持source filename命令,用于批量运行文件中的SQL语句。用户可将建库、建表、写数据等SQL命令写在同一个文件中,每条命令单独一行,在shell中运行source命令,即可按顺序批量运行文件中的SQL语句。以‘#’开头的SQL语句被认为是注释,shell将自动忽略。 -``` -CREATE USER user_name PASS ‘password’ -``` +**按数据文件导入** -创建用户,并制定用户名和密码,密码需要用单引号引起来 +TDengine也支持在shell对已存在的表从CSV文件中进行数据导入。CSV文件只属于一张表且CSV文件中的数据格式需与要导入表的结构相同, 在导入的时候,其语法如下 +```mysql +INSERT INTO FILE <'path/data.csv'>; ``` -DROP USER user_name -``` +注意:如果CSV文件首行存在描述信息,请手动删除后再导入 -删除用户,限root用户使用 +例如,现在存在一个子表d1001, 其表结构如下: +```mysql +taos> DESCRIBE d1001 + Field | Type | Length | Note | +================================================================================= + ts | TIMESTAMP | 8 | | + current | FLOAT | 4 | | + voltage | INT | 4 | | + phase | FLOAT | 4 | | + location | BINARY | 64 | TAG | + groupid | INT | 4 | TAG | ``` -ALTER USER user_name PASS ‘password’ -``` +要导入的data.csv的格式如下: -修改用户密码, 为避免被转换为小写,密码需要用单引号引用 +```csv +'2018-10-04 06:38:05.000',10.30000,219,0.31000 +'2018-10-05 06:38:15.000',12.60000,218,0.33000 +'2018-10-06 06:38:16.800',13.30000,221,0.32000 +'2018-10-07 06:38:05.000',13.30000,219,0.33000 +'2018-10-08 06:38:05.000',14.30000,219,0.34000 +'2018-10-09 06:38:05.000',15.30000,219,0.35000 +'2018-10-10 06:38:05.000',16.30000,219,0.31000 +'2018-10-11 06:38:05.000',17.30000,219,0.32000 +'2018-10-12 06:38:05.000',18.30000,219,0.31000 +``` +那么可以用如下命令导入数据 ``` -SHOW USERS +taos> insert into d1001 file '~/data.csv'; +Query OK, 9 row(s) affected (0.004763s) ``` -显示所有用户 -## 数据导入 +**taosdump工具导入** -TDengine提供两种方便的数据导入功能,一种按脚本文件导入,一种按数据文件导入。 - -**按脚本文件导入** - -TDengine的shell支持source filename命令,用于批量运行文件中的SQL语句。用户可将建库、建表、写数据等SQL命令写在同一个文件中,每条命令单独一行,在shell中运行source命令,即可按顺序批量运行文件中的SQL语句。以‘#’开头的SQL语句被认为是注释,shell将自动忽略。 - -**按数据文件导入** - -TDengine也支持在shell对已存在的表从CSV文件中进行数据导入。每个CSV文件只属于一张表且CSV文件中的数据格式需与要导入表的结构相同。其语法如下 - -```mysql -insert into tb1 file a.csv b.csv tb2 c.csv … -import into tb1 file a.csv b.csv tb2 c.csv … -``` -> 注意:导入的CSV文件不能够带表头, 且表的列与CSV文件的列需要严格对应。 -> 同样还可以使用[样例数据导入工具][1]对数据进行横向和纵向扩展导入。 +TDengine提供了方便的数据库导入导出工具taosdump。用户可以将taosdump从一个系统导出的数据,导入到其他系统中。具体使用方法,请参见博客:TDengine DUMP工具使用指南 ## 数据导出 @@ -354,66 +241,88 @@ import into tb1 file a.csv b.csv tb2 c.csv … 如果用户需要导出一个表或一个STable中的数据,可在shell中运行 ``` -select * from >> a.csv +SELECT * FROM >> ; ``` -这样,表tb中的数据就会按照CSV格式导出到文件a.csv中。 +这样,表tb_name中的数据就会按照CSV格式导出到文件data.csv中。 **用taosdump导出数据** -TDengine提供了方便的数据库导出工具taosdump。用户可以根据需要选择导出所有数据库、一个数据库或者数据库中的一张表,所有数据或一时间段的数据,甚至仅仅表的定义。其用法如下: - -- 导出数据库中的一张或多张表:taosdump [OPTION…] dbname tbname … -- 导出一个或多个数据库: taosdump [OPTION…] --databases dbname… -- 导出所有数据库(不含监控数据库):taosdump [OPTION…] --all-databases - -用户可通过运行taosdump --help获得更详细的用法说明 +TDengine提供了方便的数据库导出工具taosdump。用户可以根据需要选择导出所有数据库、一个数据库或者数据库中的一张表,所有数据或一时间段的数据,甚至仅仅表的定义。具体使用方法,请参见博客:TDengine DUMP工具使用指南 ## 系统连接、任务查询管理 系统管理员可以从CLI查询系统的连接、正在进行的查询、流式计算,并且可以关闭连接、停止正在进行的查询和流式计算。CLI里SQL语法如下: ``` -SHOW CONNECTIONS +SHOW CONNECTIONS; ``` 显示数据库的连接,其中一列显示ip:port, 为连接的IP地址和端口号。 ``` -KILL CONNECTION +KILL CONNECTION ; ``` -强制关闭数据库连接,其中的connection-id是SHOW CONNECTIONS中显示的 ip:port字串,如“192.168.0.1:42198”,拷贝粘贴即可。 +强制关闭数据库连接,其中的connection-id是SHOW CONNECTIONS中显示的第一列的数字。 ``` -SHOW QUERIES +SHOW QUERIES; ``` -显示数据查询,其中一列显示ip:port:id, 为发起该query应用的IP地址,端口号,以及系统分配的ID。 +显示数据查询,其中第一列显示的以冒号隔开的两个数字为query-id,为发起该query应用连接的connection-id和查询次数。 ``` -KILL QUERY +KILL QUERY ; ``` -强制关闭数据查询,其中query-id是SHOW QUERIES中显示的ip:port:id字串,如“192.168.0.1:42198:11”,拷贝粘贴即可。 +强制关闭数据查询,其中query-id是SHOW QUERIES中显示的 connection-id:query-no字串,如“105:2”,拷贝粘贴即可。 ``` -SHOW STREAMS +SHOW STREAMS; ``` -显示流式计算,其中一列显示ip:port:id, 为启动该stream的IP地址、端口和系统分配的ID。 +显示流式计算,其中第一列显示的以冒号隔开的两个数字为stream-id, 为启动该stream应用连接的connection-id和发起stream的次数。 ``` -KILL STREAM +KILL STREAM ; ``` -强制关闭流式计算,其中的中stream-id是SHOW STREAMS中显示的ip:port:id字串,如“192.168.0.1:42198:18”,拷贝粘贴即可。 +强制关闭流式计算,其中的中stream-id是SHOW STREAMS中显示的connection-id:stream-no字串,如103:2,拷贝粘贴即可。 + +注意:SQL语法中,< >表示需要用户输入的部分,但请不要输入< >本身 ## 系统监控 -TDengine启动后,会自动创建一个监测数据库`LOG`,并自动将服务器的CPU、内存、硬盘空间、带宽、请求数、磁盘读写速度、慢查询等信息定时写入该数据库。TDengine还将重要的系统操作(比如登录、创建、删除数据库等)日志以及各种错误报警信息记录下来存放在`LOG`库里。系统管理员可以通过客户端程序查看记录库中的运行负载信息,(在企业版中)还可以通过浏览器查看数据的图标可视化结果。 +TDengine启动后,会自动创建一个监测数据库SYS,并自动将服务器的CPU、内存、硬盘空间、带宽、请求数、磁盘读写速度、慢查询等信息定时写入该数据库。TDengine还将重要的系统操作(比如登录、创建、删除数据库等)日志以及各种错误报警信息记录下来存放在SYS库里。系统管理员可以从CLI直接查看这个数据库,也可以在WEB通过图形化界面查看这些监测信息。 + +这些监测信息的采集缺省是打开的,但可以修改配置文件里的选项enableMonitor将其关闭或打开。 + +## 文件目录结构 + +安装TDengine后,默认会在操作系统中生成下列目录或文件: + +| 目录/文件 | 说明 | +| ------------------------- | :----------------------------------------------------------- | +| /usr/local/taos/bin | TDengine可执行文件目录。其中的执行文件都会软链接到/usr/bin目录下。 | +| /usr/local/taos/connector | TDengine各种连接器目录。 | +| /usr/local/taos/driver | TDengine动态链接库目录。会软链接到/usr/lib目录下。 | +| /usr/local/taos/examples | TDengine各种语言应用示例目录。 | +| /usr/local/taos/include | TDengine对外提供的C语言接口的头文件。 | +| /etc/taos/taos.cfg | TDengine默认[配置文件] | +| /var/lib/taos | TDengine默认数据文件目录,可通过[配置文件]修改位置. | +| /var/log/taos | TDengine默认日志文件目录,可通过[配置文件]修改位置 | + +**可执行文件** + +TDengine的所有可执行文件默认存放在 _/usr/local/taos/bin_ 目录下。其中包括: + +- _taosd_:TDengine服务端可执行文件 +- _taos_: TDengine Shell可执行文件 +- _taosdump_:数据导入导出工具 +- remove.sh:卸载TDengine的脚本, 请谨慎执行,链接到/usr/bin目录下的rmtaos命令。会删除TDengine的安装目录/usr/local/taos,但会保留/etc/taos、/var/lib/taos、/var/log/taos。 + +您可以通过修改系统配置文件taos.cfg来配置不同的数据目录和日志目录。 -这些监测信息的采集缺省是打开的,但可以修改配置文件里的选项`monitor`将其关闭或打开。 -[1]: https://github.com/taosdata/TDengine/tree/develop/importSampleData diff --git a/documentation20/webdocs/markdowndocs/Evaluation-ch.md b/documentation20/webdocs/markdowndocs/Evaluation-ch.md index 5b501b7f9ff6b4375fe58c1914bf5ff4bfd730fe..b0ba5dda19657352a07b7fca56cb8712e43bb947 100644 --- a/documentation20/webdocs/markdowndocs/Evaluation-ch.md +++ b/documentation20/webdocs/markdowndocs/Evaluation-ch.md @@ -6,10 +6,10 @@ TDengine是涛思数据面对高速增长的物联网大数据市场和技术挑 TDengine的模块之一是时序数据库。但除此之外,为减少研发的复杂度、系统维护的难度,TDengine还提供缓存、消息队列、订阅、流式计算等功能,为物联网、工业互联网大数据的处理提供全栈的技术方案,是一个高效易用的物联网大数据平台。与Hadoop等典型的大数据平台相比,它具有如下鲜明的特点: -* __10倍以上的性能提升__:定义了创新的数据存储结构,单核每秒就能处理至少2万次请求,插入数百万个数据点,读出一千万以上数据点,比现有通用数据库快了十倍以上。 -* __硬件或云服务成本降至1/5__:由于超强性能,计算资源不到通用大数据方案的1/5;通过列式存储和先进的压缩算法,存储空间不到通用数据库的1/10 -* __全栈时序数据处理引擎__:将数据库、消息队列、缓存、流式计算等功能融合一起,应用无需再集成Kafka/Redis/HBase/Spark/HDFS等软件,大幅降低应用开发和维护的复杂度成本。 -* __强大的分析功能__:无论是十年前还是一秒钟前的数据,指定时间范围即可查询。数据可在时间轴上或多个设备上进行聚合。临时查询可通过Shell, Python, R, Matlab随时进行。 +* __10倍以上的性能提升__:定义了创新的数据存储结构,单核每秒能处理至少2万次请求,插入数百万个数据点,读出一千万以上数据点,比现有通用数据库快十倍以上。 +* __硬件或云服务成本降至1/5__:由于超强性能,计算资源不到通用大数据方案的1/5;通过列式存储和先进的压缩算法,存储空间不到通用数据库的1/10。 +* __全栈时序数据处理引擎__:将数据库、消息队列、缓存、流式计算等功能融为一体,应用无需再集成Kafka/Redis/HBase/Spark/HDFS等软件,大幅降低应用开发和维护的复杂度成本。 +* __强大的分析功能__:无论是十年前还是一秒钟前的数据,指定时间范围即可查询。数据可在时间轴上或多个设备上进行聚合。即席查询可通过Shell, Python, R, Matlab随时进行。 * __与第三方工具无缝连接__:不用一行代码,即可与Telegraf, Grafana, EMQ, Prometheus, Matlab, R等集成。后续将支持OPC, Hadoop, Spark等, BI工具也将无缝连接。 * __零运维成本、零学习成本__:安装、集群一秒搞定,无需分库分表,实时备份。标准SQL,支持JDBC, RESTful, 支持Python/Java/C/C++/Go, 与MySQL相似,零学习成本。 @@ -21,7 +21,7 @@ TDengine的模块之一是时序数据库。但除此之外,为减少研发的 ### 数据源特点和需求 -从数据源角度,设计人员可以从已经角度分析TDengine在目标应用系统里面的适用性。 +从数据源角度,设计人员可以从下面几个角度分析TDengine在目标应用系统里面的适用性。 |数据源特点和需求|不适用|可能适用|非常适用|简单说明| |---|---|---|---|---| @@ -33,14 +33,14 @@ TDengine的模块之一是时序数据库。但除此之外,为减少研发的 |系统架构要求|不适用|可能适用|非常适用|简单说明| |---|---|---|---|---| |要求简单可靠的系统架构| | | √ |TDengine的系统架构非常简单可靠,自带消息队列,缓存,流式计算,监控等功能,无需集成额外的第三方产品。| -|要求容错和高可靠| | | √ |TDengine的集群功能,自动提供容错灾备等高可靠功能| -|标准化规范| | | √ |TDengine使用标准的SQL语言提供主要功能,遵守标准化规范| +|要求容错和高可靠| | | √ |TDengine的集群功能,自动提供容错灾备等高可靠功能。| +|标准化规范| | | √ |TDengine使用标准的SQL语言提供主要功能,遵守标准化规范。| ### 系统功能需求 |系统功能需求|不适用|可能适用|非常适用|简单说明| |---|---|---|---|---| |要求完整的内置数据处理算法| | √ | |TDengine的实现了通用的数据处理算法,但是还没有做到妥善处理各行各业的所有要求,因此特殊类型的处理还需要应用层面处理。| -|需要大量的交叉查询处理| | √ | |这种类型的处理更多应该用关系型数据系统处理,或者应该考虑TDengine和关系型数据系统配合实现系统功能| +|需要大量的交叉查询处理| | √ | |这种类型的处理更多应该用关系型数据系统处理,或者应该考虑TDengine和关系型数据系统配合实现系统功能。| ### 系统性能需求 |系统性能需求|不适用|可能适用|非常适用|简单说明| @@ -53,8 +53,8 @@ TDengine的模块之一是时序数据库。但除此之外,为减少研发的 |系统维护需求|不适用|可能适用|非常适用|简单说明| |---|---|---|---|---| |要求系统可靠运行| | | √ |TDengine的系统架构非常稳定可靠,日常维护也简单便捷,对维护人员的要求简洁明了,最大程度上杜绝人为错误和事故。| -|要求运维学习成本可控| | | √ |同上| -|要求市场有大量人才储备| √ | | |TDengine作为新一代产品,目前人才市场里面有经验的人员还有限。但是学习成本低,我们作为厂家也提供运维的培训和辅助服务| +|要求运维学习成本可控| | | √ |同上。| +|要求市场有大量人才储备| √ | | |TDengine作为新一代产品,目前人才市场里面有经验的人员还有限。但是学习成本低,我们作为厂家也提供运维的培训和辅助服务。| ## TDengine 性能指标介绍和验证方法 diff --git a/documentation20/webdocs/markdowndocs/Getting Started-ch.md b/documentation20/webdocs/markdowndocs/Getting Started-ch.md index 041ad72ac5ff2ed07fb28a418a6db0ec098e03a4..7704a6a4e7acf3d9aff0b67db4fd7a744d88329e 100644 --- a/documentation20/webdocs/markdowndocs/Getting Started-ch.md +++ b/documentation20/webdocs/markdowndocs/Getting Started-ch.md @@ -59,7 +59,7 @@ systemctl status taosd ## TDengine命令行程序 -执行TDengine命令行程序,您只要在Linux终端执行`taos`即可 +执行TDengine命令行程序,您只要在Linux终端执行`taos`即可。 ```cmd taos @@ -74,9 +74,9 @@ taos> 在TDengine终端中,用户可以通过SQL命令来创建/删除数据库、表等,并进行插入查询操作。在终端中运行的SQL语句需要以分号结束来运行。示例: ```mysql -create database db; -use db; -create table t (ts timestamp, cdata int); +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; diff --git a/documentation20/webdocs/markdowndocs/Model-ch.md b/documentation20/webdocs/markdowndocs/Model-ch.md index 8a8d4191ed5d1a87f3c540d85cecb8370f5e5355..8d21345a924b6f0e09b779a97deb2c87f5b7a570 100644 --- a/documentation20/webdocs/markdowndocs/Model-ch.md +++ b/documentation20/webdocs/markdowndocs/Model-ch.md @@ -11,9 +11,12 @@ TDengine采用关系型数据模型,需要建库、建表。因此对于一个 ```cmd CREATE DATABASE power KEEP 365 DAYS 10 REPLICA 3 BLOCKS 4; ``` -上述语句将创建一个名为power的库,这个库的数据将保留365天(超过365天将被自动删除),每10天一个数据文件,副本数为3, 内存块数为4。详细的语法及参数请见TAOS SQL +上述语句将创建一个名为power的库,这个库的数据将保留365天(超过365天将被自动删除),每10天一个数据文件,副本数为3, 内存块数为4。详细的语法及参数请见TAOS SQL -注意:任何一张表或超级表是属于一个库的,在创建表之前,必须先创建库。 +**注意:** + +- 任何一张表或超级表是属于一个库的,在创建表之前,必须先创建库。 +- 处于两个不同库的表是不能进行JOIN操作的。 ## 创建超级表 一个物联网系统,往往存在多种类型的设备,比如对于电网,存在智能电表、变压器、母线、开关等等。为便于多表之间的聚合,使用TDengine, 需要对每个类型的设备创建一超级表。以表一中的智能电表为例,可以使用如下的SQL命令创建超级表: @@ -31,14 +34,14 @@ CREATE TABLE d1001 USING meters TAGS ("Beijing.Chaoyang", 2); ``` 其中d1001是表名,meters是超级表的表名,后面紧跟标签Location的具体标签值”Beijing.Chaoyang",标签groupId的具体标签值2。虽然在创建表时,需要指定标签值,但可以事后修改。详细细则请见 TAOS SQL。 -TDengine建议将数据采集点的全局唯一ID作为表名(比如设备序列号)。但对于有的场景,并没有唯一的ID,可以将多个ID组合成一个唯一的ID。不建议将具有唯一性的ID作为标签值。 - +TDengine建议将数据采集点的全局唯一ID作为表名(比如设备序列号)。但对于有的场景,并没有唯一的ID,可以将多个ID组合成一个唯一的ID。不建议将具有唯一性的ID作为标签值。 + **自动建表**:在某些特殊场景中,用户在写数据时并不确定某个数据采集点的表是否存在,此时可在写入数据时使用自动建表语法来创建不存在的表,若该表已存在则不会建立新表。比如: ```cmd 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。 - -**多列模型**:TDengine支持多列模型,只要这些物理量是同时采集的,这些量就可以作为不同列放在同一张表里。有的数据采集点有多组采集量,每一组的数据采集时间是不一样的,这时需要对同一个采集点建多张表。但还有一种极限的设计,单列模型,无论是否同时采集,每个采集的物理量单独建表。TDengine建议,只要采集时间一致,就采用多列模型,因为插入效率以及存储效率更高。 +上述SQL语句将记录(now, 10.2, 219, 0.32) 插入进表d1001。如果表d1001还未创建,则使用超级表meters做模板自动创建,同时打上标签值“Beijing.Chaoyang", 2。 + +**多列模型**:TDengine支持多列模型,只要这些物理量是同时采集的,这些量就可以作为不同列放在同一张表里。有的数据采集点有多组采集量,每一组的数据采集时间是不一样的,这时需要对同一个采集点建多张表。但还有一种极限的设计,单列模型,无论是否同时采集,每个采集的物理量单独建表。TDengine建议,只要采集时间一致,就采用多列模型,因为插入效率以及存储效率更高。TDengine支持最大的列数为1024列。 diff --git a/documentation20/webdocs/markdowndocs/Queries-ch.md b/documentation20/webdocs/markdowndocs/Queries-ch.md index 5cc0779551e943444abc2043ae750c6908129c54..b424baef864dde97d7e13414d25a8e15bf9ef4a0 100644 --- a/documentation20/webdocs/markdowndocs/Queries-ch.md +++ b/documentation20/webdocs/markdowndocs/Queries-ch.md @@ -59,7 +59,7 @@ Query OK, 2 row(s) in set (0.002136s) 物联网场景里,经常需要通过降采样(down sampling)将采集的数据按时间段进行聚合。TDengine 提供了一个简便的关键词 interval 让按照时间窗口的查询操作变得极为简单。比如,将智能电表 d1001 采集的电流值每10秒钟求和 ```mysql -taos> SELECT sum(current) FROM d1001 INTERVAL(10s) ; +taos> SELECT sum(current) FROM d1001 INTERVAL(10s); ts | sum(current) | ====================================================== 2018-10-03 14:38:00.000 | 10.300000191 | @@ -68,7 +68,7 @@ Query OK, 2 row(s) in set (0.000883s) ``` 降采样操作也适用于超级表,比如:将所有智能电表采集的电流值每秒钟求和 ```mysql -taos> SELECT SUM(current) FROM meters INTERVAL(1s) ; +taos> SELECT SUM(current) FROM meters INTERVAL(1s); ts | sum(current) | ====================================================== 2018-10-03 14:38:04.000 | 10.199999809 | diff --git a/documentation20/webdocs/markdowndocs/TAOS SQL-ch.md b/documentation20/webdocs/markdowndocs/TAOS SQL-ch.md index c8e6219fb058d7a005c11d77315ddb41993dffcd..952fd8f3409054396544895d04102aa34191dc99 100644 --- a/documentation20/webdocs/markdowndocs/TAOS SQL-ch.md +++ b/documentation20/webdocs/markdowndocs/TAOS SQL-ch.md @@ -59,7 +59,7 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic - **创建数据库** ```mysql - CREATE DATABASE [IF NOT EXISTS] db_name [KEEP keep] + CREATE DATABASE [IF NOT EXISTS] db_name [KEEP keep]; ``` 说明: @@ -71,21 +71,21 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic - **使用数据库** ```mysql - USE db_name + USE db_name; ``` 使用/切换数据库 - **删除数据库** ```mysql - DROP DATABASE [IF EXISTS] db_name + DROP DATABASE [IF EXISTS] db_name; ``` 删除数据库。所包含的全部数据表将被删除,谨慎使用 - **显示系统所有数据库** ```mysql - SHOW DATABASES + SHOW DATABASES; ``` @@ -93,7 +93,7 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic - **创建数据表** ```mysql - CREATE TABLE [IF NOT EXISTS] tb_name (timestamp_field_name TIMESTAMP, field1_name data_type1 [, field2_name data_type2 ...]) + CREATE TABLE [IF NOT EXISTS] tb_name (timestamp_field_name TIMESTAMP, field1_name data_type1 [, field2_name data_type2 ...]); ``` 说明: 1) 表的第一个字段必须是TIMESTAMP,并且系统自动将其设为主键; @@ -104,13 +104,13 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic - **删除数据表** ```mysql - DROP TABLE [IF EXISTS] tb_name + DROP TABLE [IF EXISTS] tb_name; ``` - **显示当前数据库下的所有数据表信息** ```mysql - SHOW TABLES [LIKE tb_name_wildcar] + SHOW TABLES [LIKE tb_name_wildcar]; ``` 显示当前数据库下的所有数据表信息。说明:可在like中使用通配符进行名称的匹配。 通配符匹配:1)’%’ (百分号)匹配0到任意个字符;2)’_’下划线匹配一个字符。 @@ -119,13 +119,13 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic - **获取表的结构信息** ```mysql - DESCRIBE tb_name + DESCRIBE tb_name; ``` - **表增加列** ```mysql - ALTER TABLE tb_name ADD COLUMN field_name data_type + ALTER TABLE tb_name ADD COLUMN field_name data_type; ``` 说明: 1) 列的最大个数为1024,最小个数为2; @@ -134,7 +134,7 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic - **表删除列** ```mysql - ALTER TABLE tb_name DROP COLUMN field_name + ALTER TABLE tb_name DROP COLUMN field_name; ``` 如果表是通过[超级表](../super-table/)创建,更改表结构的操作只能对超级表进行。同时针对超级表的结构更改对所有通过该结构创建的表生效。对于不是通过超级表创建的表,可以直接修改表结构 @@ -142,7 +142,7 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic - **创建超级表** ```mysql - CREATE TABLE [IF NOT EXISTS] stb_name (timestamp_field_name TIMESTAMP, field1_name data_type1 [, field2_name data_type2 ...]) TAGS (tag1_name tag_type1, tag2_name tag_type2 [, tag3_name tag_type3]) + CREATE TABLE [IF NOT EXISTS] stb_name (timestamp_field_name TIMESTAMP, field1_name data_type1 [, field2_name data_type2 ...]) TAGS (tag1_name tag_type1, tag2_name tag_type2 [, tag3_name tag_type3]); ``` 创建STable, 与创建表的SQL语法相似,但需指定TAGS字段的名称和类型 @@ -155,61 +155,61 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic - **删除超级表** ```mysql - DROP TABLE [IF EXISTS] stb_name + DROP TABLE [IF EXISTS] stb_name; ``` 删除STable会自动删除通过STable创建的字表。 - **显示当前数据库下的所有超级表信息** ```mysql - SHOW STABLES [LIKE tb_name_wildcar] + SHOW STABLES [LIKE tb_name_wildcar]; ``` 查看数据库内全部STable,及其相关信息,包括STable的名称、创建时间、列数量、标签(TAG)数量、通过该STable建表的数量。 - **获取超级表的结构信息** ```mysql - DESCRIBE stb_name + DESCRIBE stb_name; ``` - **超级表增加列** ```mysql - ALTER TABLE stb_name ADD COLUMN field_name data_type + ALTER TABLE stb_name ADD COLUMN field_name data_type; ``` - **超级表删除列** ```mysql - ALTER TABLE stb_name DROP COLUMN field_name + ALTER TABLE stb_name DROP COLUMN field_name; ``` ## 超级表 STable 中 TAG 管理 - **添加标签** ```mysql - ALTER TABLE stb_name ADD TAG new_tag_name tag_type + ALTER TABLE stb_name ADD TAG new_tag_name tag_type; ``` 为STable增加一个新的标签,并指定新标签的类型。标签总数不能超过128个,总长度不超过16k个字符. - **删除标签** ```mysql - ALTER TABLE stb_name DROP TAG tag_name + ALTER TABLE stb_name DROP TAG tag_name; ``` 删除超级表的一个标签,从超级表删除某个标签后,该超级表下的所有子表也会自动删除该标签。 - **修改标签名** ```mysql - ALTER TABLE stb_name CHANGE TAG old_tag_name new_tag_name + ALTER TABLE stb_name CHANGE TAG old_tag_name new_tag_name; ``` 修改超级表的标签名,从超级表修改某个标签名后,该超级表下的所有子表也会自动更新该标签名。 - **修改字表标签值** ```mysql - ALTER TABLE tb_name SET TAG tag_name=new_tag_value + ALTER TABLE tb_name SET TAG tag_name=new_tag_value; ``` 说明:除了更新标签的值的操作是针对子表进行,其他所有的标签操作(添加标签、删除标签等)均只能作用于STable,不能对单个子表操作。对STable添加标签以后,依托于该STable建立的所有表将自动增加了一个标签,所有新增标签的默认值都是NULL。 @@ -253,8 +253,8 @@ TDengine缺省的时间戳是毫秒精度,但通过修改配置参数enableMic - **同时向多个表按列插入多条记录** ```mysql - INSERT INTO tb1_name (tb1_field1_name, ...) VALUES (field1_value1, ...) (field1_value1, ...) - tb2_name (tb2_field1_name, ...) VALUES(field1_value1, ...) (field1_value2, ...) + INSERT INTO tb1_name (tb1_field1_name, ...) VALUES (field1_value1, ...) (field1_value2, ...) + tb2_name (tb2_field1_name, ...) VALUES (field1_value1, ...) (field1_value2, ...); ``` 同时向表tb1_name和tb2_name中按列分别插入多条记录 @@ -435,11 +435,11 @@ Query OK, 1 row(s) in set (0.000081s) #### 小技巧 获取一个超级表所有的子表名及相关的标签信息: ``` -SELECT TBNAME, location FROM meters +SELECT TBNAME, location FROM meters; ``` 统计超级表下辖子表数量: ``` -SELECT COUNT(TBNAME) FROM meters +SELECT COUNT(TBNAME) FROM meters; ``` 以上两个查询均只支持在Where条件子句中添加针对标签(TAGS)的过滤条件。例如: ``` @@ -486,31 +486,31 @@ Query OK, 1 row(s) in set (0.001091s) - 对于下面的例子,表tb1用以下语句创建 ```mysql - CREATE TABLE tb1 (ts timestamp, col1 int, col2 float, col3 binary(50)) + CREATE TABLE tb1 (ts timestamp, col1 int, col2 float, col3 binary(50)); ``` - 查询tb1刚过去的一个小时的所有记录 ```mysql - SELECT * FROM tb1 WHERE ts >= NOW - 1h + SELECT * FROM tb1 WHERE ts >= NOW - 1h; ``` - 查询表tb1从2018-06-01 08:00:00.000 到2018-06-02 08:00:00.000时间范围,并且col3的字符串是'nny'结尾的记录,结果按照时间戳降序 ```mysql - SELECT * FROM tb1 WHERE ts > '2018-06-01 08:00:00.000' AND ts <= '2018-06-02 08:00:00.000' AND col3 LIKE '%nny' ORDER BY ts DESC + SELECT * FROM tb1 WHERE ts > '2018-06-01 08:00:00.000' AND ts <= '2018-06-02 08:00:00.000' AND col3 LIKE '%nny' ORDER BY ts DESC; ``` - 查询col1与col2的和,并取名complex, 时间大于2018-06-01 08:00:00.000, col2大于1.2,结果输出仅仅10条记录,从第5条开始 ```mysql - SELECT (col1 + col2) AS 'complex' FROM tb1 WHERE ts > '2018-06-01 08:00:00.000' and col2 > 1.2 LIMIT 10 OFFSET 5 + SELECT (col1 + col2) AS 'complex' FROM tb1 WHERE ts > '2018-06-01 08:00:00.000' and col2 > 1.2 LIMIT 10 OFFSET 5; ``` - 查询过去10分钟的记录,col2的值大于3.14,并且将结果输出到文件 `/home/testoutpu.csv`. ```mysql - SELECT COUNT(*) FROM tb1 WHERE ts >= NOW - 10m AND col2 > 3.14 >> /home/testoutpu.csv + SELECT COUNT(*) FROM tb1 WHERE ts >= NOW - 10m AND col2 > 3.14 >> /home/testoutpu.csv; ``` ## SQL函数 @@ -521,7 +521,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **COUNT** ```mysql - SELECT COUNT([*|field_name]) FROM tb_name [WHERE clause] + SELECT COUNT([*|field_name]) FROM tb_name [WHERE clause]; ``` 功能说明:统计表/超级表中记录行数或某列的非空值个数。 返回结果数据类型:长整型INT64。 @@ -547,7 +547,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **AVG** ```mysql - SELECT AVG(field_name) FROM tb_name [WHERE clause] + SELECT AVG(field_name) FROM tb_name [WHERE clause]; ``` 功能说明:统计表/超级表中某列的平均值。 返回结果数据类型:双精度浮点数Double。 @@ -571,7 +571,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **TWA** ```mysql - SELECT TWA(field_name) FROM tb_name WHERE clause + SELECT TWA(field_name) FROM tb_name WHERE clause; ``` 功能说明:时间加权平均函数。统计表/超级表中某列在一段时间内的时间加权平均。 返回结果数据类型:双精度浮点数Double。 @@ -581,7 +581,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **SUM** ```mysql - SELECT SUM(field_name) FROM tb_name [WHERE clause] + SELECT SUM(field_name) FROM tb_name [WHERE clause]; ``` 功能说明:统计表/超级表中某列的和。 返回结果数据类型:双精度浮点数Double和长整型INT64。 @@ -605,7 +605,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **STDDEV** ```mysql - SELECT STDDEV(field_name) FROM tb_name [WHERE clause] + SELECT STDDEV(field_name) FROM tb_name [WHERE clause]; ``` 功能说明:统计表中某列的均方差。 返回结果数据类型:双精度浮点数Double。 @@ -623,7 +623,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **LEASTSQUARES** ```mysql - SELECT LEASTSQUARES(field_name, start_val, step_val) FROM tb_name [WHERE clause] + SELECT LEASTSQUARES(field_name, start_val, step_val) FROM tb_name [WHERE clause]; ``` 功能说明:统计表中某列的值是主键(时间戳)的拟合直线方程。start_val是自变量初始值,step_val是自变量的步长值。 返回结果数据类型:字符串表达式(斜率, 截距)。 @@ -644,7 +644,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **MIN** ```mysql - SELECT MIN(field_name) FROM {tb_name | stb_name} [WHERE clause] + SELECT MIN(field_name) FROM {tb_name | stb_name} [WHERE clause]; ``` 功能说明:统计表/超级表中某列的值最小值。 返回结果数据类型:同应用的字段。 @@ -667,7 +667,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **MAX** ```mysql - SELECT MAX(field_name) FROM { tb_name | stb_name } [WHERE clause] + SELECT MAX(field_name) FROM { tb_name | stb_name } [WHERE clause]; ``` 功能说明:统计表/超级表中某列的值最大值。 返回结果数据类型:同应用的字段。 @@ -691,7 +691,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **FIRST** ```mysql - SELECT FIRST(field_name) FROM { tb_name | stb_name } [WHERE clause] + SELECT FIRST(field_name) FROM { tb_name | stb_name } [WHERE clause]; ``` 功能说明:统计表/超级表中某列的值最先写入的非NULL值。 返回结果数据类型:同应用的字段。 @@ -715,7 +715,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **LAST** ```mysql - SELECT LAST(field_name) FROM { tb_name | stb_name } [WHERE clause] + SELECT LAST(field_name) FROM { tb_name | stb_name } [WHERE clause]; ``` 功能说明:统计表/超级表中某列的值最后写入的非NULL值。 返回结果数据类型:同应用的字段。 @@ -739,7 +739,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **TOP** ```mysql - SELECT TOP(field_name, K) FROM { tb_name | stb_name } [WHERE clause] + SELECT TOP(field_name, K) FROM { tb_name | stb_name } [WHERE clause]; ``` 功能说明: 统计表/超级表中某列的值最大*k*个非NULL值。若多于k个列值并列最大,则返回时间戳小的。 返回结果数据类型:同应用的字段。 @@ -766,7 +766,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **BOTTOM** ```mysql - SELECT BOTTOM(field_name, K) FROM { tb_name | stb_name } [WHERE clause] + SELECT BOTTOM(field_name, K) FROM { tb_name | stb_name } [WHERE clause]; ``` 功能说明:统计表/超级表中某列的值最小*k*个非NULL值。若多于k个列值并列最小,则返回时间戳小的。 返回结果数据类型:同应用的字段。 @@ -792,7 +792,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **PERCENTILE** ```mysql - SELECT PERCENTILE(field_name, P) FROM { tb_name | stb_name } [WHERE clause] + SELECT PERCENTILE(field_name, P) FROM { tb_name | stb_name } [WHERE clause]; ``` 功能说明:统计表中某列的值百分比分位数。 返回结果数据类型: 双精度浮点数Double。 @@ -810,7 +810,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **APERCENTILE** ```mysql - SELECT APERCENTILE(field_name, P) FROM { tb_name | stb_name } [WHERE clause] + SELECT APERCENTILE(field_name, P) FROM { tb_name | stb_name } [WHERE clause]; ``` 功能说明:统计表中某列的值百分比分位数,与PERCENTILE函数相似,但是返回近似结果。 返回结果数据类型: 双精度浮点数Double。 @@ -826,7 +826,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **LAST_ROW** ```mysql - SELECT LAST_ROW(field_name) FROM { tb_name | stb_name } + SELECT LAST_ROW(field_name) FROM { tb_name | stb_name }; ``` 功能说明:返回表(超级表)的最后一条记录。 返回结果数据类型:同应用的字段。 @@ -851,7 +851,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 ### 计算函数 - **DIFF** ```mysql - SELECT DIFF(field_name) FROM tb_name [WHERE clause] + SELECT DIFF(field_name) FROM tb_name [WHERE clause]; ``` 功能说明:统计表中某列的值与前一行对应值的差。 返回结果数据类型: 同应用字段。 @@ -871,7 +871,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **SPREAD** ```mysql - SELECT SPREAD(field_name) FROM { tb_name | stb_name } [WHERE clause] + SELECT SPREAD(field_name) FROM { tb_name | stb_name } [WHERE clause]; ``` 功能说明:统计表/超级表中某列的最大值和最小值之差。 返回结果数据类型: 双精度浮点数。 @@ -897,7 +897,7 @@ TDengine支持针对数据的聚合查询。提供支持的聚合和选择函数 - **四则运算** ```mysql - SELECT field_name [+|-|*|/|%][Value|field_name] FROM { tb_name | stb_name } [WHERE clause] + SELECT field_name [+|-|*|/|%][Value|field_name] FROM { tb_name | stb_name } [WHERE clause]; ``` 功能说明:统计表/超级表中某列或多列间的值加、减、乘、除、取余计算结果。 返回结果数据类型:双精度浮点数。 @@ -968,5 +968,5 @@ SELECT AVG(current),MAX(current),LEASTSQUARES(current, start_val, step_val), PER - 表名最大长度为193,每行数据最大长度16k个字符 - 列名最大长度为65,最多允许1024列,最少需要2列,第一列必须是时间戳 - 标签最多允许128个,可以0个,标签总长度不超过16k个字符 -- SQL语句最大长度65480个字符,但可通过系统配置参数maxSQLLength修改 +- SQL语句最大长度65480个字符,但可通过系统配置参数maxSQLLength修改,最长可配置为8M - 库的数目,超级表的数目、表的数目,系统不做限制,仅受系统资源限制 \ No newline at end of file diff --git a/documentation20/webdocs/markdowndocs/administrator-ch.md b/documentation20/webdocs/markdowndocs/administrator-ch.md index 6535647981b96704b0e1c562d3fabc18202648f7..cf3dcef7163cbfe0d16f36f859ca5af3691f3d14 100644 --- a/documentation20/webdocs/markdowndocs/administrator-ch.md +++ b/documentation20/webdocs/markdowndocs/administrator-ch.md @@ -78,7 +78,7 @@ TDengine集群的节点数必须大于等于副本数,否则创建表时将报 TDengine系统后台服务由taosd提供,可以在配置文件taos.cfg里修改配置参数,以满足不同场景的需求。配置文件的缺省位置在/etc/taos目录,可以通过taosd命令行执行参数-c指定配置文件目录。比如taosd -c /home/user来指定配置文件位于/home/user这个目录。 -下面仅仅列出一些重要的配置参数,更多的参数请看配置文件里的说明。各个参数的详细介绍及作用请看前述章节。**注意:配置修改后,需要重启*taosd*服务才能生效。** +下面仅仅列出一些重要的配置参数,更多的参数请看配置文件里的说明。各个参数的详细介绍及作用请看前述章节,而且这些参数的缺省配置都是工作的,一般无需设置。**注意:配置修改后,需要重启*taosd*服务才能生效。** - firstEp: taosd启动时,主动连接的集群中第一个dnode的end point, 缺省值为 localhost:6030。 - secondEp: taosd启动时,如果first连接不上,尝试连接集群中第二个dnode的end point, 缺省值为空。 @@ -94,6 +94,8 @@ TDengine系统后台服务由taosd提供,可以在配置文件taos.cfg里修 - maxSQLLength:单条SQL语句允许最长限制。默认值:65380字节。 - maxBinaryDisplayWidth:Shell中binary 和 nchar字段的显示宽度上限,超过此限制的部分将被隐藏。默认值:30。可在 shell 中通过命令 set max_binary_display_width nn动态修改此选项。 +**注意:**对于端口,TDengine会使用从serverPort起11个连续的TCP和UDP端口号,请务必在防火墙打开。因此如果是缺省配置,需要打开从6030都6040共11个端口,而且必须TCP和UDP都打开。 + 不同应用场景的数据往往具有不同的数据特征,比如保留天数、副本数、采集频次、记录大小、采集点的数量、压缩等都可完全不同。为获得在存储上的最高效率,TDengine提供如下存储相关的系统配置参数: - days:一个数据文件存储数据的时间跨度,单位为天,默认值:10。 @@ -111,7 +113,7 @@ TDengine系统后台服务由taosd提供,可以在配置文件taos.cfg里修 对于一个应用场景,可能有多种数据特征的数据并存,最佳的设计是将具有相同数据特征的表放在一个库里,这样一个应用有多个库,而每个库可以配置不同的存储参数,从而保证系统有最优的性能。TDengine允许应用在创建库时指定上述存储参数,如果指定,该参数就将覆盖对应的系统配置参数。举例,有下述SQL: ``` - create database demo days 10 cache 32 blocks 8 replica 3 + create database demo days 10 cache 32 blocks 8 replica 3; ``` 该SQL创建了一个库demo, 每个数据文件存储10天数据,内存块为32兆字节,每个VNODE占用8个内存块,副本数为3,而其他参数与系统配置完全一致。 @@ -150,25 +152,25 @@ TDengine系统的前台交互客户端应用程序为taos,它与taosd共享同 系统管理员可以在CLI界面里添加、删除用户,也可以修改密码。CLI里SQL语法如下: ``` -CREATE USER user_name PASS ‘password’ +CREATE USER user_name PASS ‘password’; ``` 创建用户,并指定用户名和密码,密码需要用单引号引起来 ``` -DROP USER user_name +DROP USER user_name; ``` 删除用户,限root用户使用 ``` -ALTER USER user_name PASS ‘password’ +ALTER USER user_name PASS ‘password’; ``` 修改用户密码, 为避免被转换为小写,密码需要用单引号引用 ``` -SHOW USERS +SHOW USERS; ``` 显示所有用户 @@ -186,7 +188,7 @@ TDengine的shell支持source filename命令,用于批量运行文件中的SQL TDengine也支持在shell对已存在的表从CSV文件中进行数据导入。CSV文件只属于一张表且CSV文件中的数据格式需与要导入表的结构相同, 在导入的时候,其语法如下 ```mysql -insert into tb1 file 'path/data.csv' +insert into tb1 file 'path/data.csv'; ``` 注意:如果CSV文件首行存在描述信息,请手动删除后再导入 @@ -237,7 +239,7 @@ TDengine提供了方便的数据库导入导出工具taosdump。用户可以将t 如果用户需要导出一个表或一个STable中的数据,可在shell中运行 ``` -select * from >> data.csv +select * from >> data.csv; ``` 这样,表tb_name中的数据就会按照CSV格式导出到文件data.csv中。 @@ -251,37 +253,37 @@ TDengine提供了方便的数据库导出工具taosdump。用户可以根据需 系统管理员可以从CLI查询系统的连接、正在进行的查询、流式计算,并且可以关闭连接、停止正在进行的查询和流式计算。CLI里SQL语法如下: ``` -SHOW CONNECTIONS +SHOW CONNECTIONS; ``` 显示数据库的连接,其中一列显示ip:port, 为连接的IP地址和端口号。 ``` -KILL CONNECTION +KILL CONNECTION ; ``` 强制关闭数据库连接,其中的connection-id是SHOW CONNECTIONS中显示的第一列的数字。 ``` -SHOW QUERIES +SHOW QUERIES; ``` 显示数据查询,其中第一列显示的以冒号隔开的两个数字为query-id,为发起该query应用连接的connection-id和查询次数。 ``` -KILL QUERY +KILL QUERY ; ``` 强制关闭数据查询,其中query-id是SHOW QUERIES中显示的 connection-id:query-no字串,如“105:2”,拷贝粘贴即可。 ``` -SHOW STREAMS +SHOW STREAMS; ``` 显示流式计算,其中第一列显示的以冒号隔开的两个数字为stream-id, 为启动该stream应用连接的connection-id和发起stream的次数。 ``` -KILL STREAM +KILL STREAM ; ``` 强制关闭流式计算,其中的中stream-id是SHOW STREAMS中显示的connection-id:stream-no字串,如103:2,拷贝粘贴即可。 diff --git a/documentation20/webdocs/markdowndocs/advanced features-ch.md b/documentation20/webdocs/markdowndocs/advanced features-ch.md index 60bd6ddfdb0bb9e69ffc64ff5b2b684d4fc9529c..688d867affdce7b60d09b0a484107588d3c2fff2 100644 --- a/documentation20/webdocs/markdowndocs/advanced features-ch.md +++ b/documentation20/webdocs/markdowndocs/advanced features-ch.md @@ -39,7 +39,7 @@ create table D1002 using meters tags ("Beijing.Haidian", 2); 我们已经知道,可以通过下面这条SQL语句以一分钟为时间窗口、30秒为前向增量统计这些电表的平均电压。 ```sql -select avg(voltage) from meters interval(1m) sliding(30s) +select avg(voltage) from meters interval(1m) sliding(30s); ``` 每次执行这条语句,都会重新计算所有数据。 @@ -47,14 +47,14 @@ select avg(voltage) from meters interval(1m) sliding(30s) 可以把上面的语句改进成下面的样子,每次使用不同的 `startTime` 并定期执行: ```sql -select avg(voltage) from meters where ts > {startTime} interval(1m) sliding(30s) +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) +create table avg_vol as select avg(voltage) from meters interval(1m) sliding(30s); ``` 会自动创建一个名为 `avg_vol` 的新表,然后每隔30秒,TDengine会增量执行 `as` 后面的 SQL 语句, @@ -80,7 +80,7 @@ taos> select * from avg_vol; 比如使用下面的SQL创建的连续查询将运行一小时,之后会自动停止。 ```sql -create table avg_vol as select avg(voltage) from meters where ts > now and ts <= now + 1h interval(1m) sliding(30s) +create table avg_vol as select avg(voltage) from meters where ts > now and ts <= now + 1h interval(1m) sliding(30s); ``` 需要说明的是,上面例子中的 `now` 是指创建连续查询的时间,而不是查询执行的时间,否则,查询就无法自动停止了。 diff --git a/documentation20/webdocs/markdowndocs/architecture-ch.md b/documentation20/webdocs/markdowndocs/architecture-ch.md index 5401c64685c21b89a630c72090c019797905615c..bfe3b55bd22fa9ec44e9305ed0924ef110ff7b15 100644 --- a/documentation20/webdocs/markdowndocs/architecture-ch.md +++ b/documentation20/webdocs/markdowndocs/architecture-ch.md @@ -20,41 +20,41 @@ 每一条记录都有设备ID,时间戳,采集的物理量(如上图中的电流、电压、相位),还有与每个设备相关的静态标签(如上述表一中的位置Location和分组groupId)。每个设备是受外界的触发,或按照设定的周期采集数据。采集的数据点是时序的,是一个数据流。 ### 数据特征 -除时序特征外,仔细研究发现,物联网、车联网、运维监测类数据还具有很多其他明显的特征。 +除时序特征外,仔细研究发现,物联网、车联网、运维监测类数据还具有很多其他明显的特征: 1. 数据高度结构化; 2. 数据极少有更新或删除操作; 3. 无需传统数据库的事务处理; 4. 相对互联网应用,写多读少; 5. 流量平稳,根据设备数量和采集频次,可以预测出来; -6. 用户关注的是一段时间的趋势,而不是某一特点时间点的值; +6. 用户关注的是一段时间的趋势,而不是某一特定时间点的值; 7. 数据有保留期限; -8. 数据的查询分析一定是基于时间段和地理区域; -9. 除存储查询外,还需要各种统计和实时计算操作; -10. 数据量巨大,一天采集的数据就可以超过100亿条。 +8. 数据的查询分析一定是基于时间段和空间区域; +9. 除存储、查询操作外,还需要各种统计和实时计算操作; +10. 数据量巨大,一天可能采集的数据就可以超过100亿条。 -充分利用上述特征,TDengine 采取了特殊的优化的存储和计算设计来处理时序数据,能将系统处理能力显著提高。 +充分利用上述特征,TDengine 采取了经特殊优化的存储和计算设计来处理时序数据,它将系统处理能力显著提高,同时大幅降低了系统运维的复杂度。 ### 关系型数据库模型 -因为采集的数据一般是结构化数据,而且为降低学习门槛,TDengine采用传统的关系型数据库模型管理数据。因此用户需要先创建库,然后创建表,之后才能插入或查询数据。TDengine采用的是结构化存储,而不是NoSQL的key-value存储。 +因为采集的数据一般是结构化数据,同时为降低学习门槛,TDengine采用传统的关系型数据库模型管理数据。因此用户需要先创建库,然后创建表,之后才能插入或查询数据。TDengine采用的是结构化存储,而不是NoSQL的key-value存储。 ### 一个数据采集点一张表 为充分利用其数据的时序性和其他数据特点,TDengine要求**对每个数据采集点单独建表**(比如有一千万个智能电表,就需创建一千万张表,上述表格中的d1001, d1002, d1003, d1004都需单独建表),用来存储这个采集点所采集的时序数据。这种设计有几大优点: -1. 能保证一个采集点的数据在存储介质上是一块一块连续的。如果读取一个时间段的数据,它能大幅减少随机读取操作,成数量级的提升读取和查询速度。 +1. 能保证一个采集点的数据在存储介质上是以块为单位连续存储的。如果读取一个时间段的数据,它能大幅减少随机读取操作,成数量级的提升读取和查询速度。 2. 由于不同采集设备产生数据的过程完全独立,每个设备的数据源是唯一的,一张表也就只有一个写入者,这样就可采用无锁方式来写,写入速度就能大幅提升。 3. 对于一个数据采集点而言,其产生的数据是时序的,因此写的操作可用追加的方式实现,进一步大幅提高数据写入速度。 如果采用传统的方式,将多个设备的数据写入一张表,由于网络延时不可控,不同设备的数据到达服务器的时序是无法保证的,写入操作是要有锁保护的,而且一个设备的数据是难以保证连续存储在一起的。**采用一个数据采集点一张表的方式,能最大程度的保证单个数据采集点的插入和查询的性能是最优的。** -TDengine 建议用数据采集点的名字(如上表中的D1001)来做表名。每个数据采集点可能同时采集多个物理量(如上表中的curent, voltage, phase),每个物理量对应一张表中的一列,数据类型可以是整型、浮点型、字符串等。除此之外,表的第一列必须是时间戳,即数据类型为 timestamp。对采集的数据,TDengine将自动按照时间戳建立索引,但对采集的物理量不建任何索引。数据是用列式存储方式保存。 +TDengine 建议用数据采集点的名字(如上表中的D1001)来做表名。每个数据采集点可能同时采集多个物理量(如上表中的curent, voltage, phase),每个物理量对应一张表中的一列,数据类型可以是整型、浮点型、字符串等。除此之外,表的第一列必须是时间戳,即数据类型为 timestamp。对采集的数据,TDengine将自动按照时间戳建立索引,但对采集的物理量不建任何索引。数据用列式存储方式保存。 ### 超级表:同一类型数据采集点的集合 -由于一个数据采集点一张表,导致表的数量巨大,难以管理,而且应用经常需要做采集点之间的聚合操作,聚合的操作也变得复杂起来。为解决这个问题,TDengine引入超级表(Super Table,简称为STable)的概念。 +由于一个数据采集点一张表,导致表的数量巨增,难以管理,而且应用经常需要做采集点之间的聚合操作,聚合的操作也变得复杂起来。为解决这个问题,TDengine引入超级表(Super Table,简称为STable)的概念。 -超级表是指某一特定类型的数据采集点的集合。同一类型的数据采集点,其表的结构是完全一样的,但每个表(数据采集点)的静态属性(标签)是不一样的。描述一个超级表(一特定类型的数据采集点),除需要定义采集量的表结构之外,还需要定义其标签的schema,标签的数据类型可以是整数、浮点数、字符串,标签可以有多个,可以事后增加、删除或修改。 如果整个系统有N个不同类型的数据采集点,就需要建立N个超级表。 +超级表是指某一特定类型的数据采集点的集合。同一类型的数据采集点,其表的结构是完全一样的,但每个表(数据采集点)的静态属性(标签)是不一样的。描述一个超级表(某一特定类型的数据采集点的结合),除需要定义采集量的表结构之外,还需要定义其标签的schema,标签的数据类型可以是整数、浮点数、字符串,标签可以有多个,可以事后增加、删除或修改。 如果整个系统有N个不同类型的数据采集点,就需要建立N个超级表。 -在TDengine的设计里,**表用来代表一个具体的数据采集点,超级表用来代表一组相同类型的数据采集点**。当为某个具体数据采集点创建表时,用户使用超级表的定义做模板,同时指定该具体采集点(表)的标签值。与传统的关系型数据库相比,表(一个数据采集点)是带有静态标签的,而且这些标签可以事后增加、删除、修改。**一张超级表包含有多张表,这些表具有相同的时序数据schema,但带有不同的标签值**。 +在TDengine的设计里,**表用来代表一个具体的数据采集点,超级表用来代表一组相同类型的数据采集点集合**。当为某个具体数据采集点创建表时,用户使用超级表的定义做模板,同时指定该具体采集点(表)的标签值。与传统的关系型数据库相比,表(一个数据采集点)是带有静态标签的,而且这些标签可以事后增加、删除、修改。**一张超级表包含有多张表,这些表具有相同的时序数据schema,但带有不同的标签值**。 当对多个具有相同数据类型的数据采集点进行聚合操作时,TDengine将先把满足标签过滤条件的表从超级表的中查找出来,然后再扫描这些表的时序数据,进行聚合操作,这样能将需要扫描的数据集大幅减少,从而大幅提高聚合计算的性能。 @@ -69,18 +69,18 @@ TDengine 分布式架构的逻辑结构图如下: **物理节点(pnode):** pnode是一独立运行、拥有自己的计算、存储和网络能力的计算机,可以是安装有OS的物理机、虚拟机或容器。物理节点由其配置的 FQDN(Fully Qualified Domain Name)来标识。 -**数据节点(dnode):** dnode 是 TDengine 服务器侧执行代码 taosd 在物理节点上的一个运行实例,一个工作的系统必须有至少一个数据节点。dnode包含零到多个逻辑的虚拟节点(VNODE),零或者至多一个逻辑的管理节点(mnode). dnode在系统中的唯一标识由实例的End Point(EP)决定。EP是dnode所在物理节点的FQDN(Fully Qualified Domain Name)和系统所配置的网络端口号(Port)的组合。通过配置不同的端口,一个物理节点(一台物理机、虚拟机或容器)可以运行多个实例,或有多个数据节点。 +**数据节点(dnode):** dnode 是 TDengine 服务器侧执行代码 taosd 在物理节点上的一个运行实例,一个工作的系统必须有至少一个数据节点。dnode包含零到多个逻辑的虚拟节点(VNODE),零或者至多一个逻辑的管理节点(mnode)。dnode在系统中的唯一标识由实例的End Point (EP )决定。EP是dnode所在物理节点的FQDN (Fully Qualified Domain Name)和系统所配置的网络端口号(Port)的组合。通过配置不同的端口,一个物理节点(一台物理机、虚拟机或容器)可以运行多个实例,或有多个数据节点。 -**虚拟节点(vnode)**: 为更好的支持数据分片、负载均衡,防止数据过热或倾斜,数据节点被虚拟化成多个虚拟节点(vnode,图中V2, V3, V4等)。每个 vnode 都是一个相对独立的工作单元,是时序数据存储的基本单元,具有独立的运行线程、内存空间与持久化存储的路径。一个 vnode 包含一定数量的表(数据采集点)。当创建一张新表时,系统会检查是否需要创建新的 vnode。一个数据节点上能创建的 vnode 的数量取决于该数据节点所在物理节点的硬件资源。一个 vnode 只属于一个DB,但一个DB可以有多个 vnode。一个 vnode 除存储的时序数据外,也保存有所包含的表的SCHEMA、标签值等。一个虚拟节点由所属的数据节点的EP,以及所属的Vgroup ID在系统内唯一标识,是由管理节点创建并管理的。 +**虚拟节点(vnode)**: 为更好的支持数据分片、负载均衡,防止数据过热或倾斜,数据节点被虚拟化成多个虚拟节点(vnode,图中V2, V3, V4等)。每个 vnode 都是一个相对独立的工作单元,是时序数据存储的基本单元,具有独立的运行线程、内存空间与持久化存储的路径。一个 vnode 包含一定数量的表(数据采集点)。当创建一张新表时,系统会检查是否需要创建新的 vnode。一个数据节点上能创建的 vnode 的数量取决于该数据节点所在物理节点的硬件资源。一个 vnode 只属于一个DB,但一个DB可以有多个 vnode。一个 vnode 除存储的时序数据外,也保存有所包含的表的SCHEMA、标签值等。一个虚拟节点由所属的数据节点的EP,以及所属的VGroup ID在系统内唯一标识,由管理节点创建并管理。 **管理节点(mnode):** 一个虚拟的逻辑单元,负责所有数据节点运行状态的监控和维护,以及节点之间的负载均衡(图中M)。同时,管理节点也负责元数据(包括用户、数据库、表、静态标签等)的存储和管理,因此也称为 Meta Node。TDengine 集群中可配置多个(最多不超过5个) mnode,它们自动构建成为一个虚拟管理节点组(图中M0, M1, M2)。mnode 间采用 master/slave 的机制进行管理,而且采取强一致方式进行数据同步, 任何数据更新操作只能在 Master 上进行。mnode 集群的创建由系统自动完成,无需人工干预。每个dnode上至多有一个mnode,由所属的数据节点的EP来唯一标识。每个dnode通过内部消息交互自动获取整个集群中所有 mnode 所在的 dnode 的EP。 -**虚拟节点组(VGroup):** 不同数据节点上的 vnode 可以组成一个虚拟节点组(vnode group)来保证系统的高可靠。虚拟节点组内采取master/slave的方式进行管理。写操作只能在 master vnode 上进行,系统采用异步复制的方式将数据同步到 slave vnode,这样确保了一份数据在多个物理节点上有拷贝。一个 vgroup 里虚拟节点个数就是数据的副本数。如果一个DB的副本数为N,系统必须有至少N个数据节点。副本数在创建DB时通过参数 replica 可以指定,缺省为1。使用 TDengine 的多副本特性,可以不再需要昂贵的磁盘阵列等存储设备,获得同样的数据高可靠性。虚拟节点组由管理节点创建、管理,并且由管理节点分配一系统唯一的ID,vnode group ID。如果两个虚拟节点的vnode group ID相同,说明他们属于同一个组,数据互为备份。虚拟节点组里虚拟节点的个数是可以动态改变的,容许只有一个,也就是没有数据复制。Vnode group ID是永远不变的,即使一个虚拟节点组被删除,它的ID也不会被收回重复利用。 +**虚拟节点组(VGroup):** 不同数据节点上的 vnode 可以组成一个虚拟节点组(vnode group)来保证系统的高可靠。虚拟节点组内采取master/slave的方式进行管理。写操作只能在 master vnode 上进行,系统采用异步复制的方式将数据同步到 slave vnode,这样确保了一份数据在多个物理节点上有拷贝。一个 vgroup 里虚拟节点个数就是数据的副本数。如果一个DB的副本数为N,系统必须有至少N个数据节点。副本数在创建DB时通过参数 replica 可以指定,缺省为1。使用 TDengine 的多副本特性,可以不再需要昂贵的磁盘阵列等存储设备,就可以获得同样的数据高可靠性。虚拟节点组由管理节点创建、管理,并且由管理节点分配一个系统唯一的ID,VGroup ID。如果两个虚拟节点的vnode group ID相同,说明他们属于同一个组,数据互为备份。虚拟节点组里虚拟节点的个数是可以动态改变的,容许只有一个,也就是没有数据复制。VGroup ID是永远不变的,即使一个虚拟节点组被删除,它的ID也不会被收回重复利用。 -**TAOSC:** taosc是TDengine给应用提供的驱动程序(driver),负责处理应用与集群的接口交互,内嵌于JDBC、ODBC driver中,或者C、Python、Go语言连接库里。应用都是通过taosc,而不是直接连接集群中的数据节点与整个集群进行交互的。这个模块负责获取并缓存元数据;将插入、查询等请求转发到正确的数据节点;在把结果返回给应用时,还需要负责最后一级的聚合、排序、过滤等操作。对于JDBC, ODBC, C/C++接口而言,这个模块是在应用所处的物理节点上运行,但消耗的资源很小。同时,为支持全分布式的RESTful接口,taosc在TDengine集群的每个dnode上都有一运行实例。 +**TAOSC:** taosc是TDengine给应用提供的驱动程序(driver),负责处理应用与集群的接口交互,内嵌于JDBC、ODBC driver中,或者C、Python、Go语言连接库里。应用都是通过taosc而不是直接连接集群中的数据节点与整个集群进行交互的。这个模块负责获取并缓存元数据;将插入、查询等请求转发到正确的数据节点;在把结果返回给应用时,还需要负责最后一级的聚合、排序、过滤等操作。对于JDBC, ODBC, C/C++接口而言,这个模块是在应用所处的物理节点上运行,但消耗的资源很小。同时,为支持全分布式的RESTful接口,taosc在TDengine集群的每个dnode上都有一运行实例。 ### 节点之间的通讯 -**通讯方式:**TDengine系统的各个节点之间的通讯是通过TCP/UDP进行的。因为考虑到物联网场景,数据写入的包一般不大,因此TDengine 除采用TCP做传输之外,还采用UDP方式,因为UDP 更加高效,而且不受链接数的限制。TDengine实现了自己的超时、重传、确认等机制,以确保UDP的可靠传输。对于数据量不到15K的数据包,采取UDP的方式进行传输,超过15K的,或者是查询类的操作,自动采取TCP的方式进行传输。同时,TDengine根据配置和数据包,会自动对数据进行压缩/解压缩,数字签名/认证等处理。对于数据节点之间的数据复制,只采用TCP方式进行数据传输。 +**通讯方式:**TDengine系统的各个节点之间的通讯是通过TCP/UDP进行的。因为考虑到物联网场景,数据写入的包一般不大,因此TDengine 除采用TCP做传输之外,还采用UDP方式,因为UDP 更加高效,而且不受连接数的限制。TDengine实现了自己的超时、重传、确认等机制,以确保UDP的可靠传输。对于数据量不到15K的数据包,采取UDP的方式进行传输,超过15K的,或者是查询类的操作,自动采取TCP的方式进行传输。同时,TDengine根据配置和数据包,会自动对数据进行压缩/解压缩,数字签名/认证等处理。对于数据节点之间的数据复制,只采用TCP方式进行数据传输。 **FQDN配置**:一个数据节点有一个或多个FQDN,可以在系统配置文件taos.cfg通过选项“fqdn"进行指定,如果没有指定,系统将自动获取FQDN。如果节点没有配置FQDN,可以直接使用IP地址作为FQDN,但不建议使用,因为IP地址可变,一旦变化,将让集群无法正常工作。一个数据节点的EP(End Point)由FQDN + Port组成。 @@ -96,12 +96,12 @@ TDengine 分布式架构的逻辑结构图如下: **重定向**:无论是dnode还是taosc,最先都是要发起与mnode的链接,但mnode是系统自动创建并维护的,因此对于用户来说,并不知道哪个dnode在运行mnode。TDengine只要求向系统中任何一个工作的dnode发起链接即可。因为任何一个正在运行的dnode,都维护有目前运行的mnode EP List。当收到一个来自新启动的dnode或taosc的链接请求,如果自己不是mnode,则将mnode EP List回复给对方,taosc或新启动的dnode收到这个list, 就重新尝试建立链接。当mnode EP List发生改变,通过节点之间的消息交互,各个数据节点就很快获取最新列表,并通知taosc。 -### 一典型的操作流程 +### 一个典型的消息流程 为解释vnode, mnode, taosc和应用之间的关系以及各自扮演的角色,下面对写入数据这个典型操作的流程进行剖析。
图 2 TDengine典型的操作流程
1. 应用通过JDBC、ODBC或其他API接口发起插入数据的请求。 -2. taosc会检查缓存,看是有保存有该表的meta data。如果有,直接到第4步。如果没有,taosc将向mnode发出get meta-data请求。 +2. taosc会检查缓存,看是否保存有该表的meta data。如果有,直接到第4步。如果没有,taosc将向mnode发出get meta-data请求。 3. mnode将该表的meta-data返回给taosc。Meta-data包含有该表的schema, 而且还有该表所属的vgroup信息(vnode ID以及所在的dnode的End Point,如果副本数为N,就有N组End Point)。如果taosc迟迟得不到mnode回应,而且存在多个mnode, taosc将向下一个mnode发出请求。 4. taosc向master vnode发起插入请求。 5. vnode插入数据后,给taosc一个应答,表示插入成功。如果taosc迟迟得不到vnode的回应,taosc会认为该节点已经离线。这种情况下,如果被插入的数据库有多个副本,taosc将向vgroup里下一个vnode发出插入请求。 @@ -109,7 +109,7 @@ TDengine 分布式架构的逻辑结构图如下: 对于第二和第三步,taosc启动时,并不知道mnode的End Point,因此会直接向配置的集群对外服务的End Point发起请求。如果接收到该请求的dnode并没有配置mnode,该dnode会在回复的消息中告知mnode EP列表,这样taosc会重新向新的mnode的EP发出获取meta-data的请求。 -对于第四和第五步,没有缓存的情况下,taosc无法知道虚拟节点组里谁是master,就假设第一个vnodeID就是master,向它发出请求。如果接收到请求的vnode并不是master,它会在回复中告知谁是master,这样taosc就向建议的master vnode发出请求。一旦得到插入成功的回复,taosc会缓存住master节点的信息。 +对于第四和第五步,没有缓存的情况下,taosc无法知道虚拟节点组里谁是master,就假设第一个vnodeID就是master,向它发出请求。如果接收到请求的vnode并不是master,它会在回复中告知谁是master,这样taosc就向建议的master vnode发出请求。一旦得到插入成功的回复,taosc会缓存master节点的信息。 上述是插入数据的流程,查询、计算的流程也完全一致。taosc把这些复杂的流程全部封装屏蔽了,对于应用来说无感知也无需任何特别处理。 @@ -134,7 +134,7 @@ TDengine存储的数据包括采集的时序数据以及库、表相关的元数 vnode(虚拟数据节点)负责为采集的时序数据提供写入、查询和计算功能。为便于负载均衡、数据恢复、支持异构环境,TDengine将一个数据节点根据其计算和存储资源切分为多个vnode。这些vnode的管理是TDengine自动完成的,对应用完全透明。 -对于单独一个数据采集点,无论其数据量多大,一个vnode(或vnode group, 如果副本数大于1)有足够的计算资源和存储资源来处理(如果每秒生成一条16字节的记录,一年产生的原始数据不到0.5G),因此TDengine将一张表(一个数据采集点)的所有数据都存放在一个vnode里,而不会让同一个采集点的数据分布到两个或多个dnode上。而且一个vnode可存储多个数据采集点(表)的数据,一个vnode可容纳的表的数目的上限为一百万。设计上,一个vnode里所有的表都属于同一个DB。一个数据节点上,一个DB拥有的vnode数目不会超过系统核的数目。 +对于单独一个数据采集点,无论其数据量多大,一个vnode(或vnode group, 如果副本数大于1)有足够的计算资源和存储资源来处理(如果每秒生成一条16字节的记录,一年产生的原始数据不到0.5G),因此TDengine将一张表(一个数据采集点)的所有数据都存放在一个vnode里,而不会让同一个采集点的数据分布到两个或多个dnode上。而且一个vnode可存储多个数据采集点(表)的数据,一个vnode可容纳的表的数目的上限为一百万。设计上,一个vnode里所有的表都属于同一个DB。一个数据节点上,除非特殊配置,一个DB拥有的vnode数目不会超过系统核的数目。 创建DB时,系统并不会马上分配资源。但当创建一张表时,系统将看是否有已经分配的vnode, 且该vnode是否有空余的表空间,如果有,立即在该有空位的vnode创建表。如果没有,系统将从集群中,根据当前的负载情况,在一个dnode上创建一新的vnode, 然后创建表。如果DB有多个副本,系统不是只创建一个vnode,而是一个vgroup(虚拟数据节点组)。系统对vnode的数目没有任何限制,仅仅受限于物理节点本身的计算和存储资源。 @@ -163,7 +163,7 @@ Master Vnode遵循下面的写入流程:
图 3 TDengine Master写入流程
1. Master vnode收到应用的数据插入请求,验证OK,进入下一步; 2. 如果系统配置参数walLevel打开(设置为2),vnode将把该请求的原始数据包写入数据库日志文件WAL,以保证TDengine能够在断电等因素导致的服务重启时从数据库日志文件中恢复数据,避免数据的丢失; -3. 如果有多个副本,vnode将把数据包转发给同一虚拟节点组内slave vnodes, 该转发包带有数据的版本号(version) +3. 如果有多个副本,vnode将把数据包转发给同一虚拟节点组内slave vnodes, 该转发包带有数据的版本号(version); 4. 写入内存,并加记录加入到skip list; 5. Master vnode返回确认信息给应用,表示写入成功。 6. 如果第2,3,4步中任何一步失败,将直接返回错误给应用。 @@ -180,7 +180,7 @@ Master Vnode遵循下面的写入流程: 与Master vnode相比,slave vnode不存在转发环节,也不存在回复确认环节,少了两步。但写内存与WAL是完全一样的。 ### 异地容灾、IDC迁移 -从上述Master和Slave流程可以看出,TDengine采用的是异步复制的方式进行数据同步。这种方式能够大幅提高写入性能,网络延时对写入速度不会有大的影响。通过配置每个物理节点的IDC和机架号,可以让一个虚拟节点组内,虚拟节点由来自不同IDC、不同机架的物理节点组成,从而实现异地容灾。因此TDengine原生支持异地容灾,无需再使用其他工具。 +从上述Master和Slave流程可以看出,TDengine采用的是异步复制的方式进行数据同步。这种方式能够大幅提高写入性能,网络延时对写入速度不会有大的影响。通过配置每个物理节点的IDC和机架号,可以保证对于一个虚拟节点组,虚拟节点由来自不同IDC、不同机架的物理节点组成,从而实现异地容灾。因此TDengine原生支持异地容灾,无需再使用其他工具。 另外一方面,TDengine支持动态修改副本数,一旦副本数增加,新加入的虚拟节点将立即进入数据同步流程,同步结束后,新加入的虚拟节点即可提供服务。而在同步过程中,master以及其他已经同步的虚拟节点都可以对外提供服务。利用这一特性,TDengine可以实现无服务中断的IDC机房迁移。只需要将新IDC的物理节点加入现有集群,等数据同步完成后,再将老的IDC的物理节点从集群中剔除即可。 @@ -276,14 +276,14 @@ SQL语句的解析和校验工作在客户端完成。解析SQL语句并生成 在TDengine中引入关键词interval来进行时间轴上固定长度时间窗口的切分,并按照时间窗口对数据进行聚合,对窗口范围内的数据按需进行聚合。例如: ```mysql -select count(*) from d1001 interval(1h) +select count(*) from d1001 interval(1h); ``` 针对d1001设备采集的数据,按照1小时的时间窗口返回每小时存储的记录数量。 在需要连续获得查询结果的应用场景下,如果给定的时间区间存在数据缺失,会导致该区间数据结果也丢失。TDengine提供策略针对时间轴聚合计算的结果进行插值,通过使用关键词Fill就能够对时间轴聚合结果进行插值。例如: ```mysql -select count(*) from d1001 interval(1h) fill(prev) +select count(*) from d1001 interval(1h) fill(prev); ``` 针对d1001设备采集数据统计每小时记录数,如果某一个小时不存在数据,这返回之前一个小时的统计数据。TDengine提供前向插值(prev)、线性插值(linear)、NULL值填充(NULL)、特定值填充(value)。 diff --git a/documentation20/webdocs/markdowndocs/cluster-ch.md b/documentation20/webdocs/markdowndocs/cluster-ch.md index 6102d5b020245a630626fb29adf3498c267c8e4f..2df6d2cb0eef9af7037076619efb2080568ff9ef 100644 --- a/documentation20/webdocs/markdowndocs/cluster-ch.md +++ b/documentation20/webdocs/markdowndocs/cluster-ch.md @@ -97,8 +97,11 @@ SHOW DNODES; ``` SHOW VGROUPS; ``` -##高可用性 -TDengine通过多副本的机制来提供系统的高可用性。副本数是与DB关联的,一个集群里可以有多个DB,根据运营的需求,每个DB可以配置不同的副本数。创建数据库时,通过参数replica 指定副本数(缺省为1)。如果副本数为1,系统的可靠性无法保证,只要数据所在的节点宕机,就将无法提供服务。集群的节点数必须大于等于副本数,否则创建表时将返回错误“more dnodes are needed"。比如下面的命令将创建副本数为3的数据库demo: +##vnode的高可用性 +TDengine通过多副本的机制来提供系统的高可用性,包括vnode和mnode的高可用性。 + +vnode的副本数是与DB关联的,一个集群里可以有多个DB,根据运营的需求,每个DB可以配置不同的副本数。创建数据库时,通过参数replica 指定副本数(缺省为1)。如果副本数为1,系统的可靠性无法保证,只要数据所在的节点宕机,就将无法提供服务。集群的节点数必须大于等于副本数,否则创建表时将返回错误“more dnodes are needed"。比如下面的命令将创建副本数为3的数据库demo: + ``` CREATE DATABASE demo replica 3; ``` @@ -108,7 +111,7 @@ CREATE DATABASE demo replica 3; 因为vnode的引入,无法简单的给出结论:“集群中过半dnode工作,集群就应该工作”。但是对于简单的情形,很好下结论。比如副本数为3,只有三个dnode,那如果仅有一个节点不工作,整个集群还是可以正常工作的,但如果有两个节点不工作,那整个集群就无法正常工作了。 -##Mnode的高可用 +##Mnode的高可用性 TDengine集群是由mnode (taosd的一个模块,逻辑节点) 负责管理的,为保证mnode的高可用,可以配置多个mnode副本,副本数由系统配置参数numOfMnodes决定,有效范围为1-3。为保证元数据的强一致性,mnode副本之间是通过同步的方式进行数据复制的。 一个集群有多个dnode, 但一个dnode至多运行一个mnode实例。多个dnode情况下,哪个dnode可以作为mnode呢?这是完全由系统根据整个系统资源情况,自动指定的。用户可通过CLI程序taos,在TDengine的console里,执行如下命令: @@ -120,6 +123,8 @@ SHOW MNODES; 为保证mnode服务的高可用性,numOfMnodes必须设置为2或更大。因为mnode保存的元数据必须是强一致的,如果numOfMnodes大于2,复制参数quorum自动设为2,也就是说,至少要保证有两个副本写入数据成功,才通知客户端应用写入成功。 +**注意:**一个TDengine高可用系统,无论是vnode还是mnode, 都必须配置多个副本。 + ##负载均衡 有三种情况,将触发负载均衡,而且都无需人工干预。 diff --git a/documentation20/webdocs/markdowndocs/insert-ch.md b/documentation20/webdocs/markdowndocs/insert-ch.md index b6def1d7d08a66eae84c733218f0ff72b89d6866..c4eb6e39eb027ef6ec7b6c52c1c18f89098f28fa 100644 --- a/documentation20/webdocs/markdowndocs/insert-ch.md +++ b/documentation20/webdocs/markdowndocs/insert-ch.md @@ -22,7 +22,7 @@ INSERT INTO d1001 VALUES (1538548685000, 10.3, 219, 0.31) (1538548695000, 12.6, **Tips:** -- 要提高写入效率,需要批量写入。一批写入的记录条数越多,插入效率就越高。但一条记录不能超过16K,一条SQL语句总长度不能超过64K(可通过参数maxSQLLength配置)。 +- 要提高写入效率,需要批量写入。一批写入的记录条数越多,插入效率就越高。但一条记录不能超过16K,一条SQL语句总长度不能超过64K(可通过参数maxSQLLength配置,最大可配置为8M)。 - TDengine支持多线程同时写入,要进一步提高写入速度,一个客户端需要打开20个以上的线程同时写。但线程数达到一定数量后,无法再提高,甚至还会下降,因为线程切频繁切换,带来额外开销。 ## Prometheus直接写入 @@ -49,7 +49,8 @@ go build 参考Prometheus的[配置文档](https://prometheus.io/docs/prometheus/latest/configuration/configuration/),在Prometheus的配置文件中的部分,增加以下配置 - url: bailongma API服务提供的URL, 参考下面的blm_prometheus启动示例章节 - 启动Prometheus后,可以通过taos客户端查询确认数据是否成功写入。 + +启动Prometheus后,可以通过taos客户端查询确认数据是否成功写入。 ### 启动blm_prometheus程序 blm_prometheus程序有以下选项,在启动blm_prometheus程序时可以通过设定这些选项来设定blm_prometheus的配置。 @@ -127,7 +128,7 @@ go build 一切正常的情况下,就会在对应的目录下生成一个blm_telegraf的可执行程序。 ### 安装Telegraf -目前TDengine支持Telegraf 1.7.4以上的版本。用户可以根据当前的操作系统,到Telegraf官网下载安装包,并执行安装。下载地址如下:https://portal.influxdata.com/downloads +目前TDengine支持Telegraf 1.7.4以上的版本。用户可以根据当前的操作系统,到Telegraf官网下载安装包,并执行安装。下载地址如下:https://portal.influxdata.com/downloads ### 配置Telegraf 修改Telegraf配置文件/etc/telegraf/telegraf.conf中与TDengine有关的配置项。 diff --git a/src/common/inc/tglobal.h b/src/common/inc/tglobal.h index b8d7297ee0caf3157890061fed757fbe7431231a..a04b5f32b94b61a49ab3934be2b6e425c00df66a 100644 --- a/src/common/inc/tglobal.h +++ b/src/common/inc/tglobal.h @@ -32,6 +32,7 @@ extern uint16_t tsSyncPort; extern int32_t tsStatusInterval; extern int32_t tsNumOfMnodes; extern int32_t tsEnableVnodeBak; +extern int32_t tsEnableTelemetryReporting; // common extern int tsRpcTimer; diff --git a/src/common/src/tglobal.c b/src/common/src/tglobal.c index 0d071454a7e163996c077fc27e1ddcd85682e7d6..c3dc078428cfa66298444d29ff669fc05e7f65fc 100644 --- a/src/common/src/tglobal.c +++ b/src/common/src/tglobal.c @@ -40,6 +40,7 @@ uint16_t tsSyncPort = 6040; int32_t tsStatusInterval = 1; // second int32_t tsNumOfMnodes = 3; int32_t tsEnableVnodeBak = 1; +int32_t tsEnableTelemetryReporting = 1; // common int32_t tsRpcTimer = 1000; @@ -430,6 +431,16 @@ static void doInitGlobalConfig() { cfg.unitType = TAOS_CFG_UTYPE_NONE; taosInitConfigOption(cfg); + cfg.option = "telemetryReporting"; + cfg.ptr = &tsEnableTelemetryReporting; + cfg.valType = TAOS_CFG_VTYPE_INT32; + cfg.cfgType = TSDB_CFG_CTYPE_B_CONFIG | TSDB_CFG_CTYPE_B_SHOW; + cfg.minValue = 0; + cfg.maxValue = 1; + cfg.ptrLength = 1; + cfg.unitType = TAOS_CFG_UTYPE_NONE; + taosInitConfigOption(cfg); + cfg.option = "balance"; cfg.ptr = &tsEnableBalance; cfg.valType = TAOS_CFG_VTYPE_INT32; diff --git a/src/dnode/inc/dnodeTelemetry.h b/src/dnode/inc/dnodeTelemetry.h new file mode 100644 index 0000000000000000000000000000000000000000..6fb62556ae50ec3759c7dc2260d16f36d1daf025 --- /dev/null +++ b/src/dnode/inc/dnodeTelemetry.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2020 TAOS Data, Inc. + * + * 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 . + */ + +#ifndef TDENGINE_DNODE_TELEMETRY_H +#define TDENGINE_DNODE_TELEMETRY_H + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t dnodeInitTelemetry(); +void dnodeCleanupTelemetry(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/dnode/src/dnodeMain.c b/src/dnode/src/dnodeMain.c index 6476bb78314f76ae02cbaae0dd04547bc7565313..f521fbe02bf611295fae22212080d97840c085a4 100644 --- a/src/dnode/src/dnodeMain.c +++ b/src/dnode/src/dnodeMain.c @@ -30,6 +30,7 @@ #include "dnodeMWrite.h" #include "dnodeMPeer.h" #include "dnodeShell.h" +#include "dnodeTelemetry.h" static int32_t dnodeInitStorage(); static void dnodeCleanupStorage(); @@ -47,18 +48,19 @@ typedef struct { } SDnodeComponent; static const SDnodeComponent tsDnodeComponents[] = { - {"storage", dnodeInitStorage, dnodeCleanupStorage}, - {"vread", dnodeInitVnodeRead, dnodeCleanupVnodeRead}, - {"vwrite", dnodeInitVnodeWrite, dnodeCleanupVnodeWrite}, - {"mread", dnodeInitMnodeRead, dnodeCleanupMnodeRead}, - {"mwrite", dnodeInitMnodeWrite, dnodeCleanupMnodeWrite}, - {"mpeer", dnodeInitMnodePeer, dnodeCleanupMnodePeer}, - {"client", dnodeInitClient, dnodeCleanupClient}, - {"server", dnodeInitServer, dnodeCleanupServer}, - {"mgmt", dnodeInitMgmt, dnodeCleanupMgmt}, - {"modules", dnodeInitModules, dnodeCleanupModules}, - {"mgmt-tmr",dnodeInitMgmtTimer, dnodeCleanupMgmtTimer}, - {"shell", dnodeInitShell, dnodeCleanupShell} + {"storage", dnodeInitStorage, dnodeCleanupStorage}, + {"vread", dnodeInitVnodeRead, dnodeCleanupVnodeRead}, + {"vwrite", dnodeInitVnodeWrite, dnodeCleanupVnodeWrite}, + {"mread", dnodeInitMnodeRead, dnodeCleanupMnodeRead}, + {"mwrite", dnodeInitMnodeWrite, dnodeCleanupMnodeWrite}, + {"mpeer", dnodeInitMnodePeer, dnodeCleanupMnodePeer}, + {"client", dnodeInitClient, dnodeCleanupClient}, + {"server", dnodeInitServer, dnodeCleanupServer}, + {"mgmt", dnodeInitMgmt, dnodeCleanupMgmt}, + {"modules", dnodeInitModules, dnodeCleanupModules}, + {"mgmt-tmr", dnodeInitMgmtTimer, dnodeCleanupMgmtTimer}, + {"shell", dnodeInitShell, dnodeCleanupShell}, + {"telemetry", dnodeInitTelemetry, dnodeCleanupTelemetry}, }; static int dnodeCreateDir(const char *dir) { diff --git a/src/dnode/src/dnodeTelemetry.c b/src/dnode/src/dnodeTelemetry.c new file mode 100644 index 0000000000000000000000000000000000000000..ce041d83036c83a1981b54621672d2bf91c39789 --- /dev/null +++ b/src/dnode/src/dnodeTelemetry.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2020 TAOS Data, Inc. + * + * 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 . + */ + +#define _DEFAULT_SOURCE +#include "os.h" +#include "taoserror.h" +#include "tglobal.h" +#include "tutil.h" +#include "osTime.h" +#include "tsocket.h" +#include "tbuffer.h" +#include "mnode.h" +#include "mnodeCluster.h" +#include "mnodeSdb.h" +#include "dnode.h" +#include "dnodeInt.h" +#include "dnodeTelemetry.h" + +static sem_t tsExitSem; +static pthread_t tsTelemetryThread; + +#define TELEMETRY_SERVER "telemetry.taosdata.com" +#define TELEMETRY_PORT 80 +#define REPORT_INTERVAL 86400 + +static void beginObject(SBufferWriter* bw) { + tbufWriteChar(bw, '{'); +} + +static void closeObject(SBufferWriter* bw) { + size_t len = tbufTell(bw); + if (tbufGetData(bw, false)[len - 1] == ',') { + tbufWriteCharAt(bw, len - 1, '}'); + } else { + tbufWriteChar(bw, '}'); + } + tbufWriteChar(bw, ','); +} + +#if 0 +static void beginArray(SBufferWriter* bw) { + tbufWriteChar(bw, '['); +} + +static void closeArray(SBufferWriter* bw) { + size_t len = tbufTell(bw); + if (tbufGetData(bw, false)[len - 1] == ',') { + tbufWriteCharAt(bw, len - 1, ']'); + } else { + tbufWriteChar(bw, ']'); + } + tbufWriteChar(bw, ','); +} +#endif + +static void writeString(SBufferWriter* bw, const char* str) { + tbufWriteChar(bw, '"'); + tbufWrite(bw, str, strlen(str)); + tbufWriteChar(bw, '"'); +} + +static void addIntField(SBufferWriter* bw, const char* k, int64_t v) { + writeString(bw, k); + tbufWriteChar(bw, ':'); + char buf[32]; + sprintf(buf, "%" PRId64, v); + tbufWrite(bw, buf, strlen(buf)); + tbufWriteChar(bw, ','); +} + +static void addStringField(SBufferWriter* bw, const char* k, const char* v) { + writeString(bw, k); + tbufWriteChar(bw, ':'); + writeString(bw, v); + tbufWriteChar(bw, ','); +} + +static void addCpuInfo(SBufferWriter* bw) { + char * line = NULL; + size_t size = 0; + int done = 0; + + FILE* fp = fopen("/proc/cpuinfo", "r"); + if (fp == NULL) { + return; + } + + while (done != 3 && (size = getline(&line, &size, fp)) != -1) { + line[size - 1] = '\0'; + if (((done&1) == 0) && strncmp(line, "model name", 10) == 0) { + const char* v = strchr(line, ':') + 2; + addStringField(bw, "cpuModel", v); + done |= 1; + } else if (((done&2)==0) && strncmp(line, "cpu cores", 9) == 0) { + const char* v = strchr(line, ':') + 2; + writeString(bw, "numOfCpu"); + tbufWriteChar(bw, ':'); + tbufWrite(bw, v, strlen(v)); + tbufWriteChar(bw, ','); + done |= 2; + } + } + + free(line); + fclose(fp); +} + +static void addOsInfo(SBufferWriter* bw) { + char * line = NULL; + size_t size = 0; + + FILE* fp = fopen("/etc/os-release", "r"); + if (fp == NULL) { + return; + } + + while ((size = getline(&line, &size, fp)) != -1) { + line[size - 1] = '\0'; + if (strncmp(line, "PRETTY_NAME", 11) == 0) { + const char* p = strchr(line, '=') + 1; + if (*p == '"') { + p++; + line[size - 2] = 0; + } + addStringField(bw, "os", p); + break; + } + } + + free(line); + fclose(fp); +} + +static void addMemoryInfo(SBufferWriter* bw) { + char * line = NULL; + size_t size = 0; + + FILE* fp = fopen("/proc/meminfo", "r"); + if (fp == NULL) { + return; + } + + while ((size = getline(&line, &size, fp)) != -1) { + line[size - 1] = '\0'; + if (strncmp(line, "MemTotal", 8) == 0) { + const char* p = strchr(line, ':') + 1; + while (*p == ' ') p++; + addStringField(bw, "memory", p); + break; + } + } + + free(line); + fclose(fp); +} + +static void addVersionInfo(SBufferWriter* bw) { + addStringField(bw, "version", version); + addStringField(bw, "buildInfo", buildinfo); + addStringField(bw, "gitInfo", gitinfo); + //addStringField(&bw, "installAt", "2020-08-01T00:00:00Z"); +} + +static void addRuntimeInfo(SBufferWriter* bw) { + addIntField(bw, "clusterId", mnodeGetClusterId()); + // addIntField(&bw, "numOfDnode", 1); + // addIntField(&bw, "numOfVnode", 1); + // addIntField(&bw, "numOfStable", 1); + // addIntField(&bw, "numOfTable", 1); + // addIntField(&bw, "numOfRows", 1); + // addStringField(&bw, "startAt", "2020-08-01T00:00:00Z"); + // addStringField(&bw, "memoryUsage", "10240 kB"); + // addStringField(&bw, "diskUsage", "10240 MB"); +} + +static void sendTelemetryReport() { + char buf[128]; + uint32_t ip = taosGetIpFromFqdn(TELEMETRY_SERVER); + if (ip == 0xffffffff) { + dError("failed to get IP address of " TELEMETRY_SERVER ", reason:%s", strerror(errno)); + return; + } + int fd = taosOpenTcpClientSocket(ip, TELEMETRY_PORT, 0); + if (fd < 0) { + dError("failed to create socket for telemetry, reason:%s", strerror(errno)); + return; + } + + SBufferWriter bw = tbufInitWriter(NULL, false); + beginObject(&bw); + addIntField(&bw, "reportVersion", 1); + addOsInfo(&bw); + addCpuInfo(&bw); + addMemoryInfo(&bw); + addVersionInfo(&bw); + addRuntimeInfo(&bw); + closeObject(&bw); + + const char* header = "POST /report HTTP/1.1\n" + "Host: " TELEMETRY_SERVER "\n" + "Content-Type: application/json\n" + "Content-Length: "; + + taosWriteSocket(fd, header, strlen(header)); + int contLen = tbufTell(&bw) - 1; + sprintf(buf, "%d\n\n", contLen); + taosWriteSocket(fd, buf, strlen(buf)); + taosWriteSocket(fd, tbufGetData(&bw, false), contLen); + tbufCloseWriter(&bw); + + taosReadSocket(fd, buf, 10); // read something to avoid nginx error 499 + taosCloseSocket(fd); +} + +static void* telemetryThread(void* param) { + int timeToWait = 0; + while (1) { + if (timeToWait <= 0) { + if (sdbIsMaster()) { + sendTelemetryReport(); + } + timeToWait = REPORT_INTERVAL; + } + + int startAt = taosGetTimestampSec(); + struct timespec timeout = {.tv_sec = timeToWait, .tv_nsec = 0}; + if (sem_timedwait(&tsExitSem, &timeout) == 0) { + break; + } + timeToWait -= (taosGetTimestampSec() - startAt); + } + + return NULL; +} + +int32_t dnodeInitTelemetry() { + if (!tsEnableTelemetryReporting) { + return 0; + } + + if (sem_init(&tsExitSem, 0, 0) == -1) { + // just log the error, it is ok for telemetry to fail + dError("failed to create semaphore for telemetry, reason:%s", strerror(errno)); + return 0; + } + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + int32_t code = pthread_create(&tsTelemetryThread, &attr, telemetryThread, NULL); + pthread_attr_destroy(&attr); + if (code != 0) { + dError("failed to create telemetry thread, reason:%s", strerror(errno)); + } + + return 0; +} + +void dnodeCleanupTelemetry() { + if (!tsEnableTelemetryReporting) { + return; + } + + if (tsTelemetryThread) { + sem_post(&tsExitSem); + pthread_join(tsTelemetryThread, NULL); + sem_destroy(&tsExitSem); + } +} \ No newline at end of file diff --git a/src/mnode/src/mnodeDb.c b/src/mnode/src/mnodeDb.c index c7e3085e69285570e7b6700507c4b41a62c926e4..48acc6787c0d601a1efceb182ea0da48da407a13 100644 --- a/src/mnode/src/mnodeDb.c +++ b/src/mnode/src/mnodeDb.c @@ -458,6 +458,7 @@ void mnodeRemoveVgroupFromDb(SVgObj *pVgroup) { pDb->vgList[v2] = pDb->vgList[v2 + 1]; } pDb->numOfVgroups--; + pDb->vgList[pDb->numOfVgroups] = NULL; break; } } diff --git a/src/mnode/src/mnodeSdb.c b/src/mnode/src/mnodeSdb.c index a4c2c60aa34f513550e64c2a6c83d4589dbc512a..301dd41cbc5e08917b5adf27643822da574a5abe 100644 --- a/src/mnode/src/mnodeSdb.c +++ b/src/mnode/src/mnodeSdb.c @@ -487,7 +487,13 @@ static int32_t sdbInsertHash(SSdbTable *pTable, SSdbOper *pOper) { sdbDebug("table:%s, insert record:%s to hash, rowSize:%d numOfRows:%" PRId64 ", msg:%p", pTable->tableName, sdbGetKeyStrFromObj(pTable, pOper->pObj), pOper->rowSize, pTable->numOfRows, pOper->pMsg); - (*pTable->insertFp)(pOper); + int32_t code = (*pTable->insertFp)(pOper); + if (code != TSDB_CODE_SUCCESS) { + sdbError("table:%s, failed to insert record:%s to hash, remove it", pTable->tableName, + sdbGetKeyStrFromObj(pTable, pOper->pObj)); + sdbDeleteHash(pTable, pOper); + } + return TSDB_CODE_SUCCESS; } diff --git a/src/mnode/src/mnodeTable.c b/src/mnode/src/mnodeTable.c index 1d14ec030714f5f2c6ab9703fbdc67cc654155e8..5837aed25908751e7925276ed87ba6af9291af29 100644 --- a/src/mnode/src/mnodeTable.c +++ b/src/mnode/src/mnodeTable.c @@ -2351,14 +2351,16 @@ static void mnodeProcessCreateChildTableRsp(SRpcMsg *rpcMsg) { // if the vgroup is already dropped from hash, it can't be accquired by pTable->vgId // so the refCount of vgroup can not be decreased - SVgObj *pVgroup = mnodeGetVgroup(pTable->vgId); - if (pVgroup == NULL) { - mnodeRemoveTableFromVgroup(pVgroup, pTable); - } - mnodeDecVgroupRef(pVgroup); + // SVgObj *pVgroup = mnodeGetVgroup(pTable->vgId); + // if (pVgroup == NULL) { + // mnodeRemoveTableFromVgroup(mnodeMsg->pVgroup, pTable); + // } + // mnodeDecVgroupRef(pVgroup); mnodeSendDropChildTableMsg(mnodeMsg, false); rpcMsg->code = TSDB_CODE_SUCCESS; + dnodeSendRpcMnodeWriteRsp(mnodeMsg, rpcMsg->code); + return; } if (rpcMsg->code == TSDB_CODE_SUCCESS || rpcMsg->code == TSDB_CODE_TDB_TABLE_ALREADY_EXIST) { diff --git a/src/mnode/src/mnodeVgroup.c b/src/mnode/src/mnodeVgroup.c index e46da1d8920c12a2a6b8ead97fa625df43180efe..942372024124791c1cd9a8242ce381cc03dedd09 100644 --- a/src/mnode/src/mnodeVgroup.c +++ b/src/mnode/src/mnodeVgroup.c @@ -83,11 +83,12 @@ static int32_t mnodeVgroupActionInsert(SSdbOper *pOper) { // refer to db SDbObj *pDb = mnodeGetDb(pVgroup->dbName); if (pDb == NULL) { + mError("vgId:%d, db:%s is not exist while insert into hash", pVgroup->vgId, pVgroup->dbName); return TSDB_CODE_MND_INVALID_DB; } if (pDb->status != TSDB_DB_STATUS_READY) { - mError("db:%s, status:%d, in dropping", pDb->name, pDb->status); + mError("vgId:%d, db:%s status:%d, in dropping", pVgroup->vgId, pDb->name, pDb->status); return TSDB_CODE_MND_DB_IN_DROPPING; } @@ -116,10 +117,12 @@ static int32_t mnodeVgroupActionInsert(SSdbOper *pOper) { static int32_t mnodeVgroupActionDelete(SSdbOper *pOper) { SVgObj *pVgroup = pOper->pObj; - if (pVgroup->pDb != NULL) { - mnodeRemoveVgroupFromDb(pVgroup); + if (pVgroup->pDb == NULL) { + mError("vgId:%d, db:%s is not exist while insert into hash", pVgroup->vgId, pVgroup->dbName); + return TSDB_CODE_MND_VGROUP_NOT_EXIST; } + mnodeRemoveVgroupFromDb(pVgroup); mnodeDecDbRef(pVgroup->pDb); for (int32_t i = 0; i < pVgroup->numOfVnodes; ++i) { @@ -446,6 +449,12 @@ int32_t mnodeGetAvailableVgroup(SMnodeMsg *pMsg, SVgObj **ppVgroup, int32_t *pSi } } + if (pDb->numOfVgroups < 1) { + mDebug("app:%p:%p, db:%s, failed create new vgroup since:%s, numOfVgroups:%d maxVgroupsPerDb:%d ", + pMsg->rpcMsg.ahandle, pMsg, pDb->name, tstrerror(code), pDb->numOfVgroups, maxVgroupsPerDb); + return code; + } + SVgObj *pVgroup = pDb->vgList[0]; if (pVgroup == NULL) { pthread_mutex_unlock(&pDb->mutex); @@ -517,6 +526,19 @@ static int32_t mnodeCreateVgroupCb(SMnodeMsg *pMsg, int32_t code) { dnodeReprocessMnodeWriteMsg(pMsg); return TSDB_CODE_MND_ACTION_IN_PROGRESS; + // if (pVgroup->status == TAOS_VG_STATUS_CREATING || pVgroup->status == TAOS_VG_STATUS_READY) { + // mInfo("app:%p:%p, vgId:%d, is created in sdb, db:%s replica:%d", pMsg->rpcMsg.ahandle, pMsg, pVgroup->vgId, + // pDb->name, pVgroup->numOfVnodes); + // pVgroup->status = TAOS_VG_STATUS_READY; + // SSdbOper desc = {.type = SDB_OPER_GLOBAL, .pObj = pVgroup, .table = tsVgroupSdb}; + // (void)sdbUpdateRow(&desc); + // dnodeReprocessMnodeWriteMsg(pMsg); + // return TSDB_CODE_MND_ACTION_IN_PROGRESS; + // } else { + // mError("app:%p:%p, vgId:%d, is created in sdb, db:%s replica:%d, but vgroup is dropping", pMsg->rpcMsg.ahandle, + // pMsg, pVgroup->vgId, pDb->name, pVgroup->numOfVnodes); + // return TSDB_CODE_MND_VGROUP_NOT_EXIST; + // } } } @@ -955,7 +977,7 @@ void mnodeSendDropVnodeMsg(int32_t vgId, SRpcEpSet *epSet, void *ahandle) { static void mnodeSendDropVgroupMsg(SVgObj *pVgroup, void *ahandle) { pVgroup->status = TAOS_VG_STATUS_DROPPING; // deleting - mDebug("vgId:%d, send drop all vnodes msg, ahandle:%p", pVgroup->vgId, ahandle); + mDebug("vgId:%d, send drop all vnodes msg, ahandle:%p db:%s", pVgroup->vgId, ahandle, pVgroup->dbName); for (int32_t i = 0; i < pVgroup->numOfVnodes; ++i) { SRpcEpSet epSet = mnodeGetEpSetFromIp(pVgroup->vnodeGid[i].pDnode->dnodeEp); mDebug("vgId:%d, send drop vnode msg to dnode:%d, ahandle:%p", pVgroup->vgId, pVgroup->vnodeGid[i].dnodeId, ahandle); @@ -1117,6 +1139,7 @@ void mnodeSendDropAllDbVgroupsMsg(SDbObj *pDropDb) { } mnodeDecVgroupRef(pVgroup); + numOfVgroups++; } sdbFreeIter(pIter); diff --git a/src/util/src/tqueue.c b/src/util/src/tqueue.c index d9abf0d7c358fec3c6efc8185b21ffc504507fdb..e2f061445571a41ad2e9ce9aa60070cf9a41fcee 100644 --- a/src/util/src/tqueue.c +++ b/src/util/src/tqueue.c @@ -61,6 +61,7 @@ taos_queue taosOpenQueue() { pthread_mutex_init(&queue->mutex, NULL); + uTrace("queue:%p is openned", queue); return queue; } @@ -89,6 +90,8 @@ void taosCloseQueue(taos_queue param) { pthread_mutex_unlock(&queue->mutex); pthread_mutex_destroy(&queue->mutex); free(queue); + + uTrace("queue:%p is closed", queue); } void *taosAllocateQitem(int size) { @@ -161,7 +164,7 @@ int taosReadQitem(taos_queue param, int *type, void **pitem) { } void *taosAllocateQall() { - void *p = malloc(sizeof(STaosQall)); + void *p = calloc(sizeof(STaosQall), 1); return p; } @@ -230,15 +233,31 @@ taos_qset taosOpenQset() { pthread_mutex_init(&qset->mutex, NULL); tsem_init(&qset->sem, 0, 0); + uTrace("qset:%p is openned", qset); return qset; } void taosCloseQset(taos_qset param) { if (param == NULL) return; STaosQset *qset = (STaosQset *)param; + +#if 0 + // remove all the queues from qset + pthread_mutex_lock(&qset->mutex); + while (qset->head) { + STaosQueue *queue = qset->head; + qset->head = qset->head->next; + + queue->qset = NULL; + queue->next = NULL; + } + pthread_mutex_unlock(&qset->mutex); +#endif + pthread_mutex_destroy(&qset->mutex); tsem_destroy(&qset->sem); free(qset); + uTrace("qset:%p is closed", qset); } // tsem_post 'qset->sem', so that reader threads waiting for it @@ -269,6 +288,7 @@ int taosAddIntoQset(taos_qset p1, taos_queue p2, void *ahandle) { pthread_mutex_unlock(&qset->mutex); + uTrace("queue:%p is added into qset:%p", queue, qset); return 0; } @@ -288,6 +308,7 @@ void taosRemoveFromQset(taos_qset p1, taos_queue p2) { STaosQueue *prev = qset->head; tqueue = qset->head->next; while (tqueue) { + assert(tqueue->qset); if (tqueue== queue) { prev->next = tqueue->next; break; @@ -305,11 +326,14 @@ void taosRemoveFromQset(taos_qset p1, taos_queue p2) { pthread_mutex_lock(&queue->mutex); atomic_sub_fetch_32(&qset->numOfItems, queue->numOfItems); queue->qset = NULL; + queue->next = NULL; pthread_mutex_unlock(&queue->mutex); } } pthread_mutex_unlock(&qset->mutex); + + uTrace("queue:%p is removed from qset:%p", queue, qset); } int taosGetQueueNumber(taos_qset param) { diff --git a/tests/comparisonTest/opentsdb/opentsdbtest/pom.xml b/tests/comparisonTest/opentsdb/opentsdbtest/pom.xml index 52067343ef051799150c65f91643a6475ad68157..ae288ae294a8569d10c5c98b3e82e2833ef4ed68 100644 --- a/tests/comparisonTest/opentsdb/opentsdbtest/pom.xml +++ b/tests/comparisonTest/opentsdb/opentsdbtest/pom.xml @@ -206,16 +206,6 @@ slf4j-api 1.7.25 - - org.slf4j - slf4j-log4j12 - 1.7.21 - - - org.slf4j - log4j-over-slf4j - 1.7.7 - org.apache.logging.log4j diff --git a/tests/comparisonTest/opentsdb/opentsdbtest/src/main/java/com/opentsdb/test/OpentsdbTest.java b/tests/comparisonTest/opentsdb/opentsdbtest/src/main/java/com/opentsdb/test/OpentsdbTest.java index e1a20dfb4908182e3471cac834c57b477b124998..4d02922766d6a3424a21b127ebe888b4b582dbab 100644 --- a/tests/comparisonTest/opentsdb/opentsdbtest/src/main/java/com/opentsdb/test/OpentsdbTest.java +++ b/tests/comparisonTest/opentsdb/opentsdbtest/src/main/java/com/opentsdb/test/OpentsdbTest.java @@ -1,6 +1,5 @@ import com.stumbleupon.async.Callback; import com.stumbleupon.async.Deferred; -import lombok.extern.slf4j.Slf4j; import net.opentsdb.core.TSDB; import net.opentsdb.uid.NoSuchUniqueName; @@ -40,13 +39,19 @@ import java.util.concurrent.*; import java.math.*; import java.lang.reflect.Method; +import org.apache.log4j.Logger; +import org.apache.log4j.LogManager; +import org.apache.log4j.Level; -public class OpentsdbTest{ +public class OpentsdbTest{ //static { System.setProperty("logback.configurationFile", "/home/ubuntu/fang/opentsdb/opentsdbtest/logback.xml");} static { System.setProperty("logback.configurationFile", "/etc/opentsdb/logback.xml");} public static void main(String args[]) { + + Logger logger = LogManager.getLogger(OpentsdbTest.class); + logger.setLevel(Level.OFF); // begin to parse argument String datadir = "/home/ubuntu/testdata"; String sqlchoice = "q1"; @@ -156,7 +161,7 @@ public class OpentsdbTest{ } switch (sqlchoice) { case "q1": - get_url = "http://192.168.1.114:4242/api/query?"; + get_url = "http://127.0.0.1:4242/api/query?"; /* get_url = get_url + "start=1563249700&m=none:temperature{devgroup="; get_url = get_url + String.valueOf(ig-10) +"}"; diff --git a/tests/comparisonTest/opentsdb/opentsdbtest/src/main/java/com/opentsdb/test/WriteThread.java b/tests/comparisonTest/opentsdb/opentsdbtest/src/main/java/com/opentsdb/test/WriteThread.java index f6f9ef69055fd9eee591102b598adb31ea09410c..569925ccc61b304267ac68a4a33d1ba20302c6d7 100644 --- a/tests/comparisonTest/opentsdb/opentsdbtest/src/main/java/com/opentsdb/test/WriteThread.java +++ b/tests/comparisonTest/opentsdb/opentsdbtest/src/main/java/com/opentsdb/test/WriteThread.java @@ -1,6 +1,5 @@ import com.stumbleupon.async.Callback; import com.stumbleupon.async.Deferred; -import lombok.extern.slf4j.Slf4j; import net.opentsdb.core.TSDB; import net.opentsdb.uid.NoSuchUniqueName; @@ -64,7 +63,7 @@ public class WriteThread extends Thread { public void run() { StringEntity stringEntity; String port = "4242"; - String put_url = "http://192.168.1.114:"+port+"/api/put?summary"; + String put_url = "http://127.0.0.1:"+port+"/api/put?summary"; try (CloseableHttpClient httpclient = HttpClients.createDefault()) { /* httpclient.getHttpConnectionManager().getParams() @@ -152,4 +151,4 @@ public class WriteThread extends Thread { System.out.println("failed to connect"); } }//end run -}//end class \ No newline at end of file +}//end class diff --git a/tests/perftest-scripts/cassandraTestWriteLoop.sh b/tests/perftest-scripts/cassandraTestWriteLoop.sh index a218f0d0a04d30870e50eb06542c76cbd508662b..d20b6204124696b994817c141f1cfc310a4f062d 100755 --- a/tests/perftest-scripts/cassandraTestWriteLoop.sh +++ b/tests/perftest-scripts/cassandraTestWriteLoop.sh @@ -17,7 +17,7 @@ function runTest { for r in ${!rowsPerRequest[@]}; do for c in `seq 1 $clients`; do - avgRPR[$r, $c]=0 + avgRPR[$r,$c]=0 done done @@ -46,7 +46,6 @@ function runTest { avgRPR[$r,$c]=`echo "scale=4; $totalRPR / $NUM_LOOP" | bc` printTo "r:$r c:$c avgRPR:${avgRPR[$r,$c]}" done - done printf "R/R, " @@ -79,9 +78,14 @@ while : ; do verbose=true shift ;; + -n) + NUM_LOOP=$2 + shift 2;; + -c) clients=$2 shift 2;; + *) break ;; esac diff --git a/tests/perftest-scripts/opentsdbTestWriteLoop.sh b/tests/perftest-scripts/opentsdbTestWriteLoop.sh new file mode 100755 index 0000000000000000000000000000000000000000..8bbd5aa9bd7a63294a04ff50714f3744ddb8b4c4 --- /dev/null +++ b/tests/perftest-scripts/opentsdbTestWriteLoop.sh @@ -0,0 +1,99 @@ +#!/bin/bash + +DATA_DIR=/mnt/root/testdata +NUM_LOOP=1 +NUM_OF_FILES=100 + +rowsPerRequest=(1 10 50 100 500 1000 2000) + +function printTo { + if $verbose ; then + echo $1 + fi +} + +function runTest { + declare -A avgRPR + + for r in ${!rowsPerRequest[@]}; do + for c in `seq 1 $clients`; do + avgRPR[$r, $c]=0 + done + done + + for r in ${!rowsPerRequest[@]}; do + for c in `seq 1 $clients`; do + totalRPR=0 + OUT_FILE=opentsdbWrite-rows${rowsPerRequest[$r]}-clients$c.out + for i in `seq 1 $NUM_LOOP`; do + printTo "loop i:$i java -jar \ + $TSDBTEST_DIR/opentsdbtest/target/opentsdbtest-1.0-SNAPSHOT-jar-with-dependencies.jar \ + -dataDir $DATA_DIR \ + -numOfFiles $NUM_OF_FILES \ + -writeClients $c \ + -rowsPerRequest $r" + java -jar \ + $TSDBTEST_DIR/opentsdbtest/target/opentsdbtest-1.0-SNAPSHOT-jar-with-dependencies.jar \ + -dataDir $DATA_DIR \ + -numOfFiles $NUM_OF_FILES \ + -writeClients $c \ + -rowsPerRequest ${rowsPerRequest[$r]} \ + 2>&1 | tee $OUT_FILE + RPR=`cat $OUT_FILE | grep speed | awk '{print $(NF-1)}'` + totalRPR=`echo "scale=4; $totalRPR + $RPR" | bc` + printTo "r:$r rows:${rowsPerRequest[$r]}, clients:$c, i:$i RPR:$RPR" + done + avgRPR[$r,$c]=`echo "scale=4; $totalRPR / $NUM_LOOP" | bc` + printTo "r:$r c:$c avgRPR:${avgRPR[$r, $c]}" + done + done + + printf "R/R, " + for c in `seq 1 $clients`; do + if [ "$c" == "1" ]; then + printf "$c client, " + else + printf "$c clients, " + fi + done + printf "\n" + + for r in ${!rowsPerRequest[@]}; do + printf "${rowsPerRequest[$r]}, " + for c in `seq 1 $clients`; do + printf "${avgRPR[$r,$c]}, " + done + printf "\n" + done +} + +################ Main ################ + +verbose=false +clients=1 + +while : ; do + case $1 in + -v) + verbose=true + shift ;; + + -n) + NUM_LOOP=$2 + shift 2;; + + -c) + clients=$2 + shift 2;; + + *) + break ;; + esac +done + +WORK_DIR=/mnt/root/TDengine +TSDBTEST_DIR=$WORK_DIR/tests/comparisonTest/opentsdb + +runTest + +printTo "Test done!" diff --git a/tests/perftest-scripts/tdengineTestWriteLoop.sh b/tests/perftest-scripts/tdengineTestWriteLoop.sh index 4cbb7916780316f186417f49ef86499a52c25834..5cb2a7199aa4a66fd2306cbd0736049e71cb65e3 100755 --- a/tests/perftest-scripts/tdengineTestWriteLoop.sh +++ b/tests/perftest-scripts/tdengineTestWriteLoop.sh @@ -1,7 +1,7 @@ #!/bin/bash DATA_DIR=/mnt/root/testdata -NUM_LOOP=5 +NUM_LOOP=1 NUM_OF_FILES=100 rowsPerRequest=(1 100 500 1000 2000) @@ -37,7 +37,7 @@ function runTest { -rowsPerRequest $r" RPR=`$TDTEST_DIR/tdengineTest \ -dataDir $DATA_DIR \ - -numOfFiles 1 \ + -numOfFiles $NUM_OF_FILES \ -w -clients $c \ -rowsPerRequest $r \ | grep speed | awk '{print $(NF-1)}'` @@ -80,6 +80,10 @@ while : ; do verbose=true shift ;; + -n) + NUM_LOOP=$2 + shift 2;; + master) master=true develop=false @@ -93,18 +97,19 @@ while : ; do -c) clients=$2 shift 2;; + *) break ;; esac done if $master ; then - echo "Test master branch.." + printTo "Test master branch.." cp /mnt/root/cfg/master/taos.cfg /etc/taos/taos.cfg WORK_DIR=/mnt/root/TDengine.master else - echo "Test develop branch.." - cp /mnt/root/cfg/10billion/taos.cfg /etc/taos/taos.cfg + printTo "Test develop branch.." + cp /mnt/root/cfg/perftest/taos.cfg /etc/taos/taos.cfg WORK_DIR=/mnt/root/TDengine fi @@ -113,4 +118,4 @@ TDTEST_DIR=$WORK_DIR/tests/comparisonTest/tdengine runTest -echo "Test done!" +printTo "Test done!" diff --git a/tests/pytest/crash_gen.py b/tests/pytest/crash_gen.py index 98181180e200fade047cfcc8f1be68434220af13..7cdc2b27c39fa453a30361cf00f45c513ef7365a 100755 --- a/tests/pytest/crash_gen.py +++ b/tests/pytest/crash_gen.py @@ -238,7 +238,7 @@ class WorkerThread: class ThreadCoordinator: - WORKER_THREAD_TIMEOUT = 30 + WORKER_THREAD_TIMEOUT = 60 # one minute def __init__(self, pool: ThreadPool, dbManager): self._curStep = -1 # first step is 0 @@ -388,7 +388,9 @@ class ThreadCoordinator: except taos.error.ProgrammingError as err: transitionFailed = True errno2 = err.errno if (err.errno > 0) else 0x80000000 + err.errno # correct error scheme - logger.info("Transition failed: errno=0x{:X}, msg: {}".format(errno2, err)) + errMsg = "Transition failed: errno=0x{:X}, msg: {}".format(errno2, err) + logger.info(errMsg) + self._execStats.registerFailure(errMsg) # Then we move on to the next step self._releaseAllWorkerThreads(transitionFailed) @@ -812,7 +814,7 @@ class DbConnNative(DbConn): buildPath = root[:len(root) - len("/build/bin")] break if buildPath == None: - raise RuntimeError("Failed to determine buildPath, selfPath={}".format(self_path)) + raise RuntimeError("Failed to determine buildPath, selfPath={}".format(selfPath)) return buildPath @@ -2292,6 +2294,12 @@ class ServiceManagerThread: self._thread.daemon = True # thread dies with the program self._thread.start() + self._thread2 = threading.Thread( + target=self.svcErrorReader, + args=(self._tdeSubProcess.getStdErr(), self._ipcQueue)) + self._thread2.daemon = True # thread dies with the program + self._thread2.start() + # wait for service to start for i in range(0, 10): time.sleep(1.0) @@ -2320,12 +2328,12 @@ class ServiceManagerThread: raise RuntimeError("sub process object missing") self._status = MainExec.STATUS_STOPPING - self._tdeSubProcess.stop() + retCode = self._tdeSubProcess.stop() + print("Attempted to stop sub process, got return code: {}".format(retCode)) if self._tdeSubProcess.isRunning(): # still running - print( - "FAILED to stop sub process, it is still running... pid = {}".format( - self.subProcess.pid)) + print("FAILED to stop sub process, it is still running... pid = {}".format( + self._tdeSubProcess.getPid())) else: self._tdeSubProcess = None # not running any more self.join() # stop the thread, change the status, etc. @@ -2341,6 +2349,9 @@ class ServiceManagerThread: self._thread.join() self._thread = None self._status = MainExec.STATUS_STOPPED + # STD ERR thread + self._thread2.join() + self._thread2 = None else: print("Joining empty thread, doing nothing") @@ -2421,6 +2432,10 @@ class ServiceManagerThread: print("\nNo more output from IO thread managing TDengine service") out.close() + def svcErrorReader(self, err: IO, queue): + for line in iter(err.readline, b''): + print("\nTD Svc STDERR: {}".format(line)) + class TdeSubProcess: def __init__(self): @@ -2429,9 +2444,15 @@ class TdeSubProcess: def getStdOut(self): return self.subProcess.stdout + def getStdErr(self): + return self.subProcess.stderr + def isRunning(self): return self.subProcess is not None + def getPid(self): + return self.subProcess.pid + def getBuildPath(self): selfPath = os.path.dirname(os.path.realpath(__file__)) if ("community" in selfPath): @@ -2467,24 +2488,28 @@ class TdeSubProcess: os.rename(logPath, logPathSaved) # os.mkdir(logPath) # recreate, no need actually, TDengine will auto-create with proper perms - svcCmd = [taosdPath, '-c', cfgPath] + # svcCmdSingle = "{} -c {}".format(taosdPath, cfgPath) # svcCmd = ['vmstat', '1'] if self.subProcess: # already there raise RuntimeError("Corrupt process state") + # print("Starting service: {}".format(svcCmd)) self.subProcess = subprocess.Popen( - svcCmd, + svcCmd, shell=False, + # svcCmdSingle, shell=True, # capture core dump? stdout=subprocess.PIPE, + stderr=subprocess.PIPE, # bufsize=1, # not supported in binary mode - close_fds=ON_POSIX) # had text=True, which interferred with reading EOF + close_fds=ON_POSIX + ) # had text=True, which interferred with reading EOF def stop(self): if not self.subProcess: print("Sub process already stopped") - return + return -1 - retCode = self.subProcess.poll() + retCode = self.subProcess.poll() # contains real sub process return code if retCode: # valid return code, process ended self.subProcess = None else: # process still alive, let's interrupt it @@ -2495,11 +2520,15 @@ class TdeSubProcess: self.subProcess.send_signal(signal.SIGINT) try: self.subProcess.wait(10) + retCode = self.subProcess.returncode except subprocess.TimeoutExpired as err: print("Time out waiting for TDengine service process to exit") + retCode = -3 else: print("TDengine service process terminated successfully from SIG_INT") + retCode = -4 self.subProcess = None + return retCode class ThreadStacks: # stack info for all threads def __init__(self):