分布式缓存就是指在分布式环境或系统下,把一些热门数据存储到离用户近、离应用近的位置,并尽量存储到更快的设备,以减少远程数据传输的延迟,让用户和应用可以很快访问到想要的数据。我们通常说的分布式数据缓存,属于计算机应用中的缓存的一种。而计算机应用中的缓存,一般指内存,即内存存储了用户经常访问的数据,用户或应用不再需要到磁盘中去获取相应的数据,大幅提高访问速度。
以主流的分布式缓存系统 Redis 和 Memcached 为例
Redis 的全称是 Remote Dictionary Server(远程字典服务器)。可以直观地看出,它是以字典结构将数据存储在内存中,应用可直接到内存读写 Redis 存储的数据。Redis 集群是一个典型的去中心化结构,每个节点都负责一部分数据的存储,同时,每个节点还会进行主备设计来提高 Redis 的可靠性。
Redis 中与缓存关系最紧密的三个特性:支持多数据结构、支持持久化和主备同步。
Redis 支持多数据结构。Redis 是一个基于内存的 key-value 数据库,为了方便支持多应用的缓存,比如缓存文本类型、数据库的查询结果(字段与字段对应的值)等等,支持的数据结构不仅有简单的 k/v 类型,还可以支持 List、Set、Hash 等复杂类型的存储。
Redis 支持持久化。持久化是指,将数据从内存这种易失性存储设备中写入磁盘,从而让数据永久保存。Redis 中存储的数据虽然是基于内存的,但它也提供了持久化的机制,主要有两种方式:RDB 和 AOF。
RDB(Redis DataBase),也称快照方式,简单来说就是 Redis 会定时将内存中的数据备份到磁盘中,形成一个快照,比如每天保存一下过去一周的数据。这样当节点出现故障时,可以根据快照恢复到不同版本的数据。这种方式有一个明显的缺点,是会造成数据丢失,即当节点出现故障时,新数据可能还未备份到磁盘中。
AOF(Append Only File)“仅追加文件”。它是Redis的一种持久化机制,用于记录所有写操作,以便在系统重启时可以重建数据。AOF会将每个写命令追加到文件末尾,而不是覆盖文件。这意味着即使在系统崩溃的情况下,AOF文件也能保留所有的写操作记录。
在 Redis 中,提供了三种实现 AOF 的策略:
AOF_FSYNC_NO (不同步),Redis不会对AOF文件进行同步操作。数据会被写入到操作系统的缓存中,但不会立即写入磁盘。这种方式的性能最好,但在系统崩溃时可能会丢失最近的数据。
AOF_FSYNC_EVERYSEC (每秒同步)Redis会每秒对AOF文件进行一次同步。这种方式在性能和数据安全性之间取得了平衡,能够在一定程度上减少数据丢失的风险。
AOF_FSYNC_ALWAYS (每次写都同步),Redis会在每次写操作后立即对AOF文件进行同步。这种方式提供了最高的数据安全性,但性能开销也最大,因为每次写操作都需要等待磁盘同步完成。
AOF文件是以Redis命令的形式存储的,因此可以通过Redis的redis-check-aof工具进行检查和修复。
Redis 中默认采用 AOF_FSYNC_EVERYSEC(每秒同步)的策略,因为这种策略的性能很不错,而且一旦出现故障,最多只会丢失一秒的数据。优点:数据恢复能力强,能够更精确地恢复数据。缺点:相较于RDB(快照),AOF文件可能会更大,且在高频写入的情况下性能可能会受到影响。
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 提供了两种同步模式,即完整重同步和部分重同步。
完整重同步的流程如下所示:
当备服务器启动时,会向主服务器发送 SYNC 命令;
主服务器收到命令后会生成 RDB(快照)文件,并记录从现在起新执行的写操作;
RDB 生成后会发送给备服务器,备服务器通过 RDB 文件进行数据更新;
更新完成后,主服务器再将新记录的写操作发送给备服务器,备服务器执行完这些新记录的写操作,便与主服务器的数据保持一致了。
部分重同步的流程如下所示:
部分重同步就是,当网络恢复后,主数据库将主备数据库断开连接之后的一系列写操作发送给备服务器,备数据库执行这些写操作,从而保证数据保持一致。
在这种方式的实现中,主备数据库会共同维护一个复制偏移量,这样主数据库就知道应该将哪些写操作发给备数据库,备数据库同步时也知道应该从哪里继续执行操作。
Memcached 也是一个基于内存的高性能 key-value 缓存数据库。
Redis 的集群结构是每个节点负责一部分哈希槽,且每个节点可以设计主备。与 Redis 不同,Memcached 集群采用一致性哈希的思路,使用的是 Ketama 算法。该算法的主要思想就是,带虚拟节点的一致性哈希算法。
采用带虚拟节点的一致性哈希方法,有一个优点是,当添加或移除节点时,不会出现大规模的数据迁移。
对于数据结构的支持,Memcached 仅支持简单的 k/v 数据类型,如果想要存储复杂的数据类型,比如 List、Set 和 Hash 等,需要客户端自己处理,将其转化为字符串然后进行存储。这样就导致了一个缺点,操作不灵活。比如,Memcached 存储的数组中有一个元素需要修改,则需要将整个数组的数据取出来,修改后再整体写入到数据库中 。而对于持久化,Memcached 是不支持的。这意味着断电时,Memcached 中存储的数据将会全部丢失。因为内存是一种易失性存储设备,断电后将不会存储数据。
在 Memcached 中,服务器和服务器之间没有任何通信,即自身不支持主备。如果想要实现高可用,需要通过第三方实现。比如,Repcached 实现了 Memcached 的复制功能,支持一主一备,从而使 Memcached 满足高可用。