redis内部机制的介绍和启动过程
Posted elliottx4
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis内部机制的介绍和启动过程相关的知识,希望对你有一定的参考价值。
redis(一)内部机制的介绍和启动过程
redis的基本介绍
redis是一种非关系型数据库,采用=key,value的形式来存储数据。key是二进制数据,对于value的数据类型,redis支持string、hash、list、set、sorted set五种类型。
对于单个redis实例,内部使用多线程通信,但是对外采用RESP单线程通信协议,在TCP层通过二进制方式进行传输数据,单线程采用同步的请求方式。
redis服务端
redis服务端内部结构为struct redisServer和struct redisDb。redis中默认16个数据库,可以通过配置来修改数据库数量,每一个数据库对应一个redisDb。数据库之间的数据是相互独立的。查询数据的时候,可以通过select指定具体某个数据库。
1 struct redisServer 2 int dbnum;//服务器的数据库数量,值由服务器配置的“databases”选项决定,默认为16 3 redisDb *db;//数组,保存着服务器中的所有数据库 4 5 list *clients;//一个链表,保存了所有客户端状态,每个链表元素都是“redisClient”结构 6 7 time_t unixtime;//保存秒级精度的系统当前UNIX时间戳,减少获取系统当前时间的系统调用次数,100毫秒更新一次 8 long long mstime;//保存毫秒级精度的系统当前UNIX时间戳 9 unsigned lruclock;//默认每10秒更新一次,用于计算数据库键的空转时长,数据库键的空转时长 = 服务器的“lruclock”属性值 - 数据库键值对象的“lru”属性值 10 11 long long ops_sec_last_sample_time;//上一次进行服务器每秒执行命令数量抽样的时间 12 long long ops_sec_last_sample_ops;//上一次进行服务器每秒执行命令数量抽样时,服务器已执行命令的数量 13 long long ops_sec_samples[REDIS_OPS_SEC_SAMPLE];//环形数组,每个元素记录一次服务器每秒执行命令数量抽样结果,估算服务器在最近一秒钟处理的命令请求数量(数组长度默认为16,100毫秒更新一次) 14 int ops_sec_idx;//ops_sec_samples数组的索引值,每次抽样后值增1,等于16时重置为0 15 16 size_t stat_peak_memory;//已使用内存峰值 17 18 int shutdown_asap;//关闭服务器的标识,1表示关闭,0不关闭 19 20 pid_t rdb_child_pid;//记录执行BGSAVE命令的子进程的ID,-1表示服务器没有正在执行BGSAVE 21 pid_t aof_child_pid;//记录执行BGREWRITEAOF命令的子进程的ID,-1表示服务器没有正在执行BGREWRITEAOF 22 int aof_rewrite_scheduled;//1表示有BGREWRITEAOF命令被延迟了(服务器执行BGSAVE期间收到的BGREWRITEAOF会被延迟到BGSAVE执行完成之后执行) 23 struct saveparam *saveparams;//记录了自动保存条件的数组(执行BGSAVE的条件) 24 long long dirty;//修改计数器(上一次执行BGSAVE之后已经产生了多少修改) 25 time_t lastsave;//上一次执行自动保存操作(BGSAVE)的时间 26 sds aof_buf;//AOF缓冲区 27 28 int cronloops;//serverCron函数的运行次数计数器 29 30 lua;//用于执行Lua脚本的Lua环境 31 redisClient *lua_client;//Lua脚本的伪客户端,在服务器运行的整个生命周期一直存在,直至服务器关闭才会关闭 32 dict *lua_scripts;//字典,记录所有载入的Lua脚本,键为某个Lua脚本的SHA1校验和,值为对应的Lua脚本 33 dict *repl_scriptcache_dict;//字典,记录已经传播给所有从服务器的所有Lua脚本,键为脚本的SHA1校验和,值为NULL,用于EVALSHA1命令的复制 34 35 long long slowlog_entry_id;//下一条慢查询日志的ID 36 list *slowlog;//保存了所有慢查询日志的链表 37 long long slowlog_log_slower_than;//服务器配置“slowlog-log-slower-than”选项的值,表示查询慢于多少微秒便记录慢查询日志 38 unsigned long slowlog_max_len;//服务器配置“slowlog-max-len”选项的值,表示服务器最多保存多少条慢查询日志记录,若超出,最久的记录会被覆盖 39 40 monitors;//链表,监视器客户端列表 41 42 dict *pubsub_channels;//字典,保存所有频道的订阅关系,键为某个被订阅的频道,值为链表,记录了所有订阅这个频道的客户端 43 list *pubsub_patterns;//链表,保存所有模式的订阅关系,每个链表节点都包含了订阅的客户端和被订阅的模式 44 ;
struct redisDb dict *dict;//数据库键空间字典,保存数据库中所有的键值对 dict *expires;//过期字典,保存数据库中所有键的过期时间 dict *watched_keys;//字典,正在被WATCH命令监视的键 ;
redisDb中三个字典中的key共享对象,value值不一样。对于过期的键,redis采用惰性删除和定时删除相结合的方式,惰性删除,指执行之前会查看这个key是否过期,定时删除值一段时间之后分多次遍历数据库,每次只删除部分过期的数据。
redis客户端
redis-server通过tcp端口或socket来创建和redis-cli的连接,可以修改redis.conf中的maxclients来指定最大连接数,在建立连接之后,socket会被设置为非阻塞模式,同时创造一个struct redisClient来保存客户端的连接信息。
1 struct redisClient 2 redisDb *db;//记录客户端当前正在使用的数据库,上文提到之前有16个们默认的数据库,可以通过select进行切换数据库 3 int fd;//客户端正在使用的套接字描述符,-1表示伪客户端(AOF文件或者Lua脚本),大于-1表示普通客户端 4 robj *name;//客户端名字 5 int flags;//客户端标志,记录了客户端的角色,以及客户端目前所处的状态 6 sds querybuf;//输入缓冲区,根据输入内容动态地缩小或扩大,但不能超过1GB,否则服务器将关闭这个客户端 7 robj **argv;//命令与命令参数,数组,每个元素都是一个字符串对象,argv[0]为命令,其余元素为参数 8 int argc;//argv数组的长度 9 struct redisCommand *cmd;//当前执行的命令的实现函数,指向命令表中的命令结构 10 char buf[REDIS_REPLY_CHUNK_BYTES];//固定大小输出缓冲区,数组,默认大小为16KB 11 int bufpos;//buf数组目前已使用的字节数量 12 list *reply;//可变大小输出缓冲区,链表 13 obuf_soft_limit_reached_time:记录了“reply”输出缓冲区第一次到达软性限制的时间,用于计算持续超出软性限制的时长,以此决定是否关闭客户端 14 int authenticated;//0表示未通过身份验证,1表示已通过身份验证 15 time_t ctime:创建客户端的时间,可用于计算客户端与服务器连接的时间长度 16 time_t lastinteraction:客户端与服务器最后一次进行互动的时间,可用于客户端的空转时长 17 multiState mstate;//事务状态,包含一个事务队列,以及一个已入列命令计数器 18 ;
db:是一个指针,指向Redis服务器状态结构中的“db”数组其中一个元素,表示当前客户端正在使用的数据库。默认情况下,Redis客户端的目标数据库为0号数据库,可以通过select命令切换,所以select命令的实现原理为:修改redisClient.db指针,让它指向服务器中指定的数据库。
fd:连接当前客户端与Redis服务器的套接字描述符。值为-1表示伪客户端(AOF文件或者Lua脚本),值大于-1则表示普通客户端。Redis客户端分为普通客户端与伪客户端两种类型,其中通过网络连接与Redis服务器进行连接的就是普通客户端,反之则是伪客户端了。伪客户端也有两种类型,分别是Lua脚本的伪客户端和AOF文件的伪客户端。Redis服务器状态结构的“lua_client”属性就保存了Lua脚本的伪客户端,它会在Redis服务器初始化时就被创建,负责执行Lua脚本中包含的Redis命令,在服务器运行的整个生命周期一直存在,直至服务器关闭才会关闭。而AOF伪客户端则是在载入AOF文件时被创建,用于执行AOF文件中的Redis命令,在AOF文件载入完成之后被关闭。client list:列出目前所有连接到服务器的普通客户端。
redis的持久化
AOF、RDB指的是redis持久化策略,可以通过redis.conf中的参数来配置。
appendfsync (always/everysec/no)
save [time(s)] [times]
AOF指的是保存操作命令,他会将每一次redis的数据操作命令追加到文件中,当键过期的时候会加入del命令,并且每过一段时间,会对已经生成的文件优化一次,主要指的是操作命令的合并。
RDB指的是执行save或bgsave命令创建一个新的RDB文件,新建一个子进程,把所有的数据写入文件。
redis中的文件事件和时间时间
redis中的事件分为文件事件和时间事件
文件事件:指的是通过epoll实现io多路复用程序来监听多个套接字,对读、写、关闭、连接都支持事务,支持原子操作。
时间事件:指的是redis中在默写特定时间执行操作,主要有定时事件和周期性事件。所有的时间事件会被放在一个无须列表中,新加入的事件会在事件的头部插入。每次执行事件的时候都会去遍历列表。正常情况下只有一个serverCron时间时间,在benchmark模式下,也只有两个时间事件。
redis的启动过程
(1)初始化服务器状态结构
新创建一个struct redisServer类型的实例变量作为服务器的状态,记录着整个服务器的状态,并为结构中的各个属性设置默认值,例如:服务器的运行ID、默认配置文件路径、默认端口等等,同时创建Redis命令表。
(2)载入配置选项
载入用户指定的配置参数和配置文件,并根据用户设定的配置,对服务器状态变量的相关属性进行修改。
(3)初始化服务器数据结构
这一步主要是为服务器状态中的一些数据结构分配内存,例如:
- “clients“:链表,保存所有与服务器连接的客户端的状态结构。
- ”db“:字典保存服务器的所有数据库。
- ”lua“:用于执行Lua脚本的Lua环境。
- ”slowlog“:用于保存慢查询日志。
除此之外,还会进行一些非常重要的设置操作,例如:
- 为服务器设置进程信号处理器。
- 创建共享对象,例如经常经常用到的“OK”回复字符串对象,1到10000的字符串对象等等。
- 为serverCron函数创建时间事件。
- 如果AOF持久化功能已经打开,则打开现有的AOF文件,若AOF文件不存在,则创建并打开一个新的AOF文件,为AOF写入做好准备。
- 初始化服务器的后台I/O模块,为将来的I/O操作做好准备。
(4)还原数据库状态
若服务器启用了AOF持久化功能,则载入AOF文件,否则载入RDB文件,根据AOF文件或RDB文件记录的内容还原数据库状态,同时在日志文件中打印出载入文件并还原数据库状态所耗费的时长。
(5)执行事件循环
一切准备就绪,开始执行服务器的事件循环,开始接受客户端的连接请求,处理客户端发送的命令请求。
以上是关于redis内部机制的介绍和启动过程的主要内容,如果未能解决你的问题,请参考以下文章