Redis总结3——慢查询、事务、发布订阅


  Redis提供的5种数据结构已经足够强大,但除此之外,Redis还提供了诸如慢查询分析、功能强大的Redis Shell、Pipeline、事务与Lua脚本、 Bitmaps、HyperLogLog、发布订阅、GEO等附加功能,这些功能可以在某些场景发挥重要的作用,本节将介绍以下内容。

1:慢查询分析

  所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间(并不包括命令的网络传输时间,所以没有慢查询不代表客户端没有超时),当超过预设阀值,就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录下来。

1.1:慢查询的两个配置参数

对于慢查询功能,需要明确两件事:

  • 预设阀值怎么设置?
  • 慢查询记录存放在哪?

  Redis提供了 slowlog-log-slower-than(预设阀值,单位为微秒) slowlog-max-len(慢日志最多存放条数) 配置来解决这两个问题。如果slowlog-log-slower-than=0会记录所有的命令,slowlog-log-slower- than<0对于任何命令都不会进行记录。而当慢查询日志列表已处于其最大长度时,再插入记录时最早插入的 一个命令将从列表中移出。

  在Redis中有两种修改配置的方法,一种是修改配置文件,另一种是使用config set命令动态修改。
在这里插入图片描述

  慢查询日志是存放再内存列表中的,可以通过一组命令来实现对慢查询日志的访问和管理。

1.获取慢查询日志:slowlog get [n]
在这里插入图片描述
2.获取慢查询日志列表当前的长度:slowlog len
3.慢查询日志重置:slowlog reset

开发建议
  slowlog-max-len配置建议:线上建议调大慢查询列表,记录慢查询时 Redis会对长命令做截断操作,并不会占用大量内存。增大慢查询列表可以 减缓慢查询被剔除的可能,例如线上可设置为1000以上。
   slowlog-log-slower-than配置建议:默认值超过10毫秒判定为慢查询, 需要根据Redis并发量调整该值。由于Redis采用单线程响应命令,对于高流 量的场景,如果命令执行时间在1毫秒以上,那么Redis最多可支撑OPS不到 1000。因此对于高OPS场景的Redis建议设置为1毫秒。

  慢查询只记录命令执行时间,并不包括命令排队和网络传输时间。因 此客户端执行命令的时间会大于命令实际执行时间。
  由于慢查询日志是一个先进先出的队列,也就是说如果慢查询比较多的情况下,可能会丢失部分慢查询命令,为了防止这种情况发生,可以定期 执行slow get令将慢查询日志持久化到其他存储中(例如MySQL),然后可以制作可视化界面进行查询

2:Redis Shell

2.1: redis-cli详解

  之前曾介绍过redis-cli,包括-h、-p参数,但是除了这些参数,还有很多有用的参数,要了解redis-cli的全部参数,可以执行redis-cli --help命令来进行查看,下面将对一些重要参数的含义以及使用场景进行说明。
在这里插入图片描述

  1. -r:将命令执行多次
    在这里插入图片描述

  1. -i:代表每隔几秒执行一次命令,但是-i选项必须和-r选 项一起使用.注意:此时单位是秒,可以使用小数。

  1. -x:代表从标准输入(stdin)读取数据作为redis-cli的最后一个参数

  1. -a:如果Redis配置了密码,可以用-a(auth)选项,有了这个选项就不需要 手动输入auth命令。

  1. –slave:把当前客户端模拟成当前Redis节点的从节点,可以用来 获取当前Redis节点的更新操作。

  1. –rdb:请求Redis实例生成并发送RDB持久化文件,保存在本地。 可使用它做持久化文件的定期备份。

  1. –pipe:用于将命令封装成Redis通信协议定义的数据格式,批量发送给Redis执行.

  1. –latency:latency有三个选项,分别是–latency、–latency-history、–latency-dist。 它们都可以检测网络延迟,对于Redis的开发和运维非常有帮助。

