Redis 底层结构简单介绍

AI 摘要: Redis是一个开源、支持网络、基于内存的键值对存储数据库,具有多种数据类型支持和数据持久化功能,能够解决会话缓存、消息队列等应用场景的需求。Redis 4.0改进了模块系统、复制、驱逐策略等方面的功能,并支持Raspberry Pi、Redis Cluster等新特性。

REDIS4.0改进点:

  • 模块系统
  • 更好的复制(PSYNC2)
  • 改进驱逐策略
  • 线程DEL / FLUSH
  • 混合RDB + AOF格式
  • Raspberry Pi支持作为主要平台
  • 新的MEMORY命令
  • Redis Cluster支持的Nat / Docker
  • 活动内存碎片整理
  • 内存使用和性能改进
  • 更快的Redis Cluster密钥创建

Reids概览

Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库

Redis又称数据结构型服务器,支持多种数据结构(String、Hash、List、Sets等),采用客户端-服务端模式,基于TCP和简单的协议,同时也支持数据落地

1. 数据类型支持

Redis将键映射到值的类型。

值的类型确定值本身可用的操作(称为命令)。Redis支持高级,原子,服务器端操作,如交集,并集和集之间的差异以及列表,集和排序集的排序。

Redis和其他结构化存储系统之间的一个重要区别是Redis不仅支持字符串,还支持抽象数据类型:

  • String: 字符串列表
  • Sets: 字符串集合(非重复未排序的元素的集合)
  • Sorted Sets: 排序的字符串集(由称为得分的浮点数排序的非重复元素的集合)
  • Hash Table: 散列表,其中键和值是字符串
  • HyperLogLogs: 用于近似集基数大小估计。
  • Stream: 消费者组的条目流。
  • GeoIP:自Redis 3.2以来,通过实施geohash技术的地理空间数据。

2. 数据持久化

Redis通常将全部的数据存储在内存中。2.4版本后可配置为使用虚拟内存,一部分数据集存储在硬盘上,但这个特性废弃了

默认情况下,Redis至少每2秒将数据写入文件系统,如果需要,可以使用更多或更少的健壮选项。如果在默认设置下完全系统出现故障,则只会丢失几秒钟的数据。

当前通过两种方式实现持久化:

  • 使用快照,一种半持久耐用模式。不时的将数据集以异步方式从内存以RDB格式写入硬盘。
  • 1.1版本开始使用更安全的AOF格式替代,一种只能追加的日志类型(将数据集修改操作记录起来),Redis能够在后台对只可追加的记录作修改来避免无限增长的日志。

3. 数据同步

  • Redis支持主从同步,数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器,这使得Redis可执行单层树复制。
  • 完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。
  • 同步对读取操作的可扩展性和数据冗余很有帮助。

4. 性能问题

当不需要数据的持久性时,与考虑提交事务之前将每个更改写入磁盘的数据库系统相比,Redis的内存中性质使其能够表现良好。

Redis作为单个进程运行,并且在重写AOF(仅附加文件)时是单线程或双线程的。因此,单个的Redis实例不能利用任务的并行执行诸如存储过程。

5. 集群

Redis于2015年4月推出了3.0版本的群集(3.0版本的重大改动)

集群规范实现的Redis命令的子集:所有单键命令是可用的,多键操作被限制于属于同一节点。

Redis集群能够扩展到1,000个节点,实现“可接受的”写入安全性,并在某些节点发生故障时继续运行。

6. 常见用途

会话缓存,整页缓存,消息队列应用程序,排行榜和计数等

7. 版本说明

Redis借鉴了Linux操作系统对于版本号的命名规则:

版本号第二位如果是奇数,则为非稳定版本(例如2.7、2.9、3.1),如果是偶数,则为稳定版本(例如2.6、2.8、3.0、3.2),

当前奇数版本就是下一个稳定版本的开发版本,例如2.9版本是3.0版本的开发版本,所以我们在生产环境通常选取偶数版本的Redis。

各版本的大致改动内容,可以参见文末链接。

Redis 源码结构

1
2
3
4
src 	--redis的Ascii c的实现;
test	--包含tcl的单元实现
utils	--工具
deps	--包依赖(jemalloc redis默认在linux下的内存分配、lua脚本等)

1. server.h

专注src,redis的实现方面,4.0版本做了很大一部分重构工作,比如redis3.0中的server.c、server.h被命名成了redis.credis.h

明白程序最好的方面是看懂它的数据结构,我们先来看server.h。redis中所有的结构在一个叫server的全局结构中共享配置,类型为struct redisServer。一些重要的数据结构:

  • server.db: 一组存储数据的redis数据库;
  • server.commands: 命令表;
  • server.clients: 关联一系列客户端连接到服务端的链接;
  • server.master: 如果实例是一个从的话,该结构代表一个特别的客户端,master客户端;

另一个重要的Redis的数据结构就是redis client,定义了一个连接的客户端:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
struct client {
    int fd; 	   -- 客户端套接字文件描述
    sds querybuf;	-- 从客户端的请求积累,Redis基于redis协议解析出来执行
    int argc;		-- 客户端的参数个数
    robj **argv;	-- 客户端参数的变量
    redisDb *db;
    int flags;
    list *reply;	-- redis服务器端回应给客户端的响应数据的积累;
    char buf[PROTO_REPLY_CHUNK_BYTES];
    ... many other fields ...
}

Redis对象的数据结构,redisObject,这个结构可以代表了所有基础的Redis数据类型,像strings、lists、sets、sorted sets等。

