Redis是用单线程来处理多个客户端的访问,因此作为Redis的开发和运 维人员需要了解Redis服务端和客户端的通信协议,以及主流编程语言的 Redis客户端使用方法,同时还需要了解客户端管理的相应API以及开发运维中可能遇到的问题.

一.Java客户端Jedis

Java有很多优秀的Redis客户端(详见:http://redis.io/clients#java),这 里介绍使用较为广泛的客户端Jedis

1 获取Jedis
Jedis属于Java的第三方开发包,在Java中获取第三方开发包通常有两种方式:
·直接下载目标版本的Jedis-${version}.jar包加入到项目中。
·使用集成构建工具,例如maven、gradle等将Jedis目标版本的配置加入到项目中。
通常在实际项目中使用第二种方式,但如果只是想测试一下Jedis,第一种方法也是可以的。以 Maven为例子,在项目中加入下面的依赖即可:

<dependency>    
    <groupId>redis.clients</groupId>    
    <artifactId>jedis</artifactId>    
    <version>2.8.2</version> 
</dependency>

2 Jedis的基本使用方法
Jedis的使用方法非常简单,只要下面三行代码就可以实现get功能

# 1. 生成一个Jedis对象,这个对象负责和指定Redis实例进行通信 
Jedis jedis = new Jedis("127.0.0.1", 6379); 
# 2. jedis执行set操作 
jedis.set("hello", "world"); 
# 3. jedis执行get操作, value="world" 
String value = jedis.get("hello");

可以看到初始化Jedis需要两个参数:Redis实例的IP和端口,除了这两个参数外,还有一个包含了四个参数的构造函数是比较常用的:

Jedis(final String host, final int port, final int connectionTimeout, final int     soTimeout)

参数说明:
·host:Redis实例的所在机器的IP。
·port:Redis实例的端口。
·connectionTimeout:客户端连接超时。
·soTimeout:客户端读写超时。

如果想看一下执行结果:

String setResult = jedis.set("hello", "world"); 
String getResult = jedis.get("hello"); 
System.out.println(setResult); 
System.out.println(getResult);

输出结果为:OK world
可以看到jedis.set的返回结果是OK,和redis-cli的执行效果是一样的, 只不过结果类型变为了Java的数据类型。上面的这种写法只是为了演示使 用,在实际项目中比较推荐使用try catch finally的形式来进行代码的书写

下面用一个例子说明Jedis对于Redis五种数据结构的操作,为了节省篇幅,所有返回结果放在注释中。

// 1.string 
// 输出结果:OK 
jedis.set("hello", "world"); 
// 输出结果:world jedis.get("hello"); 
// 输出结果:1 
jedis.incr("counter"); 
// 2.hash 
jedis.hset("myhash", "f1", "v1"); 
jedis.hset("myhash", "f2", "v2"); 
// 输出结果:{f1=v1, f2=v2} 
jedis.hgetAll("myhash"); 
// 3.list 
jedis.rpush("mylist", "1"); 
jedis.rpush("mylist", "2"); 
jedis.rpush("mylist", "3");
// 输出结果:[1, 2, 3] 
jedis.lrange("mylist", 0, -1); 
// 4.set 
jedis.sadd("myset", "a"); 
jedis.sadd("myset", "b"); 
jedis.sadd("myset", "a"); 
// 输出结果:[b, a] 
jedis.smembers("myset"); 
// 5.zset 
jedis.zadd("myzset", 99, "tom"); 
jedis.zadd("myzset", 66, "peter"); 
jedis.zadd("myzset", 33, "james"); 
// 输出结果:[[["james"],33.0], [["peter"],66.0], [["tom"],99.0]] jedis.zrangeWithScores("myzset", 0, -1);

参数除了可以是字符串,Jedis还提供了字节数组的参数,例如:

public String set(final String key, String value) 
public String set(final byte[] key, final byte[] value) 
public byte[] get(final byte[] key) 
public String get(final String key)

有了这些API的支持,就可以将Java对象序列化为二进制,当应用需要 获取Java对象时,使用get(final byte[]key)函数将字节数组取出,然后反序 列化为Java对象即可。和很多NoSQL数据库(例如Memcache、Ehcache)的 客户端不同,Jedis本身没有提供序列化的工具,也就是说开发者需要自己 引入序列化的工具。序列化的工具有很多,例如XML、Json、谷歌的 Protobuf、Facebook的Thrift等等,对于序列化工具的选择开发者可以根据自身需求决定.

3 Jedis连接池的使用方法
上面介绍的是Jedis的直连方式,所谓直连是指Jedis每次都会新建TCP 连接,使用后再断开连接,对于频繁访问Redis的场景显然不是高效的使用方式,因此生产环境中一般使用连接池的方式对Jedis连接进行管理,所有Jedis对象预先放在池子中(JedisPool),每次要连接Redis,只需要在池子中借,用完了在归还给池子。
客户端连接Redis使用的是TCP协议,直连的方式每次需要建立TCP连 接,而连接池的方式是可以预先初始化好Jedis连接,所以每次只需要从Jedis连接池借用即可,而借用和归还操作是在本地进行的,只有少量的并 发同步开销,远远小于新建TCP连接的开销。另外直连的方式无法限制Jedis对象的个数,在极端情况下可能会造成连接泄露,而连接池的形式可以有效 的保护和控制资源的使用。但是直连的方式也并不是一无是处,下表给出两种方式各自的优劣势。

Jedis直连方式和连接池方式对比

 

优点

缺点

直连

简单方便,适用于少量长期连接的场景

1)存在每次新建/关闭TCP连接的开销