2.2:redis-benchmark详解

  redis-benchmark可以为Redis做基准性能测试,它提供了很多选项帮助开发和运维人员测试Redis的相关性能,下面分别介绍这些选项。

  1. .-c
    -c(clients)选项代表客户端的并发数量(默认是50)。

  1. -n<requests>
    -n(num)选项代表客户端请求总量(默认是100000)。
    例如redis-benchmark-c100-n20000代表100各个客户端同时请求Redis,一 共执行20000次。redis-benchmark会对各类数据结构的命令进行测试,并给出性能指标:
    在这里插入图片描述
      例如上面一共执行了20000次get操作,在0.27秒完成,每个请求数据量 是3个字节,99.11%的命令执行时间小于1毫秒,Redis每秒可以处理 73529.41次get请求。

  1. .-q
    选项仅仅显示redis-benchmark的requests per second信息,例如:
    在这里插入图片描述
  2. -r
    在一个空的Redis上执行了redis-benchmark会发现只有3个键:
    在这里插入图片描述
    如果想向Redis插入更多的键,可以执行使用-r(random)选项,可以向 Redis插入更多随机的键。

  1. -t
    -t选项可以对指定命令进行基准测试。
    在这里插入图片描述

  1. --csv
    –csv选项会将结果按照csv格式输出,便于后续处理,如导出到Excel 等

3:Pipeline

  Redis提供了批量操作命令(例如mget、mset等),有效地节约RTT。但大部分命令是不支持批量操作的,例如要执行n次hgetall命令,并没有mhgetall命令存在,需要消耗n次RTT。Redis的客户端和服务端可能部署在不同的机器上。例如客户端在北京,Redis服务端在上海,两地直线距离约为 1300公里,那么1次RTT时间=1300×2/(300000×2/3)=13毫秒(光在真空中传输速度为每秒30万公里,这里假设光纤为光速的2/3),那么客户端在1秒 内大约只能执行80次左右的命令,这个和Redis的高并发高吞吐特性背道而驰。Pipeline(流水线)机制能改善上面这类问题,它能将一组Redis命令进 行组装,通过一次RTT传输给Redis,再将这组Redis命令的执行结果按顺序返回给客户端,图为没有使用Pipeline执行了n条命令,整个过程需要n次 RTT。
在这里插入图片描述
在这里插入图片描述
  大部分开发人员更倾向于使用高级语言客户端中的Pipeline,目前大部分Redis客户端都支持Pipeline,之后将介绍如何通过Java的Redis客 户端Jedis使用Pipeline功能。

原生批量命令与Pipeline对比:

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

4:事务与Lua

4.1:事务

  为了保证多条命令组合的原子性,Redis提供了简单的事务功能以及集 成Lua脚本来解决这个问题。
Redis提供了简单的事务功能,将一组需要一起执行的命令放到multi和 exec两个命令之间。multi命令代表事务开始,exec命令代表事务结束,它们之间的命令是原子顺序执行的,例如下面操作实现了上述用户关注问题:
在这里插入图片描述
  可以看到set命令此时的返回结果是QUEUED,代表命令并没有真正执行,而是暂时保存在Redis中。
如果要停止事务的执行,可以使用discard命令代替exec命令即可。
在这里插入图片描述
如果事务中的命令出现错误,Redis的处理机制也不尽相同。

  1. 命令错误
    例如下面操作错将set写成了sett,属于语法错误,会造成整个事务无法 执行,key和counter的值未发生变化:
    在这里插入图片描述
  2. 运行时错误
    例如用户B在添加粉丝列表时,误把sadd命令写成了zadd命令,这种就是运行时命令,因为语法是正确的:
    在这里插入图片描述
    可以看到Redis并不支持回滚功能,sadd user:a:follow user:b命令已 经执行成功,开发人员需要自己修复这类问题。

  有些应用场景需要在事务之前,确保事务中的key没有被其他客户端修改过,才执行事务,否则不执行(类似乐观锁)。Redis提供了watch命令来解决这类问题,表展示了两个客户端执行命令的时序。
