这个README只是一份快速入门文档。您可以在redis.io找到更详细的文档。
什么是Redis?
Redis通常被称为数据结构服务器。这意味着Redis通过一组命令提供对可变数据结构的访问,这些命令使用基于TCP套接字和简单协议的服务器-客户端模型发送。因此,不同的进程可以以共享方式查询和修改相同的数据结构。
Redis中实现的数据结构有一些特殊属性:
- Redis会将它们存储在磁盘上,即使它们始终在服务器内存中提供和修改。这意味着Redis既快速又非易失。
- 数据结构的实现强调内存效率,因此Redis内部的数据结构与使用高级编程语言建模的相同数据结构相比可能使用更少的内存。
- Redis提供了许多在数据库中自然存在的功能,如复制、可调节的持久性级别、集群和高可用性。
另一个很好的例子是将Redis视为memcached的更复杂版本,其中操作不仅仅是SET和GET,还包括使用列表、集合、有序数据结构等复杂数据类型的操作。
如果您想了解更多,以下是一些精选的起点:
- Redis数据类型简介。https://redis.io/topics/data-types-intro
- 直接在浏览器中试用Redis。https://try.redis.io
- Redis命令的完整列表。https://redis.io/commands
- 官方Redis文档中还有更多内容。https://redis.io/documentation
什么是Redis社区版?
Redis OSS在v7.4版本中更名为Redis社区版(CE)。
Redis Ltd.还提供Redis软件,这是一个自管理软件,具有额外的合规性、可靠性和弹性,适用于企业级扩展, 以及Redis Cloud,一个与Google Cloud、Azure和AWS集成的全托管服务,适用于生产就绪的应用程序。
在此处阅读有关Redis社区版和Redis之间差异的更多信息。
构建Redis
Redis可以在Linux、OSX、OpenBSD、NetBSD、FreeBSD上编译和使用。 我们支持大端和小端架构,以及32位和64位系统。
它可能可以在Solaris派生系统(例如SmartOS)上编译,但我们对这个平台的 支持是尽力而为的,Redis不能保证像在Linux、OSX和*BSD上那样运行良好。
它非常简单:
% make
要构建TLS支持,您需要OpenSSL开发库(例如 Debian/Ubuntu上的libssl-dev)并运行:
% make BUILD_TLS=yes
要构建systemd支持,您需要systemd开发库(如 Debian/Ubuntu上的libsystemd-dev或CentOS上的systemd-devel)并运行:
% make USE_SYSTEMD=yes
要为Redis程序名称添加后缀,请使用:
% make PROG_SUFFIX="-alt"
您可以使用以下命令构建32位Redis二进制文件:
% make 32bit
构建Redis后,最好使用以下命令进行测试:
% make test
如果构建了TLS,请使用TLS运行测试(您需要安装tcl-tls
):
% ./utils/gen-test-certs.sh
% ./runtest --tls
修复依赖项或缓存的构建选项的构建问题
Redis有一些包含在deps
目录中的依赖项。
即使依赖项的源代码发生变化,make
也不会自动重新构建依赖项。
当您使用git pull
更新源代码或以任何其他方式修改依赖项树中的代码时,
请确保使用以下命令真正清理所有内容并从头开始重新构建:
% make distclean
这将清理:jemalloc、lua、hiredis、linenoise和其他依赖项。
此外,如果您强制某些构建选项,如32位目标、无C编译器
优化(用于调试目的)以及其他类似的构建时选项,
这些选项将无限期缓存,直到您发出make distclean
命令。
修复构建32位二进制文件的问题
如果在构建Redis的32位目标后需要重新构建64位目标,
或者相反,您需要在Redis发行版的根目录中执行
make distclean
。
如果在尝试构建Redis的32位二进制文件时出现构建错误,请尝试 以下步骤:
- 安装libc6-dev-i386包(也可以尝试g++-multilib)。
- 尝试使用以下命令行代替
make 32bit
:make CFLAGS="-m32 -march=native" LDFLAGS="-m32"
分配器
在构建Redis时选择非默认内存分配器是通过设置
MALLOC
环境变量来完成的。默认情况下,Redis编译并链接到libc
malloc,但在Linux系统上默认使用jemalloc。之所以选择这个默认值,
是因为jemalloc已被证明比libc malloc具有更少的碎片问题。
要强制编译使用libc malloc,请使用:
% make MALLOC=libc
要在Mac OS X系统上编译使用jemalloc,请使用:
% make MALLOC=jemalloc
单调时钟
默认情况下,Redis将使用POSIX clock_gettime函数作为 单调时钟源来构建。在大多数现代系统上,可以使用内部处理器时钟 来提高性能。注意事项可以在这里找到: http://oliveryang.net/2015/09/pitfalls-of-TSC-usage/
要构建支持处理器的内部指令时钟,请使用:
% make CFLAGS="-DUSE_PROCESSOR_CLOCK"
详细构建
默认情况下,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中的所有选项也都支持作为命令行选项使用, 名称完全相同。
使用TLS运行Redis
请查阅TLS.md文件以获取有关如何使用TLS运行Redis的更多信息。
使用Redis
您可以使用redis-cli来使用Redis。启动一个redis-server实例, 然后在另一个终端中尝试以下操作:
% 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>
您可以在https://redis.io/commands找到所有可用命令的列表。
安装Redis
要将Redis二进制文件安装到/usr/local/bin,只需使用:
% make install
如果您希望使用不同的目标目录,可以使用make PREFIX=/some/other/directory install
。
make install
只会将二进制文件安装到您的系统中,但不会在适当的位置配置
初始化脚本和配置文件。如果您只想稍微玩一下Redis,这不是必需的,但如果您正在
为生产系统进行适当的安装,我们有一个脚本可以为Ubuntu和Debian系统执行此操作:
% cd utils
% ./install_server.sh
注意:install_server.sh
在Mac OSX上不起作用;它仅为Linux构建。
该脚本将询问您一些问题,并设置您需要的一切 以将Redis正确作为后台守护进程运行,该守护进程将在系统重新启动时再次启动。
您将能够使用名为/etc/init.d/redis_<portnumber>
的脚本停止和启动Redis,
例如/etc/init.d/redis_6379
。
代码贡献
通过以任何形式向Redis项目贡献代码,包括通过GitHub发送拉取请求、 通过私人电子邮件或公共讨论组发送代码片段或补丁,您同意根据 Redis软件授权和贡献者许可协议的条款发布您的代码。Redis软件 包含对原始Redis核心项目的贡献,这些贡献由其贡献者拥有并根据3BSD许可证 获得许可。此存储库中该许可证的任何副本仅适用于这些贡献。Redis将所有Redis社区版7.4.x及以后版本 根据LICENSE.txt文件中描述的RSALv2/SSPL双重许可发布,该文件包含在Redis社区版源代码分发中。
请参阅本源代码分发中的CONTRIBUTING.md文件以获取更多信息。对于 安全漏洞和漏洞,请参阅SECURITY.md。
Redis商标
商标的目的是识别个人或公司的商品和服务而不引起混淆。作为其名称和标志的 注册所有者,Redis接受其商标的某些有限使用,但必须遵守其商标 指南中描述的要求,可在以下网址获取:https://redis.com/legal/trademark-guidelines/。
Redis内部
如果您正在阅读此README,您可能正在GitHub页面上或者刚刚解压了Redis分发tar包。 在这两种情况下,您基本上离源代码只有一步之遥,因此在这里我们解释 Redis源代码布局,每个文件中大致包含什么内容,Redis服务器内部 最重要的函数和结构等。我们将所有讨论保持在高层次,不深入细节, 因为否则这份文档会非常庞大,而且我们的代码库不断变化,但一个总体概念 应该是理解更多内容的良好起点。此外,大部分代码都有大量注释,易于 理解。
源代码布局
Redis根目录只包含此README、调用src
目录中真正的Makefile的Makefile、
以及Redis和Redis Sentinel的示例配置。您可以找到一些shell
脚本,用于执行Redis、Redis Cluster和Redis Sentinel单元测试,
这些测试在tests
目录中实现。
根目录中有以下重要目录:
src
:包含用C语言编写的Redis实现。tests
:包含用Tcl实现的单元测试。deps
:包含Redis使用的库。编译Redis所需的一切都在这个目录中;您的系统只需提供libc
、POSIX兼容接口和C编译器。值得注意的是,deps
包含jemalloc
的副本,这是Linux下Redis的默认分配器。请注意,在deps
下还有一些东西是从Redis项目开始的,但主要存储库不是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数据结构是定义客户端的结构。
过去它被称为redisClient
,现在只是client
。该结构
有许多字段,这里我们只展示主要字段:
struct client {
int fd;
sds querybuf;
int argc
commands.c
---
此文件由utils/generate-command-code.py自动生成,内容基于src/commands文件夹中的JSON文件。这些JSON文件旨在作为Redis命令及其所有元数据的单一真实来源。这些JSON文件并不意味着供任何人直接使用,相关元数据可以通过`COMMAND`命令获取。
networking.c
---
此文件定义了与客户端、主服务器和从服务器(在Redis中只是特殊的客户端)进行I/O操作的所有函数:
* `createClient()`分配并初始化一个新客户端。
* `addReply*()`系列函数由命令实现使用,用于向客户端结构附加数据,这些数据将作为对特定执行命令的回复传输给客户端。
* `writeToClient()`将输出缓冲区中待处理的数据传输给客户端,由可写事件处理程序`sendReplyToClient()`调用。
* `readQueryFromClient()`是可读事件处理程序,将从客户端读取的数据累积到查询缓冲区中。
* `processInputBuffer()`是根据Redis协议解析客户端查询缓冲区的入口点。一旦命令准备好处理,它会调用`server.c`中定义的`processCommand()`来实际执行命令。
* `freeClient()`释放、断开连接并移除客户端。
aof.c和rdb.c
---
顾名思义,这些文件实现了Redis的RDB和AOF持久化。Redis使用基于`fork()`系统调用的持久化模型,创建一个与主Redis进程具有相同(共享)内存内容的进程。这个辅助进程将内存内容转储到磁盘上。`rdb.c`使用这种方式在磁盘上创建快照,而`aof.c`在追加只写文件变得过大时用于执行AOF重写。
`aof.c`中的实现还有额外的函数,用于实现一个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()`移除键及其关联值。
* `emptyData()`移除整个单一数据库或所有定义的数据库。
文件的其余部分实现了暴露给客户端的通用命令。
object.c
---
定义Redis对象的`robj`结构已经描述过了。`object.c`中包含所有在基本级别操作Redis对象的函数,如分配新对象、处理引用计数等函数。此文件中的重要函数包括:
* `incrRefCount()`和`decrRefCount()`用于增加或减少对象的引用计数。当计数降至0时,对象最终被释放。
* `createObject()`分配一个新对象。还有一些专门用于分配具有特定内容的字符串对象的函数,如`createStringObjectFromLongLong()`等类似函数。
该文件还实现了`OBJECT`命令。
replication.c
---
这是Redis中最复杂的文件之一,建议在熟悉其他代码库后再approach它。此文件实现了Redis的主从角色。
该文件中最重要的函数之一是`replicationFeedSlaves()`,它将命令写入代表连接到我们主服务器的从实例的客户端,这样从服务器就可以获取客户端执行的写操作:通过这种方式,它们的数据集将与主服务器保持同步。
该文件还实现了`SYNC`和`PSYNC`命令,用于执行主从服务器之间的首次同步,或在断开连接后继续复制。
脚本
---
脚本单元由3个单元组成:
* `script.c` - 脚本与Redis的集成(命令执行、设置复制/resp等)
* `script_lua.c` - 负责执行Lua代码,使用`script.c`从Lua代码中与Redis交互。
* `function_lua.c` - 包含Lua引擎实现,使用`script_lua.c`执行Lua代码。
* `functions.c` - 包含Redis函数实现(`FUNCTION`命令),如果要调用的函数需要Lua引擎,则使用`functions_lua.c`。
* `eval.c` - 包含`eval`实现,使用`script_lua.c`调用Lua代码。
其他C文件
---
* `t_hash.c`、`t_list.c`、`t_set.c`、`t_string.c`、`t_zset.c`和`t_stream.c`包含Redis数据类型的实现。它们既实现了访问给定数据类型的API,也实现了这些数据类型的客户端命令。
* `ae.c`实现了Redis事件循环,它是一个简单易读易懂的自包含库。
* `sds.c`是Redis字符串库,更多信息请查看https://github.com/antirez/sds。
* `anet.c`是一个库,用于以比内核暴露的原始接口更简单的方式使用POSIX网络。
* `dict.c`是一个非阻塞哈希表的实现,它可以增量式重哈希。
* `cluster.c`实现了Redis集群。可能只有在非常熟悉Redis代码库的其他部分后才适合阅读。如果你想阅读`cluster.c`,请确保阅读[Redis集群规范][4]。
[4]: https://redis.io/topics/cluster-spec
Redis命令剖析
---
所有Redis命令都以以下方式定义:
```c
void foobarCommand(client *c) {
printf("%s",c->argv[1]->ptr); /* 对参数做些处理。 */
addReply(c,shared.ok); /* 向客户端回复一些内容。 */
}
命令函数由JSON文件引用,连同其元数据一起,详情见上述commands.c
的描述。命令标志在server.h
中struct redisCommand
上方的注释中有文档说明。其他细节,请参考COMMAND
命令。https://redis.io/commands/command/
命令以某种方式操作后,会使用addReply()
或networking.c
中定义的类似函数向客户端返回回复。
Redis源代码中有大量命令实现可以作为实际命令实现的例子(如pingCommand)。编写一些玩具命令可能是熟悉代码库的好练习。
还有许多其他未在此描述的文件,但没必要涵盖所有内容。我们只是想帮助你迈出第一步。最终你会在Redis代码库中找到自己的方向:-)
祝你愉快!