# tsdb **Repository Path**: hunter2020/tsdb ## Basic Information - **Project Name**: tsdb - **Description**: 这是一个高性能的时序数据库,用于监控系统中保存各个测点的历史数据。具有以下特点:接口简洁、写入速度快(每秒写入10万以上采样数据)、查询速度快(10万测点、每测点每秒存储一次、持续运行30天的情况下,查询24小时的历史记录不超过1秒)、支持降频查询 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2018-05-17 - **Last Updated**: 2023-07-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # spdata-tsdb ## 名词解释 1. 时序数据库(tsdb):时序数据库,全称为时间序列数据库。时间序列数据库主要用于指处理带时间标签(按照时间的顺序变化,即时间序列化)的数据,带时间标签的数据也称为时间序列数据。时间序列数据主要各类型实时监测设备所采集、产生的数据(这种检测设备广泛存在于电力系统、化工制造企业、物联网系统、IT运维系统等),这些数据的典型特点是:产生频率快、严重依赖于采集时间(每一条数据均要求对应唯一的时间)、测点多信息量大(常规的实时监测系统均有成千上万的监测点,监测点每秒钟都产生数据,每天产生几十GB的数据量)。 2. 监测某项指标的测量点,比如“电量仪”中会有电压、电流、功率等测点,“温湿度计”中有温度、湿度测点。测点又可分为遥测测点(反应模拟量采样值的测点)和遥信测点(反应信号量采样值的测点)。 ## spdata-tsdb主要特性 1. **写入速度快** ,普通pc能每秒能写入20万以上时序数据 2. **查询速度快** ,不随数据的增加而明显延时。10万测点每秒产生一次数据的系统中,运行30天后,查询某测点1一个月内的数据仅需1秒。 3. **数据完整性** ,系统按照指令停止时不丢失数据。 4. **高速快照功能** ,在强制杀死TSDB进程时,系统只丢失极少的数据(没来得及写快照的那部分数据) 5. **高效数据压缩能力** ,采用优化的delta编码方式对归档数据进行编码,每份时序数据(一个测点的一次采样数据,包含采样时间、采样值、测点Id)仅需1-2个Byte左右,这个压缩比率和采样频率、数值变化速度相关,实际生产环境中会有一定正偏差或者负偏差。 6. **高可靠性** ,归档数据由mongodb保存,如果把mongodb配置为多副本的情况下,可以保证归档数据不丢失。 7. **对采样数据质量有很高的容忍度** ,允许采样数据的时间和服务器系统时间有较大偏差,也允许不同采样数据之间的采样时间有明显的错位。 8. **自动清盘、循环存储** ,系统能自动清除过期的历史数据,回收磁盘空间,用于存储新的时序数据。 9. **集群功能** ,多台tsdb-server同时提供服务,负载均衡、故障时自动Rebalance。(暂未完成) 10. **类SQL查询**,提供类SQL语法查询接口。(暂未完成) ## 安装说明 ### 环境准备: 需要在运行tsdb-server的机器上安装好以下程序 1. jdk1.8或者以上版本 2. mongodb3.4或者以上版本 3. tomcat8.0或者以上版本 ### 安装步骤: 1. 下载tsdb-server.rar,解压后,把文件夹tsdb-server完整复制到%TOMCAT-HOME%/webapps文件夹下。 2. 访问%TOMCAT-HOME%/webapps/tsdb-server/WEB-INF/classes,用文本编辑器打开setting.property文件,编辑其中的配置信息,并保存。各配置项含义请参考配置说明 3. 启动tomcat服务,打开浏览器,在地址栏输入:http://localhost:8080/tsdb-server,系统显示时序数据查询界面。 ## 测试 Spdata-tsdb的子项目tsdb-common自带了测试代码,你可以用这个项目来测试tsdb的写入性能,并根据写入数据测试查询性能。 ### 写入测试 1. 下载tsdb-common-src.rar,解压缩后导入到eclipse编辑其中。 2. 在eclipse中,打开源代码文件夹src/test/java,再打开包org.spdata.tsdb.client下的ClientTest.java 3. 如果tsdb-server是默认配置的,则忽略本步骤。找到setup()函数,代码如下: ``` public void setup() { cf = new ClientFactory(); cf.setHost("127.0.0.1"); cf.setPort(27328); cf.setMaxConnections(30); … } ``` 修改host和port。 4. 运行测试方法test0。该测试方法每隔2秒钟保存20万采样数据到tsdb-server中。模拟的产生数据的设备有device_0、device_1、…、device_4999。每个设备下的测点有:温度、湿度、电压、电流、功率、metric_5、…、metric_39。 5. 打开浏览器,访问localhost:8080/tsdb-server。在查询界面填入设备:device_0、测点:温度、开始时间、结束时间,然后点击查询。显示结果如下:![时序数据查询](https://gitee.com/uploads/images/2018/0417/111506_f4312511_1773840.png "query.png") 6. 调整测试参数。找到ClientTest.java中的setup方法,修改变量i的最大值,就能增加或者删除设备数量。尽量不要修改变量j的最大值。 ``` public void setup() { …… for(int i=0;i<5000;i++){ for(int j=0;j<40;j++){ DataPoint dp = new DataPoint(); LinkedHashMap tags = new LinkedHashMap(); tags.put("device", "device_"+i); tags.put("mid", "metric_"+i+"_"+j); if(j<5) dp.setMetric(metricType[j%5]); else dp.setMetric("metric_"+(j%40)); dp.setTags(tags); metrics.add(dp); } } } ``` 另外在方法test0中,变量int space = 2 指明了每次采样的时间间隔,修改这个变量可以改变模拟数据产生的频率。 ### 查询测试 用浏览器来访问http://localhost:8080/tsdb-server/, 按F12打开debug窗口,并切换到Network标签页。设置好查询条件后,点击“查询”按钮,然后在debug界面选择刚才执行过的请求(最近的query请求),debug窗口的右边会显示该请求的详细执行情况,在右边点击“Timing”标签,可以显示出该请求在各个阶段所消耗的时间,据此可以了解tsdb-server的查询性能。 ![输入图片说明](https://gitee.com/uploads/images/2018/0417/112306_124fb06d_1773840.png "query2.png") ## 存储说明 ### 归档存储 当客户端向tsdb-server写入时序数据的时候,tsdb-server首先把它缓存在一个byte array中。对每个测点而言,每当新来的时序数据和前一个时序数据不在同一个小时内,tsdb-server就把该测点的缓存数据归档存储到Mongodb的dps表中。Dps表中Document由以下字段构成: 1. id:包含测点Id和所在小时信息的主键 2. timestamp:时间戳,主要用于mongodb自动清盘功能 3. summary:当前小时内当前测点的汇总信息,包括平均值、最大值、最小值、初值、末值等 4. dps:当前小时当前测点的详细信息,包含采样的时间和采样值。采用压缩算法后,每次采样信息只需要1-2个byte。 ### 未归档数据的存储(快照) tsdb-server 每隔一段时间把内存中缓存的时序数据写入到mongodb中,当服务器由于某种原因停止并重启后,tsdb-server首先把快照数据加载到内存。快照数据文档存储在Mongodb的snapshot表中。表中document的结构如下: 1. id:主键 2. time:快照时间 3. index:同一次快照如果拆包,index表示包的次序 4. parent:同一次快照如果拆包,parent表示根包的Id 5. type:快照类型,全量快照、增量快照、拆包后的子包 6. data:快照的二进制内容 ## 配置说明 ![配置](https://gitee.com/uploads/images/2018/0417/113312_a2200385_1773840.png "config.png") ## API接口 ### 初始化Client ``` //首先初始化一个ClientFactory,代码如下: ClientFactory cf = new ClientFactory(); cf.setHost("127.0.0.1"); cf.setPort(27328); cf.setMaxConnections(30); //然后获取Client并调用其接口: Client client = null; try { client = cf.getClient(this); …… }catch(Exception e) { logger.error(e.getMessage()); }finally { if(client!=null) client.release(); } ``` ### 写入时序数据 ``` List dps = new ArrayList(); //组装时序数据 …… try { client = cf.getClient(this); //写入数据 client.put(dps); //提交数据 client.flush(); }catch(Exception e) { logger.error(e.getMessage()); }finally { if(client!=null) client.release(); } ``` ### 查询时序数据 ``` Query query = new Query(); query.setStart(startDate.getTime()).setEnd(endDate.getTime()); SubQuery subQuery = new SubQuery(); subQuery.setMetric(“温度”); Map tags = new HashMap(); tags.put(“device”,”device_0”); subQuery.setTags(tags); query.addSubQuery(subQuery); try { client = cf.getClient(this); List result = client.query(query); ...... }catch(Exception e) { logger.error(e.getMessage()); }finally { if(client!=null) client.release(); } //如果要降频查询,比如你想查一个月内某些测点的数据,可以这样设置查询条件: Query query = new Query(); query.setStart(startDate.getTime()).setEnd(endDate.getTime()); //设置降频条件:每小时取一个采样数据,采样值为该小时内的平均值 DownSample downSample = new DownSample(); downSample.setFrequence(1); downSample.setUnit(TimeUnit.HOURS); downSample.setArithmetic(Arithmetic.avg); SubQuery subQuery = new SubQuery(); subQuery.setMetric(“温度”); Map tags = new HashMap(); tags.put(“device”,”device_0”); subQuery.setTags(tags).setDownsample(downSample); query.addSubQuery(subQuery); ```