这两天要跟一家数据库厂商沟通存储引擎的事情。这些天,我更多地考虑了这方面的问题。今天要讲的是数据库存储引擎。说到存储引擎,很多朋友可能会说某个存储引擎技术比传统数据库更先进、更好。事实上,无论存储引擎多么先进,它也有其缺点。可能是高级存储引擎出现的晚,并不代表后来出现的存储引擎就一定比旧的存储引擎好。数据库的存储引擎已经发展了几十年,但实际上至今流派并不多。一般来说,有两大类:B-tree(包括HEAP)和基于日志结构的存储引擎(最著名的是LSM-TREE)。LSM-TREE的出现虽然比B-tree存储引擎晚了20多年,但它并不是作为B-tree引擎的替代品出现,而是有特定的用途。LSM-TREE的出现是在内存成本首次大幅下降之后。因此,基于memtab,可以大大优化高并发写入的性能。同时,SSTABLE可以充分利用存储,更有利于数据压缩。LSM-TREE在关系型数据库上的大规模应用是在SSD逐渐普及之后才出现的,因为巨大的开销,甚至可能导致系统不稳定的后台压缩,让对延迟非常敏感的OLTP系统难以承受。SSD可以缓解这个问题。B树存储引擎一般采用IN-PLACE-REPLACE,所以性能比较稳定。虽然会因为页面的碎片化而造成一些浪费,但总体来说还是比较均衡的。与LSM-TREE相比,因为B-tree存储引擎的这个特性,并发写入的性能会遇到瓶颈。几年前,我们测试了一个场景,每5分钟并发写入数亿条数据,需要实时统计分析。在Oracle数据库每秒写入1000万条数据后,显然很难再提升了。在LSM-TREE引擎的分布式系统中,轻松超过每秒2000万。虽然B-tree存储引擎在写入方面的性能比不上LSM-TREE,但是B-tree存储引擎在数据读取方面有着明显的优势。B-tree存储引擎在MVCC的实现和数据的复杂查询上比LSM-TREE有明显的优势。LSM-TREE存储引擎虽然也优化了通过Bloomfilter和内存主范围索引查找数据,但是它的开销还是比B-tree存储引擎高很多。目前很多基于LSM-TREE的分布式数据库往往依赖于并行扫描的方式,寡不敌众,勉强与B-tree结构的集中式数据库竞争。事实上,这两种存储引擎的一些优缺点并不容易区分清楚。如果站在某种立场上,就会有不同的分析结论。比如在写放大的问题上,LSM-TREE的支持者会说LSM-TREE的结构更紧凑,没有数据碎片,而B-tree存储引擎往往只有一小部分数据写入一个页,导致在写放大了。其实LSM-TREE虽然没有这种写放大,但是一个KEY会存放在多个地方,也会造成另一种意义上的写放大。再加上现代硬件写IO的能力有了很大的提升,这个问题其实并不影响大部分场景。从上面的分析来看,确实没有完美的存储引擎。每个引擎都有其优点和缺点。我们要做的就是利用现代硬件的特点,尽可能的弥补它的不足,发挥它的优点。随着IO越来越好,CPU越来越便宜,LSM-TREE存储引擎越来越广泛的应用也是一个很好的例子。几年前和Intel合作研究Optane内存的使用场景时,考虑过是否先将未压缩的sstable存储在Optane内存中,然后将压缩后的历史数据逐渐下沉到Optane内存中。性能相对较差的持久存储。这大大减少了压缩的负面影响。然后使用CGROUP隔离后台压缩进程,避免压缩对数据库造成的抖动。前几天看到了北京大学严宝月的一篇论文。他们提出了一种使用Optane内存优化LSM-TREE存储引擎的算法,在实践中取得了不错的效果,获得了2倍的提升。左图是基于传统LSM-TREE存储引擎的数据库,右图是他们的优化方案。其实这张图并不完整,少了一块传统的内存区。在传统内存区,他们放置了可能丢失的半持久索引数据。传统上写入memtab的数据写入非易失性内存中的sp-m。sp-m写满后,锁定为sp-im,然后在非易失性内存中压缩,写入SSD的持久化存储系统。因为主要的数据是写入到非易失性内存中,所以通过WAL来保证数据库一致性的需求不是那么强烈。因此,可以使用更轻量级的RecorderRing来代替WAL来保证事务的一致性。这种利用非易失性存储器等现代硬件来优化数据库架构,甚至颠覆传统数据库架构的尝试,是非常值得肯定的,也是非常有价值的。可能有朋友要说,非易失性内存这么贵,没有用户买得起,那么这种数据库很难普及吗?其实说这话的时候我想到了十年前,当时大家都说SSD贵到没人买得起。但现在,似乎并非如此。
