Gorrila论文阅读

简介

大规模的互联网服务旨在在出现意外故障时保持高可用性和响应性。提供这种服务通常需要在大量系统中每秒监视和分析数千万个测量值,一个特别有效的解决方案是在时间序列数据库(TSDB)中存储和查询这些测量值。Gorilla 通过村存储时间戳的变化量的变化量(delta of delta)以及对存储的value进行异或(XOR)操作从而提升读写性能,查询延迟降低了73倍,查询吞吐率提升了14倍。

Gorilla在监控数据写入HBase存储之前,起到一个write-through cache的作用。Gorilla的数据模型是一个简单的3元组,包括一个string类型的key、一个64-bit整型timestamp和一个双精度浮点类型value。监控数据中定义的key用来唯一标识一个时间序列(timeseries)。根据键值(key)将时间序列(timeseries)数据进行分片(shard),每个时间序列(timeseries)数据集会被映射到一台单独的Gorilla主机上。因此扩展Gorilla主机时,可以通过调整分片算法将新的timeseries数据映射到新的主机上

Gorilla的主要特点:

  • 写占主导地位
  • 状态转换(state transitions) 它能在数十秒之内显示状态转换,以防止错误蔓延。(个人觉得是一种预警机制吧)
  • 高可用性
  • 容错

Gorilla的压缩算法

1.对于时间戳的变化量的变化量(delta of delta)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EQKa1vCA-1632981337346)(C:\Users\86158\AppData\Roaming\Typora\typora-user-images\image-20210927170324884.png)]

首先我们有每个时间的时间戳 t n t_n tn ,然后计算时间戳变化量的变化量 D = ( t n − t n − 1 ) − ( t n − 1 − t n − 2 ) D=(t_n - t_{n-1})-(t_{n -1}-t_{n-2}) D=(tntn1)(tn1tn2) 。然后根据D的值按下面的几种方式存储。

  • 如果 D = 0 D=0 D=0 那么只存储一个0,只占1bit
  • 如果 D ∈ [ − 63 , 64 ] D \in [-63, 64] D[63,64] 首先是10作为标志位,然后是D得值(7bit) ,总共9bit
  • 如果 D ∈ [ − 255 , 256 ] D \in [-255, 256] D[255,256] 首先是110作为标志位,然后是D得值(9bit) ,总共12bit
  • 如果 D ∈ [ − 2047 , 2048 ] D \in [-2047, 2048] D[2047,2048] 首先是1110作为标志位,然后是D得值(12bit) ,总共16bit
  • 其余的使用1111作为标志位,然后是32bit的D
2.对于值的压缩

对于值的存储仍然存储差值,不过此时不用直接取差,而是使用异或。

内存数据结构

SharedMap

ShardMap用于保存shardId(分片ID)到TSmap的映射。Timeseries在保存的时候根据timeseries name哈希散列到不同shardId(0 ~ NumberOfShard)。该map也是使用大小写不敏感hash算法。系统中的Shard总数几千以内,因此存储空指针的额外开销可以忽略。同样ShardMap也使用一个读写spin lock实现并行。
由于数据根据shard进行了分区,单个map变得足够小(大约100万个条目),C++的unordered-map有足够的性能,没有锁争用的问题

TSmap

Timeseries Map (TSmap)是Gorilla实现的主要数据结构。如上图所示,TSmap包括一个C++ Vector和一个map(unordered_map)。Vector里保存指向timeseries的指针(shared_ptr)。Map里保存timeseries name(key)到timeseries指针(value)的映射,该map大小写不敏感并保留原有大小写。
Vector用于高效的分页扫描所有数据(根据vector下标)。Map用于特定时间的timeseries查询。该设计既满足快速按时间查询的需求,又能提供高效的数据扫描。
C++的shared-pointers可以在几毫秒时间内扫描拷贝整个vector(或者几个pages),可以有效避免对新写入数据流的影响。删除的timeseries会将对应的vector设置为“墓碑状态”,即对应的内存不释放,而是标记为dead,并用于新的timeseries重复使用。
Map和vector的并行访问使用一个简单的读写spin lock进行保护。每个timeseries上使用一个1-byte的spin lock实行互斥。单个timeseries相关很少的写流量,因此读写的锁争用较少。

时间序列的数据结构是由两个小时以前的封闭数据块(不能修改),以及能插入新数据的开放数据块组成,如果开放数据块满了以后,它就会变成封闭数据块。

磁盘数据结构

磁盘数据结构包含四种:

  • Key list是一个map用于保存timeseries string key到内存数据结构vector的ID的映射,新的key会被追加到当前key list文件尾,Gorilla会定期扫描每个shard里所有的keys并重写key list文件(猜测是为了保证一致性)。

  • log file用于存储最新的数据流,包括压缩后的timestamps和values。每个shard只有一个log file,所以交替保存了多个timeseries的值。它和内存中的数据结构不同点在于每个timestamp-value pair都有一个32-bit整型ID标记,该ID应该就是key list里的vectorID,标记它属于哪个timeseries。log file并不是write-ahead-log(WAL),数据写入磁盘前会缓存64KB,因此宕机会导致几秒数据的丢失。不过Gorilla不需要提供ACID特性,相比WAL日志带来的收益,提供了更好的数据写入效率。

  • complete block file存储压缩后的整个block数据,每隔2小时从内存中拷贝block数据并进行压缩,所以它比log file小了很多。该文件包含两个部分:一组连续的64KB slabs大小的block data;一个timeseriesID->data block指针pair的列表,用于标记block data属于哪个timeseries。这里timeseriesID应该和key list的vectorID是一个概念。

属于哪个timeseries。这里timeseriesID应该和key list的vectorID是一个概念。

  • checkpoint file用于标记某个时间的complete block file已经flush到磁盘。此时,对应的log file将被删除,数据流写入新的log file。宕机后接管该Shard的主机根据checkpoint file来确定从log file还是complete block file里读取数据。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值