1
2
3
4
5
6
7
typedef struct redisObject {
    unsigned type:4;		-- 指明redi的数据结构类型
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
    int refcount;			-- redis对象引用次数,被引用在多个地方而不用重新分配内存;
    void *ptr;			-- 指明真实的对象,甚至是同类型,依赖于`encoding`的使用;
} robj;

2. server.c

这个是整个Redis服务的入口点,main()函数就是在这个文件中被定义的,在启动Redis服务中,关键的几个点:

  • initServerConfig() : 初始化server结构的默认值;
  • initServer(): 分配需要操作的数据结构内存,构建套接字的监听工作等等;
  • aeMain(): 开启循环事件,监听新的请求,循环事件,周期性的调用的两个函数:
    • serverCron(): 基于server.hz频率,周期性的调用执行任务,比如检测超时的客户端;
    • beforeSleep(): 在每次循环事件触发时候被调用,redis服务少量请求,被返回到事件循环中;

其他在Redis服务中至关重要的事项:

  • call(): 在给定的客户端的上下文中调用给定的命令;
  • activeExpireCycle(): 处理回收基于EXPIRE命令设定的生存期的keys。
  • freeMemoryIfNeed(): 当内存耗尽maxmemory设定的值时候,一个新的写命令到来时候,该函数被执行;

全局变量redisCommandTable定义了所有的Redis命令,指定命令的名称、命令执行的函数,需要的变量参数,以及其他属性;

1
2
3
4
5
6
7
struct redisCommand redisCommandTable[] = {
    {"module",moduleCommand,-2,"as",0,NULL,1,1,1,0,0},
    {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
    {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},
    {"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0},
....
}

3. networking.c

该文件定义了所有在Redis中,clients、masters、slaves(特殊的客户端)有关的I/O函数,

  • createClient(): 分配和初始化一个新的客户端
  • addRply(): 基于客户端执行结果,增加结果数据到client结构中;
  • writeToClient(): 传输待定数据到缓冲到客户端的输出中,当sendReplyToClient()执行时候也被调用;
  • readQueryFromClient(): 从客户端累计的数据数据中读取查询缓冲;
  • processInputBuffer(): 根据Redis协议,解析客户的查询的缓冲中的数据,交给processCommand()执行(在server.c中定义的)。
  • freeClient():释放分配、断开连接、移除客户端;

4. aof.c和rdb.c

参考 http://wiki.jikexueyuan.com/project/redis/rdb.html

这两个文件是实现了Redis持久化(RDB以及AOF)。Redis使用一个持久化的模式,是基于fork的系统调用来创建一个线程(该线程拥有Redis主线共享的内存区域)。持久化的线程,导出内存中的数据到磁盘。这个被rdb.c使用来在磁盘上创建快照,以及aof.c来执行AOF重写(当追加的文件过大时候);

AOF(append only file) 可以记录服务器的所有写操作。在服务器重新启动的时候,会把所有的写操作重新执行一遍,从而实现数据备份。当写操作集过大(比原有的数据集还大),Redis会重写写操作集。

5. db.c

一些特点的Redis命令操作在特定的数据类型中,比如DELEXPIRE,这些操作只能针对keys操作,不能是其他值。所有这些命令是定义在db.c中,相关的函数:

  • lookupKeyRead() and lookupKeyWrite():获取一个指向关联keys的值,NULL的话代表key不存在;
  • dbAdd(): 其实是setKey()的一个更高级别的副本,用于在一个redis库上面创建一个key;
  • dbDelete(): 移除一个key以及和他关联的值;
  • emptyDb(): 移除一整个db或者所有定义的db;

6. object.c

robj结构定义了Redis的对象描述,在object.c中,所有redis对象操作的函数在同一个级别,比如分配一个新的对象、处理引用等等:

  • incrRefcount()、decrRedCount():用于增加或者减少对象的引用,当引用到0时候,对象最终将被释放;
  • createObject()分配一个新的对象

7. replication.c

Redis中最复杂的文件,其中实现了主和从的角色,同时还有SYNCPSYNC命令(解决主从同步或者断开连接后的一个持续复制),重要函数:

  • replicationFeedSlaves(): 写命令到从客户端实例中;

相对我们的master,从客户端可以获取所有需要被执行的写;

8. 其他c文件

  • t_hash.c, t_list.c, t_set.c, t_string.c and t_zset.c,包含了Redis对象类型的实现。
  • ae.c: Redis的循环事件相关
  • sds.c: redis的字符串库
  • anet.c: posix网络库
  • dict.c: 非阻塞的hash表的实现
  • scripting.c: lua脚本的实现;
  • cluster.c: redis集群的实现,(参考:http://redis.io/topics/cluster-spec)

安装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 下载地址
http://download.redis.io/releases/redis-4.0.1.tar.gz

// 编译参数 V=1 更友好的颜色输出 PREFIX=xx安装前缀
make V=1 PREFIX=/usr/local/redis-4.0.1
make install

// 安装成服务化,基本一路回车就可以,大致包括端口、配置、日志、数据存储、cli脚本位置等
[root@tkstorm-srv redis-4.0.1]# ./utils/install_server.sh

// 检测服务以及启动
[root@tkstorm-srv redis-4.0.1]# /etc/init.d/redis_6379 status
Redis is running (10623)

参考

  • Redis主从复制:http://wiki.jikexueyuan.com/project/redis/master-slave-replication.html
  • AOF持久化策略:http://wiki.jikexueyuan.com/project/redis/aof.html)
  • RDB参考: http://wiki.jikexueyuan.com/project/redis/rdb.html
  • Redis集群:http://redis.io/topics/cluster-spec
  • Redis各版本下载:http://download.redis.io/releases/
  • Redis个版本改动内容点:https://www.cnblogs.com/yangmingxianshen/p/8043851.html