<audio id="audio" title="27 | 分布式数据之缓存技术：“身手钥钱”随身带" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/69/02/69563f0e61c8f42188ca9df43be05e02.mp3"></audio>

你好，我是聂鹏程。今天，我来继续带你打卡分布式核心技术。

不知不觉，分布式数据存储这一站已经到了最后一讲。在前面几讲，我与你分享了CAP理论、分布式存储系统的三要素（顾客、导购和货架）、数据分布式分片方法和数据复制技术，其中数据分片方法和数据复制技术均是导购中的关键技术。

在这一讲，我将为你讲解分布式存储中“货架”的关键技术——缓存技术。

在计算机领域的各个方面，缓存都非常重要，是提升访问性能的一个重要技术。为什么这么说呢？

从单个计算机的体系结构来看，内存和处理器速度差异很大，如果不采用缓存技术，处理器的性能会受到很大的限制。

再看计算机应用，如果不采用缓存技术，对于每个请求，应用都要与后台数据库做一次交互，而数据库中的数据存储在磁盘上，因此每次请求都要和磁盘做交互，而磁盘访问的性能很低，造成访问延迟。

除此之外，还有网络访问，如果没有缓存机制，每次访问主机都要与远程机器做交互，速度又可想而知。

接下来，我们就一起打卡分布式缓存技术吧。

## 什么是分布式缓存？

打比方来说，缓存技术其实就像一个水缸，平时它会存储一定的水，而这些水就来自深井。如果每次都去深井打水，一方面井口比较小，导致一次能接收的用水请求有限；另一方面，井比较深，打水的工序比较复杂，导致所需时间比较长。

而有了这个水缸，我们就不需要去深井里打水，当水缸里没水时，水泵会将深井里的水抽到水缸中暂时存储起来。也就是说，“缓存技术”存储了满足人们一定时间内常用的“水量”，以提高用水效率。

在计算机领域，**缓存技术**一般是指，用一个更快的存储设备存储一些经常用到的数据，供用户快速访问。用户不需要每次都与慢设备去做交互，因此可以提高访问效率。

**分布式缓存**就是指在分布式环境或系统下，把一些热门数据存储到离用户近、离应用近的位置，并尽量存储到更快的设备，以减少远程数据传输的延迟，让用户和应用可以很快访问到想要的数据。这，是不是可以形象地理解为“身手钥钱”随身带呢？

其实，**我们通常说的分布式数据缓存，属于计算机应用中的缓存的一种**。而计算机应用中的缓存，一般指内存，即内存存储了用户经常访问的数据，用户或应用不再需要到磁盘中去获取相应的数据，大幅提高访问速度。

如下图所示，数据A是应用经常访问的数据，而数据B很少被应用访问，因此当应用访问数据A时，不需要到磁盘，而直接访问内存即可得到对应的值，速度较快；相反，访问数据B时，由于内存中没有缓存数据B，所以应用需要到磁盘中获取对应的值，速度较慢。

<img src="https://static001.geekbang.org/resource/image/67/bf/67315610b5f0cb2b14588885a05335bf.png" alt="">

那么今天，我要与你分享的分布式数据存储相关的缓存技术，就是以内存做为磁盘的缓存。

## 分布式缓存原理

接下来，我以主流的分布式缓存系统Redis和Memcached为例，与你讲述分布式缓存技术，以加深你的理解吧。

### Redis分布式缓存原理

Redis的全称是Remote Dictionary Server（远程字典服务器）。可以直观地看出，它是以字典结构将数据存储在内存中，应用可直接到内存读写Redis存储的数据。