2)资源无法控制,极端情况下会出现连接泄露

3)Jedis对象线程不安全

连接池

1)无需每次连接都生成Jedis对象,降低开销

2)使用连接池的形式保护和控制资源的使用

相对于直连,使用相对麻烦,尤其在资源的管理上需要很多参数来保证,规划不合理也会出现问题

Jedis提供了JedisPool这个类作为对Jedis的连接池,同时使用了Apache的 通用对象池工具common-pool作为资源的管理工具,下面是使用JedisPool操 作Redis的代码示例:
1)Jedis连接池(通常JedisPool是单例的):

// common-pool连接池配置,这里使用默认配置,后面介绍具体配置说明 
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); 
// 初始化Jedis连接池 
JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);

2)获取Jedis对象不再是直接生成一个Jedis对象进行直连,而是从连接池直接获取,代码如下:

Jedis jedis = null; 
try {    
    // 1. 从连接池获取jedis对象    
    jedis = jedisPool.getResource();    
    // 2. 执行操作    
    jedis.get("hello"); 
} catch (Exception e) {    
        logger.error(e.getMessage(),e); 
} finally {    
    if (jedis != null) {        
    // 如果使用JedisPool,close操作不是关闭连接,代表归还连接池        
    jedis.close();    
    } 
}

这里可以看到在finally中依然是jedis.close()操作,为什么会把连接关闭呢,这不和连接池的原则违背了吗?但实际上Jedis的close()实现方式如下:

public void close() {    
    // 使用Jedis连接池    
    if (dataSource != null) {        
        if (client.isBroken()) {            
            this.dataSource.returnBrokenResource(this);        
        } else {            
            this.dataSource.returnResource(this);        
        }    
        // 直连    
    } else { 
         client.close();    
    } 
}

·dataSource!=null代表使用的是连接池,所以jedis.close()代表归还 连接给连接池,而且Jedis会判断当前连接是否已经断开。
·dataSource=null代表直连,jedis.close()代表关闭连接。

前面GenericObjectPoolConfig使用的是默认配置,实际它提供有很多参数,例如池子中最大连接数、最大空闲连接数、最小空闲连接数、连接活性检测,等等,例如下面代码:

GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); 
// 设置最大连接数为默认值的5倍
poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 5); 
// 设置最大空闲连接数为默认值的3倍
poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 3); 
// 设置最小空闲连接数为默认值的2倍
poolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MIN_IDLE * 2); 
// 设置开启jmx功能
poolConfig.setJmxEnabled(true); 
// 设置连接池没有连接后客户端的最大等待时间(单位为毫秒)
poolConfig.setMaxWaitMillis(3000);

上面几个是GenericObjectPoolConfig几个比较常用的属性,下表给出了 Generic-ObjectPoolConfig其他属性及其含义解释。

GenericObjectPoolConfig的重要属性

参数值

含义

默认值

maxActive

连接池中最大连接数

8

maxIdle

连接池中最大空闲的连接数

8

minIdle

连接池中最小空闲的连接数

0

maxWaitMillis

当连接池资源用尽后调用者的最大等待时间(单位毫秒值),一般不建议使用默认值

-1:表示永远不超时,一直等

jmxEnabled

是否开启jmx监控,如果应用开启了jmx端口并且jmxEnabled设置为true,就可以通过jconsole或者jvisualvm看到关于连接池的相关统计,有助于了解连接池的使用情况,并且可以针对其做监控统计

