Redis的持久化使用了Buffer I/O,对持久化文件的写入和读取操作都会使用物理内存的Page Cache,而大多数数据库系统会使用Direct I/O来绕过这层Page Cache并自行维护一个数据的Cache。
而当Redis的持久化文件过大,并对其进行读写时,磁盘文件中的数据都会被加载到物理内存中作为操作系统对该文件的一层Cache,而这层Cache的数据与Redis内存中管理的数据实际是重复存储的。
虽然内核在物理内存紧张时会做Page Cache的剔除工作,但内核很可能认为某块Page Cache更重要,而让你的进程开始Swap,这时你的系统就会开始出现不稳定或者崩溃了,因此在持久化配置后,针对内存使用需要实时监控观察。
与memcached客户端支持分布式方案不同,Redis更倾向于在服务端构建分布式存储
- Redis分布式集群图1
- Redis分布式集群
Redis Cluster是一个实现了分布式且允许单点故障的Redis高级版本,没有中心节点,具有线性可伸缩的功能。节点与节点之间通过二进制协议进行通信,节点与客户端之间通过ascii协议进行通信。
在数据的放置策略上,Redis Cluster将整个key的数值域分成4096个hash槽,每个节点上可以存储一个或多个hash槽,也就是说当前Redis Cluster支持的最大节点数就是4096。
Redis Cluster使用的分布式算法也很简单:crc16( key ) % HASH_SLOTS_NUMBER
整体设计可总结为:
-
数据hash分布在不同的Redis节点实例上
-
M/S的切换采用Sentinel
-
写:只会写master Instance,从sentinel获取当前的master Instance
-
读:从Redis Node中基于权重选取一个Redis Instance读取,失败/超时则轮询其他Instance;Redis本身就很好的支持读写分离,在单进程的I/O场景下,可以有效的避免主库的阻塞风险
-
通过RPC服务访问,RPC server端封装了Redis客户端,客户端基于Jedis开发
在数据一致性问题上,Redis没有提供CAS操作命令来保障高并发场景下的数据一致性问题,不过它却提供了事务的功能
Redis的Transactions提供的并不是严格的ACID的事务(比如一串用EXEC提交执行的命令,在执行中服务器宕机,那么会有一部分命令执行了,剩下的没执行)。
但是这个Transactions还是提供了基本的命令打包执行的功能(在服务器不出问题的情况下,可以保证一连串的命令是顺序在一起执行的,中间有会有其它客户端命令插进来执行)
Redis还提供了一个Watch功能,你可以对一个key进行Watch,然后再执行Transactions,在这过程中,如果这个Watched的值进行了修改,那么这个Transactions会发现并拒绝执行
=====================================================================
-
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
-
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
-
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
-
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰;
-
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰;
-
no-enviction(驱逐):禁止驱逐数据。
===================================================================
可以充分的利用Redis的特性,大大提高效率。
- 在主页中显示最新的项目列表
Redis使用的是常驻内存的缓存,速度非常快
-
LPUSH用来插入一个内容ID,作为关键字存储在列表头部
-
LTRIM用来限制列表中的项目数最多为5000
如果用户需要的检索的数据量超越这个缓存容量,这时才需要把请求发送到数据库
- 删除和过滤
如果一篇文章被删除,可以使用LREM从缓存中彻底清除掉
- 排行榜及相关问题
排行榜(leader board)按照得分进行排序
-
ZADD命令可以直接实现这个功能
-
ZREVRANGE命令可以用来按照得分来获取前100名的用户
-
ZRANK可以用来获取用户排名,非常直接而且操作容易
-
按照用户投票和时间排序
排行榜,得分会随着时间变化。
LPUSH和LTRIM命令结合运用,把文章添加到一个列表中
一项后台任务用来获取列表,并重新计算列表的排序,ZADD命令用来按照新的顺序填充生成列表。列表可以实现非常快速的检索,即使是负载很重的站点。
- 过期处理
使用Unix时间作为关键字,用来保持列表能够按时间排序。对current_time和time_to_live进行检索,完成查找过期项目的艰巨任务。另一项后台任务使用ZRANGE…WITHSCORES进行查询,删除过期的条目。
- 计数
进行各种数据统计的用途是非常广泛的,比如想知道什么时候封锁一个IP地址
INCRBY命令让这些变得很容易,通过原子递增保持计数
GETSET用来重置计数器
过期属性用来确认一个关键字什么时候应该删除
- 特定时间内的特定项目
这是特定访问者的问题,可以通过给每次页面浏览使用SADD命令来解决
SADD不会将已经存在的成员添加到一个集合。
- Pub/Sub
在更新中保持用户对数据的映射是系统中的一个普遍任务。Redis的pub/sub功能使用了SUBSCRIBE、UNSUBSCRIBE和PUBLISH命令,让这个变得更加容易。
- 队列
在当前的编程中队列随处可见。除了push和pop类型的命令之外,Redis还有阻塞队列的命令,能够让一个程序在执行时被另一个程序添加到队列。
=======================================================================
有两种,一种采用strings存储,另外使用hashes存储。那使用哪种更好呢:
- strings
存储较简单,固定的数据。比如存储一个简单的用户信息 (用户名、昵称、头像、年龄等)。存储时需要将数据进行序列化,获取时要反序列化。在数据量较小的情况下还是可以忽略这种开销。但如果存储的的数据可能某些属性会有些变化,比如餐厅数据中,它有 likeVotes(喜欢) 和 dislikeVotes(不喜欢) 的数量,这类变的数据,那么我们采用hashes会更好,而且存储的时候没有序列化开销
- 官方推荐使用hashes
===========================================================================
全量添加
在某些特殊情况,比如初始化数据或缓存出现异常,没有将数据进行同步时,这时需要进行全量的数据同步。
全量同步方式有两种:
逐条插入
批量插入
即Pipeline 管道批量插入。通过pipeline指令完成。
Redis 是一种基于客户端-服务端模型以及请求 / 响应协议的 TCP 服务。
当请求进来后,都是经过服务器进行返回。若服务器没有响应及时,则其他请求进入等待。
这时服务器也无法处理新请求,如何解决这种现象?
答案就是管道:将多个命令发送到服务器,而不用等待响应,最后在一个步骤中读取该响应。MySQL 的批量插入就是这样。
适用场景
-
缓存异常了,将数据重新全部刷入缓存
-
为备战流量高峰期,提前将热点数据全部刷入
增量添加
比如平时有个商家入驻了:查询=》审核=》当后台系统审核新商家后,将数据写入 Redis,核心代码如下
后台修改饭馆信息时(审核通过后),要进行修改,关键代码:
当客户端的用户给饭馆投票时(喜欢 / 不喜欢餐厅),记得修改餐厅的 likeVotes 或 dislikeVotes 字段。
跟修改饭馆不一样的地方就是,只需要修改其中 likeVotes 和 dislikeVotes 属性,不需要整体进行修改
当用户查询餐厅时,若餐厅没有,会查询数据,然后再更新缓存:

先查询缓存 =》缓存没有 =》 查询数据库(再更新缓存)
参考