《redis设计与实现》
参考 redis请求与回复协议格式 请求同一为:
*<number of arguments> CR LF
$<number of bytes of argument 1> CR LF
<argument data> CR LF
...
$<number of bytes of argument N> CR LF
<argument data> CR LF
回复根据类型,有五种:
剖析Redis协议
按照redis协议格式编写.ptl
文件 ,然后用管道命令交给Redis一并执行。
关于redis管道: 参考 redis通过pipeline提升吞吐量 就是说,以前是:每条命令进行一次请求和响应。 现在是:将请求的多条命令合并成一条,一并发送给服务端执行,执行结果由客户端一次性返回。 好处是大大减少了网络I/O,增加了效率和并发能力。
serverCron
函数每100ms检查一次saveparams
:struct redisServer {
// ...
// 记录了保存条件的数组
struct saveparam *saveparams;
// ...
};
当任意一个条件满足时执行BGSAVE。
aof写入:
flushAppendOnlyFile
将aof_buf缓冲区写入aof文件。 是否同步,根据appendfsync
: aof重写:
aof开启时,优先采用aof。
文件事件
单线程Reactor模型。处理AE_READABLE
和AE_WRITABLE
事件。
原理:
时间事件
无序链表。但实际使用时,其上只有一两个事件。当前版本正常模式下,只有serverCron
一个时间事件。
serverCron
会做各种常规清理、调整工作,具体的事情可参照原文P.243。
服务器主函数
def main():
# 初始化服务器
init_server()
# 一直处理事件,直到服务器关闭为止
while server_is_not_shutdown():
aeProcessEvents()
# 服务器关闭,执行清理操作
clean_server()
aeProcessEvents: 阻塞等待最近的时间事件到达。苏醒后,处理文件事件和时间事件 事件处理
文件、时间事件处理器都会适时主动让出执行权,减少阻塞。
一个命令请求从发送到完成主要包括以下步骤:
serverCron
函数中的trackOperationsPerSecond
每100毫秒执行一次。trackOperationsPerSecond
根据上次调用后的间隔时间和执行次数,估算1秒执行次数,放入环形数组redisServer.ops_sec_samples
。INFO
命令时,getOperationsPerSecond
根据环形数组平均值计算一秒内估算执行次数。服务器从启动到能够处理客户端的命令请求需要执行以下步骤:
看原书单元末尾的总结
做法是:
sync
。BGSAVE
,保存rdb文件,发送给从服务器。缺点:
对于短线重连来说,sync
效率太低。
psync
的部分重同步中,主向从发送+continue
,并发送断线期间的数据,以完成同步。
只要断线时的offset之后的内容都在复制积压缓冲区内,则可以部分重同步。
复制积压缓冲区默认为1MB。其大小可根据second * write_size_per_second
来估算。
HSET
比SET
更适合对象的存储,因为可以对每个field
进行操作(而不用 反序列化->修改对象->序列化)。那常规的序列化存储对象的方法(比如用json serializer)是否合适呢?会不会效率低了。