NoSQL-LSDB,VecDB,TSDB-rocksdb,influx
Log Structured DB & Vector DB & Time series DB
rdbms: 数据平权, “热点”数据比较麻烦
Log Structured DB: LSM tree, 已经品鉴得够多力
普遍的一个优化 Immutable memtable, 内存表写满之后转化为只读内存表,使用新的内存表承接业务
单独起一个线程将只读内存表写到下层(只读内存表支持读操作)
优点:
- 大幅度提高插入(修改,删除)性能
- 空间放大率降低
- 访问新数据更快, 热度与level相关, 适合时序实时存储
缺点:
- 牺牲读性能
- 读写放大
- 日志结构数据树的读放大:读数据最坏的情况下需要扫描所有层的sstable, 大大增加的读未命中时的开销, 增加磁盘IO; 可以通过sstable的二分查找,布隆过滤器等优化。
- 日志结构数据树的写放大:写数据时如果触发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维护
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