Redis【有与无】【AC1】Redis编译使用说明

本文章基于Redis 6.0.9版本,对Redis配置进行说明

AC: Actual Combat 实战

目录

1.什么是Redis?

2.编译Redis

2.1.修复依赖项或缓存的构建选项的构建问题

2.2.解决构建32位二进制文件的问题

2.3.分配器(allocator)

2.4.详细构建

7.1.运行Redis

7.2.使用TLS运行Redis

7.2.1.入门

7.2.1.1.编译

7.2.1.2.测试

7.2.1.3.手动运行

7.2.2.入门连接数

7.2.3.待办事项清单(未来)

7.2.4.多端口

8.使用Redis

9.安装Redis

10.代码贡献

11.Redis内部

11.1.源代码布局

11.2.server.h

11.3.server.c

11.4.networking.c

11.5.aof.c and rdb.c

11.6.db.c

11.7.object.c

11.8.replication.c

11.9.其他C文件

11.10.Redis命令的剖析


1.什么是Redis?

Redis通常被称为数据结构服务器。 这意味着Redis通过一组命令提供对可变数据结构的访问,这些命令使用带有TCP套接字和简单协议的 server-client 模型发送。 因此,不同的进程可以以共享的方式查询和修改相同的数据结构。

在Redis中实现的数据结构具有一些特殊属性:

  • 即使始终为它们提供服务并将它们修改到服务器内存中,Redis也会将它们存储在磁盘上。 这意味着Redis速度很快,但它也是非易失性的。
  • 数据结构的实现强调内存效率,因此与使用高级编程语言建模的相同数据结构相比,Redis内部的数据结构将使用较少的内存。
  • Redis提供了许多自然可以在数据库中找到的功能,例如复制,持久性的可调级别,集群和高可用性。

另一个很好的例子是将Redis视为memcached的一个更复杂的版本,其中的操作不仅是SET和GET,而且还适用于诸如列表,集合,有序数据结构等复杂数据类型的操作。

 

2.编译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

2.1.修复依赖项或缓存的构建选项的构建问题

Redis有一些依赖关系,包含在`deps`目录中。
即使依赖关系的源代码中的某些内容发生更改,`make`也不自动重建依赖关系。

当你使用 `git pull`来更新源代码或以其他方式修改依赖关系树中的代码时,请确保使用以下命令来真正清理所有内容并从头开始重建:

make distclean

这将清除: jemalloc, lua, hiredis, linenoise

同样,如果你强制使用某些构建选项,例如32位目标,没有C编译器优化(用于调试目的)以及其他类似的构建时间选项,则这些选项将无限期缓存,直到发出`make distclean`命令为止。

2.2.解决构建32位二进制文件的问题

如果在使用32位目标构建Redis之后,你需要使用64位目标进行重建,或者相反,你需要在Redis发行版的根目录中执行`make distclean`。

如果尝试构建32位二进制Redis时发生构建错误,请尝试以下步骤:

  • 安装软件包libc6-dev-i386(也尝试使用g++-multilib)。
  • 尝试使用`make CFLAGS="-m32 -march=native" LDFLAGS="-m32"`命令行而不是`make 32bit`: 

2.3.分配器(allocator)

通过设置`MALLOC`环境变量可以在构建Redis时选择一个非默认的内存分配器。 默认情况下,Redis是针对libc malloc编译和链接的,但jemalloc是Linux系统上的默认值。 选择设置该默认值,是由于事实证明,与libc malloc相比,jemalloc的碎片问题更少。

要强制针对libc malloc进行编译,请使用:

make MALLOC=libc

要在Mac OS X系统上针对jemalloc进行编译,请使用:

make MALLOC=jemalloc

2.4.详细构建

默认情况下,Redis将使用用户友好的彩色输出进行构建。
如果要查看更详细的输出,请使用以下命令:

make V=1

7.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中的所有选项也都支持使用命令行(名称完全相同)作为选项。

7.2.使用TLS运行Redis

7.2.1.入门

