添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
坚强的鸵鸟  ·  docker ...·  1 年前    · 
唠叨的领结  ·  “ORA-01779: ...·  2 年前    · 
  • influxdb:go语言实现的高性能时序数据库。
  • OpenTSDB:Opentsdb是一个基于Hbase的时间序列数据库
  • promeseus: Prometheus 是一个监控系统,它不仅仅包含了时间序列数据库,还有全套的抓取、检索、绘图、报警的功能。仅支持单机,数据写入本地。
  • graphite
  • Druid是一个实时在线分析系统(LOAP)。其架构融合了实时在线数据分析,全文检索系统和时间序列系统的特点,使其可以满足不同使用场景的数据存储需求。
  • Beringei是Facebook在2017年最新开源的一个高性能内存时序数据存储引擎。其具有快速读写和高压缩比等特性。2015年Facebook发表了一篇论文《 Gorilla: A Fast, Scalable, In-Memory Time Series Database 》,Beringei正是基于此想法实现的一个时间序列数据库。Beringei使用Delta-of-Delta算法存储数据,使用XOR编码压缩数值。使其可以用很少的内存即可存储下大量的数据。
  • Elasticsearch 是一个分布式的开源搜索和分析引擎,适用于所有类型的数据,包括文本、数字、地理空间、结构化和非结构化数据。Elasticsearch 在 Apache Lucene 的基础上开发而成,由 Elasticsearch N.V.(即现在的 Elastic)于 2010 年首次发布。Elasticsearch 以其简单的 REST 风格 API、分布式特性、速度和可扩展性而闻名。
  • RedisTimeSeries 是 Redis 的一个扩展模块。它专门面向时间序列数据提供了数据类型和访问接口,并且支持在 Redis 实例上直接对数据进行按时间范围的聚合计算。
  • DophinDb:国产时序数据库。DolphinDB是一套轻量级的分布式数据库系统,具有非常高的写入吞吐量和非常低的查询时延,面对万亿级的数据量,可以实现毫秒级的在线查询延迟。同时也支持高可用和分布式事务。DolphinDB自带流计算功能,可以实时接入题主所说的行情数据,展开实时计算,并将原始数据和计算后的数据存入数据仓库。
  • 时序数据收集工具:

  • Telegraf是一个用于采集和上报指标的服务器程序,采集当前运行主机的指定指标,如,CPU负载等,通过标准的InfluxDB API上报InfluxDB。
  • LogStash
  • 时序数据处理相关的整条链路:

  • 数据收集系统:telegraf、logStash
  • 数据存储系统:influxdb
  • 数据分析系统:bosun,bes
  • 数据展示系统:grafana
  • 时序数据库选型

    时序数据库排名:

    如何选择一个适合自己的时间序列数据库

  • Data model:时间序列数据模型一般有两种,一种无schema,具有多tag的模型,还有一种name、timestamp、value型。前者适合多值模式,对复杂业务模型更适合。后者更适合单维数据模型。
  • Query language:目前大部分TSDB都支持基于HTTP的SQL-like查询。
  • Reliability:可用性主要体现在系统的稳定高可用上,以及数据的高可用存储上。一个优秀的系统,应该有一个优雅而高可用的架构设计。简约而稳定。
  • Performance:性能是我们必须考虑的因素。当我们开始考虑更细分领域的数据存储时,除了数据模型的需求之外,很大的原因都是通用的数据库系统在性能上无法满足我们的需求。大部分时间序列库倾向写多读少场景,用户需要平衡自身的需求。下面会有一份各库的性能对比,大家可以做一个参考。
  • Ecosystem:我一直认为生态是我们选择一个开源组件必须认真考虑的问题。一个生态优秀的系统,使用的人多了,未被发现的坑也将少了。另外在使用中遇到问题,求助于社区,往往可以得到一些比较好的解决方案。另外好的生态,其周边边界系统将十分成熟,这让我们在对接其他系统时会有更多成熟的方案。
  • Operational management:易于运维,易于操作。
  • Company and support:一个系统其背后的支持公司也是比较重要的。背后有一个强大的公司或组织,这在项目可用性保证和后期维护更新上都会有较大的体验。
  • 可以按照以下需求自行选择合适的存储:

  • 小而精,性能高,数据量较小(亿级): InfluxDB
  • 简单,数据量不大(千万级),有联合查询、关系型数据库基础:timescales
  • 数据量较大,大数据服务基础,分布式集群需求: opentsdb、KairosDB
  • 分布式集群需求,olap实时在线分析,资源较充足:druid
  • 性能极致追求,数据冷热差异大:Beringei
  • 兼顾检索加载,分布式聚合计算: elsaticsearch
  • 如果你兼具索引和时间序列的需求。那么Druid和Elasticsearch是最好的选择。其性能都不差,同时满足检索和时间序列的特性,并且都是高可用容错架构。
  • awesome-time-series-database

    时序数据库百花齐放、百家争鸣,有一个专门的网站用于比较各个时序数据库: xephonhq.github.io/awesome-tim…

    本文主要讲influxdb。

    mac下直接使用brew命令安装influxdb

    brew update
    brew install influxdb
    pip install influxdb //安装Python包,操作influxdb
    
    influxd # 启动server
    influx # 启动client,进入交互式命令行
    

    默认数据和配置存储在~/.influxdb目录下。

    和所有的数据库一样,influxdb支持交互式命令行和API两种客户端。

    influxd命令其实就相当于启动了一个HTTP服务,它的默认端口是8086。influxdb的API客户端都是通过HTTP的形式与服务器交互。

    Influxdb支持RESTful风格的接口,返回JSON格式的响应数据,并支持身份认证、JWT令牌、丰富的HTTP响应代码等。

    influxDB API接口及接口的定义描述如下图所示:

    curl -ig http://192.168.0.224:8086/ping
    

    database:数据库

    measurement:相当于表名,measurement字符串必须是一个合法的标志符,不能包含问号等特殊字符

    point:一个point相当于一个时刻对应的记录,一个记录有timestamp,tags,values三部分组成,一个measurement就是由很多条记录组成的。

    timestamp:时间戳,influxdb支持纳秒级别的时间戳,时间戳在influxdb的索引和查询中占据着至关重要的位置。同一个measurement,如果插入两条tags和时间戳都相同的数据,会发生数据覆盖。

    tags:一个measurement有若干个tags,tags可以看做一个map[string]string结构。

    列1、列2、列3....:各个字段及其对应的取值,表示当前时间系统表现出来的指标数值。

    retention_policy:存储策略,定义历史数据的删除间隔。可以为整个数据库创建默认过期间隔,也可以为每个measurement定义存储策略。

    series:不可再细分的线的集合,series=RetentionPolicy+measurement+tags。series 的 key 为 measurement + tags set 序列化字符串。series 相当于是 InfluxDB 中一些数据的集合,在同一个 database 中,retention policy、measurement、tag sets 完全相同的数据同属于一个 series,同一个 series 的数据在物理上会按照时间顺序排列存储在一起。

    type Measurement struct {
        Name       string `json:"name,omitempty"`
        fieldNames map[string]struct{}      // 此 measurement 中的所有 filedNames
        // 内存中的索引信息
        // id 以及其对应的 series 信息,主要是为了在 seriesByTagKeyValue 中存储Id节约内存
        seriesByID          map[uint64]*Series              // lookup table for series by their id
        // 根据 tagk 和 tagv 的双重索引,保存排好序的 SeriesID 数组
        // 这个 map 用于在查询操作时,可以根据 tags 来快速过滤出要查询的所有 SeriesID,之后根据 SeriesKey 以及时间范围从文件中读取相应内容
        seriesByTagKeyValue map[string]map[string]SeriesIDs // map from tag key to value to sorted set of series ids
        // 此 measurement 中所有 series 的 id,按照 id 排序
        seriesIDs           SeriesIDs                       // sorted list of series IDs in this measurement
    type Series struct {
        mu          sync.RWMutex
        Key         string              // series key
        Tags        map[string]string   // tags
        id          uint64              // id
        measurement *Measurement        // 这个series所属于的父measurement
    

    如上述代码所示,在Measurement结构体中:

  • 使用fieldNames来表示这个表所包含的全部字段,golang中没有Set结构,这里直接使用map[string]struct{}代替Set结构。
  • 使用seriesByTagKeyValue这个二维表来存储tagKV到Series的映射。seriesByTagKeyValue是一个二维表,第一维表示TagKey,第二维表示TagValue。当插入一条包含one=a,two=b两个tag的记录时,会创建一个包含one=a,two=b的Series。然后seriesByTagKeyValue['one']['a'].append(新创建的series),seriesByTagKeyValue['two']['b'].append(新创建的series)。当再插入一个包含one=a的记录时,创建一个series2,然后seriesByTagKeyValue['one']['a'].append(series2)。在以上插入过程中,Series如果已经存在,则不会执行重复的创建Series过程,SeriesIDs这个Series列表也会执行去重操作。
  • 当查询包含多个tagKV时,会首先根据seriesByTagKeyValue求出所有的seriesIDs,这个操作就是就seriesIDs的交集。

  • SeriesIDs:在上述seriesByTagKeyValue中,表中的value是一个Series列表,每个Series使用一个SeriesID进行表示。seriesID是一个int64.
  •  map[string]map[string]SeriesIDs
    

    每一条记录都可以使用“行协议”用一行字符串表示,行协议的单行文本表示一条时序数据,由表名、tag集、指标集和时间戳4部分组成,行协议的基本语法如下所示:

    如下表所示,展示了一个measurement中的数据

    时间戳TagValue
    时间戳城市气象台风级气温天气
    2020-13-01 10:18北京中国气象台112.5大雨
    2020-13-01 10:19北京美国气象台232.4大雪
    2020-13-01 10:20天津中国气象台1.522.3

    基于这个measurement可以进行查询:

  • 一段时间内,中国气象台预测的北京的天气状况
  • 一段时间内,各个气象台预测的北京的平均气温
  • 如下图,tags是device_id,values包括cpu、内存、温度、位置等信息。

  • schemaless:无需定义表结构,像mongodb一样直接存储。
  • 上手门槛低:基于SQL语句进行查询,节省学习成本。SQL-like查询语言,简化查询和聚合操作。InfluxDB自带多种函数使数据统计变得十分方便。
  • 基于go语言实现,并发支持良好
  • 高效的时间序列数据写入性能。自定义TSM引擎,快速数据写入和高效数据压缩。
  • influxdb存储基于文件系统,无额外存储依赖(OpenTSDB:我就用HBase怎么了?)。
  • 简单,高性能的HTTP查询和写入API。
  • 以插件方式支持多种协议,与各种数据收集工具、数据展示工具配合流畅。
  • 索引Tags,支持快速有效的查询时间序列。注意:tags过多会拖慢性能,例如不要为了记录用户修改个人信息的次数,把userId=xxxx作为tag。任何tagValue取值太多的字段都不应该被当做tag。
  • 存储策略有效去除过期数据。
  • 支持连续查询,连续查询自动计算聚合数据,使频繁查询更有效。
  • 支持丰富的数据类型(注意:整型数据,需要在数据后面加个i,否则会被当成浮点型)
  • 缺点:分布式版本闭源,不够开放,分布式集群需要购买或者自研。
  • InfluxQL

    influxdb支持类sql的语法进行数据查询,这种语言简称InfluxQL。

    InfluxQL支持SELECT语句、GROUP BY语句、INTO语句、正则表达式、SHOW语句、数据库管理语句、保留策略管理语句、DROP语句、持续查询、各种函数和数学运算符等。

    # 基本查询
    select cpu,mem from my_measurement;
    # 查询多个measurement
    select cpu,mem from my_measurement1,my_measurement2;
    # 查询特定数据库
    select cpu,mem from mydb.my_measurement;
    

    where查询

    select * from my_measurement where time>now()-1d and host='localhost'
    

    where语句中可以使用or和and连接多个条件。

    group by语句

    group by

    group by ,

    group by time()

    group by time(),

    order by语句

    order by time [ ASC | DESC ]

    select * from cpu_usage where hostname='localhost' and time>now()-1d and percent>10 order by time desc limit 3
    

    limit和offset,slimit和soffset

    limit和offset对记录进行限制

    slimit和soffset对分组进行限制

    例如获取cpu_usage中第10个host到第20个host的指标数据的第100条到第200条数据

    select * from cpu_usage where time>now()-1d group by hostname limit 100 offset 100 slimit 10 soffset 10
    

    influxdb中的时间描述

    绝对时间:

    rfc3339时间字符串:YYYY-MM-DDTHH:MM:SS.nnnnnnnnnZ,其中.nnnnnnnnnZ部分是可选的。

    简化版rfc3339时间字符串:YYYY-MM-DD HH:MM:SS.nnnnnnnnn,其中纳秒部分也是可选的

    使用“<数字><单位>”的方式描述时间,表示自从1970年1月1日到现在过去了多少时间。influxdb支持的时间单位包括:

  • ns 纳秒,如果是纳秒可以不带单位,默认单位就是纳秒。
  • ms 毫秒
  • select * from cpu_usage where time>123414s;
    select * from cpu_usage where time>'2019-08-26T19:02:00Z';
    

    相对时间:使用now()函数获取当前时间,然后加减一个时间段,时间段的单位如上所述。

    influxdb中的函数包括

    select count(distinct(value)) from cpu_usage;
    select mean(value) from cpu_usage where host='localhost';
    

    选择函数包括:

  • bottom():最小的n个指标值
  • top():最大的n个指标值
  • max(),min(),sample(),percentile():对value进行选择,取最值、百分比值、随机采样等。
  • first(),last():返回时间最旧和时间最新的两个记录
  • select TOP(value,4) from cpu_usage
    

    变换函数包括:

  • DERIVATIVE() 求导数
  • DIFFERENCE() 直接计算差
  • ELAPSED()
  • MOVING_AVERAGE() :滑动平均
  • NON_NEGATIVE_DERIVATIVE() :非负变化率
  • STDDEV() :标准差
  • 插入语句的格式为insert ,=,= =,=

     insert devops-idc-sz,host=server01 cpu=76.1,mem=0.83 1567158293000000000
    

    bes中的查询

    bes的查询语句

    SELECT sum("value") FROM "sc_0_go_goroutine_cn" WHERE  time>1602569220s AND time<1602570420s GROUP BY time(30s) fill(none)
    
    # 删除记录
    delete from  devops-idc-sz where "host"='server01' and  time=1567158293s
    # 删除包含特定tag的记录
    drop series from devops-idc-sz where "host"='server01'
    # 删除一张表
    drop measurement devops-idc-sz
    # 删除一个数据库
    drop database mydb
    

    元数据查询

    查询全部的measurements:SHOW MEASUREMENTS

    查询全部的tagKeys:SHOW TAG KEYS FROM "measurement_name"

    查询tagValues:SHOW TAG VALUES FROM "measurement_name" WITH KEY = "tag_key"

    存储策略是influxdb中至关重要的概念,一个influxdb数据库首先可以分为若干个存储策略,每个存储策略下面按照时间划分为若干个ShardGroup。

  • 数据过期是以shardGroup为单位执行的
  • 一个RetentionPolicy可以作用于多个ShardGroup
  • Shard是influxdb执行写入和查询的基本单位。Shard是InfluxDB的存储引擎实现,influxdb的存储引擎被称为TSM(Time Sort Merge Tree) Engine,负责数据的编码存储、读写服务等。TSM类似于LSM,因此Shard和HBase Region一样包含Cache、WAL以及Data File等各个组件,也会有flush、compaction等这类数据操作。
  • shard 在 InfluxDB 中是一个比较重要的概念,它和 retention policy 相关联。每个存储策略下会存在许多 shard,每个 shard 存储一个指定时间段内的数据,而且不重复,例如 7点-8点的数据落入 shard0 中,8点-9点的数据则落入 shard1 中。每个 shard 都对应一个底层的 tsm 存储引擎,有独立的 cache、wal、tsm file。

    映射关系:

  • ShardGroup按照时间划分
  • 一个ShardGroup内的Shard按照measurement+tags划分
  • RetentionPolicy意思是数据存储策略。不要以为RP只规定了数据的过期时间,RP在InfluxDB中是一个非常重要的概念,核心作用有3个:

  • 指定数据的过期时间
  • 指定数据副本数量
  • 指定每个分片大小:Shard Duration。
  • RP创建语句如下:

    CREATE RETENTION POLICY ON <retention_policy_name> ON <database_name> DURATION <duration> REPLICATION <n> [SHARD DURATION <duration> ] [DEFAULT]
    # demo
    CREATE RETENTION POLICY "one_day_only" ON "water_database" DURATION 1d REPLICATION 1 SHARD DURATION 1h DEFAULT
    # 查询一个数据库上的全部保留策略 
    show retention policies on telegraf 
    # 更改保留策略
    alter retention policy "rp-one-year" on "telegraf" duration 365d replication 1 default
    # 删除保留策略
    drop retention policy "rp-one-year" on
    
    
    
    
        
     "telegraf"
    

    其中retention_policy_name表示RP的名称,database_name表示数据库名称,duration表示TTL,n表示数据副本数。

    InfluxDB中Retention Policy有这么几个性质和用法:

    RP是数据库级别而不是表级别的属性。这和很多数据库都不同。

    每个数据库可以有多个数据保留策略,但只能有一个默认策略。

    不同表可以根据保留策略规划在写入数据的时候指定RP进行写入

    influxdb在LSM基础上实现了TSM用来管理数据存储。

    CentOS下influxdb默认配置路径为/etc/influxdb/influxdb.conf,influxdb数据存储主要有3个目录,分别是meta、wal和data。meta主要存放元数据,该目录下有一个meta.db文件;wal目录存放预写日志,以.wal结尾;data目录存放TSM文件,以.tsm结尾,持久化的数据就存储在这里。

    在同一个database中,retention policy、measurement、tag kv 完全相同的数据属于同一个 series(series 的 key 为 measurement + tags set 序列化字符串),同一个 series 的数据在物理上会按照时间顺序排列存储在一起。

    外部接口和内部存储的关系

    influxdb支持多个field,但是存储上influxdb会把它们分开存储。influxdb的内部存储只考虑一个field的情况。当查询时,如果查询多个field,会逐个处理每一个field,然后把它们拼起来。

    influxdb存储上以series的key作为主键进行存储,一次查询可能会变成查找多个series。当处理一个包含多个tags时,会根据多个tagKV求seriesIDs的交集,最终只查找合并后的seriesIDs,然后把它们进行拼接。一旦进入查询部分,measurement名称和tags就被seriesID替代了。

    wal(Write Ahead Log)是一种格式固定、写入速度超快的文件,它只支持append操作。

    WAL文件的作用:防止宕机导致内存中数据丢失。

    influxdb的WAL就是一系列格式为 _00xxx.wal 的文件,文件号单调递增,默认当超过10M时就会新写一个WAL文件,每个WAL文件都会存储经过压缩的数据。简言之,wal文件就是固定大小的日志文件。

    当WAL日志对应的数据被写入到TSM中后,WAL日志就可以删除了。

    wal每条记录的格式:

  • Type (1 byte) : 表示这个条目中 value 的数据类型,虽然influxdb支持多列存储,但是influxdb底层实现是多列拆开存储的,因此此处Type只表示一列的FieldName。
  • Key Len (2 bytes) : 指定接下来一个字符串key 的长度。
  • Key (N bytes) : 这里的 key 为 measument + tags + fieldName,也可以看做series+fieldName。
  • Count (4 bytes) : 表示接下来有多少个time、value对,表示同一个 key 下每个时间点的取值。
  • Time (8 bytes) : int64,单个 value 的时间戳,不管上层用什么时间单位,存储上都用int64。
  • Value (N bytes) : value 的具体内容,其中 float64, int64, boolean 都是固定的字节数存储比较简单,经过 Type 字段知道这里 value 的字节数。string 类型比较特殊,对于 string 来讲,N bytes 的 Value 部分,前面 4 字节用于存储 string 的长度,剩下的部分才是 string 的实际内容。
  • 注意:influxdb在存储数据的时候虽然支持多列,但是实际存储时列与列之间是分开存储的。查询的列越多,耗费时间线性增长。

    当一个新的Point数据被写入时,首先经过压缩写入到WAL中,然后会写入到内存的索引中,这意味着数据写入后立马可通过索引可见。

    Cache

    Cache就是WAL的内存表示,它在运行时可被查询并且与TSM中保存的文件进行合并。

    当缓存大小超过 cache-snapshot-memory-size 时会触发缓存数据写入到TSM文件,并删除对应的WAL段文件;当缓存大小超过 cache-max-memory-size 时,cache拒绝新的写入,避免内存不够用造成宕机。

    除了内存的阈值限制之外,缓存还会在 cache-snapshot-write-cold-duration 配置的时间间隔定期将缓存数据写入到TSM文件。通过重读所有WAL文件,Influxdb可以在启动时重建缓存。

    Cache就是WAL的内存表示,在内存中它就是一个map结构,其key就是 series key+fieldName,seriesKey就是measurement?tag1=value1&tag2=value2这样的字符串。

    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
    

    综合理解Cache和WAL:

  • 插入数据就是往WAL和Cache写数据
  • wal文件只是起到宕机恢复的作用
  • 当influxdb启动时,会遍历所有WAL文件构建Cache,这样保证系统出现故障也不会造成数据丢失。
  • TSM文件

    每个shard都有自己独立的TSM存储引擎,都有自己独立的TSM文件,单个TSM文件大小最大为 2GB,用于长期存放数据。

    TSM文件是influxdb数据存储的一系列只读文件集合,这些文件结构类似于leveldb中的SSTable,一个TSM文件格式如下:

  • Header:头部信息,4Byte magic字段+1Byte version字段。
  • Blocks:CRC(校验值)+数据存储字段,数据的长度在index字段存储。Blocks内部是一些连续的 Block,Block 是 InfluxDB 中的最小读取对象,每次读取操作都会读取一个 Block。每一个 Block 分为 CRC32 值和 Data 两部分,CRC32 值用于校验 Data 的内容是否有问题。Data 的长度记录在之后的 Index 部分中。
  • Index:索引按照(key,时间戳)来排序,key=measurement+tags+一个fieldName=sereisKey+fieldName。每个索引条目以key len和key开始(因为key是一个字符串,所以需要keyLen字段),然后是block的数据类型(例如可取值包括float,int,bool,string等,表示当前列的数据类型)以及该block包含的数据记录个数,之后是block的最小、最大时间戳(这对于按照时间检索至关重要),最后是block所在的文件偏移量以及block大小。TSM文件中每个block都有一个索引block条目,用于存储Block的元信息,从而可以快速定位Block。
  • footer:它是一个定长结构,存储了索引开头的offset,从而可以确定Blocks区域和Index区域的分界点。解析TSM文件时,首先读取定长的header确定版本号,这决定了采用哪种解析器。然后读取定长的footer来确定Index和Blocks分界点。最后读取Index信息来获取Blocks部分的数据格式,才能组成一个完整的TSM索引+数据信息。
  • Block对应的数据是经过压缩的,以减少存储空间。Block第一个字节表示当前列的数据类型,接下来是一个变长的整数(根据数值范围动态调整需要用几个字节来存储这个整数,类似redis),这个整数表示接下来的timestamp+value对数,最后是若干个Timestamp+Values。

    influxdb会针对不同类型数据采用不同压缩编码,比如时间戳、整型、浮点数和字符串等,字符串使用Snappy压缩进行编码,每个字符串连续打包然后压缩成一个较大的块。

    influxdb的底层查询可以归结为:给定seriesKey,查找timestamp在某个[beginTime,endTime]这个区间上fieldName字段上的取值。

    TSM文件中Index字段是有序的,它按照(seriesKey+fieldName,beginTime)这个二元组进行有序排列,因此所有的查询最终都会变成二分查找。

    如果能够把Index区域全部加载到内存,查询速度确实会快。但是Influxdb认为Index区域依旧太大,也是超出内存限制的。所以对于一个TSM文件,Influxdb需要在不把Index完全加载到内存的情况下执行查询。

    但是TSM文件中的Index区域中每个index是不定长的,无法执行二分查找(二分查找需要随机访问硬盘),所以需要在内存中构建index区域的offsets数组,表示每个index在Index区域中的偏移量,然后通过“offsets数组+硬盘读取”执行二分查找。

    找到了index之后,就可以根据index的详情找到要查询的数据在Blocks区域中的内容(index详情中描述了block的偏移量和长度)。

    数据删除在influxdb中是一个低效操作,特别是针对大数据量删除来说,并且只有等待数据合并时才会真正删除数据。

    删除数据时,需要更新三个地方:

  • WAL中内容的删除:数据删除通过向WAL写入删除条目
  • Cache中的删除是在内存中直接删除
  • TSM文件写入一个tombstone文件(墓碑文件),这些tombstone文件被用于在启动时忽略相应的block,以及在compaction时期移除已删除的数据。
  • 一次查询过程

    接下来从头讲一遍一次数据查询的过程,把存储引擎和基本概念串联起来。

    select one,two FROM cpu WHERE host='s01' and region='cn' AND time > now() - 10h
    

    从measurement结构体中的seriesByTagKeyValue根据映射获取全部的seriesIDs:对seriesByTagKeyValue['host']['s01']和seriesByTagKeyValue['region']['cn']执行集合求交集,得到一个seriesIDs,表示一个series列表。然后构建key,构建方法就是:key列表=series列表*field列表。最终,所有的查询都是基于key进行查询,然后对查询结果执行merge操作返回给用户。

    keyId,measurement结构体如下:

    type Measurement struct {
        Name       string `json:"name,omitempty"`
        fieldNames map[string]struct{}      // 此 measurement 中的所有 filedNames
        // 内存中的索引信息
        // id 以及其对应的 series 信息,主要是为了在 seriesByTagKeyValue 中存储Id节约内存
        seriesByID          map[uint64]*Series              // lookup table for series by their id
        // 根据 tagk 和 tagv 的双重索引,保存排好序的 SeriesID 数组
        // 这个 map 用于在查询操作时,可以根据 tags 来快速过滤出要查询的所有 SeriesID,之后根据 SeriesKey 以及时间范围从文件中读取相应内容
        seriesByTagKeyValue map[string]map[string]SeriesIDs // map from tag key to value to sorted set of series ids
        // 此 measurement 中所有 series 的 id,按照 id 排序
        seriesIDs           SeriesIDs                       // sorted list of series IDs in this measurement
    

    在查询过程中,需要经过RetentionPolicy+ShardGroup+Shard三层映射,这些映射都是根据时间戳来的,从而把查询key的任务分发给了各个shard,问题转化为在shard内部的查询。

    当对一个shard进行查询时,加载shard对应的tsm文件中的index部分,在内存中构建索引,执行二分查找确定key+timestamp所对应的范围,得到beg和end。在tsm文件的index部分获取索引的详情,从而得到block的地址,解压各个block就得到时序数据。

    Compactor

    compactor是压缩器,在后台持续运行,每隔 1 秒会检查一次是否有需要压缩合并的数据。

    它主要进行两种操作:

  • Cache=>TSM文件。当 cache 中的数据大小达到阀值后,进行快照,以后转存到一个新的 tsm 文件中。
  • 合并压缩TSM文件:将多个小的 tsm 文件合并成一个,使每个文件尽可能达到单个文件的最大大小,减小文件的数量,而且一些数据的删除操作也是在这个时候完成。
  • 数据压缩过程是一个将write-optimized格式优化为read-optimized的格式的过程,Influxdb的压缩包括多种压缩过程。

  • LevelCompaction:InfluxDB将TSM文件分为4个层级(Level 1-4),compaction只会发生在同层级文件内,同层级的文件compaction后会晋升到下一层级。从这个规则看,根据时序数据的产生特性,level越高数据生成时间越旧,访问热度越低。由Cache数据初次生成的TSM文件称为Snapshot,多个Snapshot文件compaction后产生Level1的TSM文件,Level1的文件compaction后生成level2的文件,依次类推。低Level和高Level的compaction会采用不同的算法,低level文件的compaction采用低CPU消耗的做法,例如不会做解压缩和block合并,而高level文件的compaction则会做block解压缩以及block合并,以进一步提高压缩率。
  • IndexOptimizationCompaction: 当Level4的文件积攒到一定个数后,index会变得很大,查询效率会变的比较低。影响查询效率低的因素主要在于同一个TimeSeries数据会被多个TSM文件所包含,所以查询不可避免的需要跨多个文件进行数据整合。所以IndexOptimizationCompaction的主要作用就是将同一TimeSeries下的数据合并到同一个TSM文件中,尽量减少不同TSM文件间的TimeSeries重合度。
  • FullCompaction: InfluxDB在判断某个Shard长时间内不会再有数据写入之后,会对数据做一次FullCompaction。FullCompaction是LevelCompaction和IndexOptimization的整合,在做完一次FullCompaction之后,这个Shard不会再做任何的compaction,除非有新的数据写入或者删除发生。这个策略是对冷数据的一个规整,主要目的在于提高压缩率。
  • reporting-disabled = false

    bind-address = "127.0.0.1:8088"

    [meta]

    dir = "/Users/bytedance/.influxdb/meta"

    retention-autocreate = true

    logging-enabled = true

    [data]

    dir = "/Users/bytedance/.influxdb/data"

    index-version = "inmem"

    wal-dir = "/Users/bytedance/.influxdb/wal"

    wal-fsync-delay = "0s"

    validate-keys = false

    query-log-enabled = true

    cache-max-memory-size = 1073741824

    cache-snapshot-memory-size = 26214400

    cache-snapshot-write-cold-duration = "10m0s"

    compact-full-write-cold-duration = "4h0m0s"

    compact-throughput = 50331648

    compact-throughput-burst = 50331648

    max-series-per-database = 1000000

    max-values-per-tag = 100000

    max-concurrent-compactions = 0

    max-index-log-file-size = 1048576

    series-id-set-cache-size = 0

    series-file-max-concurrent-snapshot-compactions = 0

    trace-logging-enabled = false

    tsm-use-madv-willneed = false

    [coordinator]

    write-timeout = "10s"

    max-concurrent-queries = 0

    query-timeout = "0s"

    log-queries-after = "0s"

    max-select-point = 0

    max-select-series = 0

    max-select-buckets = 0

    [retention]

    enabled = true

    check-interval = "30m0s"

    [shard-precreation]

    enabled = true

    check-interval = "10m0s"

    advance-period = "30m0s"

    [monitor]

    store-enabled = true

    store-database = "_internal"

    store-interval = "10s"

    [subscriber]

    enabled = true

    http-timeout = "30s"

    insecure-skip-verify = false

    ca-certs = ""

    write-concurrency = 40

    write-buffer-size = 1000

    [http]

    enabled = true

    bind-address = ":8086"

    auth-enabled = false

    log-enabled = true

    suppress-write-log = false

    write-tracing = false

    flux-enabled = false

    flux-log-enabled = false

    pprof-enabled = true

    pprof-auth-enabled = false

    debug-pprof-enabled = false

    ping-auth-enabled = false

    prom-read-auth-enabled = false

    https-enabled = false

    https-certificate = "/etc/ssl/influxdb.pem"

    https-private-key = ""

    max-row-limit = 0

    max-connection-limit = 0

    shared-secret = ""

    realm = "InfluxDB"

    unix-socket-enabled = false

    unix-socket-permissions = "0777"

    bind-socket = "/var/run/influxdb.sock"

    max-body-size = 25000000

    access-log-path = ""

    max-concurrent-write-limit = 0

    max-enqueued-write-limit = 0

    enqueued-write-timeout = 30000000000

    [logging]

    format = "auto"

    level = "info"

    suppress-logo = false

    [[graphite]]

    enabled = false

    bind-address = ":2003"

    database = "graphite"

    retention-policy = ""

    protocol = "tcp"

    batch-size = 5000

    batch-pending = 10

    batch-timeout = "1s"

    consistency-level = "one"

    separator = "."

    udp-read-buffer = 0

    [[collectd]]

    enabled = false

    bind-address = ":25826"

    database = "collectd"

    retention-policy = ""

    batch-size = 5000

    batch-pending = 10

    batch-timeout = "10s"

    read-buffer = 0

    typesdb = "/usr/share/collectd/types.db"

    security-level = "none"

    auth-file = "/etc/collectd/auth_file"

    parse-multivalue-plugin = "split"

    [[opentsdb]]

    enabled = false

    bind-address = ":4242"

    database = "opentsdb"

    retention-policy = ""

    consistency-level = "one"

    tls-enabled = false

    certificate = "/etc/ssl/influxdb.pem"

    batch-size = 1000

    batch-pending = 5

    batch-timeout = "1s"

    log-point-errors = true

    [[udp]]

    enabled = false

    bind-address = ":8089"

    database = "udp"

    retention-policy = ""

    batch-size = 5000

    batch-pending = 10

    read-buffer = 0

    batch-timeout = "1s"

    precision = ""

    [continuous_queries]

    log-enabled = true

    enabled = true

    query-stats-enabled = false

    run-interval = "1s"

    [tls]

    min-version = ""

    max-version = ""

    中文入门简介:jasper-zhang1.gitbooks.io/influxdb/co…

    英文官方Python文档:www.influxdata.com/blog/gettin…

    Python教程:influxdb-python.readthedocs.io/en/latest/e…

    时序数据库对比:www.cnblogs.com/dhcn/p/1297…

    DolphinDB教程:dolphindb/Tutorials_CN 下载地址:DolphinDB官网

    influxdb教程,举个例子网:www.hellodemos.com/hello-influ…

    OpenTSDB:developer.aliyun.com/article/104…

    开源时序数据库解析:developer.aliyun.com/article/106…

    influxdb原理详解:www.shangmayuan.com/a/059faa17b…

    influxdb查询详解:www.codenong.com/js91edeffca…

    influxdb原理那些事:luoxn28.github.io/2020/01/28/… www.linuxdaxue.com/influxdb-pr…

    一份比较详细的文档:jasper-zhang1.gitbooks.io/influxdb/co…

    官方文档:docs.influxdata.com/influxdb/v1…

    时间序列数据的存储和计算 - 开源时序数据库解析(三):zhuanlan.zhihu.com/p/32710333

  •