Redis设计与实现 (数据库技术丛书)-黄健宏 著
在线阅读 百度网盘下载(2jca)
书名:Redis设计与实现 (数据库技术丛书)
作者:黄健宏 著
格式:EPUB, HTMLZ, PDF
路径:点击打开
出版:机械工业出版社
排序作者:著, 黄健宏
排序书名:Redis设计与实现 (数据库技术丛书)
日期:08 12月 2018
uuid:5b81f7f8-6848-4b33-89b0-7e9491f7f1fb
id:498
出版日期:6月 2014
修改日期:08 12月 2018
大小:23.51MB
语言:中文
前言
时间回到2011年4月,当时我正在编写一个用户关系模块,这个模块需要实现一个“共同关注”功能,用于计算出两个用户关注了哪些相同的用户。
举个例子,假设huangz关注了peter、tom、jack三个用户,而john关注了peter、tom、bob、david四个用户,那么当huangz访问john的页面时,共同关注功能就会计算并打印出类似“你跟john都关注了peter和tom”这样的信息。
从集合计算的角度来看,共同关注功能本质上就是计算两个用户关注集合的交集,因为交集这个概念是如此的常见,所以我很自然地认为共同关注这个功能可以很容易地实现,但现实却给了我当头一棒:我所使用的关系数据库并不直接支持交集计算操作,要计算两个集合的交集,除了需要对两个数据表执行合并(join)操作之外,还需要对合并的结果执行去重复(distinct)操作,最终导致交集操作的实现变得异常复杂。
是否存在直接支持集合操作的数据库呢?带着这个疑问,我在搜索引擎上面进行查找,并最终发现了Redis。在我看来,Redis正是我想要找的那种数据库——它内置了集合数据类型,并支持对集合执行交集、并集、差集等集合计算操作,其中的交集计算操作可以直接用于实现我想要的共同关注功能。
得益于Redis本身的简单性,以及Redis手册的详尽和完善,我很快学会了怎样使用Redis的集合数据类型,并用它重新实现了整个用户关系模块:重写之后的关系模块不仅代码量更少,速度更快,更重要的是,之前需要使用一段甚至一大段SQL查询才能实现的功能,现在只需要调用一两个Redis命令就能够实现了,整个模块的可读性得到了极大的提高。
自此之后,我开始在越来越多的项目里面使用Redis,与此同时,我对Redis的内部实现也越来越感兴趣,一些问题开始频繁地出现在我的脑海中,比如:
·Redis的五种数据类型分别是由什么数据结构实现的?
·Redis的字符串数据类型既可以存储字符串(比如“hello world”),又可以存储整数和浮点数(比如10086和3.14),甚至是二进制位(使用SETBIT等命令),Redis在内部是怎样存储这些值的?
·Redis的一部分命令只能对特定数据类型执行(比如APPEND只能对字符串执行,HSET只能对哈希表执行),而另一部分命令却可以对所有数据类型执行(比如DEL、TYPE和EXPIRE),不同的命令在执行时是如何进行类型检查的?Redis在内部是否实现了一个类型系统?
·Redis的数据库是怎样存储各种不同数据类型的键值对的?数据库里面的过期键又是怎样实现自动删除的?
·除了数据库之外,Redis还拥有发布与订阅、脚本、事务等特性,这些特性又是如何实现的?
·Redis使用什么模型或者模式来处理客户端的命令请求?一条命令请求从发送到返回需要经过什么步骤?
为了找到这些问题的答案,我再次在搜索引擎上面进行查找,可惜的是这次搜索并没有多少收获:Redis还是一个非常年轻的软件,对它的最好介绍就是官方网站上面的文档,但是这些文档主要关注的是怎样使用Redis,而不是介绍Redis的内部实现。另外,网上虽然有一些博客文章对Redis的内部实现进行了介绍,但这些文章要么不齐全(只介绍了Redis中的少数几个特性),要么就写得过于简单(只是一些概述性的文章),要么关注的就是旧版本(比如2.0、2.2或者2.4,而当时的最新版已经是2.6了)。
综合来看,详细而且完整地介绍Redis内部实现的资料,无论是外文还是中文都不存在。意识到这一点之后,我决定自己动手注释Redis的源代码,从中寻找问题的答案,并通过写博客的方式与其他Redis用户分享我的发现。在积累了七八篇Redis源代码注释文章之后,我想如果能将这些博文汇集成书的话,那一定会非常有趣,并且我自己也会从中学到很多知识。于是我在2012年年末开始创作《Redis设计与实现》,并最终于2013年3月8日在互联网发布了本书的第一版。
尽管《Redis设计与实现》第一版顺利发布了,但在我的心目中,这个第一版还是有很多不完善的地方:
·比如说,因为第一版是我边注释Redis源代码边写的,如果有足够时间让我先完整地注释一遍Redis的源代码,然后再进行写作的话,那么书本在内容方面应该会更为全面。
·又比如说,第一版只介绍了Redis的内部机制和单机特性,但并没有介绍Redis多机特性,而我认为只有将关于多机特性的介绍也包含进来,这本《Redis设计与实现》才算是真正的完成了。
就在我考虑应该何时编写新版来修复这些缺陷的时候,机械工业出版社的吴怡编辑来信询问我是否有兴趣正式地出版《Redis设计与实现》,能够正式地出版自己写的书一直是我梦寐以求的事情,我找不到任何拒绝这一邀请的理由,就这样,在《Redis设计与实现》第一版发布几天之后,新版《Redis设计与实现》的写作也马不停蹄地开始了。
从2013年3月到2014年1月这11个月间,我重新注释了Redis在unstable分支的源代码(也即是现在的Redis 3.0源代码),重写了《Redis设计与实现》第一版已有的所有章节,并向书中添加了关于二进制位操作(bitop)、排序、复制、Sentinel和集群等主题的新章节,最终完成了这本新版的《Redis设计与实现》。本书不仅介绍了Redis的内部机制(比如数据库实现、类型系统、事件模型),而且还介绍了大部分Redis单机特性(比如事务、持久化、Lua脚本、排序、二进制位操作),以及所有Redis多机特性(如复制、Sentinel和集群)。
虽然作者创作本书的初衷只是为了满足自己的好奇心,但了解Redis内部实现的好处并不仅仅在于满足好奇心:通过了解Redis的内部实现,理解每一个特性和命令背后的运作机制,可以帮助我们更高效地使用Redis,避开那些可能会引起性能问题的陷阱。我衷心希望这本新版《Redis设计与实现》能够帮助读者更好地了解Redis,并成为更优秀的Redis使用者。
本书的第一版获得了很多热心读者的反馈,这本新版的很多改进也来源于读者们的意见和建议,因此我将继续在www.RedisBook.com设置disqus论坛(可以不注册直接发贴),欢迎读者随时就这本新版《Redis设计与实现》发表提问、意见、建议、批评、勘误,等等,我会努力地采纳大家的意见,争取在将来写出更好的《Redis设计与实现》,以此来回报大家对本书的支持。
黄健宏(huangz)
致谢
我要感谢hoterran和iammutex这两位良师益友,他们对我的帮助和支持贯穿整本书从概念萌芽到正式出版的整个阶段,也感谢他们抽出宝贵的时间为本书审稿。
我要感谢吴怡编辑鼓励我创作并出版这本新版《Redis设计与实现》,以及她在写作过程中对我的悉心指导。
我要感谢TimYang在百忙之中抽空为本书审稿,并耐心地给出了详细的意见。
我要感谢Redis之父Salvatore Sanfilippo,如果不是他创造了Redis的话,这本书也不会出现了。
我要感谢所有阅读了《Redis设计与实现》第一版的读者,他们的意见和建议帮助我更好地完成这本新版《Redis设计与实现》。
最后,我要感谢我的家人和朋友,他们的关怀和鼓励使得本书得以顺利完成。
第1章 引言
本书对Redis的大多数单机功能以及所有多机功能的实现原理进行了介绍,力图展示这些功能的核心数据结构以及关键的算法思想。
通过阅读本书,读者可以快速、有效地了解Redis的内部构造以及运作机制,这些知识可以帮助读者更好地、也更高效地使用Redis。
为了让本书的内容保持简单并且容易读懂,本书会尽量以高层次的角度来对Redis的实现原理进行描述,如果读者只是对Redis的实现原理感兴趣,但并不想研究Redis的源代码,那么阅读本书就足够了。
另一方面,如果读者打算深入了解Redis实现原理的底层细节,本书在RedisBook.com提供了一份带有详细注释的Redis源代码,读者可以先阅读本书对某一功能的介绍,然后再阅读该功能对应的实现代码,这有助于读者更快地读懂实现代码,也有助于读者更深入地了解该功能的实现原理。
第2章 简单动态字符串
Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SDS用作Redis的默认字符串表示。
第3章 链表
链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。
作为一种常用数据结构,链表内置在很多高级的编程语言里面,因为Redis使用的C语言并没有内置这种数据结构,所以Redis构建了自己的链表实现。
链表在Redis中的应用非常广泛,比如列表键的底层实现之一就是链表。当一个列表键包含了数量比较多的元素,又或者列表中包含的元素都是比较长的字符串时,Redis就会使用链表作为列表键的底层实现。
第4章 字典
字典,又称为符号表(symbol table)、关联数组(associative array)或映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构。
在字典中,一个键(key)可以和一个值(value)进行关联(或者说将键映射为值),这些关联的键和值就称为键值对。
字典中的每个键都是独一无二的,程序可以在字典中根据键查找与之关联的值,或者通过键来更新值,又或者根据键来删除整个键值对,等等。
字典经常作为一种数据结构内置在很多高级编程语言里面,但Redis所使用的C语言并没有内置这种数据结构,因此Redis构建了自己的字典实现。
字典在Redis中的应用相当广泛,比如Redis的数据库就是使用字典来作为底层实现的,对数据库的增、删、查、改操作也是构建在对字典的操作之上的。
第5章 跳跃表
跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。
跳跃表支持平均O(logN)、最坏O(N)复杂度的节点查找,还可以通过顺序性操作来批量处理节点。
在大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表的实现比平衡树要来得更为简单,所以有不少程序都使用跳跃表来代替平衡树。
Redis使用跳跃表作为有序集合键的底层实现之一,如果一个有序集合包含的元素数量比较多,又或者有序集合中元素的成员(member)是比较长的字符串时,Redis就会使用跳跃表来作为有序集合键的底层实现。
第6章 整数集合
整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。
第7章 压缩列表
压缩列表(ziplist)是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。
第8章 对象
在前面的数个章节里,我们陆续介绍了Redis用到的所有主要数据结构,比如简单动态字符串(SDS)、双端链表、字典、压缩列表、整数集合等等。
Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象,每种对象都用到了至少一种我们前面所介绍的数据结构。
通过这五种不同类型的对象,Redis可以在执行命令之前,根据对象的类型来判断一个对象是否可以执行给定的命令。使用对象的另一个好处是,我们可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。
除此之外,Redis的对象系统还实现了基于引用计数技术的内存回收机制,当程序不再使用某个对象的时候,这个对象所占用的内存就会被自动释放;另外,Redis还通过引用计数技术实现了对象共享机制,这一机制可以在适当的条件下,通过让多个数据库键共享同一个对象来节约内存。
最后,Redis的对象带有访问时间记录信息,该信息可以用于计算数据库键的空转时长,在服务器启用了maxmemory功能的情况下,空转时长较大的那些键可能会优先被服务器删除。
第9章 数据库
本章将对Redis服务器的数据库实现进行详细介绍,说明服务器保存数据库的方法,客户端切换数据库的方法,数据库保存键值对的方法,以及针对数据库的添加、删除、查看、更新操作的实现方法等。除此之外,本章还会说明服务器保存键的过期时间的方法,以及服务器自动删除过期键的方法。最后,本章还会说明Redis 2.8新引入的数据库通知功能的实现方法。
第10章 RDB持久化
Redis是一个键值对数据库服务器,服务器中通常包含着任意个非空数据库,而每个非空数据库中又可以包含任意个键值对,为了方便起见,我们将服务器中的非空数据库以及它们的键值对统称为数据库状态。
第11章 AOF持久化
除了RDB持久化功能之外,Redis还提供了AOF(Append Only File)持久化功能。与RDB持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的
第12章 事件
Redis服务器是一个事件驱动程序,服务器需要处理以下两类事件:
·文件事件(file event):Redis服务器通过套接字与客户端(或者其他Redis服务器)进行连接,而文件事件就是服务器对套接字操作的抽象。服务器与客户端(或者其他服务器)的通信会产生相应的文件事件,而服务器则通过监听并处理这些事件来完成一系列网络通信操作。
·时间事件(time event):Redis服务器中的一些操作(比如serverCron函数)需要在给定的时间点执行,而时间事件就是服务器对这类定时操作的抽象。
本章将对文件事件和时间事件进行介绍,说明这两种事件在Redis服务器中的应用,它们的实现方法,以及处理这些事件的API等等。
本章最后将对服务器的事件调度方式进行介绍,说明Redis服务器是如何安排并执行文件事件和时间事件的。
第13章 客户端
Redis服务器是典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复。
通过使用由I/O多路复用技术实现的文件事件处理器,Redis服务器使用单线程单进程的方式来处理命令请求,并与多个客户端进行网络通信。
对于每个与服务器进行连接的客户端,服务器都为这些客户端建立了相应的redis.h/redisClient结构(客户端状态),这个结构保存了客户端当前的状态信息,以及执行相关功能时需要用到的数据结构,其中包括:
·客户端的套接字描述符。
·客户端的名字。
·客户端的标志值(flag)。
·指向客户端正在使用的数据库的指针,以及该数据库的号码。
·客户端当前要执行的命令、命令的参数、命令参数的个数,以及指向命令实现函数的指针。
·客户端的输入缓冲区和输出缓冲区。
·客户端的复制状态信息,以及进行复制所需的数据结构。
·客户端执行BRPOP、BLPOP等列表阻塞命令时使用的数据结构。
·客户端的事务状态,以及执行WATCH命令时用到的数据结构。
·客户端执行发布与订阅功能时用到的数据结构。
·客户端的身份验证标志。
第14章 服务器
Redis服务器负责与多个客户端建立网络连接,处理客户端发送的命令请求,在数据库中保存客户端执行命令所产生的数据,并通过资源管理来维持服务器自身的运转。
本章的第一节将以服务器执行SET命令的过程作为例子,展示服务器处理命令请求的整个过程,说明在执行命令的过程中,服务器和客户端进行了什么交互,服务器中的各个不同组件又是如何协作的,等等。
本章的第二节将对serverCron函数进行介绍,详细列举这个函数执行的操作,并说明这些操作对于服务器维持正常运行有何帮助。
本章的最后一节将对服务器的启动过程进行介绍,通过了解Redis服务器的启动过程可以知道,在启动服务器程序、直到服务器可以接受客户端命令请求的这段时间里,服务器都做了些什么准备工作。
来源:我是码农,转载请保留出处和链接!
本文链接:http://www.54manong.com/?id=1135