7.2.1.1.编译

要使用TLS支持进行构建,你将需要OpenSSL开发库(例如Debian/Ubuntu上的libssl-dev)。

运行 

make BUILD_TLS=yes

7.2.1.2.测试

要使用TLS运行Redis测试套件,你需要TCL的TLS支持(即Debian/Ubuntu上的`tcl-tls`软件包)。

  1. 运行 `./utils/gen-test-certs.sh`生成根CA和服务器证书。
  2. 运行 `./runtest --tls` 或者 `./runtest-cluster --tls`以TLS模式运行Redis和Redis Cluster测试。

7.2.1.3.手动运行

要以TLS模式手动运行Redis服务器(假设调用了`gen-test-certs.sh`,因此示例证书/键(certificates/keys)可用):

    ./src/redis-server --tls-port 6379 --port 0 \
        --tls-cert-file ./tests/tls/redis.crt \
        --tls-key-file ./tests/tls/redis.key \
        --tls-ca-cert-file ./tests/tls/ca.crt

要使用`redis-cli`连接到该Redis服务器:

    ./src/redis-cli --tls \
        --cert ./tests/tls/redis.crt \
        --key ./tests/tls/redis.key \
        --cacert ./tests/tls/ca.crt

这将禁用TCP并在端口6379上启用TLS。也可以同时使用TCP和TLS,但是你需要分配其他端口。

要使副本服务器使用TLS连接到主服务器,请使用`--tls-replication yes`,并使Redis Cluster在节点间使用TLS时使用`--tls-cluster yes`

7.2.2.入门连接数

现在,所有套接字操作都经过连接抽象层,该层对调用者隐藏I/O和read/write事件处理。

TLS当前不支持多线程I/O,因为TLS连接需要自己处理不是线程安全的AE事件。 该解决方案可能是为I/O线程管理独立的AE循环以及与线程的连接的长期关联。 这也可能会改善整体性能。

TLS的Sync IO当前以一种骇人听闻的方式实现,即进行套接字阻止和配置套接字级别的超时。 这意味着超时值可能不太准确,并且会产生大量的系统调用开销。但是,我认为完全摆脱syncio以支持纯异步工作可能比尝试解决此问题更好。 对于复制来说,可能并不难。 对于群集键迁移,可能会更困难,但是无论如何,可能还有其他充分的理由来改进该部分。

7.2.3.待办事项清单(未来)

  • Redis基准支持。 当前的实现是混合使用hiredi进行解析和基本联网(建立连接),但直接操作大多数操作的套接字。 为了适当的TLS支持,需要对其进行清理。 最好的方法可能是迁移到hireddis异步模式。
  • redis-cli `--slave``--rdb`支持。

7.2.4.多端口

考虑允许将TLS配置在单独的端口上,使Redis侦听多个端口的含义:

  1. 启动横幅端口通知
  2. 标题(Proctitle)
  3. 复制节点如何宣布自己
  4. 集群总线端口计算

8.使用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>

9.安装Redis

要将Redis二进制文件安装到/usr/local/bin中,只需使用:

make install

如果你想使用其他路径,可以使用 `make PREFIX=/some/other/directory 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`

10.代码贡献

  • https://github.com/redis/redis/blob/unstable/COPYING
  • https://github.com/redis/redis/blob/unstable/CONTRIBUTING

11.Redis内部

详细请参考github或者Redis官网

11.1.源代码布局

`src`目录中调用实际Makefile以及Redis和Sentinel的示例配置。 你可以找到一些用于执行Redis,Redis Cluster和Redis Sentinel单元测试的shell脚本,这些脚本在`tests'目录中实现。

根目录内有以下重要目录:

  • `src`: 包含用C编写的Redis实现。
  • `tests`: 包含在Tcl中实现的单元测试。
  • `deps`: 包含Redis使用的库。 编译Redis所需的所有文件都在此目录中; 你的系统只需要提供`libc`,一个POSIX兼容接口和一个C编译器。 值得注意的是,`deps`包含`jemalloc`的副本,它是Linux下Redis的默认分配器。 请注意,在`deps'下还有一些以Redis项目开始的东西,但是主仓库不是`redis/redis`

