阿里资深专家整理的 Redis5 设计与源码分析宝典终于横空出世
前言
学习本文之前先问大家一个问题,为什么要学习 redis?不知道如何回答的朋友,请继续往下看!
总共总结为三点的内容,大家仔细斟酌一下:
1.Redis 应用广泛,它有卓越的性能、丰富的数据类型,简洁高效的设计理念。
2.Redis 5 带来了很多不错的新特性:
增加了新的流数据类型
更新了定时器、集群和字典相关的 API
增加了新的有序集合命令
3.要想做好日常开发和运维工作,需要对 Redis 的底层原理和实现,尤其是命令实现有一定了解。
Redis 已经是 IT 企业技术栈中重要的一环,与其相关的从业者数量也逐年增多,对大多数人来说 Redis 可谓既熟悉又神秘,只有不足 4MB 的源码却实现了一个功能丰富且健壮的数据库。
Redis 以其高速、轻量和丰富的数据结构与功能被越来越多的工程师所钟爱。然而,用 Redis 的人很多,真正懂 Redis 的人很少,本文正是写给那些使用了 Redis 并希望进一步深入理解 Redis 的读者。作者及其团队通过对 Redis 最新版本(5.x)各部分源码的分析,庖丁解牛,深入浅出,带领读者一步步探索 Redis 的方方面面,让读者从原理层面真正懂得 Redis。
本文不仅深入源码讲解了 Redis.常用的底层数据结构和常用命令处理的实际过程,还细致入微地讲述了基数计数算法的演进和 HyperLogLog 算法在 Redis 中的具体实现,这是非常有用且难得的;
本文从源码层面对 Redis 进行深入剖析,尤其是数据结构部分,其学习意义不限于 Redis,强烈推荐大家来阅读。
那,今天咱们就从目录、主要内容和总结三部分给大家进行介绍,希望大家能够好好学习,也希望本文能够得到大家的喜欢!!!
目录
主要内容
本文将用 22 章的内容给大家展开讲解 Redis5 设计、数据结构、底层命令实现,以及持久化、主从复制、集群的实现;
第 1 章,引言;Redis 是目前最流行的键值对( key-value)数据库,以出色的性能著称,官方提供的数据是可以支持 100 000 以上的+QPS。Redis 具有高性能的主要原因如下。
1 ) Redis 是基于内存的存储数据库,绝大部分的命令处理只是纯粹的内存操作,内存的读写速度非常快。
2) Redis 是单进程线程的服务(实际上一个正在运行的 Redis Server 肯定不止一个线程,但只有一个线程来处理网络请求),避免了不必要的上下文切换,同时不存在加锁/释放锁等同步操作。
3 ) Redis 使用多路 I/O 复用模型(select、poll、epoll),可以高效处理大量并发连接。
4 ) Redis 中的数据结构是专门设计的,增、删、改、查等操作相对简单。
本章主要介绍 Redis 简介、Redis 5.0 的新特性、Redis 源代码概念、Redis 安装与调试,希望对读者阅读和研究 Redis 源码有一定的帮助。
第 2 章,简单动态字符串;简单动态字符串(Simple Dynamic Strings,SDS)是 Redis 的基本数据结构之一,用于存储字符串和整型数据。SDS 兼容 C 语言标准字符串处理函数,且在此基础上保证了二进制安全。本章将详细讲解 SDS 的实现,为读者理解 Redis 的原理和各种命令的实现打下基础。
第 3 章,跳跃表;有序集合在生活中较常见,如根据成绩对学生进行排名、根据得分对游戏玩家进行排名等。对于有序集合的底层实现,我们可以使用数组、链表、平衡树等结构。数组不便于元素的插入和删除;链表的查询效率低,需要遍历所有元素;平衡树或者红黑树等结构虽然效率高但实现复杂。Redis 采用了一种新型的数据结构—―跳跃表。跳跃表的效率堪比红黑树,然而其实现却远比红黑树简单。
第 4 章,压缩列表;压缩列表 ziplist 本质上就是一个字节数组,是 Redis 为了节约内存而设计的一种线性数据结构,可以包含多个元素,每个元素可以是一个字节数组或一个整数。
Redis 的有序集合、散列和列表都直接或者间接使用了压缩列表。当有序集合或散列表的元素个数比较少,且元素都是短字符串时,Redis 便使用压缩列表作为其底层数据存储结构。列表使用快速链表( quicklist)数据结构存储,而快速链表就是双向链表与压缩列表的组合。
例如,使用如下命令创建一个散列键并查看其编码。
本章将从源码层次详细介绍压缩列表的存储结构及基本操作。
第 5 章,字典;本章讲解了字典的基本概念,并对其实现进行深入解读,字典在 Redis 数据库中起到了举足轻重的作用,想必读者读完这章之后,对字典的概念及 Redis 数据库底层是如何存储数据都会有一个较为清晰的了解。请思考,在 5.2.1 节中介绍字典的基本实现中为什么 Hash 表数组中存放的是每个 dictEntry 的指针地址,而不是直接把 dictEntry 嵌入到 Hash 表数组中去?
第 6 章,整数集合;整数集合(intset)是一个有序的、存储整型数据的结构。我们知道 Redis 是一个内存数据库,所以必须考虑如何能够高效地利用内存。当 Redis 集合类型的元素都是整数并且都处在 64 位有符号整数范围之内时,使用该结构体存储。
第 7 章,quicklist 的实现;quicklist 是 Redis 底层最重要的数据结构之一,它是 Redis 对外提供的 6 种基本数据结构中 List 的底层实现,在 Redis 3.2 版本中引入。在引入 quicklist 之前,Redis 采用压缩链表(ziplist)以及双向链表( adlist)作为 List 的底层实现。当元素个数比较少并且元素长度比较小时,Redis 采用 ziplist 作为其底层存储;当任意一个条件不满足时,Redis 采用 adlist 作为底层存储结构。这么做的主要原因是,当元素长度较小时,采用 ziplist 可以有效节省存储空间,但 ziplist 的存储空间是连续的,当元素个数比较多时,修改元素时,必须重新分配存储空间,这无疑会影响 Redis 的执行效率,故而采用一般的双向链表。
quicklist 是综合考虑了时间效率与空间效率引入的新型数据结构,本章将对其具体实现细节为读者——展现。
第 8 章,Stream;消息队列是分布式系统中不可缺少的组件之一,主要有异步处理、应用解耦、限流削峰的功能。目前应用较为广泛的消息队列有 RabbitMQ、RocketMQ、Kafka 等。Redis 在最新的 5.0.0 版本中也加入了消息队列的功能,这就是 Stream。本章将详细介绍 Redis Stream 相关的底层数据结构,帮助读者探索 Stream 实现的秘密。
第 9 章,命令处理生命周期;第 2~8 章介绍了 Redis 的基本数据结构,接下来主要讲解所有命令的源码实现。在讲解命令实现之前,需要先了解一下服务器处理客户端命令请求的整个流程,包括服务器启动监听,接收命令请求并解析,执行命令请求,返回命令回复等,这也是本章的主题“命令处理的生命周期”。
Redis 服务器是典型的事件驱动程序,因此事件处理显得尤为重要,而 Redis 将事件分为两大类:文件事件与时间事件。文件事件即 socket 的读写事件,时间事件用于处理一些需要周期性执行的定时任务,本章将对这两种事件作详细介绍。
第 10 章,键相关命令的实现;在前面的章节里,我们主要讲了 Redis 常用的底层数据结构以及命令处理的生命周期,本章将介绍键相关命令的源码实现。命令实现的过程中不是直接操作这些数据结构,我们将在 10.1 节讲解这两个结构。
在理解了 redisDb 和 redisObject 对象之后,我们按照查看键信息、设置键信息、查找和操作键将本章命令进行分类讲解,10.2 节讲解查看键信息相关命令,其中 object 和 type 命令是获取 redisObject 对象相关属性的操作,过期时间读取和修改相关命令是对 redisDb 的 expires 字典的操作,除此之外本章的命令都是对 redisDb 的 dict 字典的操作。
第 11 章,字符串相关命令的实现;字符串命令是 Redis 最常见的命令,相对其他命令来说,字符串命令操作简单,参数较少。字符串命令虽然简单但作用强大,我们可以用字符串命令实现 key-value 的设置与获取,也可以实现计数器功能,甚至可以实现位操作。
第 12 章,散列表相关命令的实现;散列表是 Redis 数据组织的基本结构。针对 key-value 中的 value,Redis 提供了 6 种结构——字符串(string)、散列表(Hash)、数据流(stream)、列表(list)、集合(set)、有序集合(sortedset)。针对不同的 value 结构,Redis 提供了不同的命令供用户使用。
当 value 为散列结构时,我们称之为散列相关命令。为了与 Redis 中的 key-value 散列做区分,我们称 value 的散列结构的键值对为 field-value(域值对)。
第 13 章,列表相关命令的实现;Redis 列表对象的底层数据结构是 quicklist,我们在第 7 章已经详细讲述了 quicklist 的数据结构以及常见操作,本章我们主要讲解如何使用 quicklist 实现列表相关的命令。
第 14 章,集合相关命令的实现;Redis 的 set 实现了无序集合,集合成员唯一。set 底层基于 dict 和 intset,在学习集合命令前,需要先了解 dict 和 intset 的结构,详见相关章节的介绍。
第 15 章,有序集合相关命令的实现;在第 14 章中,主要讲解了集合(Set)相关的命令,集合是无序的,而本章主要介绍有序集合(SortedSet)相关命令的实现,包括基本操作,比如 zadd/zrem/zscan 等,批量的操作(zrange/zremrange),以及集合相关的操作(交集 zinterstore 和并集 zunionstore)。
有序集合中,用到的关键数据结构是 ziplist 以及 dict 和 skiplist,当服务器属性 server.zset_max_ziplist_entries 的值大于 0 且元素的 member 长度小于服务器属性 server.zset_max_ziplist_value 的值(默认为 64)时,使用的是 ziplist,否则使用的是 dict 和 skiplist。对于这三种数据结构,请参考第 3、4 和 5 章。
第 16 章,GEO 相关命令;在生活中,位置信息十分重要,精确地对每个物体进行定位是我们进行其他相关操作的基础。Redis 提供了一些命令来帮助我们有效地处理位置信息,比如计算两点间的距离,这类命令统称为 GEO 相关的命令,本节将详细介绍这类命令是如何实现的。
geohash 算法在 2008 年公开,该算法可以把二维的经纬度信息降维到一维,并通过 Base32 编码将其转换为字符串。Ardb 的作者提供了 geohash-int 的实现。Redis 的核心开发者 Matt 借鉴 Ardb 的 GEO 功能,于 2014 年以 module 方式开发了 Redis 的 GEO。2016 年,在 Redis 3.2 中正式加入了 GEO。本节主要学习 geohash 算法的实现,以及 Redis 中是如何使用该算法的。
第 17 章,HyperLogLog 相关命令的实现;在工作当中,我们经常会遇到与统计相关的功能需求,比如统计网站 PV (Page View,页面访问量)或者 URL 的访问次数,可以使用 Redis 提供的 incr 或 incrby 命令轻松实现,但像 UV (Unique Visitor,独立访客)、独立 IP 数、搜索记录数等需要去重和计数的问题该如何解决呢?我们把这类求集合中不重复元素个数的问题称为基数计数。
解决基数计数有多种方法,简单来说,可以将数据存储在 MySQL 表中,使用 distinctcount 语句来计算不重复个数,也可以使用 Redis 提供的 hash、set、bitmap 等数据结构来处理,而且结果数据都非常精确,但是这些方法都会随着数据的不断增多,而导致占用的空间越来越大,对于非常大的数据集是不切实际的。那么是否能够降低一定的精度来平衡存储空间呢。有一类算法基于概率,仅使用常量和小量的内存来提供集合中唯一元素数量的近似值。
第 18 章,数据流相关命令的实现;Redis 5.0.0 引入了 Stream,本章主要介绍 Stream 相关命令的底层实现。
在 Stream 的实现中,用到的关键数据结构是 rax、listpack。其中 rax 用于快速索引;listpack 用于存储具体的消息,这些数据结构的详细介绍请参见第 8 章。
第 19 章,其他命令;该章节主要讲解事务命令、发布-订阅命令和 Lua 脚本命令 3 个部分。通过该章的学习,读者可以了解 Redis 中事务、发布–订阅的实现原理及其适用范围,以及 Redis 如何执行 Lua 脚本命令。
第 20 章,持久化;Redis 是一个内存数据库,当机器重启之后内存中的数据都会丢失。所以对 Redis 来说,持久化显得尤为重要。Redis 有两种持久化方式:一种为 RDB 方式,RDB 保存某一个时间点之前的数据;另一种为 AOF 方式,AOF 保存的是 Redis 服务器端执行的每一条命令。两种方式各有优劣,在接下来的章节中会详细介绍。我们先通过在客户端输入 info 命令,查看 Redis 服务端记录的相关持久化状态信息,然后分别详细介绍 RDB 和 AOF。
第 21 章,主从复制;Redis 支持主从复制功能,用户可以通过执行 slaveof 命令或者在配置文件中设置 slaveof 选项来开启复制功能。例如,现在有两台服务器——127.0.0.1:6379 和 127.0.0.1:7000,向服务器 127.0.0.1:6379 发送下面命令:
此时服务器 127.0.0.1:6379 会成为服务器 127.0.0.1:7000 的从服务器(slaver),服务器 127.0.0.1:7000 会成为服务器 127.0.0.1:6379 的主服务器( master);通过复制功能,从服务器 127.0.0.1:6379 的数据可以和主服务器 127.0.0.1:7000 的数据保持同步。
本章将为读者详细介绍主从复制功能的源码实现。
第 22 章,哨兵和集群;哨兵是 Redis 的高可用方案,可以在 Redis Master 发生故障时自动选择一个 Redis Slave 切换为 Master,继续对外提供服务。集群提供数据自动分片到不同节点的功能,并且当部分节点失效后仍然可以使用。
本章首先介绍 Redis 哨兵的实现,然后介绍集群的实现。
这份【Redis5 设计与源码分析】宝典总共有 433 页,需要完整版的朋友,可以转发此文关注小编,**点击这里,获得文档领取方式**
总结
本书讲了什么?
Redis 架构与源码设计
Redis 数据结构与命令实现
业务所需关键实现解读
你能得到什么?
吸收设计精髓,提升技术素养
理解设计,获得更好的性能
掌握源码,更好、更快排障
更远一点,设计自己的分布式缓存数据库
本文从底层源码的角度,对 Redis 的数据结构以及持久化、主从复制、哨兵和集群等特性的实现原理进行了详尽的剖析,图文并茂。行文中也能看出作者团队在源码分析和系统编程方面的功力,我相信本文对于所有想要了解 Redis 及其内部实现的人来说都会有所帮助。
还有对技术有点追求的程序员一定不要错过本 Redis5 源码分析宝典,本文对 Redis 的内部实现分析得非常全面透彻,如果你觉得直接阅读源码有点吃力,试试让本文来带领你探索 Redis 源码。
希望本文能够帮助到大家的学习,让大家得以提升自己的技术深度和宽度,让自己变得更加有价值,也希望本文能够得到大家的喜欢!!!
评论