时序数据库官网:https://docs.influxdata.com/
官方文档v1.7:https://docs.influxdata.com/influxdb/v1.7/
官方文档v2.0:https://docs.influxdata.com/influxdb/v2.0/get-started/
时序数据库:http://hbasefly.com/category/%e6%97%b6%e5%ba%8f%e6%95%b0%e6%8d%ae%e5%ba%93/?wypgtk=ujslu
以下部分摘自 知乎 mercury的算法铺子 https://zhuanlan.zhihu.com/p/97247465
InfluxDB 是用Go语言编写的一个开源分布式时序、事件和指标数据库,无需外部依赖。
InfluxDB在DB-Engines的时序数据库类别里排名第一。
InfluxDB以单个二进制文件提供了时间序列平台所需的一切-多租户时间序列数据库,UI和仪表板工具,后台处理和监视代理。所有这些使部署和设置变得轻而易举,并且更易于保护。
Flux是第四代编程语言,旨在用于数据脚本,ETL,监视和警报。作为一种功能语言,您可以构建查询并将通用逻辑分离为易于共享并有助于加快开发速度的函数和库。Flux还可以用于与其他SQL数据存储库(Postgres,Microsoft SQL Server,SQLite和SAP Hana)以及基于云的数据存储库(Google Bigtable,Amazon Athena和Snowflake)一起充实您的时间序列数据。丰富的时间序列数据提供了可以进一步深入了解您的数据的上下文。
现在,可以在统一的API中访问InfluxDB中的所有内容(提取,查询,存储和可视化)。因为现在可以通过编程方式访问和控制平台中的所有内容,所以这使开发人员能够更快地获得出色的表现。这与跨10种语言(例如Go,Java,PHP和Python)的一组强大的客户端库结合在一起,并且一组InfluxDB命令行工具可帮助开发人员以他们最熟悉的方式进行开发。
InfluxDB具有一流的UI,其中包括Data Explorer,仪表板工具和脚本编辑器。使用数据资源管理器快速浏览您收集的指标和事件数据,并应用常见的转换。仪表板工具随附了方便的可视化列表,可帮助您更快地查看数据见解。最后,使用脚本编辑器通过易于访问的示例,自动完成和实时语法检查来快速学习Flux。
InfluxDB模板(一套新工具,其中包括打包程序和一套预制的监控解决方案)使您可以与全世界的同事和其他社区成员共享监控专业知识。InfluxDB模板库提供的可用模板涵盖了一些最流行的工具,应用程序和协议。这些模板也可以作为代码检入,以适合您的持续集成和部署管道,从而使部署(更重要的是回滚)更改变得轻松自如。
InfluxDB 采用自研的TSM (Time-Structured Merge Tree) 作为存储引擎, 其核心思想是通过牺牲掉一些功能来对性能达到极致优化,其官方文档上有项目存储引擎经历了从LevelDB到BlotDB,再到选择自研TSM的过程,整个选择转变的思考。
时序数据库的需求
LSM 的局限性
在官方文档上有写, 为了解决高写入吞吐量的问题, Influxdb 一开始选择了LevelDB 作为其存储引擎。 然而,随着更多地了解人们对时间序列数据的需求,influxdb遇到了一些无法克服的挑战。
LSM (日志结构合并树)为 LevelDB的引擎原理, 具体细节可以参考。 LSM 树原理详解
按不同的时间范围划分为不同的分区(Shard),因为时序数据写入都是按时间线性产生的,所以分区的产生也是按时间线性增长的,写入通常是在最新的分区,而不会散列到多个分区。分区的优点是数据回收的物理删除非常简单,直接把整个分区删除即可。
DataBase: 用户可以通过 create database xxx 来创建一个database。
Retention Policy(RP): 数据保留策略, 可用来规定数据的的过期时间。
1 | CREATE RETENTION POLICY "a_year" ON "food_data" DURATION 52w REPLICATION 1 SHARD DURATION 1h |
上述语句可以创建一个保存周期为52周的RP。REPLICATION 1 表示副本数量为1。 ”SHARD DURATION”指定每个Shard Group对应多长时间。
一个数据库可以用多个RP , 不同的表可以设置不同的RP。
Shard Group : 实现了数据分区,但是Shard Group只是一个逻辑概念,在它里面包含了大量Shard,Shard才是InfluxDB中真正存储数据以及提供读写服务的概念。
Share :结构示意图如下:
Share 就是上面章节所介绍的TSM 引擎, 负责把数据写入文件。
具体的过程类似于LSM,数据来了先存到cashe, 等cashe 大小到一定程度就会异步flush 到TSM文件。 同时WAL(Write Ahead Log) 用来预防数据丢失。
先来看一下Influxdb 数据模型:
重要概念:
1 | insert census,location=1,scientist=langstroth butterflies=12,honeybees=23 1435362189575692182 |
InfluxDB 核心概念 – Series
时序数据的时间线就是一个数据源采集的一个指标随着时间的流逝而源源不断地吐出数据,这样形成的一条数据线称之为时间线。
InfluxDB在时序数据模型设计方面提出了一个非常重要的概念:SeriesKey。SeriesKey实际上就是measurement+datasource(tags)。时序数据写入内存之后按照SeriesKey进行组织。
时序数据在内存中表示为一个Map:<Key, List<Timestamp|Value>>, 其中Key = seriesKey + fieldKey。这个Map执行flush操作形成TSM文件。
TSM 文件结构
TSM文件最核心的由Series Data Section以及Series Index Section两个部分组成,其中前者表示存储时序数据的Block,而后者存储文件级别B+树索引Block,用于在文件中快速查询时间序列数据块。
Series Data Block
Map中一个Key对应一系列时序数据,因此能想到的最简单的flush策略是将这一系列时序数据在内存中构建成一个Block并持久化到文件。然而,有可能一个Key对应的时序数据非常之多,导致一个Block非常之大,超过Block大小阈值,因此在实际实现中有可能会将同一个Key对应的时序数据构建成多个连续的Block。但是,在任何时候,同一个Block中只会存储同一种Key的数据。
另一个需要关注的点在于,Map会按照Key顺序排列并执行flush,这是构建索引的需求。Series Data Block文件结构如下图所示:
Series Index Block
每个key 对应一个Index Block。
很多时候用户需要根据Key查询某段时间(比如最近一小时)的时序数据,如果没有索引,就会需要将整个TSM文件加载到内存中才能一个Data Block一个Data Block查找,这样一方面非常占用内存,另一方面查询效率非常之低。为了在不占用太多内存的前提下提高查询效率,TSM文件引入了索引,其实TSM文件索引和HFile文件索引基本相同。TSM文件索引数据由一系列索引Block组成,每个索引Block的结构如下图所示:
Series Index Block由Index Block Meta以及一系列Index Entry构成:
基于对TSM文件的了解,在一个文件内部根据Key查找一个某个时间范围的时序数据就会变得很简单,整个过程如下图所示:
上图中中间部分为索引层,TSM在启动之后就会将TSM文件的索引部分加载到内存,数据部分因为太大并不会直接加载到内存。用户查询可以分为三步:
上个章节讲的是如何根据key 查到数据。 然而,在实际场景中, 经常会有根据表名和 部分tag 来查询数据的场景。 比如最开始的那个表, 用户要查cenus 表 location=1 在一周内的所有butterflies 总数。 这种的话如果tag 和measurement 不做索引的话,则查询方式只能是遍历对比。
InfluxDB给出了倒排索引的实现,称之为TimeSeries Index,意为TimeSeries的索引,简称TSI。InfluxDB TSI在1.3版本之前仅支持Memory-Based Index实现,1.3之后又实现了Disk-Based Index实现。
Memory-Based Index
Memory-Based Index方案将所有TimeSeries索引加载到内存提供服务,核心数据结构主要有:
Q:考虑下如何查询多个tag 纬度下的series ID?A:答案是拉出多个 seriesID List 求交集。
Disk-Based Index
Disk-baesd index 的实现思路和TSM file 的实现原理一样。 先写入WAL 预写日志,然后数据存入cashe , 当cashe 积攒到一定大小后, flush 到文件。
Q: 新插入一条数据, 如何判断series key 是否已经存在? A:Bloom Filter
下载 :
1 | wget https://dl.influxdata.com/influxdb/releases/influxdb_1.7.9_amd64.deb |
启动:
1 | ./influxd 启动influxdb 服务器。 ./influx 启动shell 客户端。 |
创建数据库:
1 | create database monit |
插入数据:
1 | insert census,location=1,scientist=langstroth butterflies=12,honeybees=23 |
查询数据:
1 | select * from census |
http 接口访问:
1 | curl -G 'http://localhost:8086/query?pretty=true' --data-urlencode "db=mydb" --data-urlencode "q=SELECT \"value\" FROM \"cpu_load_short\" WHERE \"region\"='us-west'" |
q= 除了可以接查询语句, 还可以接建RP, 建CQ , 插入等等操作
支持同时多个点写入和同时多个查询
1 | curl -i -XPOST 'http://localhost:8086/write?db=mydb' --data-binary 'cpu_load_short,host=server02 value=0.67 cpu_load_short,host=server02,region=us-west value=0.55 1422568543702900257 |
支持连续查询(Continuous Queries简称CQ) 来处理数据采样。
假设我有一个数据库monitor , 里面有一张表monit.biz.douyin 记录抖音的数据打点,tag(metric,province,carrier)。 我之前创建了一个名叫3_month的RP ,我想按照metric 和province 两个tag聚合每分钟抖音的打点数量 。建立CQ 如下
1 | CREATE CONTINUOUS QUERY "douyin_1min_sum" ON "monitor" BEGIN SELECT sum("count") INTO monitor."3_month"."monit.biz.douyin.1min" FROM monitor."default"."monit.biz.douyin" GROUP BY time(1m),metric,province END |
monit.biz.douyin.1min 就是聚合完的新表。