redis Server
数据库切换
默认会创建16个数据库,客户端通过select选取。但一般情况只用第0个数据库,切换容易导致误操作
|
|
所有键空间存储在redisDb的dict中,称为key space
每个键是字符串对象,值是各种对象
读写键操作
- 更新keyspace_hits和keyspace_misses,用来输出统计数据
- 更新键的LRU时间
- 若发现该键已经过期,则删除键
- 若该键被watch,标记键为dirty,使得监听者发现后重新拉数据
- dirty计数器++,用来触发持久化和复制操作
过期字典的键是键空间的键字符串对象的指针(不会新分配空间),值是longlong类型的过期时间(毫秒精度的unix时间戳)
判断是否过期:
先在过期字典里取key的过期时间,再与当前时间比较
删除策略
(redis同时采用惰性删除和定期删除策略,其中定期删除是随机取出一定数量的键做检查):
- 定时删除:过期时立刻删除(问题:要创建大量定时器,占用太多CPU,因此不合理)
- 惰性删除:获取键时若过期才删除 (问题:内存最不友好)
- 定期删除:定期对所有key进行检查并删除 (问题:如何确定定期时间,太快或太慢都不好)
持久化
解密Redis持久化 - justjavac - 博客园 (cnblogs.com)
rdb
记录键值
主服务器初始化加载时不会加载过期的键值,从服务器会加载过期的键值,但同步之后也会被清空掉
主节点统一管理过期删除,从节点只能被动接收del命令,保证了数据一致性,但从节点里可能会有过期键值
SAVE阻塞保存,BGSAVE用子进程保存
自动保存:自动保存规则设置在一个列表中,表示一段时间内进行了多少次改动就满足保存规则
每次写入会将db的dirty计数器加1,且每次保存会保存的时间戳lastsave。当距离lastsave的时间超过条件中设置的时间,比较dirty与规则中设置的改动次数,若满足则触发BGSAVE
RDB数据格式
REDIS | db_version | database 0 | database 3 | EOF | check_sum |
---|---|---|---|---|---|
格式细节包括压缩算法略过
aof
记录写命令(启动时优先选择加载aof)
命令追加:按redis协议追加到aof_buf缓冲区中
文件写入和同步:redis server主线程每次循环结束前,将缓冲区写入aof文件,并调用fsync落盘
同步策略:always(每次都落盘),everysec(离上次落盘超过一秒触发落盘), no(靠操作系统自己落盘,一般是30s)
过期但还未被删除的键值不会追加到aof中,只有惰性删除或定期删除显示调用del后才会追加DEL命令
aof重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的 AOF 文件。
重写是子进程,进程的数据是父进程数据的副本(为了避免加锁),重写时会数据不同步,通过在父进程加一个aof重写缓冲区解决。父进程在重写过程中的写操作会同时写到aof缓冲区和aof重写缓冲区。子进程写完后通知父进程将aof重写缓冲区的数据追加到aof文件中,追加完毕后使用rename原子操作覆盖现有aof文件
事件(ae库)
事件分派器
对select,epoll的封装,当socket可读或可写时,执行事件处理器的处理函数
可读(sever侧):AE_READABLE client对socket执行write,close,connect
可写(server侧):AE_WRITABLE client对socket执行read
aeMain中循环执行aeProcessEvents,对aeEventLoop中的事件进行处理
注意为了不影响到定时器事件的执行,select,epoll的超时需在最近一次定时器事件发生前退出
文件事件处理器
此处的注册比较分散,创建事件时会注册对应的事件处理器(aeCreateFileEvent),注册顺序是先acceptTcpHandler,再在acceptTcpHandler根据连接来嵌套注册别的处理器
连接应答处理器 acceptTcpHandler
redis server在初始化时创建该事件,创建成功表示server已经起来了
使用accept新建一个client的cfd,并使用该cfd创建client
命令请求处理器 readQueryFromClient ,事件类型AE_READABLE
createClient时会同时创建该client的命令请求处理事件,当client往socket write时(发起请求),会触发事件的处理,将socket中数据存入client对象中并解析为argv和argc,之后调用对应的命令处理函数,生成返回值,最后注册命令回复事件sendReplyToClient
命令回复处理器 sendReplyToClient,事件类型AE_WRITABLE
调用write往cfd中写数据
数据同步处理器 sendBulkToSlave
时间事件
分为定时事件和周期性事件。定时事件只执行一次,aeProcessEvents执行完事件注册的处理器函数(te->timeProc)后返回AE_NOMORE,表示后续不再执行并在链表中删除该事件。周期性事件执行完后刷新事件内的when属性,并让事件保留
时间事件保存在一个无序链表(没有按照when来排序)中,每次需要整个遍历链表来获取要执行的事件,但是由于事件很少(低版本只有一个serverCron事件),该链表的遍历不会消耗太多的性能
serverCron
整个server定时cronjob的集合函数,负责:
更新统计信息
清理失效键值对
关闭清理失效的client
尝试进行持久化操作
主服务器向从服务器数据同步
若处于集群模式,与其他机器定期同步和连接测试
- 原文作者:windseek
- 原文链接:https://scottlx.github.io/posts/redisServer/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。