在这里插入图片描述
   可以看到“客户端-1”在执行multi之前执行了watch命令,“客户端-2”在“客户端-1”执行exec之前修改了key值,造成事务没有执行(exec结果 为nil),整个代码如下所示:
在这里插入图片描述

4.2:Lua用法简述

  Redis将Lua作为脚本语言可帮助开发者定制自己的Redis命令,在使用Lua之前,必须修改源码来完成。

Lua脚本功能为Redis开发和运维人员带来如下三个好处:

  • Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。
  • Lua脚本可以帮助开发和运维人员创造出自己定制的命令,并可以将这 些命令常驻在Redis内存中,实现复用的效果。
  • Lua脚本可以将多条命令一次性打包,有效地减少网络开销。
4.2.1:Lua的基本使用

Lua语言提供了如下几种数据类型:booleans(布尔)、numbers(数值)、strings(字符串)、tables(表格),和许多高级语言相比,相对简单。
(1)字符串

local strings val = "world"

  其中,local代表val是一个局部变量,如果没有local代表是全局变量。 print函数可以打印出变量的值,例如下面代码将打印world,其中"–"是Lua 语言的注释。

-- 结果是"world"
 print(hello

(2)数组

local tables myArray = {"redis", "jedis", true, 88.0} 
--true 
print(myArray[3])

如果想遍历这个数组,可以使用for和while,这些关键字和许多编程语言是一致的。
(a)for

local int sum = 0 
--for语句循环
for i = 1, 100 
do 
	sum = sum + i 
end 
-- 输出结果为5050 
print(sum)

要遍历myArray,首先需要知道tables的长度,只需要在变量前加一个# 号即可:

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

除此之外,Lua还提供了内置函数ipairs,使用for index,value ipairs(tables)可以遍历出所有的索引下标和值:

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

(b)while

local int sum = 0 
local int i = 0 
while i <= 100
do 
 	sum = sum +i 
 	i = i + 1 
 end 
 --输出结果为5050
 print(sum)

(c)if else

local tables myArray = {"redis", "jedis", true, 88.0} 
for i = 1, #myArray 
do if myArray[i] == "jedis" 
	then
		print("true") 
		break 
	else
	--do 
		nothing 
	end 
end

(3)哈希

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"))

4.3:Redis与Lua

4.3.1:在Redis中使用Lua

在Redis中执行Lua脚本有两种方法:eval和evalsha。
(1)eval

eval 脚本内容 key个数 key列表 参数列表

下面例子使用了key列表和参数列表来为Lua脚本提供更多的灵活性:

eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world "hello
redisworld"

此时KEYS[1]=“redis”,ARGV[1]=“world”,所以最终的返回结果 是"hello redisworld"。
  如果Lua脚本较长,还可以使用redis-cli–eval直接执行文件。
eval命令和–eval参数本质是一样的,客户端如果想执行Lua脚本,首先 在客户端编写好Lua脚本代码,然后把脚本作为字符串发送给服务端,服务端会将执行结果返回给客户端。
在这里插入图片描述
(2)evalsha
  首先要将Lua脚本加载到Redis服务端,得到该脚本的SHA1校验和, evalsha命令使用SHA1作为参数可以直接执行对应Lua脚本,避免每次发送 Lua脚本的开销。这样客户端就不需要每次执行脚本内容,而脚本也会常驻 在服务端,脚本功能得到了复用。
在这里插入图片描述
加载脚本:script load命令可以将脚本内容加载到Redis内存中,得到SHA1

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

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

--evalsha 脚本SHA1值 key个数 key列表 参数列表
evalsha 7413dc2440db1fea7c0a0bde841fa68eefaf149c 1 redis world 
"hello redisworld"

Lua可以使用redis.call函数实现对Redis的访问,例如下面代码是Lua使用 redis.call调用了Redis的set和get操作:

redis.call("set", "hello", "world") 
redis.call("get", "hello")
4.3.2:案例

下面以一个例子说明Lua脚本的使用,当前列表记录着热门用户的id, 假设这个列表有5个元素,如下所示:
在这里插入图片描述
user:{id}:ratio代表用户的热度,它本身又是一个字符串类型的键:
在这里插入图片描述
现要求将列表内所有的键对应热度做加1操作,并且保证是原子执行, 此功能可以利用Lua脚本来实现。

1)将列表中所有元素取出,赋值给mylist:

