数据库的存储引擎优化是一个扬长避短的过程

时间:2022-10-26 12:08:27

这两天要和一个数据库厂商交流关于存储引擎的事情,这些天这方面考虑的多一些,今天就来聊聊数据库存储引擎的事情。一说到存储引擎,可能很多朋友就会说,某某存储引擎技术比较先进,比传统数据库的好。实际上再先进的存储引擎也有其缺点,可能先进只是指出现的较晚而已,并不是后出现的存储引擎一定全面优于老的存储引擎。数据库的存储引擎经过数十年的发展,实际上到现在为止也并没有出现特别多的流派。大体上归纳起来还是B树(含HEAP)和基于日志结构的存储引擎这两大类(最著名的是LSM-TREE)。虽然LSM-TREE的出现比B树存储引擎晚了二十多年,不过其出现并不是作为替代B树引擎的,而是有着特定的目的的。

LSM-TREE的出现是在内存成本第一次大幅下降之后的,因此基于memtab可以大幅优化高并发写入的性能,同时SSTABLE可以更加充分的利用存储,更加有利于数据压缩。LSM-TREE被大规模应用到关系型数据库上则是SSD逐渐普及后才出现的,因为开销巨大甚至可能导致系统不稳定的后台压缩让对于延时十分敏感的OLTP系统无法承受。而SSD可以缓解这种问题。

B树存储引擎一般采用IN-PLACE-REPLACE,所以其性能相对稳定,虽然会因为PAGE的碎片而导致一些浪费,但是总体来说是较为均衡的。与LSM-TREE相比,因为B树存储引擎的这个特点导致并发写入的性能是会遇到瓶颈的。几年前我们测试过一个每隔5分钟有数亿条数据并发写入,并需要进行实时统计分析的场景,在Oracle数据库上在每秒写入1000万条以后明显就很难提升了,而在LSM-TREE引擎的分布式系统中,很轻松就超过2000万/秒。虽然在写入上B树存储引擎的性能无法与LSM-TREE相媲美,不过B树存储引擎在数据读取上有着明显的优势。在MVCC的实现上,以及对数据的复杂查询上面,B树存储引擎都比LSM-TREE有明显的优势。虽然LSM-TREE存储引擎在查找数据上也通过布鲁姆过滤器以及内存主范围索引等方式进行优化,但是其开销还是会比B树存储引擎要大的多。目前很多基于LSM-TREE的分布式数据库往往都是依靠并行扫描的方法,以众敌寡,勉强和B树结构的集中式数据库打个平手。

实际上两种存储引擎的一些优缺点也并不容易区分清楚,如果站在某个立场上,就会有不同的分析结论。比如写放大的问题上,LSM-TREE的支持者会说LSM-TREE结构更加紧凑,数据没有碎片,而B树存储引擎经常会出现某个PAGE中只写了很少一部分数据,导致写被放大了。实际上LSM-TREE虽然不存在这种写放大,但是一个KEY会被多处存储,也会造成另外一个意义上的写放大。再加上现代硬件对于写IO的能力已经极大提高了,这个问题其实并不会对绝大多数场景造成影响。

从上面的分析来看,确实没有完美的存储引擎,每个引擎都有其优点,也同时必然有其缺点。我们要做的实际上是利用现代硬件的特点,尽可能地弥补其缺点,发扬其优点。随着IO越来越好,CPU越来越便宜,LSM-TREE存储引擎被越来越广泛地使用也是一个很好的例子。

前些年我和INTEL合作研究傲腾内存的使用场景的时候,曾经考虑过是不是把没有压缩的sstable先存储到傲腾内存里,在傲腾内存里做压缩后,逐渐把历史数据下沉到性能相对较差的持久存储上。这样可以大大降低压缩带来的负面影响。再用CGROUP隔离后台压缩进程,这样就可以避免压缩给数据库带来的抖动。

前些天我看到一篇北京大学Baoyue Yan的论文,他们提出了一种利用傲腾内存优化LSM-TREE存储引擎的算法,在实践中获得了很好的效果,在TPCC基准测试中获得了2倍的提升。

数据库的存储引擎优化是一个扬长避短的过程

左边的图是基于传统的LSM-TREE存储引擎的数据库,而右边是他们优化过的方案。实际上这张图并不完整,还缺少了一个传统内存的区域。在传统内存区域里,他们放置了可丢失的半持久化索引数据,传统写入memtab的数据被写入非易失性内存中的sp-m,sp-m写满后被锁定为sp-im,然后在非易失性内存中进行压缩,写入SSD的持久化存储系统中。

因为主数据被写入非易失性内存,因此通过WAL来保证数据库的一致性需求也没那么强烈了,因此可以使用更为轻量级的Recorder Ring来替换WAL,保证事务一致性。这种利用非易失性内存这种现代的硬件来优化数据库架构,甚至颠覆传统数据库架构的尝试是十分值得肯定的,也是十分有价值的。

可能有朋友要说了,非易失性内存那么贵,不是任何用户用得起的,所以这种数据库是不是很难普及啊。实际上说这句话的时候想想十年前,当时大家都在说SSD那么贵,不是任何人都用得起的。而现在,好像不是那么回事了吧。