InfluxDB数据的存储

LSM Tree

LSM Tree (Log-Structured Merge Tree) 即日志合并树,被用于大量的数据库引擎中,如Hbase、LevelDB等。适用于海量数据的写入,而查询少的情况。主要思想是随机写转化为顺序写。基本流程为,最新的数据驻留在内存中,等到积累到足够多之后,再内存中有序的数据合并追加到磁盘队尾。为了解决防止数据丢失,使用WAL(Write Ahead Log)方式,写入内存的同时写入文件,用来恢复内存中的数据。以 LevelDB 为例,内存 中的数据达到指定阀值后会在写入一个新的文件。当某一层的文件数超过一定值后,就会将该层下的一个文件和更高一级的文件合并,由于 文件数据都是有序的,相当于是一个多路归并排序,所以合并操作相当快速,最终生成一个新的 文件,将旧的文件删除,这样就完成了一次合并过程。这也大概是为什么叫Level的的原因吧。

这里推荐几篇优秀的LSM Tree文章

1. LSM-Tree VS B-Tree

2. 野猪书读书笔记第三章

InfluxDB 存储架构

InfluxDB在经历了LSM Tree、B+Tree等几种尝试后,最终自研TSM,TSM全称是Time-Structured Merge Tree,思想类似LSM。我们先看它的整体架构:
在这里插入图片描述

名词解释
  1. Shard
    上一篇文章中提到过这个概念,InfluxDB 中按照数据的时间戳所在的范围,会去创建不同的Shard Group,而Shard Group中会包含一个至多个Shard,单机版本中只有一个Shard。每一个 shard 都有自己的 cache、wal、tsm files 以及 compactor。

  2. WAL
    Write ahead log 作用就是为了防止系统崩溃导致的数据丢失。WAL是一种写优化的存储格式,允许持久写入,但不用于查询。一般我们指的是数据的WAL,对应的是索引数据的WAL即后面会提到的TSL文件。

  3. Cache
    Cache是WAL中存储的数据的内存表示形式。缓存的目的是使WAL中的数据可查询。每次将Points写入WAL段时,也会将其写入内存中的缓存。当缓存的数据达到阈值,会讲缓存中的数据写入TSM,同时删除对应的WAL文件。

  4. TSM
    真正数据存储的实现,见后文分析

  5. Compactor
    compactor 组件,作用是将关闭的WAL文件的数据写入TSM文件,并删除WAL文件。将较小的TSM文件合并为较大的文件。

文件目录介绍

Linux版本的数据文件位于/var/lib/influxdb/,Windows位于C:/用户/.influxd/下。主要有三个目录,分别是 meta, wal 以及 data 。meta 用于存储数据库的一些元数据,meta.db 文件。wal 目录存放预写日志文件,以 .wal 结尾。data 目录存放实际存储的数据文件,以 .tsm 结尾。整体结构如下所示:

influxdb/
  ├── data/
  |    └── [DB name]/
  |        └── [retention policy]/
  |        |    └── [shard group id]/
  |        |        ├── 000000001-0000000001.tsm
  |        |        └── fields.idx
  |        |		    └── index/
  |        |			      └── 0/
  |        |			          └── L0-000001.tsl 
  |        |			          └── MANIFEST
  |        |			      └── .../
  |        |			      └── 7/
  |        └── _series
  |            └──0/
  |              └──0000
  |            └──.../
  |            └──7/
  ├── meta/
  |    └── meta.db
  └── wal/
      └── [DB Name]/
          └── [retention policy]/ 
              └── [shard group id]/ 
                  └── _00001.wal
WAL

新的Ponits到来时,会将被序列化,使用Snappy压缩并通过fsync写入WAL文件,然后在加入到内存中。一个WAL文件被称为一个 segment。写入文件的格式基于TLV(Type-length-value)标准。其中第一个字节代表条目的类型(写或删除),一个4字节uint32代表压缩块的长度,然后是压缩块。文件大小达到10M,则关闭才文件并开一个文件。当Cache对应的WAL文件中的数据写入TSM文件中后会删除对应的WAL文件。压缩块的格式也是有讲究的,比如执行了insert log,server="s1" load=0.8,mem="2000m",我们知道server属于tag,load和mem的属于field,那么该语句产生的block块为(当然是解压前的格式)

1 23 log,server="s1"#!~#load 1 15784946436443802001 0.8 4 22 log,server="s1"#!~#mem 1 15784946436443802001 2000m   

下面是压缩前的格式描述,压缩是将数据写byte数组后压缩

TypeKey LenKeyCountTimeValueType
1 byte2 byteN byte4 byte8 byteN byte1 byte

对照着上面的真实数据,Type值得field 的类型有五种类型int long string等,key指的是series key + filed,Count指的是filed value 的数量,因为我们这里只是插入一条数据,因此count是1,Value为field 的value,注意...中是Time和Value成对出现。接着就是下一个field的数据。另外一个ponit的所有filed value放入一个压缩块中。

Series File

_series目录保存了所有当前库下所有的Series key与Series ID的映射关系, ID<-> series key数据基于ID求余分区,分到8个目录中,在分区目录中在进行分文件存储,即超过256M就新开一个文件,防止文件过大。分区中的每个文件又称为Series segment。下面看segment的存储格式

MagicversionEntryEntry
4 bytes1 bytesNN

Magic占4byte,固定值为SSEG,意思就是Series segment,主要看Entry结构。

