写入热点打散的问题
2021-01-27
热点打散问题,可能是分布式数据库最不喜欢遇到,但是又经常会遇到的问题之一。
理想的状态,对于分布式的数据库,只要一直加机器,就可以分担负载分散流量,总是 scalable 的。然而在存在热点的情况下,就 scalable 不起来了,负载总是打到很固定的节点上面,怎么拆分都不管用。
比较典型的一个例子比如自增主键,编码之后对应都的都是紧紧相邻的一块区域,然后就形成热点了。还有就是时间类型,往往时间相关的数据都是比较连续的,也是容易造成热点。业务场景容易遇到热点问题的,比较典型的比如删数据,按时间过期这种。MVCC 的引擎,增删改查里面,最重的操作其实是删除,因为删除是追加新版本数据,然后再做 GC Compaction,而 GC 这个东西是 LSM 类引擎里面最影响性能的。热点数据的删除,再跟 GC 一起,很容易造成延迟的上升,响应时间的抖动。
对于自增主键这一类热点问题,我们有做过处理,比如用生成随机数作主键,实现数据打散。
我们的数据库是是多层的映射,先把值编码到一层虚拟的 key-value 的范围,这是第一层。然后再把 key-value 划分成一块一块的 range,这可以算第二层,这里的划分是动态的划分的,而不是均匀划分。range 再映射到不同的物理节点上面,这算第三层,这个映射关系由一个中心化的调度器来管理。
从值到 key-value 的编码,这一层的编码规则是写死了的,所以这一层不可变了。连续的值打不散,是因为它们在编码后仍然连续,所以打不散。
在数据分布的映射那一层,我们按 range 切分,并且从虚拟到 range 到物理的节点,这一层是可以动态的,调度的节点随时都在调整。一个 range 内数据越来越多以后,就被切分成多个 range 来管理了。
在 range 到物理节点映射那里,可以做一个打散功能,但是这个打散只能是基于 range 的。对于连续的值,基于 range 并不容易打散。还有一个问题是数据分布,即使提供了这一层的打散的机制,我们无法预估数据的分布情况,实际使用中也非常不灵活。
不改编码方式,用随机数当主键,只能处理部分场景。比如主键通过随机数生成,容易实现打散,但是索引就不行。索引一定是根据值来生成的,而时间这类值的特点,生成出来的索引自然是连续的。这个是我真正最想解决的点,希望有什么通用的热点打散的解决方案!
脑洞一下,是否可以在 值 => kv range 映射的过程,做一点手脚。自然想到了按 hash。但是如果按 hash 只能应对点查,范围查询会无法处理。 并且编码需要是可比较的,比如查询请求是 key > 32,对应的 range 也要能根据查询算出来 kv 空间对应的 range。所以用 hash 映射好像不太行得通。
没想到解决方案,先把问题记录下来...