本自述文件只是一份快速入门的文档。您可以在[redis.io]上找到更详细的文档(https://redis.io)
什么是Redis?
Redis通常被称为“数据结构”服务器。这意味着Redis通过一组命令提供对可变数据结构的访问,这些命令使用带有TCP套接字和简单协议的服务器-客户端模型发送。因此,不同的进程可以以共享的方式查询和修改相同的数据结构。
Redis中实现的数据结构有几个特殊的财产:
- Redis愿意将它们存储在磁盘上,即使它们总是被提供并修改到服务器内存中。这意味着Redis速度很快,但也是非易失性的。
- 数据结构的实现强调内存效率,因此与使用高级编程语言建模的相同数据结构相比,Redis内部的数据结构可能使用更少的内存。
- Redis提供了许多在数据库中很容易找到的功能,如复制、可调的耐用性、集群、高可用性。
另一个很好的例子是将Redis视为memcached的一个更复杂的版本,其中的操作不仅仅是SET和GET,而是用于处理复杂数据类型(如列表、集合、有序数据结构等)的操作。
如果您想了解更多信息,以下是所选起点的列表:
- Redis数据类型简介。Understand Redis data types | Docs
- 直接在浏览器中尝试Redis。http://try.redis.io
- Redis命令的完整列表。Commands | Docs
- Redis官方文档中还有更多内容。Docs
构建Redis
Redis可以在Linux、OSX、OpenBSD、NetBSD、FreeBSD上编译和使用。我们支持big-endian和little-endian架构,并且都支持32位和64位系统。
它可能在Solaris派生系统(例如SmartOS)上编译,但我们对该平台的支持是“尽最大努力”,Redis不能保证在Linux、OSX和BSD中也能很好地工作。
简单命令:
% make
您可以使用以下方法运行32位Redis二进制文件:
% make 32bit
在构建Redis之后,最好使用以下方法对其进行测试:
% make test
修复依赖项或缓存生成选项的生成问题
Redis有一些依赖项,这些依赖项包含在deps
目录中。make
不会自动重新生成依赖项,即使依赖项的源代码发生了更改。
当您使用git pull
更新源代码时,或者当依赖关系树以任何其他方式修改,请确保使用以下内容命令,以便真正清理所有内容并从头开始重建:
make distclean
这将清除:jemalloc、lua、hiredis、linenoise。
此外,如果您强制使用某些构建选项,如32位目标,则不使用C编译器优化(用于调试目的)以及其他类似的构建时选项,这些选项被无限期缓存,直到您发出make-distclean
命令
修复构建32位二进制文件的问题
如果在用32位目标构建Redis之后,您需要重新构建它对于64位目标,或者反过来,您需要执行make distclean
位于Redis发行版的根目录中。
如果在尝试构建Redis的32位二进制文件时出现构建错误,请尝试以下步骤:
- 安装软件包libc6-dev-i386(也可以尝试g++-multlib)。
- 请尝试使用以下命令行,而不是
make 32bit
:make CFLAGS="-m32 -march=native" LDFLAGS="-m32"
分配器
在构建Redis时选择非默认内存分配器是通过设置MALLOC
环境变量。Redis是根据libc编译和链接的默认情况下为malloc,但jemalloc是Linux上的默认值系统。之所以选择此默认值,是因为事实证明jemalloc的与libc-malloc相比,存在碎片问题。
要强制编译libc-malloc,请使用:
% make MALLOC=libc
要在Mac OS X系统上针对jemalloc进行编译,请使用:
% make MALLOC=jemalloc
详细生成
默认情况下,Redis将使用用户友好的彩色输出进行构建。
如果您希望看到更详细的输出,请使用以下内容:
% make V=1
运行Redis
要使用默认配置运行Redis,只需键入:
% cd src
% ./redis-server
如果要提供redis.conf,则必须使用附加的参数(配置文件的路径):
% cd src
% ./redis-server /path/to/redis.conf
可以通过直接传递参数来更改Redis配置作为使用命令行的选项。示例:
% ./redis-server --port 9999 --replicaof 127.0.0.1 6379
% ./redis-server /etc/redis/6379.conf --loglevel debug
redis.conf中的所有选项也支持作为使用命令的选项行,名称完全相同。
使用Redis
您可以使用redis-cli来播放redis。启动一个redis服务器实例,则在另一个终端中尝试以下操作:
% cd src
% ./redis-cli
redis> ping
PONG
redis> set foo bar
OK
redis> get foo
"bar"
redis> incr mycounter
(integer) 1
redis> incr mycounter
(integer) 2
redis>
您可以在以下位置找到所有可用命令的列表:Commands | Docs.
安装Redis
要将Redis二进制文件安装到/usr/local/bin中,只需使用:
% make install
您可以使用 make PREFIX=/some/other/directory install
如果您希望使用不同的安装目录。
Make-install只会在系统中安装二进制文件,但不会配置在适当的位置初始化脚本和配置文件。这不是如果你只想玩一点Redis,但如果你正在安装这是生产系统的正确方式,我们有一个脚本来做这件事对于Ubuntu和Debian系统:
% cd utils
% ./install_server.sh
脚本会问你几个问题,并会设置你需要的一切将Redis作为后台守护程序正确运行,该后台守护程序将在系统重新启动。
您将能够使用名为 /etc/init.d/redis_<portnumber>
, 例如 /etc/init.d/redis_6379
.
代码贡献
注意:通过以任何形式向Redis项目贡献代码,包括发送通过Github的拉取请求,通过私人电子邮件的代码片段或补丁,或公共讨论组,您同意根据条款发布您的代码您可以在Redis中包含的COPYING文件中找到BSD许可证的来源分布。
有关详细信息,请参阅此源发行版中的CONTRIBUTING文件信息
Redis内部
如果你正在阅读这个自述文件,你很可能在Github页面前面或者你只是解开了Redis分发的焦油球。在这两种情况下你基本上离源代码只有一步之遥,所以在这里我们解释Redis源代码布局,每个文件中的总体思想是什么Redis服务器内部最重要的功能和结构等等。我们把所有的讨论都保持在高水平,而不深入细节因为如果不是这样,这个文档将是巨大的,并且我们的代码库也会发生变化持续,但一个总体想法应该是一个很好的起点了解更多。此外,大多数代码都有大量注释,而且很容易紧随其后。
源代码布局
Redis根目录只包含这个README,即Makefile调用“src”目录中的真实Makefile和一个示例Redis和Sentinel的配置。你可以找到一些贝壳用于执行Redis、Redis Cluster和Redis Sentinel单元测试,在tests
目录
根目录中有以下重要目录:
src
: 包含用C编写的Redis实现。tests
: 包含在Tcl中实现的单元测试。deps
: 包含Redis使用的库。编译Redis所需的一切都在这个目录中;您的系统只需要提供libc
,一个与POSIX兼容的接口和一个C编译器。值得注意的是,deps
包含一个jemalloc
的副本,它是Linux下Redis的默认分配器。请注意,在“deps”下,也有一些东西是从Redis项目开始的,但其主存储库不是antirez/Redis
。这个规则的一个例外是deps/geohash-int
,这是Redis使用的低级别地理编码库:它起源于另一个项目,但在这一点上,它分歧太大,以至于直接在Redis存储库中作为一个独立的实体开发。
还有一些目录,但它们对我们的目标不是很重要在这里我们将主要关注src
,其中包含Redis实现,探索每个文件中的内容。文件的顺序公开是为了公开不同的层而遵循的逻辑复杂性逐渐增加。
注意:最近Redis被重构了很多。函数名称和文件名称已经更改,因此您可能会发现此文档反映了unstable
分支更紧密。例如,在Redis 3.0中,server.c
和server.h
文件分别命名为redis.c
和redis.h
结构是一样的。请记住,所有新的发展和拉动应针对 unstable
分支执行请求。
server.h
理解程序如何工作的最简单方法是理解它使用的数据结构。因此,我们将从Redis,即server.h
.
所有服务器配置以及通常所有共享状态都是在名为server
的全局结构中定义,类型为struct redisServer
。该结构中的几个重要领域是:
server.db
是存储数据的Redis数据库阵列。server.commands
是命令表。server.clients
是连接到服务器的客户端的链接列表。server.master
是一个特殊的客户端,即主机,如果实例是一个复制副本。
还有很多其他领域。大多数字段直接在内部进行注释结构定义。
Redis的另一个重要数据结构是定义客户端的数据结构。过去它被称为rediClient
,现在只是client
。结构有许多字段,在这里我们只显示主要字段:
struct client {
int fd;
sds querybuf;
int argc;
robj **argv;
redisDb *db;
int flags;
list *reply;
char buf[PROTO_REPLY_CHUNK_BYTES];
... many other fields ...
}
客户端结构定义了一个连接的客户端:
fd
字段是客户端套接字文件描述符。argc
和argv
用客户端正在执行的命令填充,这样实现给定Redis命令的函数就可以读取参数。querybuf
累积来自客户端的请求,这些请求由Redis服务器根据Redis协议解析,并通过调用客户端正在执行的命令的实现来执行。reply
和buf
是动态和静态缓冲区,用于累积服务器发送给客户端的回复。一旦文件描述符是可写的,这些缓冲区就会增量写入套接字。
正如您在上面的客户端结构中看到的,命令中的参数被描述为robj
结构。以下是完整的robj
结构,它定义了一个Redis对象:
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr;
} robj;
基本上,这个结构可以表示所有基本的Redis数据类型,比如字符串、列表、集合、排序集合等等。有趣的是它有一个“类型”字段,因此可以知道给定的类型对象有,和一个refcount
,以便可以引用同一个对象在多个位置,而无需多次分配。最后是ptr
字段指向对象的实际表示形式,可能会有所不同即使对于相同的类型也是如此,这取决于所使用的”编码“。
Redis对象在Redis内部被广泛使用,但是按照顺序为了避免间接访问的开销,最近在许多地方我们只是使用没有封装在Redis对象中的普通动态字符串。
server.c
这是Redis服务器的入口点,其中的main()
函数定义。以下是启动最重要的步骤Redis服务器。
initServerConfig()
设置“服务器”结构的默认值。initServer()
分配操作所需的数据结构、设置侦听套接字等等。aeMain()
启动侦听新连接的事件循环。
事件循环定期调用两个特殊函数:
serverCron()
定期调用(根据server.hz
频率),并执行必须不时执行的任务,如检查timedout客户端。beforeSleep()
每次触发事件循环时都会调用,Redis会提供一些请求,并返回到事件循环中。
在server.c中,您可以找到处理Redis服务器其他重要事务的代码:
call()
用于在给定客户端的上下文中调用给定命令。activeExpireCycle()
处理通过EXPIRE
命令设置的具有生存时间的密钥的验证。freeMemoryIfNeeded()
根据maxmemory
指令,当应该执行新的写入命令但Redis内存不足时调用。- 全局变量
redisCommandTable
定义了所有Redis命令,指定了命令的名称、实现命令的函数、所需参数的数量以及每个命令的其他财产。
networking.c
此文件定义了客户端、主机和副本的所有I/O功能(在Redis中只是特殊的客户端):
createClient()
分配并初始化一个新的客户端。- the
addReply*()
命令实现使用函数族,以便将数据附加到客户端结构中,这些数据将作为对所执行的给定命令的答复传输到客户端。 writeToClient()
将输出缓冲区中挂起的数据传输到客户端,并由可写事件处理程序sendReplyToClient()
调用。readQueryFromClient()
是可读的事件处理程序,并将从客户端读取的数据累积到查询缓冲区中。processInputBuffer()
是根据Redis协议解析客户端查询缓冲区的入口点。一旦命令准备好处理,它就会调processCommand()
,该命令在server.c
中定义,以便实际执行命令。freeClient()
解除分配、断开连接和删除客户端。
aof.c and rdb.c
从名称中可以猜到,这些文件实现了RDB和AOFRedis的持久性。Redis使用基于fork()
的持久性模型系统调用,以便创建具有相同(共享)内存的线程Redis主线程的内容。此辅助线程转储内容磁盘上内存的大小。这是rdb.c
用来创建快照的当仅追加文件太大。
aof.c
内部的实现具有附加功能,以便实现一个API,该API允许命令将新命令附加到AOF中文件,而客户端执行它们。
在server.c
中定义的call()
函数负责调用依次将命令写入AOF的函数。
db.c
某些Redis命令对特定的数据类型进行操作,其他命令则是通用的。通用命令的示例有DEL
和EXPIRE
。他们用钥匙操作而不是具体取决于它们的价值。所有这些通用命令都是在db.c
中定义。
此外,db.c
实现了一个API,以便执行某些操作在Redis数据集上,而无需直接访问内部数据结构。
在许多命令实现中使用的“db.c”中最重要的函数如下:
lookupKeyRead()
和lookupKeyWrite()
用于获取指向与给定键关联的值的指针,如果该键不存在,则为NULL。dbAdd()
和其更高级别的对应项setKey()`在Redis数据库中创建一个新密钥。dbDelete()
删除键及其关联值。emptyDb()
删除整个单个数据库或所有定义的数据库。
文件的其余部分实现了向客户端公开的通用命令。
object.c
已经描述了定义Redis对象的robj
结构。在…内object.c
有所有与Redis对象一起操作的函数一个基本级别,就像分配新对象的函数一样,处理引用计数等等。此文件中值得注意的功能:
incrRefcount()
和decrRefCount()
用于递增或递减对象引用计数。当它降到0时,对象最终被释放。createObject()
分配一个新对象。还有一些专门的函数来分配具有特定内容的字符串对象,如createStringObjectFromLongLong()
和类似的函数。
此文件还实现了OBJECT
命令。
replication.c
这是Redis中最复杂的文件之一,建议只有在稍微熟悉了代码库的其余部分之后,才能使用它。在该文件中,同时实现了主角色和副本角色Redis的。
该文件中最重要的函数之一是replicationFeedSlaves()
,它将命令写入表示连接的副本实例的客户端到我们的主机,以便副本可以获得客户端执行的写入:这样,它们的数据集将保持与主数据集中的数据集同步。
该文件还实现了SYNC
和PSYNC
命令用于在主设备和副本,或在断开连接后继续复制。
其他C文件
t_hash.c
,t_list.c
,t_set.c
,t_string.c
andt_zset.c
包含Redis数据类型的实现。它们既实现了访问给定数据类型的API,也实现了这些数据类型的客户端命令实现。ae.c
实现Redis事件循环,这是一个自包含的库,易于阅读和理解。sds.c
是Redis字符串库,请检查http://github.com/antirez/sds了解更多信息。anet.c
是一个库,与内核公开的原始接口相比,它可以以更简单的方式使用POSIX网络。dict.c
是一个非阻塞哈希表的实现,它以增量方式重新哈希。scripting.c
实现Lua脚本。它与Redis的其他实现完全独立,如果您熟悉Lua API,它也足够简单。cluster.c
实现Redis集群。在非常熟悉Redis的其余代码库之后,这可能是一本很好的读物。如果您想阅读cluster.c
,请确保阅读RRedis cluster规范。
Redis命令的剖析
所有Redis命令的定义方式如下:
void foobarCommand(client *c) {
printf("%s",c->argv[1]->ptr); /* Do something with the argument. */
addReply(c,shared.ok); /* Reply something to the client. */
}
然后在命令表的server.c
中引用该命令:
{"foobar",foobarCommand,2,"rtF",0,NULL,0,0,0,0,0},
在上面的例子中,2
是命令所采用的参数的数量,而rtF
是命令标志,如命令表中所述server.c
中的顶部注释。
在命令以某种方式操作之后,它向客户端返回回复,通常使用addReply()
或networking.c
中定义的类似函数。
Redis源代码中有大量的命令实现其可以作为实际命令实现的示例。书写一些玩具命令可以很好地练习熟悉代码库。
还有许多其他文件没有在这里描述,但对涵盖所有内容。我们只想帮助您完成第一步。最终,您将在Redis代码库中找到自己的方法:-)
玩的开心!