添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

最近公司有个需求需要借助 InfluxDB 实现(或者更准确的说,使用该数据库可以更容易的实现),因此稍微看了下这个数据库,把比较重要的一些东西先简单记录一下,日后如果踩坑,也会继续在下面补充。

零、下载&安装

官方地址: https://portal.influxdata.com/downloads/

一、什么是时序数据库,它可以用来做什么?

简单来说 时序数据库 就是存储带有 时间戳 且包含 随时间发生变化 的数据, InfluxDB 属于一种 时序数据库 。这类数据具体指什么数据呢?这里举几个例子:

  • 监控,比如某一时段CPU占用率、服务QPS、服务耗时等监控数据
  • 某飞机在运行过程中各时刻的高度、速度、机舱内温度、湿度
  • 某地区每时每刻的室外温度记录
  • 上述的数据都满足某一个具体的事物(监控指标、飞机、地区)随着时间的变化而需要记录的一些数据(监控数据、速度、温度等),这些数据被称为时序性数据,这些数据可以被用来做大数据分析、人工智能等。

    Q: 通过上述的例子,这些无非是一些指标数据嘛,为什么非得用时序性数据库记录?不可以用 Mysql 这种关系型数据库来做吗?

    A: 不可以,首先对于这种数据,量级上是巨大的,比如每时每刻都要记录某飞机的坐标变化,类似这种指标,量级很庞大,关系型数据库设计之初并不是为了处理这种大数据量级的,即便是后来的分表分库,也是为了优化数据量和访问量大了以后查询和写入性能问题的,所以不适合用来存储数量如此庞大的数据,所以这并不是能不能实现的问题,而是适不适合的问题,而时序型数据库的写入速度非常快,其次内部还会对存储数据进行压缩,同时提供了更高的可用性,比如数据保留策略、数据聚合函数、连续查询等。数据保留策略可以保证超过指定期限的数据被回收,释放磁盘空间。

    二、InfluxDB基本概念

    2.1:建表

    不需要专门建表,一般 insert 执行过去时,如果发现表不存在,就自动创建。

    2.2:基本概念与mysql中概念的对应关系

    2.3:point详解

    point 的概念就类似于 mysql 一行数据 (data point,直译为数据点,可以理解成一行数据), point 的构成部分有三个:

    tags fileds 的区别:

    MySQL类比 database 类似mysql里的库 measurement 类似mysql里的表 point 一行数据记录 类似mysql里的一行数据 point属性 influxdb自带字段,单位:纳秒 有各种索引的属性 可设置多个,逗号隔开 fields 没有索引的属性 可设置多个,逗号隔开
    1
    2
    1. field无索引,一般表示会随着时间戳而发生改变的属性,比如温度、经纬度等,类型可以是 string, float, int, bool
    2. tag有索引,一般表示不会随着时间戳而发生改变的属性,比如城市、编号等,类型只可为 string

    比较值得注意的是:在 InfluxDB 中, tags 决定了 series 的数量,而 series 的数量越大,对于系统CPU和 InfluxDB 的性能影响越大, series 估算方法下面会说。

    2.4:sql语句示范

    建库:

    1
    create database "库名"

    一般来说,新建完库,还要对该库设置自己的 时效策略 RP ),下面会详细说明 RP 的设置方法,一般建好库后都会有一个默认的 RP策略

    插入数据:

    1
    2
    3
    4
    5
    insert results,hostname=index2,name=index2333  value=2,value2=4

    -------- ---------------------------- -------------------------

    表名 tags的设置 fields的设置

    上面这句sql的意思就是说在名为 result 的表里,插入两个分别命名为 hostname name 取值分别为 index2 index2333 tags ,以及两个分别命名为 value value2 取值分别为2和4的 fields 的数据。

    ⚠️ 注意:这里的指令直接输入整数, InfluxDB 是按照 浮点型 处理的,如果一定要让上面的 value=2 value2=4 中的 2 4 是整型数据,那么需要在后面加上修饰词: value=2i i 就代表整型。

    检索数据:

    1
    select * from results

    打印结果为:

    1
    2
    3
    4
    name: results2
    time hostname name value value2
    ---- -------- ---- ----- ------
    1560928150794920700 index2 index2333 2 4

    分页检索:

    1
    SELECT * FROMWHERE 条件 LIMIT rows OFFSET (page - 1)*rows

    rows代表每页展示行数,page表示页码

    1
    drop measurement "表名"

    三、InfluxDB的时效设置

    很遗憾, InfluxDB 是不支持删除和修改的,删除有专门的操作,但是性能很低,不建议使用。

    InfluxDB 支持定期清除数据策略。

    查看当前库对应策略配置的命令为:

    1
    show retention policies on "库名"
    1
    2
    3
    name       duration      shardGroupDuration       replicaN     default
    ------- --------- ------------------ --------- -------
    autogen 0s 168h0m0s 1 true

    name :过期策略名

    duration :保留多长时间的数据,比如3w,意思就是把三周前的数据清除掉,只保留三周内的,支持h(小时),d(天),w(星期)这几种配法。

    shardGroupDuration :存储数据的分组结构,比如设置为1d,表示的是每组存储1d的数据量,也就是说数据将按照天为单位划分存储分组,然后根据每条数据的时间戳决定把它放到哪个分组里,因此这个概念还会影响到过期策略,因为InfluxDB在清除过期数据时不可能逐条清理,而是通过清除整个 ShardGroup 的方式进行,因为通过跟当前时间对比就可以知道哪些分组里的数据一定是过期的,从而进行整组清理,效率往往更高。

    replicaN :副本数量,一般为1个(这个大概就是备份的意思?这个配置在单实例模式下不起作用)

    default :是否是默认配置,设置为 true 表示默认的意思,主要用于查询时是否指定策略,如果不指定,则这个查询就是针对 default=true 的策略进行的,注意,本文章里的sql都没有指明保留策略的名字,如果有需要,那么请指定。一个库允许有多套策略配置(每套策略里都可以有自己的一份数据,比如同样一张表的数据在策略A和策略B的情况下是不同的,可以理解为一个库对应N个策略,每个策略里有自己的N多张表,相互独立),在不指定策略名称的情况下写的sql,默认使用 default=true 的策略。

    当然,策略也支持修改,指令如下:

    1
    alter retention policy "autogen" on "test" duration 30d default

    上面这条指令就会把上面策略明为 autogen 的策略有效保存时间改为 30天

    四、InfluxDB的存储结构

    4.1:结构层级

    了解完策略,结合上面提到的 series tags fields 等概念,画一下单个 InfluxDB库 的存储结构:

    图里没有体现出 tags fields 这些数据层面的东西,它们最终被存放在了 Shard 里,之前说过 tags 会影响 series 的大小,其实 series 就相当于一个唯一化分类, eries 估算方式为:

    1
    series的个数 = RP × measurement × tags(tags去重后的个数)

    比如一个 database 中有一个 measurement ,叫 test ,有 两个RP (7d, 30d), tag host server 。host的值有 hostA、hostB ,server为 server1,server2 。那么这个database的series值为 2RP x 4tags = 8

    4.2:LSM-Tree

    回归 图1 前,先来了解下 LSM-Tree ,几乎所有的k-v存储的写密集型数据库都采用该数据结构实现,该数据结构的结构如下(注意下面说的层级并非树的层级,而是合并逻辑发生时的层级):

    图2 ,该结构写入流程如下:

    写入的数据首先加到0层,0层的数据存储在内存中,短期内查询的数据一般在0层,由于是内存操作,因此效率会非常高,当0层的数据达到一定大小时,此时会把0层 和位于它下面的1层进行合并,然后合并出来的新的数据(0层+1层的数据)会顺序写磁盘(这里由于是顺序写入磁盘,因此写性能会非常好),然后替换掉原来老的1层数据,当1层达到一定大小的时候,将继续和它的下层合并,以此类推,一级一级的往下递,除此之外还可以将合并之后旧文件全部删掉,留下最新的。

    LSM-Tree 参考文章: LSM-tree 基本原理及应用

    然后回归 图1 ,最终的数据会被存储进 Shard Shard 里存在几个区域,就是最终存放数据的地方,下面针对这几个区域说明下它们的作用:

    4.3:Cache

    相当于上面说的 LSM-Tree 0层 ,存放于 内存 中,写入的数据时首先被该模块接收并存储,该模块在内存中表现为一个map结构,k = seriesKey + 分隔符 + fieldsName,v = 具体的filed对应的值的数组,具体结构体如下(参考网上资料):

    代码块1
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    type Cache struct {
    commit sync.Mutex
    mu sync.RWMutex
    store map[string]*entry
    size uint64 // 当前使用内存的大小
    maxSize uint64 // 缓存最大值

    // snapshots are the cache objects that are currently being written to tsm files
    // they're kept in memory while flushing so they can be queried along with the cache.
    // they are read only and should never be modified
    // memtable 快照,用于写入 tsm 文件,只读
    snapshot *Cache
    snapshotSize uint64
    snapshotting bool

    // This number is the number of pending or failed WriteSnaphot attempts since the last successful one.
    snapshotAttempts int

    stats *CacheStatistics
    lastSnapshot time.Time
    }

    基于前面说的 LSM-Tree ,可以知道这里的cache不是持续增长的,而是达到一定值就会进行跟下层存储在磁盘上的数据(1~n层)进行合并,然后清空cache,在InfluxDB中,这一部分存储在磁盘上的数据,就是指 TSM File模块 ,下面会介绍。

    4.4:WAL

    在上面对于Cache的描述中,Cache是基于内存做的写入数据接收方,那么如果中途机器宕掉,那么就会造成Cache里数据丢失的问题,为了解决这个问题,InfluxDB就设计了 WAL模块 ,实际在写入一个数据时,不仅会先写进Cache,还会 写入WAL ,可以简单理解WAL就是对Cache里数据的 备份 ,防止数据丢失,在Cache做完一次合并清除掉自身时,旧的WAL文件也会随之删除,然后新建一个WAL,迎接新一轮的写入。同样的,在InfluxDB启动时,也会先去 读取WAL文件初始化Cache模块

    4.5:TSM File

    用于组成 TSM-Tree 结构的主要磁盘文件(可以对应 图2 的1~n层),内部做了很多存储以及压缩优化,单个 TSM File 的最大大小为 2GB

    4.6:Compactor

    在后台持续运行的一个task(频率为1s),主要做以下事情:

  • 在Cache达到阈值时,进行快照,然后将数据合并并保存在TSM File中
  • 合并 TSM File ,将多个小型TSM File进行合并,使得每个文件的大小尽可能达到单个文件最大大小(也就是上面说到的2GB)
  • 检查并 删除 一些 已关闭 的WAL文件
  • 五、具体案例,以及实际的存储目录

    结合 图1 的结构以及 图2 的数据结构,再加上对 shard 内部各组件的介绍,下面通过一个实际的例子来探索下InfluxDB实际的存储目录。

    现在创建出来一个叫 style_rank 的库,且设置 默认RP为7d ,保存周期为 7天 ,默认分组策略为 24h 分一次,如图:

    图中名为 7d rp策略 被设置为默认策略,其中 duration 被设置为 168h shardGroupDuration 被设置为了 24h ,意味着该策略里的表数据将以7天为一个周期过期,期间以1天为单位分组。

    5.1:InfluxDB配置文件

    下载下来一个InfluxDB后,通过配置文件 influxdb.conf 可以配置各个文件存放的目录:

    这几个配置决定了你将产生的数据存放在哪里,建议自定义这个,默认的目录很迷=_=

    5.2:具体的数据目录

    配置完上面的文件,启动,然后录入数据,然后再去具体的目录下查找具体的数据文件:

    元数据层: 用来存放 当前库的属性 ,比如 RP 默认RP 分组策略 等。

    wal层: 用来存放刚写入数据的信息,会先写入内存,然后异步写入 wal文件 ,wal文件用来重启InfluxDB时恢复内存数据用,当wal文件达到一定大小时,会压入 data层 wal层 的结构和 data层 几乎一致

    上面进入wal目录后,需要选择正确的库,这里选择 style_rank ,进入库后,会显示出该库的所有RP,这里选择 7d ,然后对应 图1 ,你现在来到了 ShardGrou p层, 图6 中的编号,就是生成的 ShardGroup ,随便进入一个,则可以看到最终的 wal文件 (事实上只有当天建的group内的wal才有数据)。

    data层: 用来最终存放数据的目录,所有 wal文件 内的数据达到一定大小后,均会被压缩进 data层 ,现在进入 data层 ,然后进入 style_rank

    可以看到下方除了有两个 RP策略 外,还有 _series目录 ,这个目录就是存放 索引 tags 字段所产生的索引数据)的地方,用于服务重启时恢复索引数据用。

    进入 7d ,可以看到跟wal层差不多的 ShardGroup 分层,你现在又来到了 图1 ShardGroup 层:

    然后再次进入一个分组内:

    最终的 tsm文件 就存在该目录下, fields.idx 存放的是表里的 fields元数据 ,用于写入数据时做 效验 用。

    六、执行计划

    然后继续由上面的 style_rank 库,在 7d 这个 RP 下,新建两张表:

    1
    2
    insert season_views,date="201907" season_id=666,views=56789
    insert season_views_v2,season_id=666 views=56789

    目的很简单,只是为了记录下 season_id=xxx 的数据在某一时刻对应的 views值 ,俩表的区别在于, season_view 仅仅以 日期 为tag(作为索引字段,可以忽略不计), season_views_v2 则以 season_id 作为tag,某一时刻,两张表的数据量均达到了 5kw 数据一致 ,下面,让我们以最简单的方式,查询下这两张表:

    ℹ️ ps: sql 前加 explain 关键字可以查看其 执行计划 ,加 explain analyze 可以看到具体每一步执行的耗时。

    同样的查询, season_id 加了索引和不加索引的区别:

    NUMBER OF SERIES (扫描系列数,加了索引后根据series的哈希算法,同一个season_id都被分在了同一个Shard里,这里之所以为8,是因为ShardGroup不同)由不加索引的64个,减少到了8个

    NUMBER OF FILES (扫描文件数)由不加索引的54个减少到11个

    NUMBER OF BLOCKS (扫描的数据块)由不加索引的152058减少到了184

  • 在使用时,尽可能按照某 具体时间段 进行过滤,如果过滤条件筛选出的数据量过大,则会严重影响查询效率。
  • 适度使用函数,若使用,请保证筛选条件筛选出的数据量,如果过大,效率会极低,比如在 season_views_v2 中启用 TOP函数 ,查出前三名,查询耗时 超过10s ,加上 season_id 条件后,则迅速返回。
  • 加索引时需要注意,尽量避免 大数据量的属性做tag ,否则会产生 大量series ,生成 大量索引数据 ,占用过多内存,比如 用户级别 的tag(这里的 用户级别 是指大型网站用户数量级,一般1亿以上的情况)。
  • 免费版 的InfluxDB 不支持集群 ,主从需要借助 influx-proxy 实现,也可以自己通过 监听wal文件 写入,通过 消息队列 的方式实现主从同步, wal 类似mysql里的 binlog
  • 建议使用的时候,先把自己的库建好,然后再把RP建好,如果嫌麻烦,可以直接把自己新建的RP定义成默认RP,如果RP设置为默认,只会对查询产生影响(当然写的时候也需要指定),在sql中不指定RP的情况下执行,则认为就是走的默认RP。
  • 分库,建议按照日期分。
  •