Redis 为每个连接维护两块缓冲:读请求的查询缓冲和写回复的输出缓冲。它们平时很小,但可以在一个连接上涨到很大。这篇用 CLIENT LIST 里的字段把两块缓冲各自「怎么涨」量出来,并发现一个反直觉的细节:查询缓冲是按客户端声明的大小预留的。
qbuf 0 qbuf-free 0 argv-mem 0 multi-mem 0 obl 0 oll 0 omem 0 tot-mem 2304 rbs 1024 rbp 0 tot-net-in 14 tot-net-out 7 (qbuf 查询缓冲、obl 固定输出缓冲字节、oll 输出链表节点数、omem 输出缓冲总内存、rbs/rbp 读缓冲大小与峰值、tot-mem 整个客户端占用)
qbuf / qbuf-free 是查询缓冲的已用与空闲;argv-mem 是已解析出来的参数占用;obl / oll / omem 描述输出缓冲;rbs / rbp 是读缓冲的当前大小与峰值;tot-mem 是整个连接占用的内存。空闲连接总共只有 2 KB 左右。(obl 与 oll 各自的确切含义——固定缓冲字节与链表节点数——我没有单独验证。)
已发送 0 个 GET(每个回复 1 MB),客户端不读:omem = 0.0 MB (obl=0 oll=0) 已发送 1 个 GET(每个回复 1 MB),客户端不读:omem = 1.0 MB (obl=47 oll=0) 已发送 5 个 GET(每个回复 1 MB),客户端不读:omem = 5.2 MB (obl=235 oll=0) 已发送 20 个 GET(每个回复 1 MB),客户端不读:omem = 21.0 MB (obl=940 oll=0) 已发送 100 个 GET(每个回复 1 MB),客户端不读:omem = 104.9 MB (obl=987 oll=1)
客户端只发 GET 而不读回复(并把自己的接收缓冲调到 4 KB),服务器为每个未读的 1 MB 回复多缓冲约 1 MB:1 个 → 1.0 MB,20 个 → 21 MB,100 个 → 105 MB。normal 类客户端默认没有输出缓冲上限,所以这会一直涨到你设置限制为止(第 113 篇)。
qbuf = 10.5 MB,qbuf-free = 42.0 MB,argv-mem = 0.0 MB,tot-mem = 52.4 MB
发 SET k,声明 value 长度 52428800 字节(50 MB),只发送 10 MB 后不再发送:qbuf = 10.5 MB,qbuf-free = 42.0 MB,合计 52.4 MB。也就是说,服务器在收到头部之后就为声明的 50 MB 预先分配了缓冲,而不是随实际收到的数据增长。所以「声明一个很大的 bulk」本身就能让服务器占内存,上限由 proto-max-bulk-len 决定(默认 512 MB;本实验里调成了 100 MB)。
200 个空闲连接:connected_clients = 202,mem_clients_normal = 1.76 MB(平均每个连接 8.5 KB)
200 个空闲连接:mem_clients_normal 1.76 MB,平均每个连接约 8.5 KB。(第 114 篇用 used_memory 的变化量得到 1000 个连接每个约 17 KB,口径不同:那里包含了连接相关的其它分配。)