SSTable
SSTable是一种拥有持久化,有序且不可变的的键值存储结构
它的key和value都是任意的字节数组,并且了提供了按指定key查找和指定范围的key区间迭代遍历的功能。
结构(key有序)
SSTable内部包含了一系列可配置大小的Block块,典型的大小是64KB,关于这些Block块的index存储在SSTable的尾部,用于帮助快速查找特定的Block。当一个SSTable被打开的时候,index会被加载到内存,然后根据key在内存index里面进行一个二分查找,查到该key对应的磁盘的offset之后,然后去磁盘把响应的块数据读取出来。当然如果内存足够大的话,可以直接把SSTable直接通过MMap的技术映射到内存中,从而提供更快的查找
查找过程
先取出来Index,通过key进行二分查找,找到对应的offset(偏移量),然后把磁盘里对应的Block(块数据)取出来。
LSM
- 一种分层,有序,面向磁盘的数据结构
组织架构:
Memory(active memtable、immutable memtable、block cache)、Disk(sstable、WAL)
active memtable:活跃的内存表
immutable memtable:不变的内存表
block cache:缓存
sstable:有序表
WAL:预写日志
-
有序性
虽然sstable是有序的,但同一level中不同的sstable可能会出现key重叠,这取决于compaction策略。
size-tiered compaction:同一level的sstable会出现重叠
leveled compaction:level0以上的同一level的sstable不会出现重叠
key重叠会导致读变大 -
写:
- 写active memtable、WAL;2. active memtable写满后变为immutable memtable,并flush到磁盘,成为L0 sstable 3. 每L的sstable体积达到一定数量或大小,进行compaction操作, 合并到更高L去
- 读
- 读取active memtable 2. 读取immutable memtable 3. 读取block cache 4. 前三步都是读内存,下面读磁盘;依次读取每层的sstable(读取sstable时使用二分查找)
- 如果读取一个不存在的key,开销很大,所以可以维护一个布隆过滤器。
- Compaction
随着sstable的不断写入,系统打开的文件就会越来越多,并且对于同一个key积累的数据改变(更新、删除)操作也就越多。由于sstable是不可变的,为了减少文件数并及时清理无效数据,就要进行compaction操作,将多个key区间有重合的sstable进行合并。
但compaction操作非常消耗CPU和磁盘IO,所以需要调整Compaction操作的启用时间。 - 空间放大
指存储引擎的数据实际占用的磁盘空间比数据的真正大小偏多的情况。例如真实数据是10M,但实际存储耗费了25M,空间放大因子就是2.5
为什么?
LSM存储引擎中的数据只会增加,更改和删除操作都不是in-place的,需要等待compaction执行到对应的key才可以。所以一个key可能会对应多个value(删除标记也算一个特殊的value),而只有一个value是有效的(最新的哪个),其余的都算空间放大。
另外,在Compaction时,原始数据在执行完成之前不能删除(防止出现意外无法恢复),所以同一份被Compaction的数据最多可能膨胀为2倍,这也算空间放大。 - 写放大
一个数据会随着Compaction过程向更高的层重复写入,有多少层就写多少次。 - 读放大
读放大是指:读取一次数据会产生多次的io,即为读放大
SSL读取顺序为内存->存储0level->存储nlevel,最坏的可能要读取到n level(每一层io一次)。
另外,如果是STCS 策略(size-tiered compaction),每一层的sstable会存在key重叠,最坏的情况要遍历所有的sst才能获得结果(每一层io次数=这一层sstable的数量)
参考:
https://blog.csdn.net/u010454030/article/details/90414063
https://www.jianshu.com/p/e89cd503c9ae?utm_campaign=hugo
TSM - 概念
tag与field区别
tag: 建立索引,不必须,常用作筛选条件
field:不建立索引,必须,不推荐用作筛选条件
InfluxDB概念
InfluxDB 不是一个完整的 CRUD 数据库,而是更像一个 CR-ud,将创建和读取数据的性能优先于更新和销毁,并防止一些更新和销毁行为以使创建和读取性能更高:
要更新一个点,请插入一个具有相同测量值、标签集和时间戳的点。
您可以删除或删除一个系列,但不能基于字段值删除单个点。作为一种解决方法,您可以搜索字段值,检索时间,然后根据time字段删除。
您还不能更新或重命名标签 - 请参阅 GitHub 问题#4157了解更多信息。要修改一系列点的标记,请找到具有违规标记值的点,将值更改为所需的值,将点写回,然后删除具有旧标记值的系列。
您不能按标签键(而不是值)删除标签 - 请参阅 GitHub 问题#8604。
_time | _measurement | location | scientist | _field | _value |
---|---|---|---|---|---|
2019-08-18T00:00:00Z | census | klamath | anderson | bees | 23 |
2019-08-18T00:00:00Z | census | portland | mullen | ants | 30 |
2019-08-18T00:06:00Z | census | klamath | anderson | bees | 28 |
2019-08-18T00:06:00Z | census | portland | mullen | ants | 32 |
Timestamp(时间戳)
存储字段_time
,
每条数据都有的列,磁盘上存储精确到纳秒。
写入数据时,需要注意时间戳的精度
Measurement(测量)
存储字段_measurement
,字符串格式
充当temstamp、tags、field的容器
注:便于理解,可以认为是一个表
Fields(字段**必须
**)
包括字段键_field
、和字段值_value
- Field Key(字段键)
表示字段名称的字符串。
上述例子中,bees
和ants
是字段键 - Field value
表示关联字段的值
支持:string、float、integer、boolean类型
样本数据中的字段值显示指定时间的bees
(蜜蜂数量)23和28,以及指定时间的ants
(蚂蚁数量)30和32。 - Field Set(字段集)
字段集是与时间戳关联的字段键值对的集合。样本数据包括以下字段集
census bees=23i,ants=30i 1566086400000000000
census bees=28i,ants=32i 1566086760000000000
-----------------
Field set
Tags(标签**非必须
**)
标签包括存储为字符串和元数据的标记键和标记值。
样本数据中,列location、scientist都是标签
不建议讲包含高度可变信息(如 UUID、哈希和随机字符串)设置为标签,这样会导致high series cardinality
(高系列技术),会导致数据库内存负载大幅度增高
- Tag key(标签键)
样本数据中的标签键是location和scientist - Tag value(标签值)
标签键location有两个标签值:klamath和portland。标签键scientist也有两个标签值:anderson和mullen。 - Tag set(标签集)
标签键值对的集合
样本数据包括以下四个标签集
location = klamath, scientist = anderson
location = portland, scientist = anderson
location = klamath, scientist = mullen
location = portland, scientist = mullen
Bucket schema(存储桶架构)
在InfluxDB云中,具有显式模式类型的bucket需要为每个度量提供显式模式。测量值包含标签、字段和时间戳。显式模式约束可写入该度量的数据的形状。
以下是measurement census的架构
name | type | data_type |
---|---|---|
time | timestamp | |
location | tag | string |
scientist | tag | string |
ants | field | integer |
bees | field | integer |
Series(系列)
- series key(系列键)
一个系列键是measurement
(共享测量值)、tag set
(标记集)和field key
(字段键)的点的集合(注意,不包括字段值)
例如,示例数据包括两个唯一的序列键
_measurement | tag set | _field |
---|---|---|
census | location=klamath,scientist=anderson | bees |
census | location=portland,scientist=mullen | ants |
- series (系列)系列键下的集合,包括时间戳、field(value)
一个序列包括给定序列键的时间戳和字段值。
从示例数据中,这里有一个系列键和相应的系列:
# series key(系列键)
census,location=klamath,scientist=anderson bees
# series (系列)
2019-08-18T00:00:00Z 23
2019-08-18T00:06:00Z 28
Point(点)
一个点包括序列键、字段值和时间戳(其实就是一个系列的全值)。
例如2019-08-18T00:00:00Z census ants 30 portland mullen
Bucket(桶)
所有 InfluxDB 数据都存储在存储桶中。
存储桶结合了数据库的概念和保留期(每个数据点保留的持续时间)。
一个桶属于一个组织。
Organization(组织)
InfluxDB组织是一组用户的工作空间。
所有仪表板、任务、存储桶和用户都属于一个组织。
系列键(series key):measurement、tag(key和value)、field(key)相同的集合
系列(serie):系列键下的集合,包括时间戳、field(value)
点(point):包括序列键、field(value)、时间戳; 其实就是一个系列的全值
InfluxDb架构
InfluxDB actually looks like two databases in one, a time series data store and an inverted index for the measurement, tag, and field metadata.
InfluxDB看起来是两个数据库,一个时间序列数据存储,一个倒排索引(为了measurement、tag、field的元数据)
InfluxDb存储引擎包含四个组件:WAL、CACHE、TSM、TSI
influxdb基础TSM和TSI来提取数据,先从TSI查询series,再从TSM文件读取对应的points。
TSM和TSI主要充当存储引擎,类似于Innodb和Mysql的关系。
桶、分片、测量之间的关系
一个桶有多个分片组(按照时间范围划分)
一个分片组包含多个分片(按照集群的数据节点区分,这些分片的时间范围是一样的)
一个桶会有多个测量(按照数据维度划分)
一个测量会存储在多个分片里
一个分片会包含多个测量的数据
一份分片包含多个TSM文件
TSM
TSM Files:存储TimeSeries(measurement + tags,注意没有field key)一段时间内的所有point;
问题:类似于SSTable?
Shard(分片)
influxDB将数据存储到磁盘时,将数据组织成分片。每个分片都属于一个分片组。(正常单机程序一个分片组只有一个分片。在集群中,分片组包含分布在多个数据节点上的多个分片)
shard group duration(分片组持续时间):指定每个分片组的时间范围。
默认情况下influxDB会根据bucket(存储桶)的保留策略设置碎片组的持续时间。
bucket retention period(存储桶保留时间) | Default shard group duration (默认分片组持续时间) |
---|---|
两天内 | 1小时 |
2天-6个月 | 1天 |
六个月前 | 7天 |
也就是说,桶保留时间越短,分片组的时间范围就越小。比如桶的数据只保留两天,那每个分片组就只存储1个小时的数据
分片组
分片组属于InfluxDB bucket,包含由碎片组持续时间 定义的特定时间范围的时间序列数据。
当分片组持续时间为1天时,那每分片组就包含一天的数据,
shard group是从时间上,对shard做一个逻辑上的分组注意这里说的是逻辑上,shard group只是一个逻辑概念,在存储的时候,这些shard是平铺开的,并不是一个shard group的shard 存储在一起。
influxdb实现shardgroup时,实现的一些思想:
预先创建shardgroup,避免临时创建
shardgroup的时间是完全连续的,并且开始和结束时间都是shard duration的倍数。按照shard duration对齐。
分片
分片包含由 分片组持续时间 定义的给定时间范围的编码和压缩时间序列数据。
指定碎分组持续时间内的系列中的所有点都存储在同一个分片中。
单个分片包含磁盘上的多个series(系列)、一个或多个TSM文件,并且属于碎片组。
分片组图
下例为:bucket保留时间为4天,分片组持续时间为1天的桶数据分布
需要截图:https://docs.influxdata.com/influxdb/v2.4/reference/internals/shards/#shard-group-duration
分片写入
通常influxDB会把数据写入到最新的分片组(热分片),当分片不再被写入数据时会被压缩数据称为冷分片。
如果要回填历史数据,需要先把冷分片数据解压缩,回填结束后再重新压缩。
分片compaction(下面翻译成压缩,也可以理解为合并、压实都可以)
InfluxDB定期压缩分片内的数据来优化磁盘使用率。开启压缩功能后,每秒检查是否需要进行压缩。
分片的压缩分两种情况
- 压缩分片内所有TSM文件
一段时间(通过参数compact-full-write-cold-duration
控制)内没有数据写入 - 分级别压缩
有数据写入的分片,将对分片内的TSM文件进行分级别压缩。
分为四个级别:
级别 0 (L0): 日志文件 ( LogFile) 被视为级别 0 (L0)。一旦这个文件超过一个5MB阈值,InfluxDB 就会创建一个新的活动日志文件,并且前一个文件开始压缩成一个IndexFile. 第一个索引文件位于级别 1 (L1)。
级别 1 (L1): InfluxDB 将保存在内存缓存中的所有新写入数据刷新到磁盘到IndexFile.
级别 2 (L2): InfluxDB 通过将包含相同系列的多个块组合成一个或多个新文件中的更少块,将多达 8 个 L1 压缩文件压缩成一个或多个 L2 文件。
级别 3 (L3): InfluxDB 迭代 L2 压缩文件块(超过一定大小)并将包含相同系列的多个块组合成一个新文件中的一个块。
级别 4 (L4): 完全压缩InfluxDB 迭代 L3 压缩文件块,并将包含相同系列的多个块组合成一个新文件中的一个块。
分级压缩的优先级:
- 级别越低(文件被压缩的次数越少),压缩文件的权重越大
- 级别中可压缩的文件越多,压缩该级别的优先级就越高。如果每个级别的文件数量都相等就优先压缩低级别
- 如果较高级别有更多的压缩候选者,它可能会在较低级别之前被压缩。InfluxDB 将收集组的数量(要压缩成单个下一代文件的文件的集合)乘以每个级别的指定权重(0.4、0.3、0.2 和 0.1),以确定压缩优先级。
分片压缩相关的配置设置
以下配置设置对于负载不规则的系统特别有用,因为它们在高使用率期间限制压缩,并在负载较低期间让压缩赶上:
storage-compact-full-write-cold-duration
storage-compact-throughput-burst
storage-max-concurrent-compactions
storage-max-index-log-file-size
storage-series-file-max-concurrent-snapshot-compactions
storage-series-file-max-concurrent-snapshot-compactions
在具有稳定负载的系统中,如果压缩会干扰其他操作,通常情况下,系统的负载过小,并且配置更改不会有太大帮助。
分片删除
InfluxDB的保留强制服务会定期检查分片组是否早于其存储桶的保留期。一旦分片组的开始时间超过存储桶的保留期,InfluxDB 就会删除分片组和关联的分片和 TSM 文件。
在具有无限保留期的存储桶中,分片无限期地保留在磁盘上。
InfluxDB 只删除冷分片。如果回填数据超出存储桶的保留期,则回填数据将保留在磁盘上,直到发生以下情况:
分片返回冷状态。
保留强制服务删除分片组。
问题?
1、TSM里存储的内容和结构?
猜测:TSM里,每一个SSTable只存储一个field的value,其中,时间戳为key,field_value为value;格式如下: time1:value1;time2:value2;time3:value3