前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《redis设计与实现》读后总结

《redis设计与实现》读后总结

作者头像
平凡的学生族
发布2019-05-25 08:54:05
5350
发布2019-05-25 08:54:05
举报
文章被收录于专栏:后端技术后端技术

参考

《redis设计与实现》

1. redis 协议

1.1 redis请求与回复协议格式

参考 redis请求与回复协议格式 请求同一为:

代码语言:javascript
复制
*<number of arguments> CR LF
$<number of bytes of argument 1> CR LF
<argument data> CR LF
...
$<number of bytes of argument N> CR LF
<argument data> CR LF

回复根据类型,有五种:

  • 用单行回复,回复的第一个字节将是“+”
  • 错误消息,回复的第一个字节将是“-”
  • 整型数字,回复的第一个字节将是“:”
  • 批量回复,回复的第一个字节将是“$”
  • 多个批量回复,回复的第一个字节将是“*”

1.2 编写redis协议并执行

剖析Redis协议 按照redis协议格式编写.ptl文件 ,然后用管道命令交给Redis一并执行。

关于redis管道: 参考 redis通过pipeline提升吞吐量 就是说,以前是:每条命令进行一次请求和响应。 现在是:将请求的多条命令合并成一条,一并发送给服务端执行,执行结果由客户端一次性返回。 好处是大大减少了网络I/O,增加了效率和并发能力。

2. redis载入与保存

2.1 保存

2.1.1 rdb模式

  • SAVE 阻塞服务器进程
  • BGSAVE fork子进程保存,完成后通知主进程。 BGSAVE serverCron函数每100ms检查一次saveparams
代码语言:javascript
复制
struct redisServer {
    // ...
    // 记录了保存条件的数组
    struct saveparam *saveparams;
    // ...
};

当任意一个条件满足时执行BGSAVE。

2.1.2 aof模式

  • BGREWRITEAOF 重写时,fork子进程

aof写入

  1. 所有写入都会保存记录在aof_buf缓冲区
  2. 每个事件循环结束前调用flushAppendOnlyFile 将aof_buf缓冲区写入aof文件。 是否同步,根据appendfsync
    1. 若为always,同步到文件
    2. 若为everysec,若距离上次同步超过1s,则同步到文件。
    3. 若为no,不同步到文件,由os决定是否同步。

aof重写

  1. fork出子进程,根据写时复制,读取快照,写入aof重写文件
  2. 期间的新写入操作,都追加到aof重写缓冲区
  3. 子进程完成时,向父进程发送一个信号
  4. 父进程收到信号,阻塞完成信号处理函数:
    1. 将AOF重写缓冲区全部写入新AOF文件中,这时新AOF文件所保存的数据库状态将和服务器当前的数据库状态一致
    2. 对新的AOF文件进行改名,原子地(atomic)覆盖现有的AOF文件,完成新旧两个AOF文件的替换。

2.2 载入

aof开启时,优先采用aof。

3. 事件

文件事件 单线程Reactor模型。处理AE_READABLEAE_WRITABLE事件。 原理:

  1. 用I/O多路复用(evport、epoll等之一)监听各个socket,并维护了一个处理队列。触发事件的socket会进入这个队列。
  2. 文件事件分派器不断从队列取出socket,并处理事件。一个socket的触发事件都被处理完成后,才取出下一个处理。

时间事件 无序链表。但实际使用时,其上只有一两个事件。当前版本正常模式下,只有serverCron一个时间事件。 serverCron会做各种常规清理、调整工作,具体的事情可参照原文P.243。 服务器主函数

代码语言:javascript
复制
def main():

    # 初始化服务器
    init_server()

    # 一直处理事件,直到服务器关闭为止
    while server_is_not_shutdown():
        aeProcessEvents()

    # 服务器关闭,执行清理操作
    clean_server()

aeProcessEvents: 阻塞等待最近的时间事件到达。苏醒后,处理文件事件和时间事件 事件处理

文件、时间事件处理器都会适时主动让出执行权,减少阻塞。

4. 客户端

5. 服务端

5.1 命令请求的步骤

一个命令请求从发送到完成主要包括以下步骤:

  1. 客户端将命令请求发送给服务器;
  2. 服务器读取命令请求,并分析出命令参数;
  3. 命令执行器根据参数查找命令的实现函数,然后执行实现函数并得出命令回复;
  4. 服务器将命令回复返回给客户端。

5.2 计算每秒执行次数

  1. serverCron函数中的trackOperationsPerSecond每100毫秒执行一次。
  2. trackOperationsPerSecond根据上次调用后的间隔时间和执行次数,估算1秒执行次数,放入环形数组redisServer.ops_sec_samples
  3. 客户端执行INFO命令时,getOperationsPerSecond根据环形数组平均值计算一秒内估算执行次数。

5.3 启动流程

服务器从启动到能够处理客户端的命令请求需要执行以下步骤:

  1. 初始化服务器状态;
  2. 载入服务器配置;
  3. 初始化服务器数据结构;
  4. 还原数据库状态;
  5. 执行事件循环。

其它

看原书单元末尾的总结

6. 多机数据库的实现

6.1 主从复制

6.1.1 旧版主从复制

做法是:

  1. 从服务器向主服务器发送sync
  2. 主服务器执行BGSAVE,保存rdb文件,发送给从服务器。
  3. 期间主服务器的额外写入都保存到缓冲区。
  4. 主服务器发送rdb后,将缓冲区也一并发送给从服务器。

缺点: 对于短线重连来说,sync效率太低。

6.1.2 新版主从复制

psync的部分重同步中,主向从发送+continue,并发送断线期间的数据,以完成同步。

只要断线时的offset之后的内容都在复制积压缓冲区内,则可以部分重同步。 复制积压缓冲区默认为1MB。其大小可根据second * write_size_per_second来估算。

6.2

思考

  1. 似乎HSETSET更适合对象的存储,因为可以对每个field进行操作(而不用 反序列化->修改对象->序列化)。那常规的序列化存储对象的方法(比如用json serializer)是否合适呢?会不会效率低了。
    • 考虑如何用hset实现对象存储
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.04.19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 参考
  • 1. redis 协议
    • 1.1 redis请求与回复协议格式
      • 1.2 编写redis协议并执行
      • 2. redis载入与保存
        • 2.1 保存
          • 2.1.1 rdb模式
          • 2.1.2 aof模式
        • 2.2 载入
        • 3. 事件
        • 4. 客户端
        • 5. 服务端
          • 5.1 命令请求的步骤
            • 5.2 计算每秒执行次数
              • 5.3 启动流程
                • 其它
                • 6. 多机数据库的实现
                  • 6.1 主从复制
                    • 6.1.1 旧版主从复制
                    • 6.1.2 新版主从复制
                  • 6.2
                  • 思考
                  相关产品与服务
                  云数据库 Redis
                  腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档