Redis Redis原理

Redis原理

Redis内存模型

redisServer
public class redisServer {
    int dbnum;// 当前redis节点内数据库数量,默认16
    redisDb[] db;// 数组,保存数据库信息
    redisClient clients;// 链表,保存客户端信息

    // serverCron函数维护的属性
    Date unixtime;// 秒级别时间戳
    long mstime;// 毫秒级别时间戳
    Date lruclock;// LRU时钟,每十秒更新一次
    long ops_sec_samples;// Redis server每秒执行命令次数
    long stat_peak_memory;// Redis server内存峰值记录
    int shutdown_asap;// Redis server运行状态 1关闭 0运行
    int cronloops;// serverCron函数计数器

    // 持久化相关
    String rdb_child_pid;// 执行BGSAVE子进程ID,-1表示未执行
    String aof_child_pid;// 执行BGREWRITEAOF子进程ID,-1表示未执行
    long dirty;// 修改计数器
    Date lastsave;// 上次BGSAVE时间
    sdshdr aof_buf;// AOF缓冲区

    // 慢查询相关
    long slowlog_entry_id;// 下一条慢查询日志ID
    Object slowlog;// 慢查询日志链表
    long slowlog_log_slower_than;// 超出该属性值则为慢查询,单位微秒
    long slowlog_max_len;// 慢查询日志保存数量
}
redisDb
public class redisDb{
    dict dict;// 保存键值对
    dict expires;// 保存设置过期时间的键和过期时间
    dict watched_keys;// 保存被WATCH监视的键
}
redisClient
public class redisClient{
    redisDb db;// 当前客户端正在使用的数据库
    sdshdr querybuf;// 输入缓冲区
    String[] argv;// 命令与命令参数数组
    int argc;// argv长度
    sdshdr buf;// 输出缓冲区
    int bufpos;// buf已使用长度
    int authenticated;// 0未通过身份验证 1通过身份验证
}

Redis数据结构

Redis Java对象形式展示Redis数据结构

Redis运行机制

Redis server初始化
  1. 实例化redisServer对象
  2. 根据用户指定参数和配置文件初始化redisServer对象属性
  3. 初始化redisServer对象其他属性
  4. 创建常量:“OK"字符串和"1”-"10000"字符串
  5. 为serverCron创建时间事件
  6. 加载持久化文件(AOF或RDB)
  7. 开始执行时间事件

第六步加载持久化文件流程图

Created with Raphaël 2.2.0 redis启动 是否开启aof? 是否存在aof文件 加载aof文件 启动成功 启动失败 是否存在rdb文件 加载rdb文件 yes no yes no yes no yes no yes no
Redis client发送请求
  1. 将操作命令根据RESP协议进行封装
  2. 通过套接字发送给Redis server
Redis server接收请求
  1. 通过套接字接收请求内容,保存至redisClient.querybuf(输入缓冲区)
  2. 解析请求内容,保存至redisClient.argv(操作命令与操作命令参数数组)redisClient.argc(argv长度)
  3. 调用操作命令执行器
Redis server处理请求
  1. 根据操作命令去命令表查询操作命令对应的命令函数(redisCommand)
  2. 根据redisClient.argc和redisCommand.arity验证操作命令参数个数是否正确
  3. 根据redisClient.authenticated验证客户端是否通过身份验证
  4. 调用命令函数
  5. 将处理结果保存至redisClient.buf(输出缓冲区)
  6. 进行后续处理(慢查询日志 & redisCommand计数+1 & AOF & 同步)
  7. 将处理结果发送给Redis client

Redis事件

文件事件

套接字:socket
IO多路复用程序:Redis底层使用epoll
文件事件分派器:事件执行者
事件处理器:连接应答处理器、命令请求处理器和命令回复处理器
文件事件处理流程

  1. 套接字准备好执行连接应答、写入、读取、关闭等操作时,会产生一个文件事件
  2. IO多路复用程序监听多个套接字,把产生事件的套接字放在一个队列里
  3. IO多路复用程序有序推送套接字给文件事件分派器
  4. 文件事件分派器根据事件类型,选择事件处理器调用函数
时间事件serverCron函数
  1. 更新时间戳redisServer.unixtime和redisServer.mstime
  2. 更新LRU时钟redisServer.lruclock
  3. 更新Redis server每秒执行命令次数redisServer.ops_sec_samples
  4. 更新Redis server内存峰值记录redisServer.stat_peak_memory
  5. 处理SIGTERM信号,接收到SIGTERM信号把redisServer.shutdown_asap设置为1
  6. 检查客户端资源
  7. 检查数据库资源
  8. 检查持久化操作运行状态
  9. 如果开启AOF持久化,将AOF缓冲区内容写入AOF
  10. redisServer.cronloops+1

Redis持久化

RDB

触发条件
手动触发(save命令和bgsave命令)
自动触发(save m n)(主从复制)(shutdown命令)

  • save命令会阻塞Redis server,直至RDB文件创建完成(基本弃用)
  • bgsave命令会创建子进程来生成RDB文件,创建子进程过程中父进程阻塞
  • save m n指m秒发生n次操作,会自动触发bgsave;根据redisServer.dirty和redisServer.lastsave属性进行判断,由时间事件serverCron负责触发
  • 主从复制场景下,从节点执行全量复制,主节点会执行bgsave命令,将RDB文件发送给从节点
  • shutdown命令会自动生成RDB文件,然后再结束进程
