Skip to main content

NoSQL-LSDB,VecDB,TSDB-rocksdb,influx

Log Structured DB & Vector DB & Time series DB

rdbms: 数据平权, “热点”数据比较麻烦

Log Structured DB: LSM tree, 已经品鉴得够多力

普遍的一个优化 Immutable memtable, 内存表写满之后转化为只读内存表,使用新的内存表承接业务

单独起一个线程将只读内存表写到下层(只读内存表支持读操作)

优点:

  • 大幅度提高插入(修改,删除)性能
  • 空间放大率降低
  • 访问新数据更快, 热度与level相关, 适合时序实时存储

缺点:

  • 牺牲读性能
  • 读写放大
  1. 日志结构数据树的读放大:读数据最坏的情况下需要扫描所有层的sstable, 大大增加的读未命中时的开销, 增加磁盘IO; 可以通过sstable的二分查找,布隆过滤器等优化。
  2. 日志结构数据树的写放大:写数据时如果触发compaction, 会使得大量sstable在磁盘上进行重新的写入,此时的写开销远大于单条数据,称为写放大。写放大增加磁盘IO和磨损,相当于一条数据在磁盘上重复写了多次。可以通过键值分离,压缩等进行优化。

leveldb & rocksdb

rocksdb opt: wal + multi-immutable memtable + manifast(追踪fs变化)

因为rocksdb是嵌入式的, 同步需要手动完成, 所有方法都需要加锁/synchronized/...

问题: 分布式支持? 不怎么使用分布式的原因是本身这个需求有点奇怪

需求: 热点数据尽快被访问到

->推出: 热点数据不应该非常多, 非常多的时候也可能不用LSM(读写放大)

OLTP(online transaction processing): 低延迟, 高并发, 数据量小 -> 对随机读写支持需要比较好 行存

OLAP(online analytical process): 高延迟, 低并发, 数据量大 -> 列存

HTAP(hybrid Transactional/Analytical Processing): 混合事务分析处理

rocksdb 由于写优化好, 所以TP支持好, 不少数据库都开始支持rocksdb作为底层存储引擎

行存到列存的转换较难一步完成, 但可以取一个折中的实现, 即列族(Column Family), 相当于垂直分区, 将某几列作为sstable里面的value数据, 另外几列写入另一棵lsm tree作为另外的value数据

这样可以把需要做OLTP的大部分数据放在一组, OLAP的少部分一两个放在一族, 分析时直接导出OLAP的, 而写入时由于列存族导致慢的部分也不是不能接受

rdbms例如mysql就直接不支持了, 垂直分区索引维护等太复杂

更进一步的优化

  • 写阻塞问题: 在应用程序和db 存储层之间加一层collector(例如apache flink), collector可以是一个集群, 负载均衡, 然后app要写的数据落到collector的内存里面之后再去落盘,同时在collector上还可以做计算、监控等, 对于一个成熟的集群来说, 监控和分配写请求到collector集群的master节点可能还有主备...
  • 读放大问题: 在rocksdb里面增加列存
    • 列存实际上也不是说 把所有的数据的某一列存完再存下一列, 而是以比如M行作为一个row group(data block), row group的列作为一个column block, 而meta data和index data从对single row维护变为对data block维护

image-20241127162723872

image-20241127162903522

image-20241127162950850

vectorDB pipecone-client

核心, 向量计算和存储

相似度: 余弦, 曼哈顿, 欧氏, jaccard

时序数据库 influxDB promethus:

  • 格式简单
  • 可以存差值而不是原值
  • 数据有TTL,短生命周期
  • timestamp
  • 不太可能会建立索引,一般不会有必要建立索引(比如建个温度在时间的索引,这没有意义)

突出数据生命周期管理, 总结, 大range scan

优化

  • 可以不记录时间戳:如果传输来的数据比较稳定的话,例如每秒1个数据,我可以记录某一个特定数据(比如开始的数据的时间戳)然后后面每秒记录一个数据就好。
  • 可以记录数据的增量:比如记录温度,变化比较小。我就记录相比上一个数据增加或者减少了多少度。这样存储的数据的空间得以节省了,所以可以存更多的数据。

influxDB存储:

  • 存储是以桶来存储的,my_bucket就是相当于表名。
  • 注意:带有_开头的,都是系统保留的字段,也就是一定会有的一个列,反之都是用户自定义的字段
  • _time:时间戳,数据对应的时间,因为可能同一个时间会接收到很多数据,所以很可能同一个时间接收到很多数据,所以时间戳不能唯一的标识。时间戳非常准确,精确到纳秒级别。
  • _measurement:起一个名字,一个统称,这个表格在干啥。census就是调查种群数量。
  • _field:存储的是key,比如下表里面存储的就是某个物种的名字
  • _value:存储的是值,类型可以是strings, floats, integers, or booleans,之所以不能是别的,是因为如果是复杂的数据类型转换会耽误时间,效率降低,所以就只能存这些基础的数据类型。
  • TagValue_开头的字段的差别就是:前者可以建立索引,后者不会建立索引。例如下图里面我要统计所有叫mullen的科学家的数据有哪些,系统就会根据索引去查找,但是如果根据_field查找,系统就会做全表扫描。Tag的含义就是打标签。
  • Field_set:数据集,根某一个时间戳关联的所有的key-value的数据集就是Field-set
  • Tag_Set:标签集合,根某一个特定标签值关联的,所有的数据集

tag 参与索引, field 不参与索引

时间戳不会存多个, 而是每一个数据关联一个时间戳

  • Field不建立索引,是必要组成部分
  • Field相关的查询,必须经过全表扫描,然后匹配检测
  • 带有>标记的查询往往效率更高,因为数据都是按照追加的方法,如果能给一个时间范围查找效率就更高。
  • Tag是可选的,不是必要的组成部分
  • Tag会建立索引,对于Tag相关的查询更快
  • 对于经常要查询的列作为一个Tag,会是一个很好的选择

telegraf 收集机器指标外挂工具

series key: measurement + a set of tag + field key

influxDB 严格限制update和delete, 时序顺序写

influxdb的query: 最终一致性, 不保证能拿到高负载情况下的最新数据

存储: 如果数据没有变化则不存(不存连续重复值,相当于利用差值)

data -> 压缩 -> 写WAL -> 写Cache, queryable -> 异步落盘

TSM 时序合并 落盘的数据认为是冷数据, 会被压缩, 并且是列存, 存差值, 时间换空间(由于差值很小,所以可以少存几位, 每隔一段时间存一个初值)

wal和data不应该放在一个地方, 至少不应该放在一个硬盘,这样可以很大程度上减少硬盘损坏带来的数据丢失问题

shards 按照time range shard, 配置文件里面指定period, periods内按照time range shared, 之外删除

内存: hot shard 不压缩, 硬盘: cold shard 压缩

L0 - L4 每层10倍放大, 一般L0内存

索引: 就是series key