FlagSeriesIdSizeMeasurementSizeMeasurementTagCountTags
1 byte8 byteN bytes2 byteN byteN byteN byte

Flag标记Series key是新加还是删除,SeriesId对应一个int64。Size表示Series key的长度,TagCount表示tag的数量。这里用了varint变长编码,Series key的长度基本上会小于127,因此这种编码压缩方式基本上只会占用一个字节,TagCount同理。Tags是由多个Tag组成,具体格式如下。

Tag key sizetag keyTag value sizetag value
2 bytesN byte2 bytesN byte

当新的SeriesKey到来,根据(ID-1)%8分区追加到对应的分区文件中。同时内存中保留了索引, 每个分区对应两个map。

keyIDMap    *rhh.HashMap // Series key到ID
idOffsetMap map[SeriesID]int64 // ID到Series key 在Series segment file中的坐标即索引信息

当然这两个map不会保存全部的key/value,当map中元素数量到达128k,将索引数据写入个Series Sement同目录下的Index文件,每一个分区都对应一个Index文件。

Index文件有三部分组成,分别是Header,OffsetIDBolck、IDOffsetBlock。

MagicVersionMaxSeriesIDMaxOffsetSeriesIDcountCapacityOffsetIDBolckOffsetOffsetIDBolckSizeIDOffsetBlockOffsetIDOffsetBlockSizeOffsetIDBolckIDOffsetBlock
4 byte1 byte8 byte8 byte8 byte8 byte8 byte8 byte8 byte8 byteEntrys(Offset ID)Entrys(ID Offset)

其中Header中的10个属性比较直观,Magic固定位SIDX,Capacity代表Block中的数量。两个Block中的Entry并不是连续存储。Entry中的Offset 和ID分别占用8个Byte。两个Block通过mmap映射,Entry通过hash存储。hash的key分别为Series key和Series ID。比如通过key找ID,先从keyIDMap中看是否存在,如果不存在则计算出hashcode(一个int值),计算出该key在OffsetIDBolck中的偏移量,取出entry从而得到id。如果通过ID找Key,先从idOffsetMap中看是否存series key在Series Segment中的偏移量,如果不存在则去IDOffsetBlock中取偏移量。然后再去Series Segment中拿到series key。

TSM File

WAL中的数据会不间断的写入TSM file,TSM之间也会不断的合并。一个TSM文件由四个部分组成:head,block,index和Footer。

HeaderDataBlocksIndex BlocksFooter
Magic 4byte Version 1byteN bytesN bytes4 bytes

Head用于标识文件类型和版本号。

  • Magic: 用于区分是哪一个存储引擎,目前使用的 tsm1 引擎,MagicNumber 为 0x16D116D1。
  • Version (1 byte): 目前是 tsm1 引擎,此值固定为 1

Data Blocks 内部是一些连续的 Block,block 是 InfluxDB 中的最小读取对象,每次读取操作都会读取一个 block。每一个 Block 分为 CRC32 值和 Data 两部分,CRC32 值用于校验 Data 的内容是否有问题。Data 的长度记录在之后的 Index 部分中。

每个block内存储的是某个TimeSeries的一段时间范围内的值,即某个时间段下measurement+tag set+field的值,Block内部会根据field的不同的值的类型采取不同的压缩策略,以达到最优的压缩效率。Block具体格式如下

CRCFieldValueTypeTimeStamp SizeTimeStampsvalues
4 bytes1 bytesN byteN byteN byte
  • FieldValueType: 表示该DataBlock存储的FieldValue类型,InfluxDB中存在5中FieldValueType: Float, Integer, Unsigned, Boolean, String
  • TimestampSize: 表示TimeStamps block的长度,使用可变长编码
  • Timestamp: 时间为递增,使用delta-delta编码压缩
  • Value: 将FieldValue值按列进行压缩,不同类型的FieldValue使用不同的压缩算法

Index Block是对key的索引,首先按键字典顺序排列,然后按时间排序。key就是Series key + 分隔符 + Field name。有些key对应的value数据放在多个data block中,那么该index block就会有多个index entry。

Key LenKeyTypeCountIndex Entry
2 byteN byte1 byte2 byte28 byte
  • **Key Len **: key 的长度。
  • **Key **: 这里的 key 指的是 Series Key + 分隔符 + fieldName。
  • Type: FieldValue 的类型,也就是 Block 中 Data 内的数据的类型。
  • Count : Index Entry的个数

Index Entry 格式如下

Min TimeMax TimeOffsetSize
8 byte8 byte8 byte4 byte
  • Min Time: block 中 最小时间戳。
  • Max Time : block 中最大时间戳。
  • Offset : block 在整个 TSM File 中的偏移量。
  • Size : block 的大小。根据 Offset + Size 字段就可以快速读取出一个 block 中的内容。

Footer用于存储索引起点的偏移量占8 bytes ,方便将索引信息加载到内存中。

为了方便查到Index Block中的key,在内存中有对每个Index Block的地址索引,因为Index Block时有序的,但IndexBlock的大小不确定,因此记录下每个Block的偏移地址方便进行二分查找。结构如下:

 ┌────────────────────────────────────────────────────────────────────┐
 │                              Offsets                               │
 ├────┬────┬────┬─────────────────────────────────────────────────────┘
 │ 0  │ 62 │145 │
 └────┴────┴────┘
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值