从一到无穷大 #16 ByteSeries,思考内存时序数据库的必要性

news/2024/7/4 7:49:09 标签: 时序数据库, 数据库

在这里插入图片描述本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权。

引言

在[3]中我基于Gorilla讨论了数据库>时序数据库设置cache的可行性,最后得出结论:RAM去换实时查询的高效,显著增加成本的同时增加系统复杂性。

但是如果换个思路,缓存即为内存数据库>时序数据库,长久存储为为其他(分布式文件系统,对象存储,基于磁盘的数据库>时序数据库),这个选择又变得合理起来,因为不少研究对于使用内存加速数据写入和实时查询做了广泛的研究[4][5][6],这意味着内存数据库>时序数据库相比基于磁盘的数据库>时序数据库(写入过程可能存在多次IO操作,查询会触发多次IO操作)有着得天独厚的优势。

此时的权衡点回到了上篇文章中讨论的重点,是否存在Gorilla中提到的“26H”呢,如果线上负载存在的话此类假设,cache的存在是一个杀器,不但加速写入(批量写入后端磁盘存储),实时查询的速率也极其优秀(可以达到10ms级别)。

另外如果使用基于磁盘的数据库>时序数据库作为远端存储,对于历史数据的查询也会很快。但是缺陷就是宕机恢复流程会相对复杂,所以如果存在一个副本和主一样应用状态机问题就不是很大,缺陷是如果多副本宕机启动会非常慢。但是问题在于基于磁盘的数据库>时序数据库一般对于内存要求较高,无论是compaction,shard的forward index/inverted index的缓存,TSM的缓存,查询期间的内存消耗都不小,这导致如果要在基于磁盘的数据库>时序数据库内存中做cache的话要么提升机器内存规格(问题是请求进入引擎以及apply了raft log,在引擎内部做cache意味着需要在raft log外再维护一个log,用于宕机时内存数据恢复,好处是实时写入和实时查询性能提升),要么再加一层(类似于ByteSeries,Gorilla)。

结论:线上负载要求写入和实时查询高性能,数据库>时序数据库+缓存(内存数据库>时序数据库)可行。

ByteSeries的文章只是阐述了内存存储引擎,对分布式实现(数据如何分片,分布式查询聚合如何实现,难道真的只有一个节点?)和如何沉降冷数据没有提及。

内存布局

Active Segment

在这里插入图片描述

数据首先写入Active Segment,且这个关键路径不做复杂的数据转换和压缩,写入根据SeriesKey的哈希值决定写入哪个Segment ShardSegment Shard由一个追加写入的数组和动态的倒排索引组成。

分片的原因有两个:

  1. 并行数据写入
  2. 将数据从Active Segment转入Static Segment需要暂停写入,并重新分配新的segment,在分片的情况下转换可以是segment级别的,不需要执行整个缓冲区级别的内存分配。

Static Segment

线上100亿时间线的数据在元数据上可以看做10万个tag以及每个tag下数万个tag值。

Static Segment分为两个部分,Static SegmentCompressed Segment,SS数据累计到一定量时转换为CS,这里的目的主要是为了分批进行压缩,均摊昂贵的压缩开销。

在这里插入图片描述

新添加的数据直接添加到数组中,倒排索引中存储数组的偏移量,然后多个Temporary Static Segment合并成一个Static Segment,显然此时属于一个series key的数据还分布在数组的各处,并没有存储在连续空间内,也会导致索引中存储大量的下标值,而且数据点本身也没有办法压缩。

所以Static Segment会进一步转换为Compressed Segment,CS中属于一个Series key的数据都属连续存储的,这样可以减小数据的大小,索引中也只需要存储一个偏移。注意,这里其实存储结构其实是:

  1. 使用tire存储series key,指向compressed list的下标,compressed list内部指向此series key的Compressed data string
  2. Compressed data string指向压缩的数据点

文章最精髓的地方其实在于倒排索引的压缩:
在这里插入图片描述

构建倒排的步骤如下:

  1. 对所有的tagkv构建倒排索颖。即tagkv -> series keys 的偏移
  2. tagkey:使用前缀树压缩,这个思路没什么问题,不但可以极致的压缩tagkey,还允许范围查询。cedar[1]实现,个人认为redis的Redix tree也很适合这里的存储。
  3. tagvalue:把所有的series key list按顺序写入total inverted index array数组,这其中可以存在重复,因为不同的tagkey可以指向同样的serieskey,inverted index offset存储每个tagkv在total inverted index array数组的起始值,这意味这可以迅速找到tagkv对应的serieskey list。使用p4nzenc64来压缩这两个数组