true

minEvictableIdleTimeMillis

连接的最小空闲时间,达到此值后空闲连接将被移除

1000Lx60Lx30毫秒=30分钟

numTestsPerEvictionRun

做空闲连接检测时每次的采样数

3

testOnBorrow

向连接池借用连接时是否做连接有效性检测(ping).无效连接会被移除,每次借用多执行一次ping命令

false

testOnReturn

向连接池归还连接时是否做连接有效性检测(ping).无效连接会被移除,每次借用多执行一次ping命令

false

testWhileIdle

向连接池借用连接时是否做连接空闲检测,空闲超时的连接会被移除

false

timeBetweenEvictionRunsMillis

空闲连接的检测周期(单位为毫秒)

-1:表示不做检测

blockWhenExhausted

当连接池用尽时调用者是否要等待,这个参数是和maxWaitMillis对应的,只有当次参数为true时,maxWaitMillis才会生效

true

4 Redis中Pipeline的使用方法
Jedis支持Pipeline特性,我们知道 Redis提供了mget、mset方法,但是并没有提供mdel方法,如果想实现这个功 能,可以借助Pipeline来模拟批量删除,虽然不会像mget和mset那样是一个原 子命令,但是在绝大数场景下可以使用。下面代码是mdel删除的实现过程。这里为了节省篇幅,没有写try catch finally,没有关闭jedis

public void mdel(List<String> keys) {    
    Jedis jedis = new Jedis("127.0.0.1");    
    // 1)生成pipeline对象    
    Pipeline pipeline = jedis.pipelined();    
    // 2)pipeline执行命令,注意此时命令并未真正执行    
    for (String key : keys) {        
        pipeline.del(key);    
    }    
    // 3)执行命令    
    pipeline.sync(); 
}

·利用jedis对象生成一个pipeline对象,直接可以调用 jedis.pipelined()。
·将del命令封装到pipeline中,可以调用pipeline.del(String key),这个 方法和jedis.del(String key)的写法是完全一致的,只不过此时不会真正的执行命令。
·使用pipeline.sync()完成此次pipeline对象的调用。

除了pipeline.sync(),还可以使用pipeline.syncAndReturnAll()将 pipeline的命令进行返回,例如下面代码将set和incr做了一次pipeline操作,并顺序打印了两个命令的结果:

Jedis jedis = new Jedis("127.0.0.1"); 
Pipeline pipeline = jedis.pipelined(); 
pipeline.set("hello", "world"); 
pipeline.incr("counter"); 
List<Object> resultList = pipeline.syncAndReturnAll(); 
for (Object object : resultList) {    
    System.out.println(object); 
}

输出结果为OK,1

5 Jedis的Lua脚本

Jedis中执行Lua脚本和redis-cli十分类似,Jedis提供了三个重要的函数实 现Lua脚本的执行:

Object eval(String script, int keyCount, String... params) 
Object evalsha(String sha1, int keyCount, String... params) 
String scriptLoad(String script)

eval函数有三个参数,分别是:
·script:Lua脚本内容。
·keyCount:键的个数。
·params:相关参数KEYS和ARGV。

以一个最简单的Lua脚本为例子进行说明:

return redis.call('get',KEYS[1])

在redis-cli中执行上面的Lua脚本,方法如下:

127.0.0.1:6379> eval "return redis.call('get',KEYS[1])" 1 hello 
"world"

在Jedis中执行,方法如下:

String key = "hello"; 
String script = "return redis.call('get',KEYS[1])"; 
Object result = jedis.eval(script, 1, key); 
// 打印结果为world 
System.out.println(result)

scriptLoad和evalsha函数要一起使用,首先使用scriptLoad将脚本加载到 Redis中,代码如下:

String scriptSha = jedis.scriptLoad(script);

evalsha函数用来执行脚本的SHA1校验和,它需要三个参数:
·scriptSha:脚本的SHA1。
·keyCount:键的个数。
·params:相关参数KEYS和ARGV。
执行效果如下:

Stirng key = "hello"; 
Object result = jedis.evalsha(scriptSha, 1, key); 
// 打印结果为world 
System.out.println(result);

总体来说,Jedis的使用还是比较简单的,重点注意以下几点即可:
1)Jedis操作放在try catch finally里更加合理。
2)区分直连和连接池两种实现方式优缺点。
3)jedis.close()方法的两种实现方式。
4)Jedis依赖了common-pool,有关common-pool的参数需要根据不同的使用场景,各不相同,需要具体问题具体分析。
5)如果key和value涉及了字节数组,需要自己选择适合的序列化方法。