还有更多目录,但是对于我们的目标而言,它们并不是很重要。 我们将主要关注包含Redis实现的`src`,探索每个文件中包含的内容。 公开文件的顺序是合乎逻辑的顺序,以便逐步披露不同层次的复杂性。

注意:Redis最近被大量重构。 函数名称和文件名已更改,因此你可能会发现本文档更紧密地反映了“unstable”分支。 例如,在Redis 3.0中,`server.c``server.h`文件分别命名为`redis.c``redis.h`。 但是总体结构是相同的。 请记住,所有新的开发和请求请求都应在“unstable”分支上执行。

11.2.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;
        robj **argv;
        redisDb *db;
        int flags;
        list *reply;
        char buf[PROTO_REPLY_CHUNK_BYTES];
        ... many other fields ...
    }

客户端结构定义了一个“连接的客户端(connected client)”:

  • `fd`字段是客户端套接字(socket)文件描述符。
  • `argc`和`argv`填充有客户端正在执行的命令,因此实现给定Redis命令的函数可以读取参数。
  • `querybuf`累积来自客户端的请求,这些请求由Redis服务器根据Redis协议进行解析,并通过调用客户端正在执行的命令的实现来执行。
  • `reply` and `buf` 是动态和静态缓冲区,用于累积服务器发送给客户端的回复。 文件描述符可写后,这些缓冲区就会递增地写入套接字(socket)。

如你在上面的客户端结构中所看到的,命令中的参数被描述为`robj`结构。 以下是完整的`robj`结构,它定义了"Redis object":

    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数据类型,例如strings, lists, sets, sorted sets等。 有趣的是,它具有一个 `type` 字段,以便可以知道给定对象具有什么类型,并具有一个 `refcount`,以便可以在多个位置引用同一对象而无需多次分配它。 最后,`ptr`字段指向对象的实际表示形式,即使使用相同的类型,它也可能有所不同,具体取决于所使用的 `encoding`

Redis objects在Redis内部广泛使用,但是为了避免间接访问的开销,最近在许多地方,我们只是使用未包装在Redis object内部的纯动态字符串。

11.3.server.c

这是Redis服务器的入口点,其中定义了 `main()` 函数。 以下是启动Redis服务器的最重要步骤。

  • `initServerConfig()` 设置 `server` 结构的默认值。
  • `initServer()` 分配所需操作的数据结构,设置监听套接字(socket)等。
  • `aeMain()` 启动侦听新连接的事件循环。

事件循环会定期调用两个特殊功能:

  1. 定期调用`serverCron()`(根据`server.hz`的频率),并执行必须不时地执行的任务,例如检查超时的客户端。
  2. 每次触发事件循环时,都会调用`beforeSleep()` ,Redis处理了一些请求,并返回到事件循环中。

server.c内,你可以找到处理Redis服务器其他重要内容的代码:

  • 使用`call()`以便在给定客户端的上下文中调用给定命令。
  • `activeExpireCycle()`通过`EXPIRE`命令处理具有有效生存时间的键的收回。
  • 当应该执行新的写命令,但根据maxmemory指令Redis内存不足时,将调用`freeMemoryIfNeeded()` 
  • 全局变量 `redisCommandTable`定义了所有Redis命令,指定了命令的名称,实现命令的函数,所需参数的数量以及每个命令的其他属性。

11.4.networking.c

该文件使用客户端,主节点和复制节点(在Redis中只是特殊客户端)定义了所有 I/O 功能:

  • `createClient()` 分配并初始化一个新客户端。
  • 命令实现使用`addReply*()` 系列函数将数据追加到客户端结构,该数据将作为对给定命令执行的回复传输到客户端。
  • `writeToClient()`将输出缓冲区中待处理的数据传输到客户端,并由可写事件处理程序`sendReplyToClient()`调用。
  • `readQueryFromClient()`可读事件处理程序,它将从客户端读取的数据累积到查询缓冲区中。
  • `processInputBuffer()`是用于根据Redis协议解析客户端查询缓冲区的入口点。 一旦准备好处理命令,它将调用在server.c内部定义的 `processCommand()`以便实际执行命令。
  • `freeClient()` 取消分配,断开连接和删除客户端。

