InfluxDB(七):数据写入

【关于作者】

关于作者,目前在蚂蚁金服搬砖任职,在支付宝营销投放领域工作了多年,目前在专注于内存数据库相关的应用学习,如果你有任何技术交流或大厂内推及面试咨询,都可以从我的个人博客(https://0522-isniceday.top/)联系我

1.写入总体框架

转载链接:http://hbasefly.com/2018/03/27/timeseries-database-6/

数据写入的方式:

  1. collector采集上传
  2. opentsdb作为输入
  3. http或udp协议批量写入数据

批量数据进入InfluxDB经过三个步骤处理,总体架构图如下:

image-20210531212235136

  1. 分组到Shard:批量数据首先会分组到不同的Shard(先是根据RP,再根据Shard Group再根据SeriesKey哈希到Sjard)
  2. 倒排索引引擎构建倒排索引:InfluxDB中Shard由两个LSM引擎组成–倒排索引引擎和TSM引擎,时序数据会首先构建倒排索引
  3. TSM引擎持久化数据:TMS处理数据流程基本和LSM一样,先将请求写入WAL日志文件,再写入Cache,一旦达到阈值就将Cache中的数据flush落盘形成TSM文件

2.批量时序数据Shard路由

批量数据写入InfluxDB之后做的第一件事情是分组**,将时序数据点按照所属shard划分为多组(称为Shard Map)**,每组时序数据点将会发送给对应的shard引擎并发处理

入库流程还是之前的shard策略(range+Hash),即按时间分片,比如7天一个分片的话,最近7天的数据会分到一个shard,一周前到两周前的数据会被分到上一个shard,以此类推;在时间分片的基础上还可以再执行Hash Sharding,按照SeriesKey执行Hash(保证同一个SeriesKey对应的所有数据都落到同一个shard),再将数据分散到指定的多个shard中。

当然,经过笔者深进一步了解,发现单机InfluxDB只有第一层sharding,即只有根据时间进行Range Sharding,并没有执行Hash Sharding。Hash Sharding只会在分布式InfluxDB中才会用到。

3.倒排索引引擎构建倒排索引

InfluxDB中倒排索引引擎使用LSM引擎构建,LSM引擎非常适合写多读少的场景,例如HBase、Kudu都是使用的LSM存储引擎,InfluxDB的戴欧索引引擎既然使用的是LSM,那么流程必然是:

  1. 先写WAL以及Cache:数据写入WAL再写入Cache,就返回写入成功,WAL可以保证即使发生异常宕机也可以恢复Cache丢失的数据(MySQL也有类似的double writer机制)
  2. Cache的flush:一旦满足特定条件系统会将Cache中的时序数据执行flush操作落盘形成文件,文件数量超过一定的阈值就合并成一个大文件

具体流程如下:

3.1.WAL追加写入

Inverted Index WAL的文件格式如下,由一个个LogEntry组成,用于记录数据的每一次的写入,如下图:

image-20210531214808990

一个LogEntry由如下组成

  • Flag:更新类型,写入或删除等
  • Measurement:表示数据表
  • Key/Value:表示写入的Tag Set和Checksum。Checksum用于根据WAL恢复数据时验证LogEntry的完整性

3.2.Cache的写入( Inverted Index在内存中构建)

  1. 拼接seriesKey:时序数据写入到系统之后先将measurement和所有的维度值拼成一个seriesKey
  2. 确认seriesKey是否已经构建过索引:在文件中确认seriesKey是否存在,这就是Series Block中Bloom Filter的核心作用,首先使用Bloom Filter进行判断,如果不存在,肯定不存在。如果存在,不一定存在,需要进一步判断。再进一步使用B+树以及HashIndex进一步查找判断
  3. 如果seriesKey在文件中不存在,则将其写入内存倒排索引。倒排索引内存结构主要包含两个Map:<measurement, List> 和 <tagKey, <tagValue, List>>。InfluxDB中SeriesKey就是一把钥匙,只有拿到这把钥匙才能找到这个SeriesKey对应的数据。而倒排索引就是根据一些线索去找这把钥匙

3.3.flush(Inverted Index Cache Flush流程)

触发时机:当Inverted Index WAL日志的大小超过阈值(默认5MB),就会执行flush操作将缓存中的两个Map写成文件

基本流程

  1. 缓存Map排序:<measurement, List>以及<tagKey, <tagValue, List>都需要经过排序处理,排序的意义在于有序数据可以结合Hash Index实现范围查询,另外Series Block中B+树的构建也需要SeriesKey排序。
  2. 构建并持久化Series Block:在排序的基础上首先持久化<tagKey, tagValue, List>结构中所有的SeriesKey,也就是先构建Series Block。依次持久化SeriesKey到SeriesKeyChunk,当Chunk满了之后,根据Chunk中最小的SeriesKey构建B+树中的Index Entry节点。当然,Hash Index以及Bloom Filter是需要实时构建的。需要注意的是,Series Block在构建的同时需要记录下SeriesKey与该Key在文件中偏移量的对应关系,即<SeriesKey, SeriesKeyOffset>,这一点至关重要。
  3. 内存中将SeriesKey映射为SeriesId:将<tagKey, <tagValue, List>结构中所有的SeriesKey由上一步中得到的<SeriesKey, SeriesKeyOffset >中的SeriesKeyOffset代替。形成新的结构:<tagKey, <tagValue, List>,即<tagKey, <tagValue, List>>,其中SeriesKeyId就是SeriesKeyOffset。
  4. 构建并持久化Tag Block:在新结构<tagKey, <tagValue, List>>的基础上首先持久化tagValue,将同一个tagKey下的所有tagValue持久化在一起并生成对应Hash Index写入文件,接着持久化下一个tagKey的所有tagValue。所有tagValue都持久话完成之后再依次持久化所有的tagKey,形成Tag Block。
  5. 构建并持久化Measurement Block:最后持久化measurement形成Measurement Block。

4.时序数据写入流程

数据写入引擎也是用的TSM类似于LSM的引擎,写入流程也大致如下:先写WAL、再写Cache、最后满足一定条件的阈值后,将Cache中的数据flush到文件

4.1.WAL追加写入

时间线数据数据会经过两重处理,首先格式化为WriteWALEntry对象,该对象字段元素如下图所示。然后经过snappy压缩后写入WAL并持久化到文件

image-20210531224747429

4.2.写入Cache(时序数据写入内存结构)

  1. 时序数据点格式化:将时序数据点point按照时间线性组织成一个Map,<SeriesKey+FieldKey, List>,即将相同的Key(SeriesKey+FieldKey)的时序数据集中放在一个List中并写入TSM File的block中

  2. 时序数据点写入Cache:InfluxDB中Cache是一个crude hash ring(一致性hash),这个ring由256个partition组成,每个partition负责存储一部分时序数据key对应的值,就相当于数据写入Cache的时候又根据Key Hash了一次,根据Hash结果映射到不同的partition。

    这样做的原因:个人认为有点像Java中ConcurrentHashMap的思路,将一个大HashMap切分成多个小HashMap,每个HashMap内部在写的时候需要加锁。这样处理可以减小锁粒度,提高写性能。

4.3.Flush流程(Data Cache Flush流程)

触发时机:

  1. Cache大小超过一定阈值,可以通过参数’cache-snapshot-memory-size’配置,默认是25M大小。
  2. 超过一定时间没有数据写入WAL,默认时间间隔是10分钟,以通过参数’cache-snapshot-write-cold-duration’配置

基本流程

  1. 内存中构建Series Data Block:顺序遍历Map:<SeriesKey+FieldKey, List>中的时序数据,分别对时序数据的时间列和数值列进行相应的编码,按照Series Data Block的格式进行组织,当Block大小超过一定的阈值就构建成功,并记录这个Block

  2. 将构建好的Series Data Block写入文件:使用输出流将内存中的数据输出到文件,并返回Block在文件中的偏移量offset以及总大小Siez

  3. 构建文件级别B+树索引:在内存中为该Series Data Block构建一个索引节点Index Entry,使用数据Block在文件中的偏移量offset、总大小size以及MinTime,MaxTime构建一个Index Entry对象,写入到内存搞Series Index Block

    image-20210527202648094

这样每构建一个Series Data Block并写入文件后都会在内存中顺序构建(注意是顺序,代表这里的B+树是顺序写,这样的再平衡就会快很多,并且也可以利用到B+树查询的快速)一个Index Entry,写入内存Series Index Block对象,一旦一个Key对应的所有时序数据都持久化完成,一个Series Index Block就构建完成(因为一个Series Index Block只会存储一个SeriesKey的数据),构建完成之后填充Index Block Meta信息。接着新建一个新的Series Index Block开始构建下一个Key的对应的数据索引信息

5.InfluxDB数据删除(DropMeasurement,DropTagKey)

一般LSM存储引擎采取的删除操作通常都是Tag标记的方式,即删除操作和写入流程一致,只是数据上会多一个Tag标记 – deleted,表示该值已经被deleted。但是这种方案删除的代价是变低了,但是Tag方案在读取的时候需要对标记有deleted的数值进行特殊处理,这个代价还是很大的,但是InfluxDB采取的方案是通常不会删除一条记录,而是会删除某段时间或某个维度下的数据,甚至一张表

InfluxDB一共有两个LSM引擎,一个是倒排索引引擎(存储维度列到SeriesKey的映射关系,方便多维度查找),一个是TSM 引擎(用于存储时序数据),如果只删除一条记录,倒排索引引擎不需要操作,但是如果删除Measurement的话,两个引擎就都需要操作:

  • TSM采取的是同步删除策略
  • 倒排索引引擎采取标记删除策略

image-20210602231453261

5.1.TSM 引擎的删除操作

同步删除策略流程如下:

  1. TSM FIle Index相关处理:在内存中删除Index Entry,通常删除会带有时间筛选或key筛选,而且TSM File Index会在引擎启动之后加载到内存。因此删除操作会将满足条件的Index Entry从内存中删除

  2. 生成tombstoner文件:tombstoner文件会记录当前TSM File中所有被删除的时序数据,时序数据用[key, min, max]三个字段表示,其中Key即SeriesKey+fileKey,[min, max]表示要删除的时间段

    image-20210602231503645

  3. 删除Cache中满足条件的Series

  4. 在WAL中生成一条删除series的记录并持久化到硬盘

5.2.倒排索引引擎的删除操作

标记Tag删除策略,标记Tag删除非常简单,和一次写入流程基本相同

  1. 在WAL中生成一条flag为deleted的LogEntry并持久化到硬盘
  2. 将要删除的维度信息写入Cache,需要标记deleted(设置type=deleted)
  3. 当WAL大小超过阈值之后标记为deleted的维度信息会随Cache Flush到倒排索引文件
  4. 和HBase一样,Inverted Index Engine中索引信息真正被删除发生在compact阶段
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈哈哈张大侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值