一个redis服务器保存多个数据库。
struct redisServer {
//一个数组,多个数据库
redisDb *db;
}
当执行select 1
,就是切换数据库到db[1]
,具体就是会修改redisClient.db指针到redisServer.db[1]
typedef struct redisDb{
dict *dict;//数据库键空间
dict *expires;//过期时间
}
这里的dict就是上面说的字典数据结构。
这个字典的key就是redis里面的key,每个key都是字符串对象
值就是数据库的值,可以是字符串对象,列表对象,哈希对象,集合对象,游戏集合对象。
如果我们对一个键设置过期时间
redis就会在字典expires里面,加上key=过期的时间戳(精确到毫秒)。
执行ttl,redis会比较expires里面的时间戳和当前时间的差值,然后返回差值。
过期key的判定
存在内存中的数据,称为数据库状态
持久化就是把数据库状态,保存为RDB文件,RDB文件是存在硬盘的。
执行命令save,bgsave,可以立刻把数据库状态保存为RDB文件。
redis也会定期执行保存操作
服务器的配置:
save 900 1
表示服务器在900秒内,对数据库执行了至少一次修改,服务器就会执行保存操作
如果有多个svae配置,它们直接的关系是或的关系,即满足其中一个,就会执行保存
struct redisServer{
long long dirty;
time_t lastsave
}
数据库对象中,有两个属性:
redis通过这两个属性来实现定期保存的机制
RDB文件由
REDIS db_version databases EOF cehcks_sum
构成
databases部分的构成:
SELECTDB db_number PAIRS
键值对构成
EXPIRETIME_MS ms TYPE KEY VALUE
redis会根据TYPE这个常量,来决定怎么读取VALUE的数据。
KEY肯定就是字符串类型了。
如果TYPE=REDIS_ENCODING_STRING,表示这个对象是数值字符串对象
字符串对象有以下三种存储类型
int类型
构成:
ENCODING int
无压缩字符串
如果TYPE=REDIS_ENCODING_RAW,表示这个是普通字符串
len string
压缩字符串
如果字符串的长度大于20字节,就会压缩字符串。
REDIS_RDB_ENC_LZF compressed_len origin_len compressed_string
问题:
程序怎么知道这是int类型,还是无压缩字符串,还是压缩字符串的?
当TYPE=REDIS_RDB_TYPE_LIST 表示这是一个列表对象
list_length item1 item2 itemN
如果TYPE=REDIS_RDB_TYPE_SET 那么表示这是一个集合对象
set_size elem1 ... elemN
如果TYPE=REDIS_RDB_TYPE_HASH 那么表示这是一个哈希表对象
hash_size key_value_pair1 。。。。。。key_value_pairN
键值对的构成
key1 value1 key2 value2
如果TYPE=REDIS_RDB_TYPE_ZSET,表示这是一个有序集合对象
sorted_set_size element1 。。。。 elementN
每个元素的构成
member1 score1
REDIS 0 0 0 6 376\0\0 003 MSG 005 HELLO 377 207z=304fTL
343
在Redis启动的时候,会自动加载RDB文件,加载成功后,服务器才处理客户端的请求。
不同于RDB一次存储整个数据库状态
AOF(Append Only File)是每次执行写命令,就append一条指令到文件。
struct redisServer {
sds aof_buf;
}
在redis 服务器对象中,有一个aof_buf属性,用于存储AOF命令
每次服务器执行写命令的时候,都会往这个缓冲区写AOF命令。
在Redis的时间事件中,会调用flshAppendOnlyFile函数,决定是否吧缓冲区的数据flush到文件。
例如如果执行命令 set key value
就会产生下面的AOF命令:
*3\4\n$3\r\nSET\4\n$3\r\nKEY\r\n$5\r\nVALUE\r\n
在启动REDIS的时候,会加载AOF文件
当写命令很多的时候,甚至都是操作很少的几个键的话,例如不断地修改一个key的值,这样就会有很多命令。
这时候就可以执行重写命令,来把AOF命令压缩。
例如命令:
set test a
set test b
set test c
会被压缩为
set test c
因为上面的两条命令就没意义了。
实际的重写进程,不会去扫描AOF文件,而是会扫描数据库的键值对,然后执行SET或者push等命令。
当执行BGREWRITEAOF命令,Redis就会执行后台重写
redis是两种事件
Redis使用IO多路复用的方式,当有客户端的请求(例如Socket有数据可以读取,有数据可以写),就会生成一个事件,塞到处理队列里面
每当Redis执行文件事件的时候,就从中取一个事件来执行。
事件包含
redis里面会有一个时间事件队列
一个时间事件包含
具体实现就是,redis会每个100毫秒,就去这个队列里面遍历,看哪个时间可以执行(when小于当前时间),如果可以就执行。
其实现在Redis只有一个事件事件,就是serverCron
这个事件会执行:
serverCron每秒运行10次,也就是每100毫秒一次。
当Redis服务器启动后,跑完了初始化的任务,就会死循环得跑下面这个流程:
def aeProcessEvents():
time_event=aeSearchNearestTimer() #寻找最近的时间事件
remaind_ms=time_event.when-unix_ts_now() #计算事件要在多少毫秒后执行
if remaind_ms<0:
remaind_ms=0
timeval=create_timeval_with_ms(remaind_ms) #通过remaind_ms计算最长阻塞时间
aeApiPoll(timeval) #等待文件事件,超时时间为最长阻塞时间,如果remaind_ms=0,就马上返回,不阻塞
processFileEvents() #执行文件事件
processTimeEvents() #执行时间事件
所以总的来说
这样可以保证
Redis的服务端对象有一个列表,保存所有的客户端对象,每个连接过来的客户端,都会创建一个客户端实例。
struct redisServer {
list *clients;
}
redis命令 client list
可以查看所有连接的客户端。
客户端的属性有:
客户端的生命周期:
set test aa
,参数就是[‘set‘,‘test‘,‘aa‘]
,个数是3
参考上面的客户端生命周期
参考时间事件里面的serverCron说明。
除此之外还有:
当启动服务器的时候,服务器会做以下操作
原文:https://www.cnblogs.com/Xjng/p/12085109.html