客户端通信

客户端通信

每个Redis客户端(以下简称”Client”)都有多个状态属性,而理解和分析这些属性,对于我们设计Redis键空间和运营管理都有帮助。使用redis client命令可查看当前Redis实例的所有客户端;每行数据对应一个客户端。

127.0.0.1:6390> client list
id=2 addr=127.0.0.1:53184 fd=8 name= age=33 idle=24 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=client
id=3 addr=127.0.0.1:53190 fd=7 name= age=2 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client

以上为两个客户端,每个包含18个字段属性;其中属性的基本含义此处简单说明,后文会对重启指标深入分析。

id:客户端唯一标识, 每新创建一个连接就自增1;redis重启后重置1。
addr: 客户端源ip:port;用于分析异常的客户端,定位是由哪个服务器哪个进程引起的;如id=2的客户端 netstat -anp | grep 53184
fd: socket的文件描述符;数值同lsof的FD字段相同
name: 客户端的名字,默认不会设置,一般用处不大。可手动执行[clientsetname](http://redis.io/commands/client-setname)
age: 客户端存活的秒数
idle: 空闲的秒数;用于回收客户端和分析大量连接时有用
flages:客户端类型的标志, 共13种,常用的几种:N(普通客户端),M(master),S(slave),O(执行monitor)
db:客户端当前使用的database序号
sub/psub: 快订阅的频道/模式数
multi:当前事务中已执行命令个数
qbuf: query buffer的字节数         重要
qbuf-free: query buffer的剩余字节数
obl:定长Output buffer的使用字节数
oll:可变大小output buffer的对象个数
omem:可变大小output buffer的内存使用字节数  重要
events: 文件描述符事作件(r/w)
cmd:客户端最近一次执行的命令,不包含参数

Buffer

Query Buffer

每个Client都有一个query buffer(查询缓存区或输入缓存区),它用于保存客户端的发送命令,redis serverquery buffer获取命令并执行。每个客户端query buffer自动动态调整使用内存大小的,范围在0~1GB之间;当某个客户端的query buffer使用超过1GB, server会立即关闭它,为避免过度使用内存,触发oom killerquery buffer的大小限制是硬编码的1GB,没法控制配置参数修改。

server.h#163
/* Protocol and IO related defines */
#define PROTO_MAX_QUERYBUF_LEN  (1024*1024*1024) /* 1GB max query buffer. */

client list命令,观察qbufqbuf-free两个字段,就是client query buffer使用内存大小。 如下示例(省去部分字段)

127.0.0.1:6390> client list
id=169  qbuf=128679888 qbuf-free=425984 obl=0 oll=0 omem=0 events=r cmd=NULL
id=171  qbuf=128679888 qbuf-free=425984 obl=0 oll=0 omem=0 events=r cmd=NULL
id=218  qbuf=128679888 qbuf-free=425984 obl=0 oll=0 omem=0 events=r cmd=NULL
id=151  qbuf=128696272 qbuf-free=425984 obl=0 oll=0 omem=0 events=r cmd=NULL

Query Buffer过载

模拟100个客户端,连续写入大小为500MB(生产建议小于1KB)Key; redis server设置maxmemory4gb,redis实际已用内存43gb(used_memory)。结论是query buffer使用内存不受maxmemory的限制,这BUG已经提给官方,如不能限制redis使用的内存量,很易导致redis过度使用内存,无法控制出现oom

127.0.0.1:6390> info memory
# Memory
used_memory:46979129016
used_memory_human:43.75G
used_memory_rss:49898303488
used_memory_rss_human:46.47G
used_memory_peak:54796105584
used_memory_peak_human:51.03G
total_system_memory:134911881216
total_system_memory_human:125.65G
maxmemory:4294967296
maxmemory_human:4.00G
maxmemory_policy:allkeys-random
mem_fragmentation_ratio:1.06
mem_allocator:jemalloc-4.0.3
## 当client断开后,rss会马上释放内存给OS

query buffer占用内存,会计入maxmemory,如果达到maxmemory限制,会触发KEYLRU淘汰或无法写入新数据。

127.0.0.1:6390> set a b
(error) OOM command not allowed when used memory > 'maxmemory'.

我们在程序中要避免Query Buffer过度使用,可以参考如下建议:

  • 禁用大KEY,尽量保证key小于1KB;redis支持512MB大小string
  • 监控redis内存使用,如果忽高忽低,极有可能query buffer引起
  • 核心Redis集群定期收集client list并分析qbuf的使用量
  • 建议官方提供query buffer size的设置参数,以保证过载保护

Output Buffer

客户端输出缓存区:执行命令所返回的结果会保存到output buffer,返回给客户端。每个客户端都有2query buffer

  • 静态定长16KB的缓存区;主要快速存储返回比较小的结果;如简单的get
  • 动态大小缓冲区;存储返回较大的结果,如大的集合类型:set/list/hash 因为静态的buffer,一般无性能和风险影响,这里简单介绍。
#define PROTO_REPLY_CHUNK_BYTES (16*1024) /* 16k output buffer */
/* With multiplexing we need to take per-client state.
 * Clients are taken in a linked list. */
typedef struct client {
    uint64_t id;            /* Client incremental unique ID. */
    redisDb *db;            /* Pointer to currently SELECTed DB. */
    robj *name;             /* As set by CLIENT SETNAME. */
    sds querybuf;           /* Buffer we use to accumulate client queries. */
    list *reply;            /* List of reply objects to send to the client. */
    /* Response buffer */
    int bufpos;
    char buf[PROTO_REPLY_CHUNK_BYTES];
} client;

我们常说的output buffer都是指“动态大小的输出缓冲区”。和qeury buffer不同,output buffer提供配置参数”client-output-buffer-limit”设置buffer的使用大小。下面是limit的设置格式:

client-output-buffer-limit normal 10mb 5mb 60
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

redis3种不同客户端类型,可设置对应的buffer limit规则

  • normal:普通的客户端
  • slave:从库复制,连接到主库的客户端
  • pubsub:发布/订阅客户端

设置的limit规则3个值: hard limit size, soft limit size, soft limit second;只要客户端使用output buffer内存大小超过hard limit限制,redis会立即关闭此客户端;使用buffer内存大小超过soft limit,并且持续soft limit秒数,redis也会立即关闭此客户端。被关闭客户端信息会打印到redis日志文件中,格式如下:

569:M 18 Jun 21:12:57.775 # Client id=972 addr=127.0.0.1:57934 fd=107 name= age=2 idle=0
flags=O db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=366 omem=10492208 events=rw cmd=monitor
scheduled to be closed ASAP for overcoming of output buffer limits.

要查看output buffer使用,主要查开client listobl(静态定长buffer)omem:当前客户端使用output buffer的内存字节数,如下客户端执行monitor命令(cmd=monitor),已使用buffer内存是10492208,超过normalhard limit 10mb,所以被redis关闭。

id=972 addr=127.0.0.1:57934 idle=0 flags=O db=0 qbuf=0 qbuf-free=0 obl=0 oll=366 omem=10492208 events=rw cmd=monitor

另外output buffermaxmemory的限制,基本不会超过maxmemory设置值。因为output buffer是每个客户端都有,如使用不当,每个占用1mb * 10000 clients就约使用10G内存; 所以要有效限制程序滥用。

  • 对于normal限制尽量小,可避免程序过度使用output buffer.
  • 监控redis used_memory如果抖动严重,极有可能
  • 增加slavelimit限制,避免slave同步线程被杀,导致无限循环同步数据;且slave线程和挂载的slave个数相同,理论只有几个
  • 禁止生产环境使用monitor命令,在高QPS环境下,monitor很快会产生output query使用
上一页
下一页