Redis-实战

redis实现延迟队列

延迟队列是指把当前要做的事情,往后推迟一段时间再做。延迟队列的常见的使用场景有以下几种:

  • 在购物平台上下单,如果订单超过一定的时间后没有付款,订单会自动取消

  • 打车的时候,如果在规定的时间内,没有司机接单,那么订单会被取消

  • 点外卖时,如果商家在规定的时间没有接单,就会自动取消订单

redis是用有序集合的方式来实现延迟消息队列的,有序集合有一个Score属性可以储存延迟的时间

工作原理:使用zadd score1 value1 命令可以一直往内存中生产消息。再利用zrangebyscore查询符合条件的所有待处理的任务,通过循环执行队列任务即可

基于redis实现分布式锁

Redis本身可以被多个客户端共享访问,正好就是一个共享存储系统,可以用来保存分布式锁,而且redis的读写性能高,可以应对高并发的锁操作场景

redis使用SET命令来实现分布式锁,SET命令中有个NX的参数:

  • 如果key不存在,则显示插入成功,可以用来表示加锁成功

  • 如通key存在,则会显示插入失败,可以用来表示加锁失败

基于Redis节点实现分布式锁时,对于加锁操作,我们需要满足三个条件

  • 加锁包括了读取锁变量检查锁变量值设置锁变量值三个操作,但需要以原子操作的方式完成,所以,我们使用SET命令带上NX选项来实现加锁

  • 锁变量需要设置过期时间,以免客户端拿到锁后发生异常,导致锁一直无法释放,一般要在加锁时加上EX/PX选项,设置过期时间

  • 锁变量的值需要能区分来自不同客户端的加锁操作,以免在释放锁时,出现误释放操作,所以,在加锁时,需要给每个用户端设置一个唯一值,用于标识客户端

SET lock_key unique_value NX PX 10000 
  • lock_key就是key键

  • unique_value是客户端唯一标识符

  • NX代表只在lock_key不存在时,才对lock_key进行设置操作

  • PX表示设置过期时间10s,保证只有加锁的客户端才能释放锁

解锁是需要两个操作,需要Lua脚本来保证解锁的原子性,因为Redis在执行Lua脚本时,可以以原子 性的方式执行,保证了锁释放操作的原子性

//释放锁时,先比较unique value是否相等,避免锁的误释放
if redis.call("get" ,KEYS[1]) == ARGV[1] then
    return redis .call("del" ,KEYS[1])
else
    return 0
end
​

这样一来,就通过使用SET命令和Lua脚本在Redis单节点上完成了分布式锁的加锁和解锁

基于Redis实现分布式锁的优点:
  1. 性能高效

  2. 实现方便

  3. 避免单点故障,redis是跨集群部署的,自然就避免了单点故障

基于Redis实现分布式锁的缺点:
  1. 超时时间不好设置。锁的超时时间过长,会影响性能;时间过短,会保护不到共享资源

    • 解决方法:可以基于续约的方式设置超时时间,先给锁设置一个超时时间,然后启动一个守护线程,让守护线程去判断锁的情况,当锁快失效的时候,再次进行续约加锁,当主线程执行完成后,销毁续约即可。

  2. Redis主从复制模式的数据是异步复制的,这样导致分布式锁的不可靠性。如果在Redis主节点获取到锁后,在没有同步到其他节点时。Redis主节点宕机了,此时新的Redis主节点依然可以获取锁 ,所以多个应用服务可以同时获取到锁

Redis如何解决集群情况下分布式锁的可靠性?

采用红锁(Redlock),是由Redis官方设计的一个分布式锁算法。它基于多个Redis节点的分布式锁,即使有节点发生了故障,锁变量依然存在,客户端还是可以完成锁操作。官方推荐是至少部署5个Redis节点,而且都是主节点,他们之间没有任何关系,都是一个个孤立的节点。

Redlock 算法的基本思路就是让客户端和多个独立的Redis节点依次请求申请加锁,如果客户端能够和半数以上的节点成功地完成加锁操作,那么我们救人位,客户端成功地获得分布式锁,否则加锁失败。

