• redis的使用场景及涉及到的一些问题


    缓存

    也不知道下面三个概念一开始是谁提出来的,归类方式很不科学…

    缓存穿透

    请求了redis和DB中不存在的数据(无效请求),导致请求redis直接访问DB

    方案
    • 将无效返回值存入redis,使无效请求不会访问DB
    • 在应用层拦截请求,例如逻辑校验、布隆过滤器…
    缓存雪崩

    集中创建redis缓存,导致缓存集中失效,大量访问导致DB周期性压力

    方案
    • 在预设定的ttl上添加一个随机值,打散key的失效时刻
    缓存击穿

    热点数据(热key)失效时刻导致DB巨大压力

    方案
    • 设置缓存不要失效
    • 锁/队列

    队列

    使用list类型的LPUSH+BRPOPLPUSH+LREM可以构成一个简单的消息队列,相比lpush+rpop的实现多了ack机制

    简单流程:

    1. producer使用LPUSH发送消息(send)
    2. consumer使用BRPOPLPUSH接收消息(consume)
    3. consumer处理完消息后使用LREM确认消息(acknowledge)
    LPUSH queue message
    BRPOPLPUSH queue process_queue
    LREM process_queue -1 message
    

    其中涉及几个细节:

    • 调用BRPOPLPUSH时,如果一直阻塞可能导致redis主动断开连接,抛异常。需要做个重试逻辑
    • 如果根据key做了分片,需要要保证两个list在同一分片

    延时队列

    使用zset类型的ZADD+ZREVRANGEBYSCORE+ZREM可以构成一个简单的延时队列。生产者将需要处理消息时刻作为zset中的score,消费者则轮询zset中score大于now的消息进行处理

    分布式锁

    可重入性

    如果不关注锁的可重入性,那么加锁逻辑很简单:

    SET key_name constant_value NX PX ttl
    

    如果需要可重入性的话,为了原子性必须借助lua,以下给出Spring提供的加锁lua

    local lockClientId = redis.call('GET', KEYS[1])
    if lockClientId == ARGV[1] then
      redis.call('PEXPIRE', KEYS[1], ARGV[2])
    elseif not lockClientId then
      redis.call('SET', KEYS[1], ARGV[1], 'PX', ARGV[2])
      return true
    end
    return false
    

    Spring在锁逻辑外用ConcurrentHashMap加了一层缓存。即先在单机竞争锁,竞争到了再去分布式环境竞争锁。

    Redlock

    先前提到的都是单节点方案,而redlock是一种分布式锁的方案。在有效期内超过半数的实例获得锁则算作获得锁…
    与zookeeper的实现相比,redis更注重CAP中的AP,zookeeper更注重CP。使用redis实现分布式锁的优点在于成本更低。如果对于一致性有高要求的话(主从/多IDC)个人觉得不如选择zookeeper。
    关于redis分布式锁可见:https://redis.io/topics/distlock

  • 相关阅读:
    [java]Java中父类强制转换成子类的原则
    基于 JWT + Refresh Token 的用户认证实践
    [java]idea设置及debug使用
    [java]Class类和Object类的关系
    [java]文件操作
    [java]javabean
    [java]string类型
    [java]求数据的hmac sha1的并用base64编码
    java 多线程——一个定时调度的例子
    设计模式 4 —— 迭代器和组合模式(组合)
  • 原文地址:https://www.cnblogs.com/niceboat/p/12359608.html
Copyright © 2020-2023  润新知