redis集群、慢查询及内存优化
# 简介
作为缓存数据库,肯定要考虑缓存服务稳定性相关的保障机制。
持久化机制就是一种保障方式。持久化机制保证了 Redis 服务器重启的情况下也不会损失(或少量损失)数据,因为持久化会把内存中数据保存到硬盘上,重启会从硬盘上加载数据。
随着 Redis 使用场景越来越多,技术发展越来越完善,在 Redis 整体服务上的容错、扩容、稳定各个方面都需要不断优化。
因此在 Redis 的集群模式上也有不同的搭建方式来应对各种需求
。
总结来说,Redis 集群模式有三种:
- 主从模式
- 哨兵模式
- Cluster 集群模式
集群最大节点个数是16384 个
# 架构模式分类:有1+3种
- Redis
单节点单机
器部署 - Redis
主从节点
部署 - Redis Sentinel
(哨兵)模式
部署 - Redis
集群模式
# 主从(replication )模式
为了 Redis 服务避免单点故障,通常的做法是将 Redis 数据复制多个副本以部署在不同的服务器上
。这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。为此,Redis 提供了复制( replication )功能
,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。
Redis 服务器分为两类:一类是主数据库(Master),另一类是从数据库(Slave)
。
主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。
从数据库一般是只读的,并接受主数据库同步过来的数据。
一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。
如图所示: 优点
一个主,可以有多个从,并以非阻塞的方式完成数据同步;
从服务器提供读服务,分散主服务的压力,实现读写分离;
从服务器之前可以彼此连接和同步请求,减少主服务同步压力。
缺点
不具备容错和恢复功能,主服务存在单点风险;
Redis 的主从复制采用全量复制,需要服务器有足够的空余内存;
主从模式较难支持在线扩容。
# 哨兵(Sentinel)模式
Redis 提供的 sentinel(哨兵)机制
,通过 sentinel 模式启动redis后,自动监控 Master/Slave 的运行状态,基本原理是:心跳机制 + 投票裁决。哨兵模式本身是基于主从为基础,所以写并发比较多的情况下,主压力肯定是有的
简单来说,哨兵的作用就是监控 Redis 系统的运行状况。它的功能包括以下两个:
- 监控主数据库和从数据库是否正常运行;
- 主数据库出现故障时自动将从数据库转换为主数据库。
哨兵模式主要有下面几个内容:
监控( Monitoring ):Sentinel 会定期检查主从服务器是否处于正常工作状态。
提醒( Notification ):当被监控的某个 Redis 服务器出现异常时,Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
自动故障迁移(Automatic failover):当一个主服务器不能正常工作时,Sentinel 会开始一次自动故障迁移操作,它会将失效主服务器的其中一个从服务器升级为新的主服务器,并让失效主服务器的其他从服务器改为复制新的主服务器;当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。
Redis Sentinel 是一个分布式系统,你可以在一个架构中运行多个 Sentinel 进程( progress )。
如图所示:
优点
- 哨兵模式
主从可以切换,具备基本的故障转移能力
;保证高可用; 监控各个节点; 自动故障迁移; - 哨兵模式具备主从模式的所有优点。
缺点
1: 哨兵模式也很难支持在线扩容
操作;
2: 集群的配置信息管理比较复杂
。
主从模式,切换需要时间丢数据,没有解决 master 写的压力
# Cluster集群模式
Redis Cluster 是一种服务器 Sharding 技术,3.0 版本开始正式提供
。采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。
如图所示: Cluster 集群结构特点:
- Redis Cluster 所有的物理节点都映射到 [
0-16383
] slot 上(不一定均匀分布),Cluster 负责维护节点、桶、值之间的关系; - 在 Redis 集群中放置一个 key-value 时,
根据 CRC16(key) mod 16384 的值,从之前划分的 16384 个桶中选择一个;
- 所有的 Redis 节点彼此互联(PING-PONG 机制),内部使用二进制协议优化传输效率;
- 超过半数的节点检测到某个节点失效时则判定该节点失效;
- 使用端与 Redis 节点链接,不需要中间 proxy 层,直接可以操作,使用端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
优点
- 无中心架构,节点间数据共享,可动态调整数据分布;
- 节点可动态添加删除,扩展性比较灵活;
- 部分节点异常,不影响整体集群的可用性。
缺点
- 集群实现比较复杂;
- 批量操作指令( mget、mset 等)支持有限;
- 事务操作支持有限。
# 主从模型详解
Redis 中的主从复制,也就是 Master-Slave 模型,多个 Redis 实例间的数据同步以及 Redis 集群中数据同步会使用主从复制。
主从复制主要是数据同步, 数据同步分为全量同步和增量同步。
全量同步: 全量同步一般发生在 Slave 机器初始化阶段
, 这时候 Slave 需要将 Master 上的所有数据都进行同步复制。
大概步骤如下所示:
- 从服务器发送 SYNC 命令,链接主服务器
- 主服务器收到 SYNC 命令后,进行存盘的操作,并继续收集后续的写命令,存储缓冲区
- 存盘结束后,将对应的数据文件发送到 Slave 中,完成一次全量同步
- 主服务数据发送完毕后,将进行增量的缓冲区数据同步
- Slave 加载数据文件和缓冲区数据,开始接受命令请求,提供操作
增量更新:
Slave 节点初始化完成之后,开始正常工作
,Master 节点进行的写操作都会同步到 Slave 节点上面。Master 节点每执行一个写命令都会向从服务器发送相同的写命令
,从服务器接收到命令,并执行。
# 模式比较
# Redis 的主从复制模式优缺点
- **优点:**可靠性相对于单个节点部署模式有所提高,
实现读写分离提高读写效率
- **缺点:**写数据依赖于主节点,主节点空间有限,而且主节点存在单点的风险
# Redis sentinel(哨兵)模式优缺点
- **优点:**保证高可用,
各个节点自动故障转移
- **缺点:**主从模式,依旧存在主节点单点风险,主从切换有丢失数据的问题
# 集群的实现方案
1. Redis Cluster 集群方案(服务端 Sharding 技术)
Redis Cluster 是 3.0 版本开始正式
提供的服务器 Sharding 技术。引入 slot(槽)的概念,整个集群共有 16384 个槽
。根据 key 进行散列(CRC16 后 16384 取模),分配到其中一个槽当中。
2. Redis Sharding 集群(客户端 Sharding 技术) Redis Sharding 出现先与 Redis Cluster 方案,他是多 Redis 实例集群方案
。
客户端 Sharding 方式,该向哪个 Redis 节点操作数据,由客户端来进行控制。服务端 Redis 还是一个个相对独立的 Redis 实例节点,没有做任何变动。节点选择可采用的方法:一致性 hash 算法或者虚拟节点算法。
3. Twemproxy 中间件实现
Twemproxy 就是一种中间件 Sharding 分片的技术,处于客户端和服务器的中间
,将客户端发来的请求,进行一定的处理后(如 Sharding),再转发给后端真正的 Redis 服务器。
也就是说,客户端不直接访问 Redis 服务器,而是通过 Twemproxy 代理中间件间接访问
。
4. Codis 中间件实现
目前用的最多的集群方案, 基本和 twemproxy 一致的效果, 但它支持在 节点数量改变情况下, 旧节点数据可恢复到新 hash 节点
。
# Redis分区
分区可以让 Redis 管理更大的内存, Redis 将可以使用所有机器的内存。如果没有分区, 你最多只能使用一台机器的内存。分区使 Redis 的计算能力通过简单地增加计算机得到成倍提升,Redis 的网络带宽也会随着计算机和网卡的增加而成倍增长。
# 缺点
涉及多个 key 的操作通常不会被支持。例如你不能对两个集合求交集, 因为他们可能被存储到不同的 Redis 实例(实际上这种情况也有办法, 但是不能直接使用交集指令)。
同时操作多个 key,则不能使用 Redis 事务.分区使用的粒度是key,不能使用一个非常长的排序key存储一个数据集;
当使用分区的时候, 数据处理会非常复杂。 例如为了备份你必须从不同的 Redis 实例和主机同时收集 RDB / AOF 文件。
**分区时动态扩容或缩容可能非常复杂。**Redis 集群在运行时增加或者删除 Redis 节点, 能做到最大程度对用户透明地数据再平衡,但其他一些客户端分区或者代理分区方法则不支持这种特性。然而, 有一种预分片的技术也可以较好的解决这个问题。
# 慢查询
# Redis 慢查询及配置
所谓慢查询就是指,系统执行命令之后,计算统计每条指令执行的时间,如果执行时间超过设置的阈值,就说明该命令为慢指令。
Redis 慢查询配置参数为:
- **slowlog-log-slower-than:**设置慢查询定义阈值,超过这个阈值就是慢查询。
单位为微秒,默认为 10000
。 - slowlog-max-len:
慢查询的日志列表的最大长度
,当慢查询日志列表处于最大长度的时候,最早插入的一个命令将从列表中移除。
# 慢查询修复经验
- 将一些效率比较低或者算法复杂度比较高的命令,
禁用或者替换为效率较高的指令
。 例如禁用:sort、keys 命令 拆分大对象数据
,防止一次命令操作过多的数据
# Redis优化
# 优化 Redis 服务的性能
Master 节点禁止持久化工作
- 持久化策略要有正确的选择,
关键数据可以采用 slave 节点 AOF 备份
- 主从节点部署
在同一个局域网内
,保证复制速度与稳定性 - 设置或者增加从库需要考虑主库现有的压力
- 主从复制一定要单向结构,避免使用图状结构
# 内存消耗分类及内存统计命令
Redis的内存消耗分为:
- **对象内存:**这是
占用最大的一块,存储用户的所有数据
。可以理解为:所有 key 的大小 + 所有 value 的大小 - **缓冲内存:**客户端缓冲、复制积压缓冲、AOF 缓冲
- **内存碎片:**更新操作、过期数据都可能造成内存碎片
# 内存管理方式
Redis 内存管理方式主要方向上有两个:
- 控制内存上限; Maxmemory 参数
可以限制最大可用内存
- 优化回收策略,进行内存回收
# Redis 报内存不足怎么处理
- 修改配置文件,
增加 Redis 的内存空间 maxmemory
设置缓存淘汰策略
, 具体的淘汰策略;- 使用
Redis 集群模式来存储
# 内存优化
- Redis 的所有数据都进行 redisObject 来封装
- 缩减键、值对象的长度,简化键名,提高内存使用效率
尽量使用 hash 数据结构,减少 key 的数量
共享对象池,并设置空间极限值
# 几个可能导致 Redis 阻塞的原因
内部原因:
- Redis 的 API 或者指令数据结构使用不合理
- Redis 主机 CPU 负载过高,导致系统崩溃
- 持久化工作资源占用过多
外部原因:
- CPU 竞争
- 内存交换
- 网络问题
# 怎么去发现 Redis 阻塞异常情况
1. 通过应用服务监控发现
当 Redis 阻塞的时候,线上应用服务应该会感知发现。比如说发现 Redis 链接超时等。这种就需要应用服务增加对于异常的统计,并针对 Redis 相关的异常,进行报警。
2. 通过 Redis 自身监控系统
借助 Redis 监控系统发现阻塞问题,当监控系统发现各个监控指标存在异常的时候,发送报警。 指标有:CPU/内存/磁盘等, 慢查询,命令耗时增加等。
# 问题
# Redis 集群会有写操作丢失吗?为什么?
Redis 并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作
。
# 什么是 bigkey? 有什么影响?
bigkey 是指存储 value 占用内存空间非常大的 key
。例如一个 String 类型的 value 占用了 100MB 空间。
redis-cli-bigkeys 命令可以统计 bigkey 的分布。
生产环境下执行 debug object key
查看 serializedlength 属性,表示 key 对应的 value 序列化之后的字节数
。
bigkey 的影响主要体现在:
- **造成内存空间不平衡:**如果 bigkey 存储量比较大,同一个 key 在同一个节点或者服务器中存储,造成一定的影响
- **超时阻塞:**由于占用空间比较大,那么操作起来效率肯定比较低,也就表示出现阻塞可能性增加
- **网络阻塞:**获取 bigkey 的时候,自然传输的数据量比较大,导致宽带的压力。
# 为什么Redis集群有16384个槽?
(1)如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。
如上所述,在消息头中,最占空间的是myslots[CLUSTER_SLOTS/8]
。当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb
因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。
(2)redis的集群主节点数量基本不可能超过1000个。
如上所述,集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者,不建议redis cluster节点数量超过1000个。那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。
(3)槽位越小,节点少的情况下,压缩比高
Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,但是如果bitmap的填充率slots / N很高的话(N表示节点数),bitmap的压缩率就很低。如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。