11.5.aof.c and rdb.c

从名称中可以猜到,这些文件实现了Redis的RDB和AOF持久性。 Redis使用基于`fork()`系统调用的持久性模型来创建一个线程,该线程具有与Redis主线程相同(共享)的内存内容。 该辅助线程转储磁盘上内存的内容。 rdb.c使用此文件在磁盘上创建快照,而aof.c使用此文件,以便在仅追加文件太大时,执行AOF重写。

`aof.c`内部的实现具有其他功能,以实现API,该API允许命令在客户端执行命令时将新命令追加到AOF文件中。

server.c内部定义的`call()`函数负责调用这些函数,这些函数又会将命令写入AOF。

11.6.db.c

某些Redis命令对特定的数据类型起作用。 通用命令的例子有`DEL``EXPIRE`。 它们对键进行操作,而不是针对其值进行操作。 所有这些通用命令都在`db.c`内部定义。

此外,`db.c`实现API是为了在Redis数据集上执行某些操作而无需直接访问内部数据结构。

`db.c`内部最重要的功能是在许多命令实现中使用的:

  • `lookupKeyRead()`和`lookupKeyWrite()` 用于获取指向与给定键关联的值的指针,如果键不存在,则使用`NULL`
  • `dbAdd()` 及其更高级别的`setKey()`在Redis数据库中创建新键。
  • `dbDelete()` 删除键及其关联的值。
  • `emptyDb()` 删除整个单个数据库或定义的所有数据库。

文件的其余部分实现了公开给客户端的通用命令。

11.7.object.c

已经描述了定义Redis objects的`robj` 结构。 在 `object.c` 内部,有所有在基本级别上与Redis objects一起运行的函数,例如分配新对象,处理引用计数等功能。 该文件中的重要功能:

  • `incrRefCount()`和`decrRefCount()`用于增加或减少对象引用计数。 当它降为0时,对象最终被释放。
  • `createObject()`分配一个新对象。还有一些专门的函数可以分配具有特定内容的字符串对象,例如`createStringObjectFromLongLong()`和类似的函数。

该文件还实现了`OBJECT`命令。

11.8.replication.c

这是Redis内部最复杂的文件之一,建议仅在对其余代码库有所了解后再进行处理。 在此文件中,实现了Redis的主节点角色和复制节点角色。

该文件中最重要的功能之一是`replicationFeedSlaves()`,它可以将命令写入表示被连接到主节点的复制节点实例的客户端,以便复制节点可以获取客户端执行的写操作:这样,他们的数据集将与主节点中的数据集保持同步。

该文件还实现了`SYNC``PSYNC`命令,这些命令用于执行主节点和复制节点之间的首次同步,或在断开连接后继续复制。

11.9.其他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字符串库,了解更多信息
  • `anet.c`是一个与内核公开的原始接口相比,以更简单的方式使用POSIX网络的库。
  • `dict.c` 是非阻塞哈希表的实现,该哈希表将逐步进行哈希刷新。
  • `scripting.c` 实现Lua脚本。 它是完全独立的,并且与其余Redis实现隔离,并且足够简单,如果你熟悉Lua API,就可以理解。
  • `cluster.c` 实现Redis集群。 仅在非常熟悉Redis代码库的其余部分之后,才可能是一篇不错的文章。 如果你想阅读`cluster.c`,请务必阅读Redis群集规范

11.10.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源代码中有大量的命令实现,可以用作实际命令实现的示例。 编写一些命令可能是熟悉代码库的好习惯。

还有许多其他文件在这里没有描述,但是覆盖所有内容是没有用的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴 韵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值