二.客户端管理

Redis提供了客户端相关API对其状态进行监控和管理,下面介绍各个API的使用方法.
1.client list

client list命令能列出与Redis服务端相连的所有客户端连接信息,例如下 面代码是在一个Redis实例上执行client list的结果:

127.0.0.1:6379> client list
id=307 addr=127.0.0.1:55112 fd=6 name= age=16 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

(1)标识:id、addr、fd、name
这四个属性属于客户端的标识:
·id:客户端连接的唯一标识,这个id是随着Redis的连接自增的,重启 Redis后会重置为0。
·addr:客户端连接的ip和端口。
·fd:socket的文件描述符,与lsof命令结果中的fd是同一个,如果fd=-1 代表当前客户端不是外部客户端,而是Redis内部的伪装客户端。
·name:客户端的名字,后面的client setName和client getName两个命令会对其进行说明。
(2)输入缓冲区:qbuf、qbuf-free
Redis为每个客户端分配了输入缓冲区,它的作用是将客户端发送的命 令临时保存,同时Redis从会输入缓冲区拉取命令并执行,输入缓冲区为客 户端发送命令到Redis执行命令提供了缓冲功能.

client list中qbuf和qbuf-free分别代表这个缓冲区的总容量和剩余容量, Redis没有提供相应的配置来规定每个缓冲区的大小,输入缓冲区会根据输入内容大小的不同动态调整,只是要求每个客户端缓冲区的大小不能超过 1G,超过后客户端将被关闭。

输入缓冲使用不当会产生两个问题:
·一旦某个客户端的输入缓冲区超过1G,客户端将会被关闭。
·输入缓冲区不受maxmemory控制,假设一个Redis实例设置了 maxmemory为4G,已经存储了2G数据,但是如果此时输入缓冲区使用了 3G,已经超过maxmemory限制,可能会产生数据丢失、键值淘汰、OOM等情况。
上面已经看到,输入缓冲区使用不当造成的危害非常大,那么造成输入缓冲区过大的原因有哪些?输入缓冲区过大主要是因为Redis的处理速度跟不上输入缓冲区的输入速度,并且每次进入输入缓冲区的命令包含了大量 bigkey,从而造成了输入缓冲区过大的情况。还有一种情况就是Redis发生了阻塞,短期内不能处理命令,造成客户端输入的命令积压在了输入缓冲区,造成了输入缓冲区过大。
那么如何快速发现和监控呢?监控输入缓冲区异常的方法有两种:
·通过定期执行client list命令,收集qbuf和qbuf-free找到异常的连接记录并分析,最终找到可能出问题的客户端。
·通过info命令的info clients模块,找到最大的输入缓冲区,例如下面命 令中的其中client_biggest_input_buf代表最大的输入缓冲区,例如可以设置超 过10M就进行报警:
(3)输出缓冲区:obl、oll、omem
Redis为每个客户端分配了输出缓冲区,它的作用是保存命令执行的结果返回给客户端,为Redis和客户端交互返回结果提供缓冲,与输入缓冲区不同的是,输出缓冲区的容量可以通过参数client-outputbuffer-limit来进行设置,并且输出缓冲区做得更加细致,按照客户端的不同分为三种:普通客户端、发布订阅客户端、slave客户端.

对应的配置规则是:

client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>

·<class>:客户端类型,分为三种。a)normal:普通客户端;b) slave:slave客户端,用于复制;c)pubsub:发布订阅客户端。
·<hard limit>:如果客户端使用的输出缓冲区大于<hard limit>,客户端会被立即关闭。
·<soft limit>和<soft seconds>:如果客户端使用的输出缓冲区超过了<soft limit>并且持续了<soft limit>秒,客户端会被立即关闭。
Redis的默认配置是:

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

和输入缓冲区相同的是,输出缓冲区也不会受到maxmemory的限制,如 果使用不当同样会造成maxmemory用满产生的数据丢失、键值淘汰、OOM等情况。
实际上输出缓冲区由两部分组成:固定缓冲区(16KB)和动态缓冲区,其中固定缓冲区返回比较小的执行结果,而动态缓冲区返回比较大的结 果,例如大的字符串、hgetall、smembers命令的结果等,固定缓冲区使用的是字节数组,动态缓冲区使用的是列表。当固定缓冲 区存满后会将Redis新的返回结果存放在动态缓冲区的队列中,队列中的每 个对象就是每个返回结果

