分布式缓存一般被定义为一个数据集合,它将数据分布(或分区)于任意数目的集群节点上。集群中的一个具体节点负责缓存中的一部分数据,整体对外提供统一的访问接口。分布式缓存一般基于冗余备份机制实现数据高可用。
目前,在所有可实现分布式缓存的开源软件中,Redis
应用最为广泛,开源社区也最为活跃,开源客户端支持语言也最为丰富。
1. Redis 可以做什么
Redis
是一个开源的,基于内存存储亦可持久化的 Key-Value
存储系统,可用作数据库、高速缓存、锁和消息队列。它支持字符串、哈希表、列表、集合、有序集合、位图、HyperLogLogs
等数据类型。内置复制、Lua
脚本、老化逐出、事务以及不同级别磁盘持久化功能,同时,Redis
还支持 Sentinel
和 Cluster
(从3.0开始)等高可用集群方案。
作为缓存的常见业务场景有:
- 缓存热点数据,减轻数据库负载;
- 基于
List
结构显示最新的项目列表; - 基于
Sorted Set
来做排行榜,取Top N
; - 基于
Set
来做uniq
操作,如页面访问者排重; - 基于
Hset
做单Key
下多属性的项目,例如商品的基本信息、库存、价格等设置成多属性;
2. Redis 特点
与其它 Key-Value
缓存产品相比,Redis
有以下特点:
2.1 支持多种数据类型
Redis
不是仅仅支持简单的Key-Value
类型的数据,还支持字符串、列表、集合、散列表、有序集合数据结构的存储,这一优势使Redis
适用于更广泛的应用场景;
Redis 底层实现采用了很多优秀的数据结构,使其具有优异的性能,例如 Redis 使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员是比较长的字符串时,Redis 就会使用跳跃表作为有序集合键的底层实现。跳跃表以有序的方式在层次化的链表中保存元素,效率可与平衡树媲美——查找、删除、添加等操作都可以在对数期望(LogN)时间下完成。
2.2 支持数据持久化
Redis
支持数据的持久化(包括AOF
和RDB
两种模式),可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用,性能与可靠性兼顾;
需要注意的是,RDB 模式是定时的持久机制,发生宕机时可能会导致数据丢失,而 AOF 模式提供了 appednfsync 参数,通过设置 appednfsync 参数(设置为 always)可以最大限度保证数据安全,但也会降低效率。
2.3 高可用策略
Redis
支持数据的备份,即Master-Slave
模式,Master
故障时,对应的Slave
将通过选举升主,保障可用性;此外,Redis 还支持 Sentinel 和 Cluster(从3.0版本开始)等高可用集群方案。
2.4 数据淘汰策略
Redis
支持配置最大内存,当内存不够用时,会通过淘汰策略来回收内存,Redis
提供了丰富的淘汰策略,粒度粗细皆有,适用多种应用场景。
volatile-lru
:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰;volatile-ttl
:从已设置过期时间的数据集中挑选将要过期的数据淘汰;volatile-random
:从已设置过期时间的数据集中任意选择数据淘汰;allkeys-lru
:从数据集中挑选最近最少使用的数据淘汰;allkeys-random
:从数据集中任意选择数据淘汰;no-enviction
:当内存使用达到阈值的时候,所有引起申请内存的命令会报错;
2.5 内存管理
Redis
使用 C
语言编写,但为了提高内存的管理效率,并没有直接使用 malloc/free
函数,Redis
默认选择 jemalloc
作为内存分配器,以减小内存碎片率。
jemalloc
在 64 位系统中,将内存空间划分为小、大、巨大三个范围。每个范围内又划分了许多小的内存块单位。当 Redis
存储数据时,会选择大小最合适的内存块进行存储。同时,Redis
为 Key-Value
存储定制了两种对象,其中 Key
采用 SDS
(Simple Dynamic String
),Value
采用 redisObject
,为内部编码和回收内存的高效实现奠定了基础。
2.6 支持事务
Redis
主进程是单线程工作,因此,Redis
的所有操作都是原子性的,即操作要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性;
由于缓存操作都是内存操作,只有很少的计算,因此即便在单线程下,Redis 性能也很优秀。目前,大多数 CPU 都是多核的,为了提高多核 CPU 的利用率,通常在同一台机器上部署多个 Redis 实例(注意配置不同的端口),官方的推荐是一台机器部署8个实例。
Redis
提供了一些在一定程度上支持线程安全和事务的命令,例如 multi/exec
、watch
、inc
等。由于 Redis
服务器是单线程的,任何单一请求的服务器操作命令都是原子的,但跨客户端的操作并不保证原子性,所以对于同一个连接的多个操作序列也不保证事务。
Redis
性能优越,读的速度达 110000次/s,写的速度达 81000次/s;此外,Key
和Value
的大小限制均为 512M,这阈值相当可观。
除了上述特点,Redis
还支持 Publish/Subscribe
、通知等特性。
3. Redis 高可用解决方案
Redis
有很多高可用的解决方案,只简单介绍其中三种。
3.1 方案一:Redis Cluster
从 3.0 版本开始,Redis
支持集群模式 — Redis Cluster
,可线性扩展到 1000 个节点。Redis-Cluster
采用无中心架构,每个节点都保存数据和整个集群状态,每个节点都和其它所有节点连接,客户端直连 Redis
服务,免去了 Proxy
代理的损耗。Redis Cluster
最小集群需要三个主节点,为了保障可用性,每个主节点至少挂一个从节点(当主节点故障后,对应的从节点可以代替它继续工作),三主三从的 Redis Cluster
架构如下图所示:
3.2 方案二:Twemproxy
Twemproxy
是一个使用 C
语言编写、以代理的方式实现的、轻量级的 Redis
代理服务器。它通过引入一个代理层,将应用程序后端的多台 Redis
实例进行统一管理,使应用程序只需要在 Twemproxy
上进行操作,而不用关心后面具体有多少个真实的 Redis
实例,从而实现了基于 Redis
的集群服务。当某个节点宕掉时,Twemproxy
可以自动将它从集群中剔除,而当它恢复服务时,Twemproxy
也会自动连接。由于是代理,Twemproxy
会有微小的性能损失。
Twemproxy
架构如下图所示:
3.3 方案三:Codis
Codis
是一个分布式 Redis
解决方案,对于上层的应用来说,连接到 Codis Proxy
和连接原生的 Redis Server
没有明显的区别(部分命令不支持), 上层应用可以像使用单机的 Redis
一样使用,Codis
底层会处理请求的转发,不停机的数据迁移等工作。