Redlock 算法加锁三个过程:

  • 第一步是,客户端获取当前时间(t1)

  • 第二步是,客户端按顺序依次向N个Redis节点执行加锁操作:

    • 加锁操作使用SET命令,带上NX,EX/PX选项,以及带上客户端的唯一标识

    • 如果某个Redis节点发生了故障,为了保证在这种情况下,Redlock算法能厚朴继续运行,我们需要对【加锁操作】设置一个超时时间(不是对锁设置超时时间,而是对加锁操作设置超时时间),加锁操作的超时时间需要远远小于锁的过期时间,一般也就是设置为几十毫秒

  • 第三步是,一旦客户端从超过板书(大于等于(N/2) +1)的Redis节点上成功获取到了锁,就再次获取当前时间(t2)然后计算整个加锁过程的总耗时(t2-t1)。如果t2-t1 < 锁的过期时间,此时,认为客户端加锁成功,否则认为加锁失败

可以看到,加锁成功要同时满足两个条件:

  • 条件一:客户端从超过半数的Redis节点上成功获取到了锁

  • 条件二:客户端从大多数节点获取锁的总耗时小于锁这是的过期时间

这样以来,即使有某个Redis节点发生故障,因为锁的数据在其他节点也有保存,所以客户端仍可以正常地进行锁操作,锁的数据也不会丢失

加锁成功后,客户端需要重新计算这把锁的有效时间,计算的结果是「 锁最初设置的过期时间」减去「[客户端从大多数节点获取锁的总耗时(t2-t1) 」。如果计算的结果已经来不及完成共享数据的操作了,我们可以释放锁,以晚出现还没完成数据操作,锁就过期了的情况。

加锁失败后,客户端向所有Redis节点发起释放锁的操作,释放锁的操作和在单节点上释放锁的操作一样,只要执行释放锁的Lua脚本就可以了。

Redis的大key如何处理?

什么是大key?

大key是指key对应的value很大,一般而言,下面两种情况被称为大key:

  • String类型的值大于10KB

  • Hash、List、Set、Zset类型的元素的个数超过5000个

大key有什么影响?

大key会带来一下影响:

  • 客户端超时阻塞。由于Redis执行命令式单线程处理,然后在操作大key时会比较好使,那么就会阻塞Redis,从客户端这一视角看,就是很久没有响应

  • 引发网络阻塞。每次获取大key产生的网络流量较大,如果一个key的大小是1MB,美妙访问量为1000,那么没被秒就会产生1000MB的流量,这对于普通千兆网卡的服务器来说是灾难性的

  • 阻塞工作线程。如果使用del删除大key时,会阻塞工作线程,这样就没办法处理后续的命令

  • 内存分布不均匀。集群模型在slot分片均匀情况下,会出现数据和查询倾斜情况,不分有大key的Redis节点占用内存多,QPS(每秒查询数,用于评估系统的性能和吞吐量)也会比较大

如何找到大key?
  • redis-cli --bigkes 查找大key

    redis-cli -h 127.0.0.1 - p6379 -a ”password"bigkeys
  • 使用SCAN命令查找大key

  • 使用Rdb Tools 工具查找大key

    rdb dump.rdb -c memory --bytes 10240 -fredis. csv
如何删除大key?
  • 分批次删除,使用hscan 命令,每次获取100个字段,再用hdel(删除Hash)、ltrim(删除List)、srem(删除Set)、zremrangebyrank(删除ZSet)命令删除元素

  • 异步删除:用unlink命令代替del来删除

Redis 管道(pipeline)

使用管道和不使用管道的区别:

普通模式

管道模式

可以见得,使用管道技术可以解决多个命令执行时间的网络等待,它是把多个命令还早呢哥哥到一起发送给服务器,服务器处理后统一返回客户端,这样就避免了每条命令执行后都需要等待的情况,提高了效率,减少网络延迟

但是使用管道技术也要注意避免发送的命令过大,或者管道内的数据太多而导致的网络阻塞

Redis支持事务回滚吗?

不支持。

这里指不支持事务运行时错误的事务回滚

Redis 中虽然提供了DISCARD 命令,但这个命令只是用来主动放弃事务执行,把暂存的命令队列清空,是起不到回滚的效果的。

Why?

官方文档大致意思是

  • 原因一,他认为redis 事务的执行时,错误通常都是编程错误造成的,这种错误通常只会出现在开发环境中,而很少会在实际的生产环境中出现,所以他认为没有必要为redis开发事务回滚功能

  • 原因二,他认为这种复杂的功能和redis追求简单高效的设计主旨不符合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值