client list中的obl代表固定缓冲区的长度,oll代表动态缓冲区列表的长 度,omem代表使用的字节数。例如下面代表当前客户端的固定缓冲区的长 度为0,动态缓冲区有4869个对象,两个部分共使用了133081288字节=126M内存:

id=7 addr=127.0.0.1:56358 fd=6 name= age=91 idle=0 flags=O db=0 sub=0 psub=0 multi=-1     qbuf=0 qbuf-free=0 obl=0 oll=4869 omem=133081288 events=rw cmd=monitor

监控输出缓冲区的方法依然有两种:
·通过定期执行client list命令,收集obl、oll、omem找到异常的连接记录并分析,最终找到可能出问题的客户端。
·通过info命令的info clients模块,找到输出缓冲区列表最大对象数,例如:

127.0.0.1:6379> info clients 
# Clients 
connected_clients:502  
client_longest_output_list:4869 
client_biggest_input_buf:0  
blocked_clients:0

其中,client_longest_output_list代表输出缓冲区列表最大对象数,这两种统计方法的优劣势和输入缓冲区是一样的,这里就不再赘述了。相比于输入缓冲区,输出缓冲区出现异常的概率相对会比较大,那么如何预防呢?方法如下:
·进行上述监控,设置阀值,超过阀值及时处理。
·限制普通客户端输出缓冲区的,把错误扼杀在摇篮中,例如可以进行如下设置:

client-output-buffer-limit normal 20mb 10mb 120

·适当增大slave的输出缓冲区的,如果master节点写入较大,slave客户 端的输出缓冲区可能会比较大,一旦slave客户端连接因为输出缓冲区溢出 被kill,会造成复制重连。
·限制容易让输出缓冲区增大的命令,例如,高并发下的monitor命令就是一个危险的命令。
·及时监控内存,一旦发现内存抖动频繁,可能就是输出缓冲区过大。

(4)客户端的存活状态
client list中的age和idle分别代表当前客户端已经连接的时间和最近一次的空闲时间:

id=2232080 addr=10.16.xx.55:32886 fd=946 name= age=603382 idle=331060 flags=N db=0     sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=get

例如上面这条记录代表当期客户端连接Redis的时间为603382秒,其中 空闲了331060秒:

(5)客户端的限制maxclients和timeout
Redis提供了maxclients参数来限制最大客户端连接数,一旦连接数超过 maxclients,新的连接将被拒绝。maxclients默认值是10000,可以通过info clients来查询当前Redis的连接数:

127.0.0.1:6379> info clients 
# Clients 
connected_clients:1414 ...

可以通过config set maxclients对最大客户端连接数进行动态设置:

127.0.0.1:6379> config get maxclients 
1) "maxclients" 
2) "10000" 
127.0.0.1:6379> config set maxclients 50 
OK 
127.0.0.1:6379> config get maxclients 
1) "maxclients" 
2) "50"

一般来说maxclients=10000在大部分场景下已经绝对够用,但是某些情 况由于业务方使用不当(例如没有主动关闭连接)可能存在大量idle连接, 无论是从网络连接的成本还是超过maxclients的后果来说都不是什么好事, 因此Redis提供了timeout(单位为秒)参数来限制连接的最大空闲时间,一 旦客户端连接的idle时间超过了timeout,连接将会被关闭.在实际开发和运维中,需要将timeout设置成大于0,例如 可以设置为300秒,同时在客户端使用上添加空闲检测和验证等等措施,例 如JedisPool使用common-pool提供的三个属性:minEvictableIdleTimeMillis、 testWhileIdle、timeBetweenEvictionRunsMillis

(6)客户端类型

client list中的flag是用于标识当前客户端的类型,例如flag=S代表当前客 户端是slave客户端、flag=N代表当前是普通客户端,flag=O代表当前客户端 正在执行monitor命令,下表列出了11种客户端类型。

RediSearch开发文档_Redis

2.client setName和client getName

client setName用于给客户端设置名字,这样比较容易标识出客户端的来 源,例如将当前客户端命名为test_client,可以执行如下操作:

127.0.0.1:6379> client setName test_client 
OK

此时再执行client list命令,就可以看到当前客户端的name属性为 test_client:

127.0.0.1:6379> client list id=55 addr=127.0.0.1:55604 fd=7 name=test_client age=23 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

如果想直接查看当前客户端的name,可以使用client getName命令,例如下面的操作:

127.0.0.1:6379> client getName 
"test_client"

