深入浅出存储引擎
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.1.3 内存型存储组件与磁盘型存储组件

在计算机发展的几十年里,计算机的整体结构依然没有太大的变化,计算机中充当存储介质的主要有内存、磁盘两类。内存的访问速度要比磁盘快几个量级,但是内存的容量比磁盘要小很多。一般对磁盘进行访问时,首先会从磁盘文件加载数据到内存中,然后响应用户。内存和磁盘的各维度对比如图1-4所示。

图1-4 内存和磁盘对比

结合前面的介绍,在大部分系统设计时,通常会选择一种主要存储介质来存储数据,另一种存储介质则作为辅助使用。在目前的各种存储组件中,根据每个存储组件存储数据的主要介质,可将其分为两类:内存型组件和磁盘型组件。

1.内存型组件

内存型组件的典型特点是读/写性能高、访问速度快。其缺点在于,保存的数据量受限于当前机器的内存容量,并且当机器宕机或发生突发情况断电后,保存在内存中的数据会全部丢失。例如,Redis是大家较熟悉也较常用的一个内存型存储组件。Redis主要采用内存存储数据,而磁盘则用于做辅助的持久化存储。RabbitMQ也主要采用内存存储消息,同时支持将消息持久化到磁盘。

对采用内存存储数据的方案而言,难点之一在于如何在不降低访问效率的情况下,充分利用有限的内存空间来存储尽可能多的数据。这个过程少不了对数据结构的选型、优化,以及对数据过期、数据淘汰等方案的选择。同时,绝大多数的内存型组件在保证单机功能完备的情况下,都会优先考虑对存储的数据进行分片,并构建集群系统对外提供服务,以解决单机内存容量这一限制。另一个难点在于,内存型组件如何保证在机器发生故障的情况下,数据尽可能少丢失。针对这类问题,业界经典的解决方案是快照+广泛意义的WAL(Write Ahead Log)日志,其典型代表有很多,比如Redis。

2.磁盘型组件

和内存型组件不同,磁盘型组件的特点是单机磁盘存储的数据量非常大,要远大于内存型组件。同时,在机器宕机或者发生突发情况断电的情况下不会出现数据丢失(排除极端情况)。然而,磁盘型组件,尤其是典型的HDD(机械磁盘),由于先天性的磁盘结构,其访问数据的速度比内存慢得多。同时,在相同的磁盘结构下,对磁盘的访问方式决定了磁盘访问的耗时。磁盘顺序访问要远远快于磁盘随机访问。磁盘型组件有关系数据库、NoSQL数据库等,例如MySQL、Oracle、MongoDB等数据库主要采用磁盘组织数据,以合理利用内存提升性能。而像RocketMQ、Kafka、Pulsar消息队列,也是主要将数据存储在磁盘,通过内存来提升系统的性能。

对采用磁盘存储数据的方案而言,难点之一在于如何根据系统要解决的特定场景进行合理的磁盘布局。在读多写少的情况下,采用B+树方式存储数据;在写多读少的情况下,采用LSM这类方案处理。另一个难点在于如何减少对磁盘的频繁访问。有几种解决思路:①采用Mmap进行内存映射,提升读性能;②采用缓存机制缓存经常访问的数据;③采用巧妙的数据结构布局,充分利用磁盘预读特性,以保证系统性能。

总的来说,针对写磁盘的优化,可采用顺序写提升性能,或采用异步写提升性能(异步写磁盘时需要结合WAL日志保证数据的持久化,事实上WAL日志也主要采用顺序写磁盘的特性)。针对读磁盘的优化,一方面是缓存经常访问的热点数据或者尽可能利用磁盘预读能力来降低访问磁盘的开销,另一方面是采用操作系统提供的Mmap内存映射等功能来加快读的过程。

上述存储方案上的权衡在关系数据库、NoSQL数据库和NewSQL数据库中都可以看到。抛开数据库不谈,这些存储方案的选择对于消息队列等中间件选型也是通用的。