分布式数据库LSM树
LSM树的核心结构与操作流程
Log-Structured Merge Tree,日志 结构化 合并 树。
追加写:永远不改,就算是update操作,也是追加写,一直新生成文件。
刷盘触发:追加到一定程序,比如到了几MB,就生成一个MemTable,MemTable是跳表结构,当MemTable容量达到阈值时,转换为ImmutableMemTable,不可变跳表。并且生成一个新的MemTable,后台线程将ImmutableMemTable的数据刷入磁盘SSTable。
分层合并:这一层满了,比如10M,再往下,比如100M满了,再往下,1G。一直到Level n层。SSTable是排好序的。
MemTable -> ImmutableMemTable -> SSTable level 0 -> SSTable level 1 -> SSTable level 2
读流程
多级查询读放大:基于这种结构,读的时候就会造成读放大,需要读多次。如果读的数据是新的数据,那可能在内存里,如果读的数据是旧数据,那要去level n层。因此,读性能非常不稳定。
写的时候,写放大,如果刚好每一层都满了,写的时候一直下沉到level n层,一层一层合并。
写内存的方式速度巨快。
数据可靠性:防止数据丢失
数据容易丢失,怎么设计解决的?
通过写入log日志,相当于redo log,确保崩溃后可通过日志恢复未刷盘数据。
冷热数据分离
什么场景会发生读放大?
冷数据。
从这一点出发,避免读取冷数据就可以避免读放大的问题。
clickhouse
clickhouse列式数据库,多核数据库,是做统计数据的,很容易发生读放大,因此他的设计是只有level 0,硬盘层面只有一层。一层就导致文件非常多,当你插入停下来,后台线程开始文件合并。
如果有一万个文件,怎么查询数据在哪个文件呢?clickhouse是通过文件合并成一个大文件,不停的merge。merge会消耗性能,但是合并了文件会使查询速率提高。但如果跟业务高峰期碰巧撞上,那会影响业务。
一个key在不在某个文件里,数据库引擎用布隆过滤器,通过位数组和哈希函数快速判断键是否可能存在于SSTable中,若返回“不存在”则直接跳过该文件,避免无效的磁盘读取。实现时间复杂度O(1)快速判断数据是否存在某个文件里。
布隆过滤器存在误判率,多级布隆过滤器架构优化,将布隆过滤器按数据冷热分层:内存中维护热数据的高精度过滤器,磁盘中存储冷数据的低精度过滤器。
并行:多个读,多核同时读。并行Compaction,利用多核CPU加速合并。
存储冗余问题
字段name,第一次写郭靖,update操作第二次写郭大靖,同一key多份存储。
读取时需从内存(MemTable)到磁盘(L0→L1→…→Lk)逐层检索,增加读放大。
当SSTable超过单文件最大容量,或者SSTable数量超过限制,底层数据文件会合并,合并时保留最新版本,删除无效记录。
合并结果写入下一层。
数据倾斜问题
分布式数据库是要做数据分布的,hash计算分布到某个节点上,比如存储视频播放,按照视频id做hash,大up主一个视频几千万播放,都会hash到同一个节点,造成数据严重倾斜,非常不均匀。
用uuid做主键,那么查询的话要去很多个节点去查询,造成读放大。
所以,数据不均匀的场景不适合用分布式数据库。
关系型数据库mysql做业务系统,大数据归档。hbase慢慢跑去吧,sql语句转换成LSM结构去分布式执行。
分片内有序
分布式无法支持全局有序。
每个分片(Shard)内部SSTable按主键有序,但跨分片无法全局有序。适用于分片内范围查询场景(如按用户ID查询)。