Redis学习之旅 管道/脚本/过期/事务

Redis学习之旅 管道/脚本/过期/事务

本文尝试将 管道、脚本、过期和事务一起归纳一下

管道

我们都知道,一个命令通常都是由一个客户端通过网络连接发送到某个服务,由服务做出响应并给出回复,相对于Redis的快速,网络的传输速度往往是制约Redis发挥最大功效的因素,因此Redis设计了管道的概念

  • 常规的使用,就是由客户端 1.通过网络发送请求-2.Redis处理计算-3.通过网络返回结果,所有的命令都是这样123-123-123式的进行处理,性能的消耗基本上都消耗在了网络传输上,在此基础上,设计了管道,可以一次性传输一批命令,由redis一起处理后,再统一返回
    Redis学习之旅 管道/脚本/过期/事务_第1张图片

  • 使用管道后,命令的处理方式可能就变成 1.传输10条命令-2.做十次计算-3.返回结果,通过管道技术,将网络的请求减少,提升处理的速度
    Redis学习之旅 管道/脚本/过期/事务_第2张图片

  • 优点:版本支持早,基本上任何版本的redis都可以直接运行管道,提升性能

  • 缺点:不支持批量操作时,使用运算中间产生的结果,即语句间需要相互无关

Lua脚本

管道技术不支持使用上一条运算的结果的情况,可以使用lua脚本进行解决,Lua脚本可以理解为一个原子性的语句集合,在脚本执行中不会插入其他线程输入的命令或者脚本,因此对于其他请求线程来说,要么脚本处于未执行状态,要么处于执行完毕状态
因此这个特性决定了,你需要熟悉每个命令可能的开销,并尽量去规避一些特别缓慢的操作,因为这会引起其他的线程阻塞,引起吞吐量的下降

命令解释

可以看到,lua脚本相关的命令都是在redis2.6.0版本开始引入,参数的结构基本一致
Redis学习之旅 管道/脚本/过期/事务_第3张图片

EVAL script numkeys key [key …] arg [arg …]

eval 是命令关键字 不解释了
script 是一段lua脚本的命令
numKeys 告诉lua我后面会跟上几个现在redis库里的key,这些key在前面的lua脚本里使用
key 将在lua脚本内使用的key,配合numkeys告诉lua脚本从哪截断,在lua脚本里通过KEYS[1],KEYS[2]来调用
arg 一些参数在redis库里没有对应的存储,因此通过arg传入 ,在lua脚本里通过ARGV[1],ARGV[2]来调用
Redis学习之旅 管道/脚本/过期/事务_第4张图片

redis.call() redis.pcall()

这两个命令都可以用来调用redis的命令,不同点在于call命令在出错的时候会直接将错误招聘,pcall则会将错误以lua表的形式返回
两者都可以按redis的相关命令组织命令调用,比如redis里 HSET key field value [field value …]
可以使用 eval “return redis.call(‘hset’,‘hk1’,‘name’,‘xiaoyingge’)” 0 或者 eval “return redis.call(‘hset’,‘KEYS[1]’,‘name’,‘xiaoyingge’)” 1 hk1 来调用

Redis学习之旅 管道/脚本/过期/事务_第5张图片

注意事项

在使用lua脚本时,上面的几种方式虽然都能达到同样的效果,但是一般推荐使用最后一种形式
原因有两点,一是把key放到外部,可以在集群模式下让redis知道去哪台机器取key
二是参数外部化,可以让脚本的复用性变高

问题引出

如果我的脚本命令特别复杂,可能多达上千上万个字节,难道我每次调用脚本都要传输如此大量的脚本文件内容?

EVALSHA sha1 numkeys key [key …] arg [arg …]

命令和eval命令没有区别 ,只是把script换成了sha校验和

Q1:脚本是何时缓存下来的?

  • Redis 保证所有被运行过的脚本都会被永久保存在脚本缓存当中,这意味着,当 EVAL 命令在一个 Redis 实例上成功执行某个脚本之后,随后针对这个脚本的所有 EVALSHA 命令都会成功执行