其实可以看到,本质上和基于磁盘的invert index+TSM没有区别,无非去除了forward index,改为数组的下标。这带来的劣势时必须看作这个Compressed Segment已经封闭,且无法修改,不然整个结构全部都要变化。

Data Conversion Scheduler(DCS)

有三个上面提到的功能:

  1. AS转换为TSS
  2. 多个TSS与SS合并
  3. SS转换转换为CSS

值得关注的是这里需要考虑AS的写入吞吐和AS转换为TSS的速率是否匹配,否则可能导致压缩为TSS成为瓶颈,进而导致写入受限。

具体细节没什么好说的,DCS运行在一个单独的线程,执行一系列提前预设好的规则执行调度,其实就是基于维度,大小等条件判断。

评估

文章中把ByteSeries与 tsdc, Prometheus and Gorilla三个数据库从三个维度做了评估,分别为:

  1. 每个维度使用的字节数
  2. 写入速率
  3. single group bydouble group by查询速率

可以预想的,在高维度的场景下ByteSeries原数据所使用的内存非常少,这也很好理解,因为tagkey本身被极致压缩,元数据还只需要指向一个index。写入速率受转换的原因不如Gorilla。

总结

很优秀的内存压缩元数据思路,事实上可以认为这种方法是解决维度爆炸场景下写入和实时查询性能的一种可行方式。

引用:

  1. cedar - C++ implementation of efficiently-updatable double-array trie
  2. TurboPFor-Integer-Compression
  3. 从一到无穷大 #15 Gorilla,论黄金26H与数据库>时序数据库缓存系统的可行性
  4. Btrdb: Optimizing storage system design for timeseries processing FAST 2016
  5. Akumuli Numeric B+tree
  6. RTSI: An Index Structure for Multi-Modal Real-Time Search on Live Audio Streaming Services ICDE 2018

http://www.niftyadmin.cn/n/5029896.html

相关文章

基于SSM+Vue的人力资源管理系统

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用Vue技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

pve 故障记录

pve 故障记录 通过网页pve console 执行 参考网页: cat /etc/os-release proxmox ve https://foxi.buduanwang.vip/pve https://www.reddit.com/r/Proxmox/comments/vy33ho/stuck_at_grub_rescue_after_an_update_and_reboot/?rdt37867 https://pve.proxmox.c…

【深度学习】Pytorch 系列教程(十二):PyTorch数据结构:4、数据集(Dataset)

目录 一、前言 二、实验环境 三、PyTorch数据结构 0、分类 1、张量(Tensor) 2、张量操作(Tensor Operations) 3、变量(Variable) 4、数据集(Dataset) 随机洗牌 一、前言 Ch…

LuatOS-SOC接口文档(air780E)--bit64 - 32位系统上对64位数据的基本算术运算和逻辑运算

bit64.to32(data64bit) 64bit数据转成32bit输出 参数 传入值类型 解释 string 9字节数据 返回值 返回值类型 解释 any 根据64bit数据输出int或者number 例子 无 bit64.to64(data32bit) 32bit数据转成64bit数据 参数 传入值类型 解释 int/number 32bit数据 …

HTML5 Canvas 数据持久化存储之属性列表

正常我们设置属性的时候,属性和属性值的 key value 对应,但是在实际开发中,经常遇到属性值可能需要从多项中选择,这个时候用原生的 HTML5 配合 JavaScript 来实现这个功能会让人非常头疼,我试着用 HT for Web 来实现了…

每日刷题-6

目录 一、选择题 二、算法题 1.Fibonacci数列 2.合法括号序列判断 一、选择题 1、 解析:内联函数是一种可以提高函数执行效率的方法,它的原理是编译时在函数调用点直接展开函数体的代码,从而避免了函数调用的开销。 但是,内联函…

Python--测试代码

目录 1、使用pip安装pytest 1.1 更新pip 1.2 安装putest 2、测试函数 2.1 单元测试和测试用例 2.2 可通过的测试 2.3 运行测试 2.4 未通过的测试 2.5 解决测试未通过 2.6 添加新测试 3、测试类 3.1 各种断言 3.2 一个测试的类 3.3 测试AnonymousSurvey类 3.4 使…

数据结构-leetcode-环形链表

解题图解: 代码如下: bool hasCycle(struct ListNode *head) {struct ListNode * fasthead;//在这里fast是快指针//head作为low指针//因为这个题不需要做修改也只需返回true或false//就少开辟一个空间while(fast!NULL&&fast->next!NULL){hea…