local mylist = redis.call("lrange", KEYS[1], 0, -1)

2)定义局部变量count=0,这个count就是最后incr的总次数:

local count = 0

3)遍历mylist中所有元素,每次做完count自增,最后返回count:

for index,key in ipairs(mylist) 
do 
	redis.call("incr",key) 
	count = count + 1 
end 
return count

将上述脚本写入lrange_and_mincr.lua文件中,并执行如下操作,返回结果为5
在这里插入图片描述
执行后所有用户的热度自增1:
在这里插入图片描述

4.3.3:Redis如何管理Lua脚本

Redis提供了4个命令实现对Lua脚本的管理
(1)script load
此命令用于将Lua脚本加载到Redis内存中,前面已经介绍并使用过了, 这里不再赘述。
(2)script exists

scripts exists sha1 [sha1 …]

此命令用于判断sha1是否已经加载到Redis内存中。
(3)script flush

script flush

此命令用于清除Redis内存已经加载的所有Lua脚本。
(4)script kill

script kill

此命令用于杀掉正在执行的Lua脚本。如果Lua脚本比较耗时,甚至Lua 脚本存在问题,那么此时Lua脚本的执行会阻塞Redis,直到脚本执行完毕或 者外部进行干预将其结束。

5: 发布订阅

  Redis提供了基于“发布/订阅”模式的消息机制,此种模式下,消息发布者和订阅者不进行直接通信,发布者客户端向指定的频道(channel)发布消息,订阅该频道的每个客户端都可以收到该消息,如所示。Redis提供了若干命令支持该功能,在实际应用开发时,能够为此类问题提供实现方法。和很多专业的消息队列系统(例如Kafka、RocketMQ)相比,Redis的发布订阅略显粗糙,例如无法实现消息堆积和回溯。但胜在足够简单,如果当前场景可以容忍的这些缺点,也不失为一个不错的选择。
在这里插入图片描述

5.1:命令

1.发布消息

publish channel message

在这里插入图片描述


2.订阅消息

subscribe channel [channel ...]

订阅者可以订阅一个或多个频道,下面操作为当前客户端订阅了 channel:sports频道:
在这里插入图片描述

有关订阅命令有两点需要注意:
一:新开启的订阅客户端,无法收到该频道之前的消息,因为Redis不会对发布的消息进行持久化。
二:客户端在执行订阅命令之后进入了订阅状态,只能接收subscribe、 psubscribe、unsubscribe、punsubscribe的四个命令。


3.取消订阅

unsubscribe [channel [channel ...]]

客户端可以通过unsubscribe命令取消对指定频道的订阅,取消成功后, 不会再收到该频道的发布消息


4.按照模式订阅和取消订阅

psubscribe pattern [pattern...]
punsubscribe [pattern [pattern ...]]

5.查询订阅
(1)查看活跃的频道

pubsub channels [pattern]

所谓活跃的频道是指当前频道至少有一个订阅者,其中[pattern]是可以指定具体的模式
在这里插入图片描述
(2)查看频道订阅数

pubsub numsub [channel ...]

当前channel:sports频道的订阅数为2:
在这里插入图片描述

5.2:使用场景

   聊天室、公告牌、服务之间利用消息解耦都可以使用发布订阅模式,下 面以简单的服务解耦进行说明。如图所示,图中有两套业务,上面为视频管理系统,负责管理视频信息;下面为视频服务面向客户,用户可以通过各种客户端(手机、浏览器、接口)获取到视频信息。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤独的偷学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值