我该怎么用我的Redis(三) | 青训营

85 阅读1分钟

使用场景

限流

业务需求

我们需要对业务进行限流,比如我们要求1秒内只能让某个服务访问 N 次,如果超过 N 那么就紧张访问,或者让他们等待。

业务代码

基本的思路是使用一个 Key + Incr 操作来完成:

例如:

  • Key: comment_freq_limit_xxx (timestamp)

对这个Key调用Incr,超过限制N则禁止访问。

数据结构

是我们(一)中所讲到的 String 结构

分布式锁

业务需求

并发场景,要求一次只能由一个协程执行,执行完成后其他在等待的协程才能够执行。

业务代码

func ex02Work(ctx context.Context, cInstParam common.CInstParams) {
	routine := cInstParam.Routine
	eventLogger := cInstParam.ConcurrentEventLogger
	defer ex02ReleaseLock(ctx, routine, eventLogger)
	for {
		acquired, err := RedisClient.SetNX(ctx, resourceKey, routine, exp).Result()
		if err != nil {
			eventLogger.Append(common.EventLog{
				EventTime: time.Now(), Log: fmt.Sprintf("[%s] error routine[%d], %v", time.Now().Format(time.RFC3339Nano), routine, err),
			})
			panic(err)
  }
		if acquired {
			eventLogger.Append(common.EventLog{
				EventTime: time.Now(), Log: fmt.Sprintf("[%s] routine[%d] 获取锁", time.Now().Format(time.RFC3339Nano), routine),
			})
			time.Sleep(10 * time.Millisecond)
			eventLogger.Append(common.EventLog{
				EventTime: time.Now(), Log: fmt.Sprintf("[%s] routine[%d] 完成业务逻辑", time.Now().Format(time.RFC3339Nano), routine),
			})
			return
		} else {
			time.Sleep(100 * time.Millisecond)
		}
	}
}

然后我们在做一个释放 SetNX 的逻辑即可。

可以使用 Redis 的 SetNX 实现,利用的特性有:

  • Redis 单线程执行操作
  • SetNX 只有没有设置过才能够执行成功

但是上面使用的 SetNX 也不能够做成真正的高可用的锁,有以下实现上的问题:

  • 业务超时解锁,导致并发问题。业务执行时间超过锁超时时间
  • redis主备切换临界点问题。主备切换后,A持有的锁还未同步到新的主节点时,B可在新主节点获取锁,导致并发问题。
  • redis集群脑裂,导致出现多个主节点