1. 背景
本文是系统能通过对influxdb 1.8的代码进行基础的走读来理解这个influxdb的工作原理。 理解其中的一些设计思路。
2. 感性认知
将influxdb和传统的数据库MySQL做个简单比较
MySQL | Influxdb | ||
类似点 | table | measurement | |
index field | tag | ||
unindex field | field | ||
pk | timestamp | ||
差异点 | CRUD | CRD, 较少Update | |
数据值 | 数据的趋势分析 | ||
结构化数据 | schemaless |
初步感性操作Influxdb
这个是登录到influxdb里进行的SQL操作。可以看到并没有MySQL那种强的表结构的概念,字段是可以随意增加(如图第二个insert增加了一个addr字段), 从这个角度看到是schemaless的。
3. 基础概念
3.1 influxdb版本演进
主要是存储引擎的演进。
第一个阶段:
使用leveldb来做数据落地持久化的存储引擎,但是发现随着数据量的增加,leveldb耗用文件句柄过多;第二个问题就是按照时间删除数据代价过高,因为leveldb不是按照时间进行数据分片存储的,可能会涉及到很多文件的查找,性能比较底下。
第二个阶段:
使用一个go写的内存map数据库boltDb,好处就是一个db就是一个文件,虽然解决了文件句柄的问题,但是IOPS有比较大毛刺,性能不够稳定。
第三个阶段
使用boltDB+WAL, 通过日志来缓冲写请求,改为批次写如。
第四个阶段
就是最新的TSM存储引擎,自研。 从0.95和1.X版本都用这个存储引擎。
3.2 influxdb的逻辑结构
数据的逻辑组织结构。 它不像传统的MySQL(Database->table->data)库表的逻辑组织结构。
3.2.1 Database
可以理解为类似MySQL的database概念,这个没太大区别。
3.2.2 RetentionPolicy-保留策略
但是第二级并不是measurement(类似MySQL的table), 因为 一个measurement可以在不同的RetentionPolicy都存在。
例子: 下面这个写入数据的db=mydb,measurement=disk_free, 然后写入的api可以指定rp,也就是可以支持写入到多个rp的。
代码样例: 调用/write接口进行数据写入
curl -X POST 'https://localhost:8086/write?db=mydb&rp=six_month_rollup' --data-binary 'disk_free,hostname=server01 value=442221834240i 1435362189575692182'
创建保留策略: create retention policy one_month on ABC duration 30d replication 2;
说明: 从这个创建语句我们可以看出来,它定义的是在某个database上的数据保留时间和数据副本数,还可以定义每个shard的时间分片长度。
3.2.3 Shard
数据根据时间进行的数据分片。 其实还有shard group的概念的。 这个shard分片主要就是根据时间进行分区, 和measurement没太大关系。 从这里可以看出来时间在数据组织中的重要性。 这样子在数据删除的时候,就变得简单了,直接可以从某个shard进行数据删除,不用想leveldb那样子,全局搜索了。
每个Shard其实就是一个小型的Level,有自己的cache、wal和TSM 物理文件
3.3 数据写入流程
influxdb写入流程代码还是绕了几个弯弯, 才找到写入的最终落地代码位置: influxdb/tsdb/engine/tsm1/engine.go
这里有一个疑问: 如果写入cache成功了,但是写入wal失败了? 那么这个数据能被读取到吗?
从这里也能看出来WAL并不是最先更新写入的。
3.4 物理文件
首层目录:
wal: 就是存放各个shard的wal的, 例如 wal/abc/autogen/5: 就表示存放abc这个库, autogen存放策略(rp), 分片5的wal数据
data: 同上
meta: 存放集群的成员的元数据信息
hh: 存放需要同步到其他集群成员的落地数据,这样子即便influxdb重启,也能异步进行数据同步。
4. influxdb源码初探
4.1 github仓库代码目录
这里只说比较重要的三个目录。 其他的逐步摊开就可以了。 influxdb本质是个http服务,那么一个典型的http服务,必定是有(路由,业务逻辑)这样子的结构。
cmd: 存放main函数入口, 这里注册各个services的路由。可以看看下面这个代码文件的这个位置。
services: 各个组件模块的。 例如httpd服务
例如以httpd模块来说, 它提供了很多http api来支持 influxdb的操作,例如上面提到的/write,你都能从handler.go文件里找到http路由注册入口,然后找到对应的处理函数,就能逐步梳理出这个influxdb的写入流程了。
5. influxdb集群版本
5.1 集群版本原理
官方的influxdb的集群版本是做成企业收费版,没有对外开放代码,github开源项目(https://github.com/chengshiwen/influxdb-cluster)参考如下的官方的实现来实现了一套。
meta nodes: 就是基于raft协议实现一套元数据的存储,包括节点成员、数据分布、账号验证信息都存储在meta节点上。
5.2 集群版本搭建
* 使用一个influxd-ctl的工具,add-meta来首先将3个节点组件成一个raft group。
* 然后使用add-data来讲data节点加入到这个meta group。
* 如果想知道上述这两个add操作做了啥,可以到项目里看看这个工具的操作(就是调用influxd的api进行节点操作)
* 就会在data节点的 ${HOME}/meta节点下有meta服务的节点信息
influxd-ctl add-meta influxdb-meta-01:8091
influxd-ctl add-meta influxdb-meta-02:8091
influxd-ctl add-meta influxdb-meta-03:8091
influxd-ctl add-data influxdb-data-01:8088
influxd-ctl add-data influxdb-data-02:8088
influxd-ctl show
5.3 写入一致性
集群版本写入可以配置写入一致性要求, 就是client 可以要求: 写入到全部节点、写入到至少2个节点、写入到任意一个节点就返回给client,这几种数据一致性的要求。
例如如果某个节点挂了,那么这个时候数据没法立即写入到相关节点。 那么这部分数据是通过hint-handoff的模块进行缓存和持久化落地到hh数据目录。这部分的代码实现主要在这个目录里,还是比较好理解的。 因为它主要就暴露2个接口: WriteShard和Empty接口。
6. 总结
本文简单讲述了influxdb的版本演进、逻辑结构、写入流程、集群原理等信息,下一步将讲解一下influxdb的读流程。整体来说 influxdb的代码还是比较易懂的,可以从具体api入手,然后看看对应的api代码实现。
influxdb源码还是比较规整的, 可以作为go学习入门挺好的一个学习项目。