client getName和setName命令可以做为标识客户端来源的一种方式,但 是通常来讲,在Redis只有一个应用方使用的情况下,IP和端口作为标识会 更加清晰。当多个应用方共同使用一个Redis,那么此时client setName可以作为标识客户端的一个依据。

3.client kill(client kill ip:port)

此命令用于杀掉指定IP地址和端口的客户端

4.client pause
client pause命令用于阻塞客户端timeout毫秒数,在此期间客户端连接将被阻塞。
例如在一个客户端执行:

127.0.0.1:6379> client pause 10000 
OK

在另一个客户端执行ping命令,发现整个ping命令执行了9.72秒(手动 执行redis-cli,只为了演示,不代表真实执行时间):

127.0.0.1:6379> ping 
PONG 
(9.72s)

该命令可以在如下场景起到作用:
·client pause只对普通和发布订阅客户端有效,对于主从复制(从节点内部伪装了一个客户端)是无效的,也就是此期间主从复制是正常进行的,所以此命令可以用来让主从复制保持一致。
·client pause可以用一种可控的方式将客户端连接从一个Redis节点切换 到另一个Redis节点。
需要注意的是在生产环境中,暂停客户端成本非常高。

5.monitor
monitor命令用于监控Redis正在执行的命令,如图4-11所示,我们打开 了两个redis-cli,一个执行set get ping命令,另一个执行monitor命令。可以看 到monitor命令能够监听其他客户端正在执行的命令,并记录了详细的时间戳。

RediSearch开发文档_Jedis_02

每个客户端都有自己的 输出缓冲区,既然monitor能监听到所有的命令,一旦Redis的并发量过大, monitor客户端的输出缓冲会暴涨,可能瞬间会占用大量内存

其他配置:

·timeout:检测客户端空闲连接的超时时间,一旦idle时间达到了 timeout,客户端将会被关闭,如果设置为0就不进行检测。
·maxclients:客户端最大连接数这个参数会受到操作系统设置的限制,第12 章Linux相关配置小节还会对这个参数进行介绍。
·tcp-keepalive:检测TCP连接活性的周期,默认值为0,也就是不进行 检测,如果需要设置,建议为60,那么Redis会每隔60秒对它创建的TCP连接进行活性检测,防止大量死连接占用系统资源。
·tcp-backlog:TCP三次握手后,会将接受的连接放入队列中,tcpbacklog就是队列的大小,它在Redis中的默认值是511。通常来讲这个参数不 需要调整,但是这个参数会受到操作系统的影响

三.客户端统计片段

例如下面就是一次info clients的执行结果:

127.0.0.1:6379> info clients 
# Clients 
connected_clients:1414 
client_longest_output_list:0 
client_biggest_input_buf:2097152 
blocked_clients:0

说明如下:
1)connected_clients:代表当前Redis节点的客户端连接数,需要重点监 控,一旦超过maxclients,新的客户端连接将被拒绝。
2)client_longest_output_list:当前所有输出缓冲区中队列对象个数的最大值。
3)client_biggest_input_buf:当前所有输入缓冲区中占用的最大容量。
4)blocked_clients:正在执行阻塞命令(例如blpop、brpop、 brpoplpush)的客户端个数。
除此之外info stats中还包含了两个客户端相关的统计指标,如下:
127.0.0.1:6379> info stats

# Stats 
total_connections_received:80 ... 
rejected_connections:0

·total_connections_received:Redis自启动以来处理的客户端连接数总数。
·rejected_connections:Redis自启动以来拒绝的客户端连接数,需要重点监控。

小结:

1)RESP(Redis Serialization Protocol Redis)保证客户端与服务端的正常通信,是各种编程语言开发客户端的基础。
2)要选择社区活跃客户端,在实际项目中使用稳定版本的客户端。
3)区分Jedis直连和连接池的区别,在生产环境中,应该使用连接池。
4)Jedis.close()在直连下是关闭连接,在连接池则是归还连接。
5)Jedis客户端没有内置序列化,需要自己选用。
6)客户端输入缓冲区不能配置,强制限制在1G之内,但是不会受到 maxmemory限制。
7)客户端输出缓冲区支持普通客户端、发布订阅客户端、复制客户端 配置,同样会受到maxmemory限制。
8)Redis的timeout配置可以自动关闭闲置客户端,tcp-keepalive参数可以周期性检查关闭无效TCP连接
9)monitor命令虽然好用,但是在大并发下存在输出缓冲区暴涨的可能性。
10)info clients帮助开发和运维人员找到客户端可能存在的问题。