• Redis源码漂流记(一)


    Redis源码漂流记(一)

    由于早期redis面试被问挂过,近期依然问挂,反正是各种各样的方式。放下过往,重新思索Redis到底是什么,Redis能提供什么。

    源码功能

    • 数据结构:学习Redis 主要数据结构的设计思想和实现,包括字符串的实现方法、内存紧凑型结构的设计、哈希表性能优化设计,以及 ziplist、quicklist、listpack、跳表的设计与实现等。
    • 网络通信与执行模型:试着去掌握 Redis server 的启动流程、高性能网络通信设计与实现、事件驱动框架的设计与实现、Redis 线程类型的设计和优化等。缓存:你将了解常见缓存替换算法如何从原理转变为代码。
    • 可靠性保证:试着去掌握 RDB、AOF 的具体实现,分布式系统中 Raft 一致性协议的设计实现,故障切换的关键代码实现等。
    • 切片集群:学习到 Redis 切片集群中关键机制的设计与实现,包括 Gossip 通信协议、请求重定向、数据迁移等。

    这些都是宝贵的计算机领域宝贵的知识,如果打算写/用中间件、分布式系统等都是必不可少的。如果我们主导一个选型技术上线的时候,不了解它的机制,遇到问题的时候,可能只有重启一个办法。遇到它,分析它,解决它。(除非是祖传改不了的屎山,还是不动为好)

    学习目标

    • 能讲清某个模块的基本运行原理
    • 能画出运行流程图
    • 能用代码实践它的基本功能
    • 做出部门内的分享(不要提前进行,最好进度过了90%,太早了很虚浮)

    基于历史的经验,大部分系列都夭折了,最长的坚持了三篇。期待这次能走的更远一些。

    学习前提

    • 会一些c的基本语法。
    • 跟随 极客时间蒋德钧老师《Redis 源码剖析与实战》。

    他的是以Redis5.0,我的是Redis6.0

    • 一些国内外的博客

    Start

    deps目录

    • 一是 Redis 依赖的、实现更加高效的功能库,如内存分配;
    • 二是独立于 Redis 开发演进的代码,如客户端;
    • 三是 lua 脚本代码。后续你在学习这些功能的设计实现时,就可以在 deps 目录找到它们。

    tests目录:

    Redis 实现的测试代码可以分成四部分,分别是

    • 单元测试(对应 unit 子目录)
    • Redis Cluster 功能测试(对应 cluster 子目录)
    • 哨兵功能测试(对应 sentinel 子目录)
    • 主从复制功能测试(对应 integration 子目录)

    除了有针对特定功能模块的测试代码外,还有一些代码是用来支撑测试功能的,这些代码在 assets、helpers、modules、support 四个目录中。

    utils目录:

    还有一些功能属于辅助性功能,包括

    • 用于创建 Redis Cluster 的脚本
    • 用于测试 LRU 算法效果的程序,以及
    • 可视化 rehash 过程的程序。

    在 Redis 代码结构中,这些功能代码都被归类到了 utils 目录中统一管理。

    配置文件

    除了 deps、src、tests、utils 四个子目录以外,Redis 源码总目录下其实还包含了两个重要的配置文件:

    • 一个是 Redis 实例的配置文件 redis.conf,
    • 另一个是哨兵的配置文件 sentinel.conf。

    当你需要查找或修改 Redis 实例或哨兵的配置时,就可以直接定位到源码总目录下。

    src

    这个目录里面包含了 Redis 所有功能模块的代码文件,也是 Redis 源码的重要组成部分.

    • adapters
    • modules
    • 各功能模块代码

    服务器示例

    Redis 在运行时是一个网络服务器实例,因此相应地就需要有代码实现服务器实例的初始化和主体控制流程,而这是由 server.h/server.c 实现的,Redis 整个代码的 main 入口函数也是在 server.c 中。如果你想了解 Redis 是如何开始运行的,那么就可以从 server.c 的 main 函数开始看起

    对于一个网络服务器来说,它还需要提供网络通信功能。Redis 使用了基于事件驱动机制的网络通信框架,涉及的代码文件包括 ae.h/ae.c,ae_epoll.c,ae_evport.c,ae_kqueue.c,ae_select.c。

    与网络通信相关的功能还包括底层 TCP 网络通信和客户端实现。Redis 对 TCP 网络通信的 Socket 连接、设置等操作进行了封装,这些封装后的函数实现在 anet.h/anet.c 中。这些函数在 Redis Cluster 创建和主从复制的过程中,会被调用并用于建立 TCP 连接。

    客户端在 Redis 的运行过程中也会被广泛使用,比如实例返回读取的数据、主从复制时在主从库间传输数据、Redis Cluster 的切片实例通信等,都会用到客户端。Redis 将客户端的创建、消息回复等功能,实现在了 networking.c 文件中,如果你想了解客户端的设计与实现,可以重点看下这个代码文件

    数据库数据类型与操作

    Redis 数据库提供了丰富的键值对类型,其中包括了 String、List、Hash、Set 和 Sorted Set 这五种基本键值类型。此外,Redis 还支持位图、HyperLogLog、Geo 等扩展数据类型。而为了支持这些数据类型,Redis 就使用了多种数据结构来作为这些类型的底层结构。比如,String 类型的底层数据结构是 SDS,而 Hash 类型的底层数据结构包括哈希表和压缩列表 (引用蒋老师原图) Redis如何优化内存?它从下面三个方面优化:

    • 内存分配

    在内存分配方面,Redis 支持使用不同的内存分配器,包括 glibc 库提供的默认分配器 tcmalloc、第三方库提供的 jemalloc。Redis 把对内存分配器的封装实现在了 zmalloc.h/zmalloc.c。

    • 内存回收

    Redis 支持设置过期 key,并针对过期 key 可以使用不同删除策略,这部分代码实现在 expire.c 文件中。同时,为了避免大量 key 删除回收内存,会对系统性能产生影响,Redis 在 lazyfree.c 中实现了异步删除的功能,所以这样,我们就可以使用后台 IO 线程来完成删除,以避免对 Redis 主线程的影响。

    • 内存替换

    针对数据替换,如果内存满了,Redis 还会按照一定规则清除不需要的数据,这也是 Redis 可以作为缓存使用的原因。Redis 实现的数据替换策略有很多种,包括 LRU、LFU 等经典算法。这部分的代码实现在了 evict.c 中。

    • 高可靠性和高扩展性

    ,虽然 Redis 一般是作为内存数据库来使用的,但是它也提供了可靠性保证,这主要体现在 Redis 可以对数据做持久化保存,并且它还实现了主从复制机制,从而可以提供故障恢复的功能

    • 持久化

    主要分为 RDB(Redis Database)AOF(Append Only File),分别实现在了 rdb.h/rdb.caof.c 中。 注意,在使用 RDB 或 AOF 对数据库进行恢复时,RDB 和 AOF 文件可能会因为 Redis 实例所在服务器宕机,而未能完整保存,进而会影响到数据库恢复。

    因此针对这一问题,Redis 还实现了对这两类文件的检查功能,对应的代码文件分别是 redis-check-rdb.credis-check-aof.c

    • 主从复制

    Redis把主从复制功能实现在了 replication.c 文件中,Redis 的主从在进行恢复时,主要是依赖于哨兵机制,而这部分功能则直接实现在了sentinel.c 文件中。

    其次,与 Redis 实现高可靠性保证的功能类似,Redis 高可扩展性保证的功能,是通过Redis Cluster来实现的,这部分代码也非常集中,就是在 cluster.h/cluster.c 代码文件中。所以这样,我们在学习 Redis Cluster 的设计与实现时,就会非常方便,不用在不同的文件之间来回跳转了。

    • 辅助功能

    Redis 还实现了一些用于支持系统运维的辅助功能。比如,为了便于运维人员查看分析不同操作的延迟产生来源,Redis 在 latency.h/latency.c 中实现了操作延迟监控的功能;

    为了便于运维人员查找运行过慢的操作命令,Redis 在 slowlog.h/slowlog.c 中实现了慢命令的记录功能,等等。

    此外,运维人员有时还需要了解 Redis 的性能表现,为了支持这一目标,Redis 实现了对系统进行性能评测的功能,这部分代码在 redis-benchmark.c 中

    Redis源码全景图

    精选评论:

    (Kaito)

    数据类型

    • String(t_string.c、sds.c、bitops.c)
    • List(t_list.c、ziplist.c)
    • Hash(t_hash.c、ziplist.c、dict.c)
    • Set(t_set.c、intset.c)
    • Sorted Set(t_zset.c、ziplist.c、dict.c)
    • HyperLogLog(hyperloglog.c)
    • Geo(geo.c、geohash.c、geohash_helper.c)
    • Stream(t_stream.c、rax.c、listpack.c)

    全局

    • Server(server.c、anet.c)
    • Object(object.c)
    • 键值对(db.c)
    • 事件驱动(ae.c、ae_epoll.c、ae_kqueue.c、ae_evport.c、ae_select.c、networking.c)
    • 内存回收(expire.c、lazyfree.c)
    • 数据替换(evict.c)
    • 后台线程(bio.c)
    • 事务(multi.c)
    • PubSub(pubsub.c)
    • 内存分配(zmalloc.c)
    • 双向链表(adlist.c)

    高可用&集群

    • 持久化:RDB(rdb.c、redis-check-rdb.c)、AOF(aof.c、redis-check-aof.c)
    • 主从复制(replication.c)
    • 哨兵(sentinel.c)
    • 集群(cluster.c)

    辅助功能

    • 延迟统计(latency.c)
    • 慢日志(slowlog.c)
    • 通知(notify.c)
    • 基准性能(redis-benchmark.c)
  • 相关阅读:
    第22章 算法
    第二十一章 数据结构
    mysql 索引
    MySQL 视图
    MySQL 子查询
    MySQL 批量更新数据
    MySQL 默认值
    Spring 整体架构和环境搭建
    python之字符串
    python学习
  • 原文地址:https://www.cnblogs.com/fancunwei/p/16244410.html
Copyright © 2020-2023  润新知