文章目录

  • 一、Redis中的辅助功能
    • 1.1 慢查询
      • 1.1.1 慢查询相关的两个参数
      • 1.1.2 慢查询使用建议
    • 1.2 Redis Shell
      • 1.2.1 redis-cli
      • 1.2.2 redis-benchmark
    • 1.3 Pipeline
    • 1.4 事务
      • 1.4.1 事务命令
      • 1.4.2 Redis事务保证原子性吗,支持回滚吗
    • 1.5 Lua
      • 1.5.1 使用Lua脚本
      • 1.5.2 管理Lua脚本
  • 二、Redis客户端
    • 2.1 Jedis的使用
      • 2.1.1 Jedis的简单使用
      • 2.1.2 Jedis常用API
      • 2.1.3 Jedis连接池
    • 2.2 客户端管理
      • 2.2.1 client list
      • 2.2.2 client setName和client getName
      • 2.2.3 client kill
      • 2.2.4 client pause
      • 2.2.5 monitor
      • 2.2.6 config set
      • 2.2.7 info stats
    • 2.3 常见异常
      • 2.3.1 无法从连接池获取到连接
      • 2.3.2 客户端读写超时
      • 2.3.3 客户端连接超时
      • 2.3.4 客户端缓冲区异常
      • 2.3.5 JedisDataException
      • 2.3.6 Redis使用的内存超过maxmemory配置
      • 2.3.7 客户端连接数过大
    • 2.4 Redis客户端的一些问题
      • 2.4.1 Redis和Redisson有什么关系?
      • 2.4.2 Jedis和Redisson对比有什么优缺点?

本系列文章:
  Redis(一)数据类型、常用命令
  Redis(二)Redis客户端的使用
  Redis(三)持久化、主从复制
  Redis(四)Redis内存
  Redis(五)哨兵、集群
  Redis(六)缓存、分布式锁
  Redis(七)Redis优化建议

一、Redis中的辅助功能

1.1 慢查询

  Redis客户端执行一条命令分为如下4个部分:

  慢查询只统计命令执行的时间。

1.1.1 慢查询相关的两个参数

  • slowlog-log-slower-than
      slowlog-log-slower-than是预设阀值,单位是微秒,默认值是10000,假如执行了一条“很慢”的命令(例如keys*),如果它的执行时间超过了10000微秒,那么它将被记录在慢查询日志中。

如果slowlog-log-slower-than=0会记录所有的命令,slowlog-log-slower-than<0对于任何命令都不会进行记录。

  • slowlog-max-len
      Redis使用了一个列表来存储慢查询日志,slowlog-max-len就是列表的最大长度。一个新的命令满足慢查询条件时被插入到这个列表中,当慢查询日志列表已处于其最大长度时,最早插入的一个命令将从列表中移出,例如slowlog-max-len设置为5,当有第6条慢查询插入的话,那么队头的第一条数据就出列,第6条慢查询就会入列。

  Redis中有两种修改配置的方法,一种是修改配置文件,另一种是用config set命令动态修改,示例:

 config set slowlog-log-slower-than 20000config set slowlog-max-len 1000config rewrite

  从上面可以看出慢查询日志是存放在Redis内存列表中的,但是Redis并没有暴露这个列表的键,而是通过一组命令来实现对慢查询日志的访问和管理。慢查询相关命令:

  • 1、获取慢查询日志
 slowlog get [n]

  每个慢查询日志有4个属性组成,分别是慢查询日志的标识id、发生时间戳、命令耗时、执行命令和参数。

  • 2、获取慢查询日志列表当前的长度
 slowlog len
  • 3、清空慢查询日志列表
 slowlog reset

1.1.2 慢查询使用建议

  slowlog-max-len配置建议:线上建议调大慢查询列表,记录慢查询时Redis会对长命令做截断操作,并不会占用大量内存。增大慢查询列表可以减缓慢查询被剔除的可能,例如线上可设置为1000以上。
  slowlog-log-slower-than配置建议:默认值超过10毫秒判定为慢查询,需要根据Redis并发量调整该值。由于Redis采用单线程响应命令,对于高流量的场景,如果命令执行时间在1毫秒以上,那么Redis最多可支撑OPS不到1000。因此对于高OPS场景的Redis建议设置为1毫秒。
  由于慢查询日志是一个先进先出的队列,也就是说如果慢查询比较多的情况下,可能会丢失部分慢查询命令,为了防止这种情况发生,可以定期执行slow get命令将慢查询日志持久化到其他存储中。

1.2 Redis Shell