AOF

开启AOF持久化:appendonly yes
AOF执行流程分为三步:命令追加+文件写入+文件重写

  • 命令追加:将执行成功的修改操作写入redisServer.aof_buf
  • 文件写入:根据策略将缓冲区数据写入磁盘
    always:缓冲区有数据就写入磁盘
    no:等待操作系统通过write命令调用,通常为30秒一次
    everysec:等待操作系统通过fsync命令调用,每秒一次(默认策略)
  • 文件重写:将Redis内的数据转换成命令,写入新的AOF文件

文件重写触发条件
手动触发(bgrewriteaof命令)
自动触发(AOF文件超过64MB 并且 新AOF文件大于原AOF文件)
为什么AOF最多可能丢失2秒的数据
Redis会记录上次fsync命令成功的时间,如果不到2秒,不触发fsync;如果超过2秒,则阻塞进行fsync。因此在触发fsync之前突然宕机可能会丢失2秒数据。

Redis4.0混合持久化

开启混合持久化:aof-use-rdb-preamble yes
Redis5.0默认开启混合持久化
混合持久化执行流程

  1. 手动/自动触发bgrewriteaof命令
  2. 主进程fork子进程,fork过程中主进程阻塞
  3. 子进程将全量数据以RDB格式写入AOF文件,主进程将操作命令写入AOF缓冲区和AOF重写缓冲区
  4. 子进程通知主进程,主进程将AOF重写缓冲区数据以AOF格式写入AOF文件
  5. 主进程将新AOF文件替换原AOF文件(AOF文件前半段是RDB格式数据,后半段是AOF格式命令)

Redis高可用

主从复制模式

优点
读写分离:主节点写,从节点读
故障恢复:主节点宕机,将从节点升级为主节点
缺点
主/从节点故障恢复需要人工干预
写操作无法负载均衡
连接建立阶段

  1. 从节点masterhost记录主节点ip,masterport记录主节点port
  2. 从节点发送slaveof命令给主节点,主节点返回OK
  3. 从节点与主节点建立socket连接,从节点socket用于接收RDB文件以及其他命令,主节点socket保存在redisServer.clients
  4. 从节点发送ping命令给主节点,主节点返回pong
  5. 从节点发送auth命令给主节点进行身份验证
  6. 从节点发送端口号给主节点,主节点后续会将数据发送至该端口

数据同步阶段

  • 从节点发送psync命令给主节点,主节点判断全量复制或者部分复制

命令传播阶段

  • 主节点操作命令执行成功后,发送操作命令给从节点
  • 主从节点心跳机制和REPLCONF ACK机制

repl-disable-tcp-nodelay yes:合并操作命令,40ms发送一次
repl-disable-tcp-nodelay no:每次操作命令发送一次
心跳机制:主节点每10秒发送ping命令给从节点
REPLCONF ACK机制:从节点每秒发送REPLCONF ACK命令给主节点,维护offset属性

哨兵模式

优点
自动实现主节点故障恢复
缺点
从节点故障恢复仍需要人工干预
写操作无法负载均衡
实现原理
每个哨兵节点维护了三个定时任务

  1. 向主节点发送info命令获取最新主从结构
  2. 通过发布订阅获取其他哨兵节点信息
  3. 向其他节点发送ping命令进行心跳检测

心跳检测过程中,主节点没有回复,哨兵节点将主节点主观下线,并通过sentinel is-master-down-by-addr命令询问其他哨兵节点主节点状态,如果判断主观下线的哨兵节点到达一定数量,则对该主节点进行客观下线,开始进行选举。
选择领导者哨兵节点算法:Raft算法,先到先得。
选择主节点算法:

  1. 过滤掉不健康的从节点
  2. 选择优先级最高的从节点
  3. 若优先级无法区分,选择offset最大的从节点
  4. 若offset无法区分,选择runid最小的从节点
集群模式

优点
解决写操作无法负载均衡的问题
实现原理
集群模式将16384个槽分布在各个主节点上,数据通过数据分区方案落在各个槽中。
数据分区方案

  • 哈希取余分区
  • 一致性哈希分区
  • 虚拟节点一致性哈希分区

集群成员

  • 数据节点(主节点和从节点)
  • 哨兵节点
    主节点读写,从节点只负责备份数据
    每个节点维护2个端口
  • 普通端口:用户提供服务
  • 集群端口(普通端口+10000):用于集群各个节点通信

增加节点

  1. 启动节点
  2. 节点握手
  3. 迁移槽
  4. 指定主从关系

减少节点

  1. 迁移槽
  2. 节点下线

故障转移
哨兵节点识别主节点客观下线,由其他主节点投票选一个从节点成为主节点

发布了14 篇原创文章 · 获赞 3 · 访问量 560

猜你喜欢

转载自blog.csdn.net/qq_37956177/article/details/104046476