Redis集群是一个典型的去中心化结构，每个节点都负责一部分数据的存储，同时，每个节点还会进行主备设计来提高Redis的可靠性，具体原理你可以再回顾下[第10篇文章](https://time.geekbang.org/column/article/149653)中的相关内容。

接下来，我与你分享下，Redis中与缓存关系最紧密的三个特性**：支持多数据结构、支持持久化和主备同步。**

**第一，Redis支持多数据结构**。

Redis是一个基于内存的key-value数据库，为了方便支持多应用的缓存，比如缓存文本类型、数据库的查询结果（字段与字段对应的值）等等，支持的数据结构不仅有简单的k／v类型，还可以支持List、Set、Hash等复杂类型的存储。

以Hash这种复杂类型的存储为例，Redis将Hash视作一个整体当作数据库的value（可以是一个对象，比如结构体对象）进行存储。如果把Hash结构的整体看作对象的话，Hash结构里的key-value相当于该对象的属性名和属性值。

比如，插入Hash数据类型的命令：HMSET test field1 “Hello” field2 “World”中，如下图所示，test为key值， field1 “Hello” field2 “World” 为value值，如果把整个Hash结构看做对象的话，则field1、field2类似于对象中的属性名，“Hello”“World”类似于对象中的属性值。

<img src="https://static001.geekbang.org/resource/image/9f/1a/9f025bfd4d3731d378d01c4c19ede21a.png" alt="">

**第二，Redis支持持久化。**

持久化是指，将数据从内存这种易失性存储设备中写入磁盘，从而让数据永久保存。Redis中存储的数据虽然是基于内存的，但它也提供了持久化的机制，主要有两种方式：RDB和AOF。

**RDB**（Redis DataBase），也称快照方式，简单来说就是Redis会定时将内存中的数据备份到磁盘中，形成一个快照，比如每天保存一下过去一周的数据。这样当节点出现故障时，可以根据快照恢复到不同版本的数据。这种方式有一个明显的缺点，是会造成数据丢失，即当节点出现故障时，新数据可能还未备份到磁盘中。

**AOF**（Append Only File）的出现主要弥补了RDB数据不一致的问题，其思想与上一讲提到的数据库复制技术中binary log类似，即记录下Redis中所有的更新操作。

在Redis中，提供了三种实现AOF的策略：

- AOF_FSYNC_NO （不同步），即不会自动触发写操作的同步；
- AOF_FSYNC_EVERYSEC （每秒同步），即每隔一秒都会将写操作同步到磁盘；
- AOF_FSYNC_ALWAYS （每次写都同步），即每次发生写操作会立即同步到磁盘。

Redis中默认采用AOF_FSYNC_EVERYSEC（每秒同步）的策略，因为这种策略的性能很不错，而且一旦出现故障，最多只会丢失一秒的数据。

**第三，Redis支持主备同步。**

说到主备同步，我相信你应该想到了上一讲提到的数据复制技术。在Redis中，采用的是异步复制技术，但Redis可以通过配置min-replicas-to-write和min-replicas-max-lag这两个参数来有效地保证数据一致性。

比如，设置min-replicas-to-write=3、min-replicas-max-lag=10，表示当至少有3个备数据库连接主数据库的延迟时间小于10s时，主数据库才可以执行写操作。延迟时间是从最后一次收到备数据库的心跳开始计算，通常每秒发送一次心跳。

除了上面对写操作的同步，在Redis中，还有两种情况是需要进行数据同步的：

- 一种情况是初次同步，即备数据库刚启动时的数据同步；
- 另一种情况是，因网络故障导致主备数据库断开连接，待网络恢复后的备数据库的数据同步。

针对这两种情况，Redis提供了两种同步模式，即完整重同步和部分重同步。

**完整重同步**的流程如下所示：

1. 当备服务器启动时，会向主服务器发送SYNC命令；
1. 主服务器收到命令后会生成RDB（快照）文件，并记录从现在起新执行的写操作；
1. RDB生成后会发送给备服务器，备服务器通过RDB文件进行数据更新；
1. 更新完成后，主服务器再将新记录的写操作发送给备服务器，备服务器执行完这些新记录的写操作，便与主服务器的数据保持一致了。

<img src="https://static001.geekbang.org/resource/image/4c/fa/4cc71fd16815139a2af93b9721a586fa.png" alt="">

简单地说，**部分重同步**就是，当网络恢复后，主数据库将主备数据库断开连接之后的一系列写操作发送给备服务器，备数据库执行这些写操作，从而保证数据保持一致。

在这种方式的实现中，主备数据库会共同维护一个复制偏移量，这样主数据库就知道应该将哪些写操作发给备数据库，备数据库同步时也知道应该从哪里继续执行操作。

如图所示，主数据库的复制偏移量为5000时，向备数据库发送了100个字节的数据，发送结束后复制偏移量为5100。

此时主备数据库连接断开，备数据库没有接收到这100个字节的数据，等到备数据库重新与主数据库连接上之后，会给主数据库发送PSYNC命令，并带上自己的复制偏移量5000，主数据库接收到信息后，根据接收到的复制偏移量，将5000之后的数据发给备数据库，从而完成数据的部分重同步。

<img src="https://static001.geekbang.org/resource/image/3d/93/3da4db478862a8643d278333fdff5e93.png" alt="">

以上，就是分布式缓存系统Redis中涉及的关键技术，包括支持的数据结构、数据持久化方法和数据同步方法，相信通过上面的介绍，你对分布式缓存技术已经有了一定的了解。

接下来，我再带你学习另一个缓存数据库Memcached。

### Memcached分布式缓存原理

与Redis类似，Memcached也是一个基于内存的高性能key-value缓存数据库。Memcached比Redis问世更早，也有很多公司在使用，比如Facebook、Vox、LiveJournal等。

其实，Memecached的缓存原理和Redis类似。所以接下来的内容，我会着重于你讲述这两个数据库在支持的数据结构、持久化和主备同步上的不同。这样，你可以对比着学习这两个数据库，也会理解得更全面、深入些。

首先，我要先带你了解一下Memcached的集群结构。

Redis的集群结构是每个节点负责一部分哈希槽，且每个节点可以设计主备。与Redis不同，Memcached集群采用一致性哈希的思路，使用的是Ketama算法。该算法的主要思想就是，**带虚拟节点的一致性哈希算法**。

在实际应用中，每个物理节点对应100~200个虚拟节点，才能到达一个较好的存储均衡。这里为了方便理解，我对Memcached的集群结构做了简化，如下图所示，物理节点1对应两个虚拟节点1-1、1-2，物理节点2对应三个虚拟节点2-1、2-2和2-3。

<img src="https://static001.geekbang.org/resource/image/af/68/af2a8805451df9b15d18a8672bd98c68.png" alt="">

采用带虚拟节点的一致性哈希方法，有一个优点是，当添加或移除节点时，不会出现大规模的数据迁移。你可以再回顾下[第25篇文章](https://time.geekbang.org/column/article/168940)中的相关内容。

对于数据结构的支持，Memcached仅支持简单的k／v数据类型，如果想要存储复杂的数据类型，比如List、Set和Hash等，需要客户端自己处理，将其转化为字符串然后进行存储。这样就导致了一个缺点，操作不灵活。比如，Memcached存储的数组中有一个元素需要修改，则需要将整个数组的数据取出来，修改后再整体写入到数据库中 。

而对于持久化，Memcached是不支持的。这意味着断电时，Memcached中存储的数据将会全部丢失。因为内存是一种易失性存储设备，断电后将不会存储数据。

在Memcached中，服务器和服务器之间没有任何通信，即自身不支持主备。如果想要实现高可用，需要通过第三方实现。比如，Repcached实现了Memcached的复制功能，支持一主一备，从而使Memcached满足高可用。

### 对比分析

上面，我以Redis和Memcached这两个主流的分布式缓存系统为例，带你学习了分布式缓存技术。接下来，我以一个表格对它们进行分析对比，以便于你理解和查阅。

<img src="https://static001.geekbang.org/resource/image/4b/91/4b3069a37873b26db686a439a1921691.jpg" alt="">

## 知识扩展：除了分布式存储中的缓存，还有计算机体系结构和网络中的缓存，它们又分别是什么呢？

**计算机体系结构中的缓存**，通常是指专用的缓存设备。由于内存和CPU访问速度相差很大，为了提高CPU的性能，计算机内部在CPU与内存之间设置了相应的缓存。

现在大多数机器分为三级缓存：L1高级缓存、L2高级缓存和L3高级缓存。就访问速度来讲，L1高级缓存 &gt; L2高级缓存 &gt; L3高级缓存&gt;内存。其中，L1高级缓存的访问速度，几乎和CPU中寄存器的访问速度一样快。

有了这三级缓存，很多数据不需要到内存中读取，而直接读取这三级缓存中的数据即可，缩短了数据访问的时间，使得计算机运行速度变得更快。

**网络访问中的缓存**，通常是指本地的“磁盘”。通过网络访问数据时，需要与远程服务器交互来进行传输，而网络间数据传输以及远程服务器对请求的响应，会耗费很多时间。如果本机器的磁盘可以对你经常访问的远程内容进行存储，这样就不用每次都与远程服务器交互，从而减少网络数据传输与服务器响应的延迟，极大地提高性能。

可以看出，**缓存的概念是相对的，基于不同的背景或应用场景，缓存所映射的存储设备是不一样的。**

## 总结

今天，我主要与你分享了分布式数据的缓存技术。

首先，我以水缸的例子带你直观了解了什么是缓存，并引出了什么是分布式数据缓存。分布式数据缓存是以内存作为磁盘的缓存，存储一些用户经常需要用的数据，以提高访问速度。

其次，我以主流的Redis和Memcached为例，与你介绍了分布式缓存技术中的关键技术，包括支持的数据存储结构（比如k/v、Set、List等）、持久化技术（包括快照方式等）和数据同步技术（具体技术原理，可参见[第26篇文章](https://time.geekbang.org/column/article/168963)）。

最后，我再通过一张思维导图来归纳一下今天的核心知识点吧。

<img src="https://static001.geekbang.org/resource/image/1e/45/1e625c22f729a87ea551f6bbc343f045.png" alt="">

相信通过本讲的学习，你已不再觉得分布式缓存有多么神秘了，不管是使用Redis还是看Redis等系统的源码，一定会更容易理解和上手。加油，行动起来吧！

## 思考题

本讲我主要介绍了Redis和Memcached分布式数据缓存系统，你还知道哪些主流的分布式数据缓存系统呢？它们的缓存核心技术是什么呢？

我是聂鹏程，感谢你的收听，欢迎你在评论区给我留言分享你的观点，也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再会！