1.2.1 redis-cli

  可以执行redis-cli-help命令来查看该命令的全部参数。

  • 1、-r
      -r(repeat)选项代表将命令执行多次,示例:
 redis-cli -r 3 ping
  • 2、-i
      -i(interval)选项代表每隔几秒执行一次命令,但是-i选项必须和-r选项一起使用。每隔1秒执行一次ping命令,一共执行5次示例:
 redis-cli -r 5 -i 1 ping
  • 3、-x
      -x选项代表从标准输入(stdin)读取数据作为redis-cli的最后一个参数,将字符串world作为set hello的值示例:
 echo "world" | redis-cli -x set hello
  • 4、-a
      如果Redis配置了密码,可以用-a(auth)选项,有了这个选项就不需要手动输入auth命令。
  • 5、–slave
      --slave选项是把当前客户端模拟成当前Redis节点的从节点,可以用来获取当前Redis节点的更新操作。
  • 6、–rdb
      --rdb选项会请求Redis实例生成并发送RDB持久化文件,保存在本地。可用来做持久化文件的定期备份。
  • 7、–eval
      用于执行指定Lua脚本。
  • 8、–latency
      latency有三个选项,分别是–latency、–latency-history、–latency-dist,它们都可以检测网络延迟。
      --latency可以测试不同客户端到目标Redis示例的网络延迟。比如客户端B和Redis在机房B,客户端A在机房A,则机房A和机房B是跨地区的:
      --latency的执行结果只有一条,如果想以分时段的形式了解延迟信息,可以使用--latency-history选项,示例:
 redis-cli -h 10.10.xx.xx --latency-historymin: 0, max: 1, avg: 0.28 (1330 samples) -- 15.01 seconds range …min: 0, max: 1, avg: 0.05 (1364 samples) -- 15.01 seconds range

  延时信息每15秒输出一次,可以通过-i参数控制间隔时间。
  --latency-dist会使用统计图表的形式从控制台输出延迟统计信息。

  • 9、–stat
      --stat选项可以实时获取Redis的重要统计信息,示例:

1.2.2 redis-benchmark

  redis-benchmark可以为Redis做基准性能测试。

  • 1、-c
      -c(clients)表示客户端的并发数量(默认是50)。
  • 2、-n
      -n(num)表示客户端请求总量(默认是100000)。
  • 3、-q
      -q仅仅显示redis-benchmark的吞吐量信息,示例:
 redis-benchmark -c 100 -n 20000 -qPING_INLINE: 74349.45 requests per secondPING_BULK: 68728.52 requests per secondSET: 71174.38 requests per second …LRANGE_500 (first 450 elements): 11299.44 requests per secondLRANGE_600 (first 600 elements): 9319.67 requests per secondMSET (10 keys): 70671.38 requests per second
  • 4、-r
      -r(random)可以向Redis插入更多随机的键。示例:
 redis-benchmark -c 100 -n 20000 -r 10000

  -r选项会在key、counter键上加一个12位的后缀,-r10000代表只对后四位做随机处理。

  • 5、-P
      -P表示每个请求pipeline的数据量(默认为1)。
  • 6、-k
      -k表示客户端是否使用keepalive,1为使用,0为不使用,默认值为1。
  • 7、-t
      -t可以对指定命令进行基准测试。示例:
 redis-benchmark -t get,set -qSET: 98619.32 requests per secondGET: 97560.98 requests per second

1.3 Pipeline

  Redis客户端执行一条命令分为如下四个过程:发送命令、命令排队、命令执行、返回结果。这四步合起来称为Round Trip Time(RTT,往返时间)。
  Redis提供了批量操作命令(例如mget、mset等),有效地节约RTT。但大部分命令是不支持批量操作的,例如要执行n次hgetall命令,并没有mhgetall命令存在,需要消耗n次RTT。
  Pipeline(流水线)机制能改善上面这类问题,它能将一组Redis命令进行组装,通过一次RTT传输给Redis,再将这组Redis命令的执行结果按顺序返回给客户端。
  Redis命令真正执行的时间通常在微秒级别,所以才会有Redis性能瓶颈是网络这样的说法
  redis-cli的–pipe选项实际上就是使用Pipeline机制。
  非Pipeline和Pipeline执行命令的区别:

  1. Pipeline执行速度一般比逐条执行要快。
  2. 客户端和服务端的网络延时越大,Pipeline的效果越明显。

  原生批量命令与Pipeline的区别:

  1. 原生批量命令是原子的,Pipeline是非原子的。
  2. 原生批量命令是一个命令对应多个key,Pipeline支持多个命令。
  3. 原生批量命令是Redis服务端支持实现的,而Pipeline需要服务端和客户端的共同实现。

  Pipeline虽然好用,但是每次Pipeline组装的命令个数不能没有节制,否则一次组装Pipeline数据量过大,一方面会增加客户端的等待时间,另一方面会造成一定的网络阻塞,可以将一次包含大量命令的Pipeline拆分成多次较小的Pipeline来完成。

1.4 事务

  Redis 事务的本质是通过MULTI、EXEC、WATCH等一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
  总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令
  在 Redis 中,事务具有一致性和隔离性,并且当 Redis 运行在某种特定的持久化模式下时,事务也具有持久性。但是,Redis中的事务不具有一致性,可能部分命令会执行成功,而另一部分命令执行失败。

  Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  1. 批量操作在发送 EXEC 命令前被放入队列缓存。
  2. 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  3. 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

  一个事务从开始到执行会经历以下三个阶段:开始事务、命令入队、执行事务。

  事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

1.4.1 事务命令

  Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH实现的。事务功能有以下特点:

  1. Redis会将一个事务中的所有命令序列化,然后按顺序执行。
  2. redis 不支持回滚,“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。
  3. 如果在一个事务中的命令出现错误,那么所有的命令都不会执行
  4. 如果在一个事务中出现运行错误,那么正确的命令会被执行
  • 1、Multi
      Multi用于开启一个事务。 Multi执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被按照先后顺序放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
  • 2、Exec
      Exec命令用于执行所有事务块内的命令。这些命令按先后顺序排列。 当操作被打断时,返回空值 nil 。
      前两个命令的使用示例:
  • 3、Discard
      discard 命令用于取消事务(清空事务队列),放弃执行事务块内的所有命令。示例:
  • 4、Watch
      Watch命令是一个乐观锁,可以为 Redis 事务提供(CAS)行为。其功能是:可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。示例:
  • 5、Unwatch
      Unwatch命令用于取消WATCH命令对所有key的监视。示例:

1.4.2 Redis事务保证原子性吗,支持回滚吗

  Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行
  如果想实现回滚,就需要用WATCH命令,具体的做法是:

  需要在MULTI之前使用WATCH来监控某些键值对,然后使用MULTI命令来开启事务,执行对数据结构操作的各种命令,此时这些命令入队列。
  当使用EXEC执行事务时,首先会比对WATCH所监控的键值对,如果没发生改变,它会执行事务队列中的命令,提交事务;如果发生变化,将不会执行事务中的任何命令,同时事务回滚。当然无论是否回滚,Redis都会取消执行事务前的WATCH命令。

1.5 Lua

  Lua语言提供了如下几种数据类型:booleans(布尔)、numbers(数值)、strings(字符串)、tables(表格)。

  • 1、字符串的简单使用
      定义一个字符串示例:
 local strings val = "world"

  local代表val是一个局部变量,如果没有local代表是全局变量。打印该变量示例:

 -- 结果是 "world"print(hello)
  • 2、数组的简单使用
      如果要使用类似数组的功能,可以用tables类型。Lua的数组下标从1开始计算。定义tables类型的变量示例:
 local tables myArray = {"redis", "jedis", true, 88.0}--trueprint(myArray[3])

  使用for遍历数组示例:

local int sum = 0
for i = 1, 100
dosum = sum + i
end
-- 输出结果为 5050
print(sum)

  table类型变量前加一个#,就代表table类型变量的长度。示例:

for i = 1, #myArray
doprint(myArray[i])
end

  遍历索引和值示例:

for index,value in ipairs(myArray)
doprint(index)print(value)
end

  除了可以用for,也可以用while来遍历,示例:

local int sum = 0
local int i = 0
while i <= 100
dosum = sum +ii = i + 1
end
-- 输出结果为 5050
print(sum)
  • 3、哈希的简单使用
      如果要使用类似哈希的功能,同样可以使用tables类型。示例定义一个tables,每个元素包含了key和value,其中strings1…string2是将两个字符串进行连接:
local tables user_1 = {age = 28, name = "tome"}
--user_1 age is 28
print("user_1 age is " .. user_1["age"])

  如果要遍历user_1,可以使用Lua的内置函数pairs,示例:

for key,value in pairs(user_1)
do print(key .. value)
end
  • 4、函数定义
      在Lua中,函数以function开头,以end结尾,funcName是函数名,中间部分是函数体。示例:
function funcName()...
end
contact 函数将两个字符串拼接:
function contact(str1, str2)return str1 .. str2
end
--"hello world"
print(contact("hello ", "world"))

1.5.1 使用Lua脚本

  • 1、eval和evalsha
      在Redis中执行Lua脚本有两种方法:eval和evalsha。
      eval使用语法:
 eval 脚本内容 key 个数 key 列表 参数列表

  如果Lua脚本较长,还可以使用redis-cli–eval直接执行文件。
  eval命令和–eval参数本质是一样的,客户端如果想执行Lua脚本,首先在客户端编写好Lua脚本代码,然后把脚本作为字符串发送给服务端,服务端会将执行结果返回给客户端,过程图示:

  除了使用eval,Redis还提供了evalsha命令来执行Lua脚本。使用evalsha执行Lua脚本过程图示:

  如图所示,首先要将Lua脚本加载到Redis服务端,得到该脚本的SHA1校验和,evalsha命令使用SHA1作为参数可以直接执行对应Lua脚本,避免每次发送Lua脚本的开销。这样客户端就不需要每次执行脚本内容,而脚本也会常驻在服务端,脚本功能得到了复用。
  加载脚本:script load命令可以将脚本内容加载到Redis内存中,例如将lua_get.lua加载到Redis中,得到SHA1:

 # redis-cli script load "$(cat lua_get.lua)""7413dc2440db1fea7c0a0bde841fa68eefaf149c"

  执行脚本:evalsha的使用方法如下,参数使用SHA1值,执行逻辑和eval一致:

 evalsha 脚本 SHA1 值 key 个数 key 列表 参数列表
  • 2、Lua的Redis API
      Lua可以使用redis.call函数实现对Redis的访问,Lua使用redis.call调用了Redis的set和get操作示例:
 redis.call("set", "hello", "world")redis.call("get", "hello")

  除此之外,Lua还可以使用redis.pcall函数实现对Redis的调用,redis.call和redis.pcall的不同在于,如果redis.call执行失败,那么脚本执行结束会直接返回错误,而redis.pcall会忽略错误继续执行脚本。

1.5.2 管理Lua脚本

  Redis提供了4个命令实现对Lua脚本的管理。

  • 1、script load
 script load script

  此命令用于将Lua脚本加载到Redis内存中。

  • 2、script exists
 scripts exists sha1 [sha1 … ]

  此命令用于判断sha1是否已经加载到Redis内存中。示例:

127.0.0.1:6379> script exists a5260dd66ce02462c5b5231c727b3f7772c0bcc5
1) (integer) 1

  返回结果代表sha1[sha1…]被加载到Redis内存的个数。

  • 3、script flush
      此命令用于清除Redis内存已经加载的所有Lua脚本。
  • 4、script kill
      此命令用于杀掉正在执行的Lua脚本。
      Redis提供了一个lua-time-limit参数,默认是5秒,它是Lua脚本的“超时时间”,但这个超时时间仅仅是当Lua脚本时间超过lua-time-limit后,向其他命令调用发送BUSY的信号,但是并不会停止掉服务端和客户端的脚本执行,所以当达到lua-time-limit值之后,其他客户端在执行正常的命令时,将会收到“Busy Redis is busy running a script”错误,并且提示使用script kill或者shutdown nosave命令来杀掉这个busy的脚本。
      如果当前Lua脚本正在执行写操作,那么script kill将不会生效。

二、Redis客户端

  几乎所有的主流编程语言都有Redis的客户端,Redis的客户端的特点:

  1. 客户端与服务端之间的通信协议是在TCP协议之上构建的。
  2. Redis制定了RESP(REdis Serialization Protocol,Redis序列化协议)实现客户端与服务端的正常交互,这种协议简单高效,既能够被机器解析,又容易被人类识别。

2.1 Jedis的使用

  Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持。

2.1.1 Jedis的简单使用

  • 1、引入依赖
      创建一个SpringBoot项目,引入Jedis的依赖:
<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.8.2</version>
</dependency>

  对于第三方开发包,版本的选择也是至关重要的,通常来讲选取第三方开发包有如下两个策略:

  1. 选择比较稳定的版本,也就是尽可能选择稳定的里程碑版本。
  2. 选择更新活跃的第三方开发包,例如Redis3.0有了Redis Cluster新特性。
  • 2、Jedis的使用
      就可以查看Jedis的使用,示例:
     //生成一个Jedis对象,初始化了Redis实例的IP和端口,这个对象和指定Redis实例进行通信Jedis jedis = new Jedis("127.0.0.1", 6379);jedis.set("hello", "world");System.out.println(jedis.get("hello")); //world

  除了上面的Jedis构造方法,还有一个包含了四个参数的构造函数是比较常用的:

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

  4个参数的意义:

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

  在实际使用Jedis时,肯定要注意关闭流之类的操作:

     Jedis jedis = null;try {jedis = new Jedis("127.0.0.1", 6379);System.out.println(jedis.get("hello"));} catch (Exception e) {System.out.println(e.getMessage());} finally {//关闭流if (jedis != null) {jedis.close();}}

  Jedis对于Redis五种数据结构的简单操作示例:

         Jedis jedis = new Jedis("127.0.0.1", 6379);// 1.stringjedis.set("hello", "world"); System.out.println(jedis.get("hello")); //world// 2.hashjedis.hset("myhash", "f1", "v1");jedis.hset("myhash", "f2", "v2");System.out.println(jedis.hgetAll("myhash")); //{f2=v2, f1=v1}// 3.listjedis.rpush("mylist", "1");jedis.rpush("mylist", "2");jedis.rpush("mylist", "3");System.out.println(jedis.lrange("mylist", 0, -1)); //[1, 2, 3]// 4.setjedis.sadd("myset", "a");jedis.sadd("myset", "b");jedis.sadd("myset", "a");System.out.println(jedis.smembers("myset")); //[a, b]// 5.zsetjedis.zadd("myzset", 99, "tom");jedis.zadd("myzset", 66, "peter");jedis.zadd("myzset", 33, "james");System.out.println(jedis.zrange("myzset", 0, -1)); //[james, peter, tom]

2.1.2 Jedis常用API

  • 1、Jedis中对键通用的操作
方法 描述 返回值 /补充说明
boolean exists(String key) 判断某个键是否存在
set(String key,String value) 新增键值对(key,value) 返回String类型的OK代表成功
Set< String > jedis.keys(*) 获取所有key 返回set 无序集合
del(String key) 删除指定key
expire(String key,int i) 设置键为key的过期时间为i秒
int jedis.ttl(String key) 获取key数据项的剩余时间(秒)
persist(String key) 移除键为key属性项的生存时间限制
type(String key) 查看键为key所对应value的数据类型
  • 2、Jedis中的字符串操作
      字符串类型是Redis中最为基础的数据存储类型,在Redis中字符串类型的Value最多可以容纳的数据长度是512M。
语法 描述
set(String key,String value) 增加(或覆盖)数据项
setnx(String key,String value) 不覆盖增加数据项(重复的不插入)
setex(String ,int t,String value) 增加数据项并设置有效时间
del(String key) 删除键为key的数据项
get(String key) 获取键为key对应的value
append(String key, String s) 在key对应value 后边追加字符串 s
mset(String k1,String V1,String K2,String V2,…) 增加多个键值对
String[] mget(String K1,String K2,…) 获取多个key对应的value
del(new String[](String K1,String K2,.... )) 删除多个key对应的数据项
String getSet(String key,String value) 获取key对应value并更新value
String getrange(String key , int i, int j) 获取key对应value第i到j字符 ,从0开始,包头包尾
  • 3、Jedis中的增减操作
语法 描述
incr(String key) 将key对应的value加1
incrBy(String key,int n) 将key对应的value加n
decr(String key) 将key对应的value减1
decrBy(String key , int n) 将key对应的value减n
  • 4、Jedis中的列表操作
语法 描述
lpush(String key, String v1, String v2,....) 添加一个List , 如果已经有该List对应的key, 则按顺序在左边追加 一个或多个
rpush(String key , String vn) key对应list右边插入元素
lrange(String key,int i,int j) 获取key对应list区间[i,j]的元素,注:从左边0开始,包头包尾
ltrim(String key,int i,int j) 删除list区间[i,j] 之外的元素
lpop(String key) 左弹出一个key对应的元素
rpop(String key) 右弹出一个key对应的元素
llen(String key) 获取key对应list的长度
lset(String key,int index,String val) 修改key对应的list指定下标index的元素
lindex(String key,int index) 获取key对应list下标为index的元素
  • 5、Jedis中的集合操作
语法 描述
sadd(String key,String v1,String v2,…) 添加一个set
smenbers(String key) 获取key对应set的所有元素
srem(String key,String val) 删除集合key中值为val的元素
srem(String key, Sting v1, String v2,…) 删除值为v1, v2 , …的元素
sinter(String key1, String key2) 获取集合key1和集合key2的交集
sunion(String key1, String key2) 获取集合key1和集合key2的并集
sdiff(String key1, String key2) 获取集合key1和集合key2的差集
  • 6、Jedis中的有序集合操作
语法 描述
zadd(String key,Map map) 添加一个ZSet
hset(String key,int score , int val) 往 ZSet插入一个元素(Score-Val)
zrange(String key, int i , int j) 获取ZSet 里下表[i,j] 区间元素Val
zscore(String key,String value) 获取ZSet里value元素的Score
zrem(String key,String value) 删除ZSet里的value元素
zcard(String key) 获取ZSet的元素个数
zcount(String key , int i ,int j) 获取ZSet总score在[i,j]区间的元素个数
zincrby(String key,int n , String value) 把ZSet中value元素的score+=n
  • 7、Jedis中的哈希操作
语法 描述
hmset(String key,Map map) 添加一个Hash
hset(String key , String key, String value) 向Hash中插入一个元素(K-V)
hgetAll(String key) 获取Hash的所有(K-V) 元素
hkeys(String key) 获取Hash所有元素的key
hvals(String key) 获取Hash所有元素的value
hdel(String key , String k1, String k2,…) 从Hash中删除一个或多个元素
hlen(String key) 获取Hash中元素的个数
hexists(String key,String K1) 判断Hash中是否存在指定key对应的元素
hmget(String key,String K1,String K2) 获取Hash中一个或多个元素value

2.1.3 Jedis连接池

  客户端连接Redis使用的是TCP协议,直连的方式每次需要建立TCP连接,而连接池的方式是可以预先初始化好Jedis连接,每次只需要从Jedis连接池借用即可,只有少量的并发同步开销,远远小于新建TCP连接的开销。两者的比较:

优点 缺点
直连 简单方便,适用于少量长期连接的场景 1、存在每次新建/关闭TCP连接开销;
2、资源无法控制,可能会出现连接泄露;
3、Jedis对象线程不安全
连接池 1、无需每次连接都生成Jedis对象,降低开销;
2、使用连接池控制开销
使用较麻烦,要熟悉各个参数的意义

  连接池的基本使用:

     //使用默认配置GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();//初始化Jedis连接池JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);Jedis jedis = null;try {//从连接池获取jedis对象jedis = jedisPool.getResource();System.out.println(jedis.get("hello")); //world} catch (Exception e) {} finally {if (jedis != null) {//close操作不是关闭连接,代表归还连接池jedis.close();}}

  在GenericObjectPoolConfig中,可以设置很多关于Redis连接池的属性,一些较常用的设置:

 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的重要属性:

参数名 含义 默认值
maxActive 连接池中最大连接数 8
maxIdle 连接池中最大空闲的连接数 8
minIdle 连接池中最少空闲的连接数 0
maxWaitMillis 当连接池资源耗尽后,调用者的最大等待时间(单位为毫秒),一般不建议使用默认值 -1永远不超时,一直等:
jmxEnabled 是否开启jmx监控,如果应用开启了jmx端口,并且jmxEnabled设置为true,就可以通过jconsole或jvisualvm看到关于连接池的相关统计,有助于了解连接池的使用情况,并且可以针对做监控统计 true
minEvictableIdleTimeMillis 连接的最小空闲时间,达到此值后空闲连接将被移除 1000L x 60L x 30毫秒 = 30分钟
numTestsPerEvictionRun 做空闲连接检测时,每次的采样数 3
testOnBorrow 向连接池借用连接时是否做连接有效性检测(ping),无效连接将被移除,每次借用多执行一次ping命令 false
testOnReturn 是否做周期性空闲检测 false
testWhileIdle 向连接池借用连接时是否做连接空闲检测,空闲超时的连接会被移除 false
timeBetweenEvictionRunsMillis 空闲连接的检测周期(单位为毫秒) -1:表示不做检测
blockWhenExhausted 当连接池用尽后,调用者是否要等待,这个参数和maxWaitMillis对应,当此参数为true时,maxWaitMillis 才生效 false

2.2 客户端管理

2.2.1 client list

  client list命令能列出与Redis服务端相连的所有客户端连接信息,示例:

  输出结果的每一行代表一个客户端的信息。

  • 1、id
      客户端连接的唯一标识,这个id是随着Redis的连接自增的,重启Redis后会重置为0。
  • 2、addr
      客户端连接的ip和端口。
  • 3、fd
      socket的文件描述符。
  • 4、name
      客户端的名字。
  • 5、qbuf、qbuf-free
      这两个属性都表述输入缓冲区相关的信息。
      Redis服务端为每个Redis客户端分配了输入缓冲区,它的作用是将客户端发送的命令临时保存,同时服务端从会输入缓冲区拉取命令并执行。图示:

      qbuf代表这个缓冲区的总容量,qbuf-free代表这个缓冲区的剩余容量。Redis没有提供相应的配置来规定每个缓冲区的大小,输入缓冲区会根据输入内容大小的不同动态调整,只是要求每个客户端缓冲区的大小不能超过1G,超过后客户端将被关闭
      对于Redis服务端而言,假设一个Redis实例设置了maxmemory(Redis服务端的最大内存)为4G,已经存储了2G数据,但是如果此时输入缓冲区使用了3G,已经超过maxmemory限制,可能会产生数据丢失、键值淘汰、OOM等情况。
      查看Redis服务端内存配置的命令是info memory,示例:

      造成输入缓冲区过大的原因有哪些?
  1. 主要是因为Redis的处理速度跟不上输入缓冲区的输入速度,并且每次进入输入缓冲区的命令包含了大量bigkey。
  2. Redis发生了阻塞,短期内不能处理命令,造成客户端输入的命令积压在了输入缓冲区。

  监控输入缓冲区异常的方法有两种:

  1. 通过定期执行client list命令,收集qbuf和qbuf-free找到异常的连接记录并分析,最终找到可能出问题的客户端。
  2. 通过info clients命令,找到最大的输入缓冲区,下面命令中的其中client_biggest_input_buf代表最大的输入缓冲区,例如可以设置超过10M就进行报警:

      client list和info clients的对比:
命令 优点 缺点
client list 能精准分析每个客户端来定位问题 执行速度较慢,频繁执行存在阻塞Redis的可能
info clients 执行速度比client list快,分析过程较为简单 不能精确定位到客户端;
不能显示所有输入缓冲区的总量,只能显示最大量
  • 6、obl、oll、omem
      这三个属性都表述输出缓冲区相关的信息
      Redis服务端为每个Redis客户端分配了输出缓冲区,它的作用是保存命令执行的结果返回给客户端,为服务端和客户端交互返回结果提供缓冲,图示:

      输出缓冲区的容量可以通过参数client-output-buffer-limit来进行设置,按照客户端的不同分为三种:普通客户端、发布订阅客户端、slave客户端,图示:

      client-output-buffer-limit命令的使用:client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>。参数:

1、<class>:客户端类型,分为三种。
  normal:普通客户端;
  slave:slave客户端,用于复制;
  pubsub:发布订阅客户端。
2、<hard limit>:如果客户端使用的输出缓冲区大于<hard limit>,客户端会被立即关闭。
3、<soft limit><soft seconds>:如果客户端使用的输出缓冲区超过了<soft limit>并且持续了<soft seconds>秒,客户端会被立即关闭。

  client-output-buffer-limit的默认配置:

  输出缓冲区由两部分组成:固定缓冲区(16KB)和动态缓冲区,其中固定缓冲区返回比较小的执行结果,而动态缓冲区返回比较大的结果,例如大的字符串、hgetall、smembers命令的结果等。
  固定缓冲区使用的是字节数组,动态缓冲区使用的是列表。当固定缓冲区存满后会将Redis新的返回结果存放在动态缓冲区的队列中,队列中的每个对象就是每个返回结果。
  client list中的obl代表固定缓冲区的长度,oll代表动态缓冲区列表的长度,omem代表使用的字节数。例如下面代表当前客户端的固定缓冲区的长度为0,动态缓冲区有4869个对象,两个部分共使用了133081288字节=126M内存:

  监控输出缓冲区的方法依然有两种:

  1. 通过定期执行client list命令,收集obl、oll、omem找到异常的连接记录并分析,最终找到可能出问题的客户端。
  2. 通过info命令的info clients模块,找到输出缓冲区列表最大对象数。示例:

      client_longest_output_list代表输出缓冲区列表最大对象数
      如何预防输出缓冲区出现异常呢?主要方法有以下几种:

  1、监控并设置阀值,超过阀值及时处理。
  2、根据client-output-buffer-limit命令对普通缓冲区设置,示例:client-output-buffer-limit normal 20mb 10mb 120
  3、及时监控内存,一旦发现内存抖动频繁,可能就是输出缓冲区过大。

  • 7、age和idle
      age代表当前客户端已经连接的时间,idle代表当前客户端最近一次的空闲时间。示例:
      上面这条记录代表当期客户端连接Redis的时间为8888581秒,其中空闲了8888581秒,实际上这种就属于不太正常的情况,当age等于idle时,说明连接一直处于空闲状态
  • 8、和maxclients/timeout配合使用
      Redis提供了maxclients参数来限制最大客户端连接数,一旦连接数超过maxclients,新的连接将被拒绝。maxclients默认值是10000,可以通过info clients来查询当前Redis的连接数:

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

      一般来说maxclients=10000在大部分场景下已经绝对够用。同时,Redis提供了timeout(单位为秒)参数来限制连接的最大空闲时间,一旦客户端连接的idle时间超过了timeout,连接将会被关闭。Redis 默认的 timeout 是 0 ,也就是不会检测客户端的空闲
      将timeout设置为30秒示例:

      在实际开发和运维中,需要将timeout设置成大于0,例如可以设置为300秒,这样可以避免Redis的客户端使用不当或者客户端本身的一些问题,造成没有及时释放客户端连接的问题。
  • 9、flags
      flags是用于标识当前客户端的类型,例如flags=S代表当前客户端是slave客户端、flags=N代表当前是普通客户端。客户端类型:
客户端类型 说明
N 普通客户端
M master节点
S slave节点
o 正在执行monitor命令
x 正在执行事务
b 正在等待阻塞时间
u 客户端未被阻塞
A 尽可能快地关闭连接
  • 10、client list所有参数
参数 含义
id 客户端连接id
addr 客户端连接IP和端口
fd socket的文件描述符
name 客户端连接名
age 客户端连接存活时间
idle 客户端连接空闲时间
flags 客户端连接标识
db 当前客户端正在使用的数据库索引下标
sub/psub 当前客户端订阅的频道数或模式数
multi 当前事务中已执行命令个数
qbuf 输入缓冲区总容量
qbuf-ree 输入缓冲区剩余容量
obl 固定缓冲区的长度
oll 动态缓冲区列表的长度
omem 固定缓冲区和动态缓存区使用的容量
cmd 当前客户端最后一次执行的命令

2.2.2 client setName和client getName

  用于给当前客户端设置和获取名称,示例:

2.2.3 client kill

  用法为client kill ip:port,此命令用于杀掉指定IP地址和端口的客户端。

2.2.4 client pause

  用法为’client pause timeout’,表示阻塞客户端timeout毫秒数,在此期间客户端连接将被阻塞。

  1. client pause只对普通和发布订阅客户端有效,对于主从复制(从节点内部伪装了一个客户端)是无效的,所以此命令可以用来让主从复制保持一致。
  2. client pause可以用一种可控的方式将客户端连接从一个Redis节点切换到另一个Redis节点。

2.2.5 monitor

  monitor命令用于监控Redis正在执行的命令。示例:

2.2.6 config set

  该命令用于设置客户端属性。

  • 1、timeout
      检测客户端空闲连接的超时时间,一旦idle时间达到了timeout,客户端将会被关闭,如果设置为0就不进行检测。示例:
  • 2、maxclients
      客户端最大连接数。示例:
  • 3、tcp-keepalive
      检测TCP连接活性的周期,默认值为0,也就是不进行检测,如果需要设置,建议为60,那么Redis会每隔60秒对它创建的TCP连接进行活性检测,防止大量死连接占用系统资源。示例:
  • 4、tcp-backlog
      TCP三次握手后,会将接受的连接放入队列中,tcp-backlog就是队列的大小,它在Redis中的默认值是511。通常来讲这个参数不需要调整,示例:

2.2.7 info stats

   info stats可以统计一些Redis总的状态,示例:

   total_connections_received表示Redis自启动以来处理的客户端连接数总
数。
  rejected_connections表示Redis自启动以来拒绝的客户端连接数。

2.3 常见异常

2.3.1 无法从连接池获取到连接

  1、假设JedisPool中的Jedis对象个数是8个,8个Jedis对象被占用,并且没有归还,此时还要从JedisPool中借用Jedis,就需要进行等待(例如设置maxWaitMillis>0),如果在maxWaitMillis时间内仍然无法获取到Jedis对象就会抛出JedisConnectionException
  2、设置了blockWhenExhausted=false,那么调用者发现池子中没有Jedis资源时,会立即抛出异常不进行等待。

该属性的作用是:连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true。

  一些可能会造成连接池中资源被耗尽的原因:

1、高并发下连接池设置过小。
2、没有正确使用连接池,比如没有进行释放。
3、存在慢查询操作,这些慢查询持有的Jedis对象归还速度会比较慢。
4、客户端是正常的,但是Redis服务端由于一些原因造成了客户端命令执行过程的阻塞。

2.3.2 客户端读写超时

  SocketTimeoutException,原因有以下几种:

1、读写超时间设置得过短。
2、命令本身执行就比较慢。
3、客户端与服务端网络不正常。
4、Redis服务端自身发生阻塞。

2.3.3 客户端连接超时

  JedisConnectionException,原因有以下几种:

1、连接超时设置得过短,修改示例:jedis.getClient().setConnectionTimeout(time);,单位毫秒。
2、Redis服务端发生阻塞,造成tcp-backlog已满,造成新的连接失败。
3、客户端与服务端网络不正常。

2.3.4 客户端缓冲区异常

  Jedis在调用Redis时,如果出现客户端数据流异常,会出现JedisConnectionException。原因有以下几种:

1、输出缓冲区满。
2、长时间闲置连接被服务端主动断开。
3、不正常并发读写:Jedis对象同时被多个线程并发操作,可能会出现该异常。

2.3.5 JedisDataException

  Jedis调用Redis时,如果Redis正在加载持久化文件,会出现JedisDataException

2.3.6 Redis使用的内存超过maxmemory配置

  Jedis执行写操作时,如果Redis的使用内存大于maxmemory的设置,会报如下异常:

2.3.7 客户端连接数过大

  如果客户端连接数超过了maxclients,新申请的连接就会出现如下异常:

2.4 Redis客户端的一些问题

2.4.1 Redis和Redisson有什么关系?

  Redisson是一个高级的分布式协调Redis客户端,能帮助用户在分布式环境中轻松实现一些Java的对象,如:Bloom filter、BitSet、Set、SortedSet、Map、ConcurrentMap、List、Queue、BlockingQueue、Semaphore、ReadWriteLock、AtomicLong、CountDownLatch等。

2.4.2 Jedis和Redisson对比有什么优缺点?

  Jedis是Redis的Java实现的客户端,其AP提供了比较全面的Redis命令的支持。
  Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,让使用者将精力更集中地放在处理业务逻辑上。

Redis(二)Redis客户端的使用相关推荐

  1. Redis(二) -- redis.conf详解(redis6)

    1. 重要配置: 1.1 大小写不敏感: # Note on units: when memory size is needed, it is possible to specify # it in ...

  2. redis(二)redis实战 使用redis进行文章的排序

    2019独角兽企业重金招聘Python工程师标准>>> http://www.beckbi.cn/?p=172 redis实战使用redis进行文章的排序 转载于:https://m ...

  3. 认识常见中间件-redis(二)-redis缓存雪崩、缓存击穿、缓存穿透

    缓存雪崩   缓存雪崩指的是大量的请求无法在 Redis 缓存系统中处理,请求全部打到数据库,导致数据库压力激增,甚至宕机.出现该错误的原因主要有两种: 大量热点数据同时过期,导致大量请求需要查询数据 ...

  4. redis 系列19 客户端

    redis 系列19 客户端 原文:redis 系列19 客户端 一. 概述 Redis服务器是可以与多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请 ...

  5. Linux服务器安装JavaWeb环境(二) Redis,MySql,Zookeeper,Keepalive

    /*****************[安装RedisServer]****************/ 第一步:下载redis安装包 http://download.redis.io/releases/ ...

  6. Redis的Java客户端之Jedis(Jedis)

    文章目录 Redis的Java客户端(Jedis) 一.简介 二.Jedis快速入门 1.引入依赖 2.建立连接 3.使用Jedis 4.释放资源 三.Jedis连接池 1.创建Jedis连接池配置 ...

  7. Redis:redis通用命令;redis常见数据结构;redis客户端;redis的序列化

    一.redis命令 1.redis通用命令 Redis 通用命令是一些 Redis 下可以作用在常用数据结构上的常用命令和一些基础的命令 常见的命令有: keys 查看符合模板的所有key,不建议在生 ...

  8. redis的nodejs客户端ioredis初识

    转载请注明出处哈:http://carlosfu.iteye.com/blog/2240426  声明: 我是java程序员,对node不是很熟悉,只是写书的需要,要了解一下redis的node客户端 ...

  9. redis | 二、redis安装

    redis系列文章: https://liudongdong.top/categories/redis 本篇来源: https://liudongdong.top/archives/rediser-r ...

  10. 管理连接redis server的客户端

    一.redis server管理连接redis客户端的命令 1.查看与redis服务端相连的所有客户端的连接信息 查看命令:client list # 查看连接当前redis的所有客户端 127.0. ...

最新文章

  1. 设计模式学习笔记-原型模式
  2. ventory制作U盘启动盘
  3. matlab引擎函数,Matlab引擎库函数
  4. [Linux主机] 优化你的php-fpm(php5.3+)让你的网站跑得更快
  5. 乖乖,腾讯天美研发20万月薪刷爆朋友圈,网友:小丑竟是我自己
  6. 谷粒商城集群篇爬坑笔记--Gitee拉取项目报错、项目target文件不存在(部分项目不全)、SonarQube报错
  7. 截取台风后的图片_今年首个台风来袭!“大黄蜂”下,货代如何“防台”?
  8. json动态生成复杂表头excel_Excel办公用品管理系统,全函数统算,图表动态展示高效轻松...
  9. win10 x64中 windbg x64 安装配置符号库
  10. 互联网对实体经济的三轮冲击
  11. 银行资产配置的新变化
  12. 2010年的读书计划
  13. 太阳系(Python)
  14. 面:【1】笔试. mgj 再菜也能做出来,,,有点信心好不好
  15. Webots水下机器人仿真
  16. 忠告:使用TNT版本PD的不要升级新版本Mac系统
  17. 差之毫厘:etcd 3 完美支持 HTTP 访问
  18. 根据SQL必知必会学习SQL(MYSQL)
  19. ERROR 1130 (HY000): Host ‘192.168.78.128‘ is not allowed to connect to this MySQL server
  20. 虫师 python_python学习虫师笔记 (一)

热门文章

  1. 分享 Android 识音识别 挺有想法的。
  2. 这几个Python数据可视化探索实例,拿走不谢
  3. WebService-服务端与客户端
  4. LayaBox---知识点
  5. 会议室预约系统(小程序+web端管理系统)2020.11.11-2021.02.11
  6. 极光推送入门教程-后端
  7. 数据总线,地址总线,控制总线
  8. 使用fusion app制作b站app
  9. 国有资产管理处组织召开新版固定资产管理系统操作培训会
  10. 【Python-tkinter】拼音输入方法——小学拼音练习题