Skip to the content.

首页

Redis


技术内幕

纯内存存储、IO多路复用、单线程架构。

Redis服务器是一个事件驱动程序,需要处理两类事件:文件事件和时间事件,文件事件用于处理服务器和客户端之间的网络IO,时间事件主要用来处理定时操作。

文件事件处理器分为四部分:套接字、I/O多路复用程序、文件事件分派器以及事件处理器。


数据结构

编码方式

SDS

简单动态字符串,只有值才会使用SDS,键还是使用C语言的字符串,SDS记录了字符串长度。优势在于:不需要遍历即可以直接获取到字符串长度;杜绝了缓存区溢出;减少修改时内存重分配,包括空间预分配以及惰性空间释放;二进制安全,允许空字符(不再需要用于标记字符串的结束),支持保存任意格式的二进制数据。


其他功能


事务

ACID

包含逻辑处理的事务


Lua

通过lua脚本可以执行redis命令,高效且是原子性的,同时可以被缓存以实现复用,绝大多数情况下都应该使用lua脚本替代事务。


持久化

RDB

为当前进程数据生成快照保存,适合冷备份,优点是恢复速度快,缺点是无法很好的保证高可用性和实时性。

包括save和bgsave命令,save命令会阻塞redis,不建议使用;bgsave命令会fork一个子进程,只有在fork阶段才会阻塞redis,为主流方式。

bgsave流程

  1. 执行bgsave,会先判断当前是否存在bgsave命令,存在则直接返回;
  2. 执行fork,过程中会阻塞父进程,fork完成后,不再阻塞父线程;
  3. 子进程使用copy-on-write机制与父进程共享内存,根据父进程内存生成临时快照文件,完成后原子替换原RDB文件;
  4. 替换完成后发送信号通知父进程,父进程更新统计信息。

AOF

以独立日志的方式记录每次写命令,通过重新执行AOF文件中的命令来恢复数据,优点是能保证实时性,缺点是恢复速度慢。

所有写命令以文本协议格式追加到AOF缓冲区,根据指定的策略(一般使用everysec)将缓冲区同步到磁盘的AOF文件,同时定期会对AOF文件执行重写以防止文件过大。

‘no-appendfsync-on-rewrite’配置表示AOF重写期间AOF追加操作会阻塞,即可能会导致服务器阻塞,默认开启,如果关闭则在重写期间AOF追加会被写入缓冲池内,重写完成后再追加到AOF文件,如果服务宕机则会丢失缓冲区内的数据。也可以选择在服务器压力较小时手动触发bgrewriteaof,以避免在流量高峰时触发AOF自动重写。

RDB和AOF混合

4.0版本开始支持混合持久化,AOF重写的时候会把RDB的内容写到AOF文件的开头。


内存管理

内存碎片

Redis服务器的统计值为mem_fragmentation_ratio,一般不要大于1.5。

产生内存碎片的原因有:Redis向操作系统申请的内存空间可能会大于数据实际需要的存储空间;频繁修改数据,Redis的内存策略是不轻易释放空间给操作系统。

内存回收策略

内存溢出控制

内存优化


主从复制

  1. 从节点保存主节点信息;
  2. 主从建立socket连接;
  3. 发送ping命令;
  4. 权限验证;
  5. 同步数据集
  6. 持续异步复制。

同步数据集

使用psync命令执行数据同步,分为全量复制和部分复制。


部署模式

主从

使用一台主库和多台从库,可以实现读写分离,但不支持主从切换。

哨兵

在主从模式的基础上额外增加了哨兵节点集群,哨兵节点为不存储数据的redis实例,其节点个数需要为奇数个,每一个哨兵节点都会监控所有普通节点和其他哨兵节点,当发现普通节点失效会将其标记为下线,如果下线的是主节点,则哨兵集群会通过半数原则推选出一个哨兵节点来完成故障转移,并通知到应用方和整个集群。

故障发现及转移通过pub/sub功能实现,整个集群使用固定的Channel进行通信,普通节点定时发送消息以维持心跳,哨兵集群订阅频道监控普通节点,并在故障转移完成后发送消息通知到整个集群。

集群

由多个节点组成,包括主节点和对应的从节点,主节点负责读写请求和集群维护,从节点只作为主节点的备份,对主节点的数据和状态信息进行复制,并在主节点故障时替换主节点。

集群模式只允许使用0号数据库。

使用虚拟槽分区,范围为0-16383,使用bitmap标识则需要16383位,即大小为2KiB;每个主节点只负责其中一个子集,由节点自身维护槽和键的映射关系;因为解耦了数据和节点的关系,所以节点可以动态增加或删除。

节点间使用Gossip协议通信,即彼此不断的交换信息,追求的是整个集群的最终一致性。每个节点需要额外开启一个通信端口,并在固定的周期内根据一定的规则选择几个节点通信。

优化建议


可能造成阻塞的原因

  1. 不合理的使用命令,如keys、hgetall、flushall等,可以通过使用slowlog命令分析慢查询;
  2. 由于命令执行效率下降导致CPU饱和,如放宽了使用ziplist的条件而导致过度内存优化;
  3. 持久化阻塞,内存实例过大会导致fork阻塞,硬盘压力过大会导致fsync阻塞。
  4. 短连接优化,如果客户端未使用连接池,会导致Redis服务器浪费大量资源在连接处理上。