Q2:redis怎么知道我在调用哪个脚本?

  • 计算一个sha值,下次请求只要使用sha值去检查下有没有就可以执行,如果没有对应的脚本,会返回一个Noscript的错误

Q3:脚本如果更新了,怎么清理缓存?

  • SCRIPT FLUSH :清除所有脚本缓存
  • SCRIPT EXISTS :根据给定的脚本校验和,检查指定的脚本是否存在于脚本缓存
  • SCRIPT LOAD :将一个脚本装入脚本缓存,但并不立即运行它
  • SCRIPT KILL :杀死当前正在运行的脚本

Q4:可以在管道内使用evalsha么?

  • 可以,但是要特别小心,因为在管道中,必须保证命令的执行顺序
  • 一旦在管道中因为 EVALSHA 命令而发生 NOSCRIPT 错误,那么这个管道就再也没有办法重新执行了,否则的话,命令的执行顺序就会被打乱
  • 可以考虑使用eval命令替代evalsha

过期 EXPIRE / EXPIRE AT /PERSIST

现在主流都是将redis作为缓存服务器,而不是数据库服务器,这也就意味着缓存的数据数量是远远小于数据库内容的,因此缓存服务器提供的是热Key的保存与查询,一些过期的key就要面临淘汰
EXPIRE和 PERSIST是一对命令,前者是将键设置为带过期时间的key,在key过期后,就不能再访问到里面的数据(但是键不一定删除了),PERSIST的作用就是将临时key改为永久key

Redis学习之旅 管道/脚本/过期/事务_第6张图片

如何淘汰过期数据?

  • 被动式:在键过期后,下一次访问时检查发现键已过期 ,删除对应的键值
  • 主动式:每100毫秒随机取20个key进行过期检查,发现有过期键就删除,如果本次抽检有超过5个键(25%)是过期键,再重新做一次

内存快满时,redis如何回收空间?Redis的回收策略?

  • 有如下几个回收策略
  • noeviction:不处理,写入指令报错
  • allkeys-lru: 尝试回收最少使用的键(LRU),会淘汰永久数据
  • volatile-lru: 尝试回收最少使用的键(LRU),淘汰已过期的数据
  • allkeys-random: 随机回收,会淘汰永久数据
  • volatile-random: 随机回收,淘汰已过期的数据
  • volatile-ttl: 回收在过期集合的键,可能还没到过期时间就被干掉了

事务

提起事务,就容易让人想起数据库的事务,ACID特性等等,但是redis的事务只是一种弱事务,没有回滚之类的相关机制
事务内的语句可以顺序的,不被打断的执行,可能会有个疑问,这和管道有啥区别?有区别!管道或者脚本是一次性将命令输入并执行,事务是多次网络连接将命令一条条的发送过来,然后再执行的。
使用 AOF 方式做持久化的时候, Redis 会使用单个 write(2) 命令将事务写入到磁盘中

命令

  • MULTI:事务开启标记
  • EXEC:事务提交标记
  • DISCARD:取消标记
  • WATCH:监听标记

命令集中发现错误怎么处理?

  • redis会放弃数据处理 ,且数据的处理是以exec命令提交时刻来计算的,并非以multi开启事务时刻的数据来计算的
  • 因此可能出现一种情况,就是在事务的代码发送期间,比如需要读取一个键k1,但是其他的线程在你的线程exec执行将就del k1了,等你exec时,你就发现取到了一个nil值,有点类似于脏读、幻读的意思
  • 如果命令集中本身就有错误,redis会放弃事务执行

为啥没有回滚机制?

  • 因为redis的设计目标是一个简单快速的K-V存储,如果尽最大可能保持快才是它的设计目标,它的功能设计目标也是尽可能在不影响性能的情形下去解决应用的问题,如果真的需要事务,可以由开发者自行实现或者使用支持事务的工具来实现,redis并不是主要来保证事务的

乐观锁实现

WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。

被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。 如果有至少一个被监视的键在 EXEC 执行之前被修改了, 那么整个事务都会被取消, EXEC 返回nil-reply来表示事务已经失败。

你可能感兴趣的:(#,Redis)