Redis核心命令以及技术方案参考文档(分布式锁,缓存业务逻辑)

文章目录

Redis核心命令以及技术方案参考文档(分布式锁,缓存业务逻辑)

redis基础命令操作

一、Redis 通用命令

命令 功能描述 示例
select <db-index> 切换数据库(Redis 默认 16 个库,索引从 0 开始) select 1 (切换到第 2 个数据库)
DBSIZE 查看当前数据库中 key 的数量 DBSIZE
set <key> <value> 设置指定 key 的 value set username mike
get <key> 获取指定 key 的 value get username
keys * 获取当前数据库中所有 key keys *
flushdb 清空当前数据库的所有数据 flushdb
flushall 清空所有数据库的所有数据 flushall

二、Redis 基本命令

命令 功能描述 示例
exists <key> 查询指定 key 是否存在 exists username
move <key> <db-index> 将指定 key 移动到目标数据库 move username 1
expire <key> <seconds> 设置 key 的过期时间(单位:秒) expire username 10
ttl <key> 查看 key 的剩余过期时间(-1 = 永不过期,-2 = 已过期) ttl username
type <key> 查看 key 对应值的数据类型 type username

三、Redis 五种数据结构命令

1.String(字符串)类型
命令 功能描述 示例
set <key> <value> 设置字符串值 set name htt
get <key> 获取字符串值 get name
append <key> <suffix> 拼接字符串到指定 key 的 value 末尾 append name study
strlen <key> 获取 value 的长度 strlen name
incr <key> 数值型 value 自增 1(value 非数值会报错) incr view
decr <key> 数值型 value 自减 1 decr view
incrby <key> <num> 数值型 value 自增指定数值 incrby view 10
decrby <key> <num> 数值型 value 自减指定数值 decrby view 10
getrange <key> <start> <end> 截取字符串(闭区间,下标从 0 开始) getrange name 0 3
setrange <key> <offset> <value> 从指定下标替换字符串 setrange name 1 000
setex <key> <seconds> <value> 设置值并指定过期时间 setex name 10 hello
setnx <key> <value> 仅当 key 不存在时设置值(原子操作) setnx title redis
mset <k1> <v1> <k2> <v2> ... 批量设置多个键值对 mset k1 v1 k2 v2 k3 v3 / mset user:1:name htt user:1:age 2
mget <k1> <k2> ... 批量获取多个 key 的值 mget k1 k2 k3 / mget user:1:name user:1:age
msetnx <k1> <v1> <k2> <v2> ... 批量设置(所有 key 都不存在才成功,原子性) msetnx k1 v1 k4 v4
getset <key> <new-value> 获取原 value 并设置新 value(key 不存在返回 nil) getset username htt
2.List(列表)类型
命令 功能描述 示例
lpush <list-key> <value> 从列表头部插入值 lpush list 1
rpush <list-key> <value> 从列表尾部插入值 rpush list 4
lrange <list-key> <start> <end> 获取指定区间的元素(0=-1 表示所有) lrange list 0 -1
lpop <list-key> 移除并返回列表第一个元素 lpop list
rpop <list-key> 移除并返回列表最后一个元素 rpop list
lindex <list-key> <index> 通过下标获取列表中的元素 lindex list 0
llen <list-key> 获取列表长度 llen list
lrem <list-key> <count> <value> 移除指定个数的匹配元素(count=1 移除 1 个) lrem list 1 2
ltrim <list-key> <start> <end> 截取指定区间元素并覆盖原列表 ltrim list 1 2
lset <list-key> <index> <value> 更新指定下标的元素(下标不存在报错) lset list 0 bbb
linsert <list-key> BEFORE/AFTER <pivot> <value> 在指定元素前 / 后插入值 linsert list BEFORE kkk aaa / linsert list AFTER kkk aaa
3.Set(集合)类型(无序、唯一)
命令 功能描述 示例
sadd <set-key> <value> 向集合添加元素 sadd set hello
smembers <set-key> 查看集合所有元素 smembers set
sismember <set-key> <value> 判断元素是否在集合中(返回 1 = 存在,0 = 不存在) sismember set world
srandmember <set-key> [count] 随机抽取指定个数元素(默认 1 个) srandmember set / srandmember set 2
spop <set-key> 随机删除并返回集合中的一个元素 spop set
smove <src-set> <dst-set> <value> 将元素从一个集合移动到另一个集合 smove set set2 world
sdiff <set1> <set2> 求两个集合的差集(set1 有、set2 无) sdiff set2 set
sinter <set1> <set2> 求两个集合的交集 sinter set set2
sunion <set1> <set2> 求两个集合的并集(去重) sunion set set2
4.Hash(哈希)类型(键值对的集合)
命令 功能描述 示例
hset <hash-key> <field> <value> 向哈希表添加字段和值 hset hash username mike
hget <hash-key> <field> 获取哈希表指定字段的值 hget hash username
hmset <hash-key> <f1> <v1> <f2> <v2> ... 批量添加哈希表字段和值 hmset hash username jack age 2
hmget <hash-key> <f1> <f2> ... 批量获取哈希表指定字段的值 hmget hash username age
hgetall <hash-key> 获取哈希表所有字段和值 hgetall hash
hdel <hash-key> <field> 删除哈希表指定字段 hdel hash username
hlen <hash-key> 获取哈希表字段数量 hlen hash
hexists <hash-key> <field> 判断哈希表指定字段是否存在 hexists hash username
hkeys <hash-key> 获取哈希表所有字段名 hkeys hash
hvals <hash-key> 获取哈希表所有字段值 hvals hash
hincrby <hash-key> <field> <num> 哈希表数值型字段自增指定数值 hincrby hash views 1
hsetnx <hash-key> <field> <value> 仅当字段不存在时设置值 hsetnx hash password 123456
5.Zset(有序集合)类型(有序、唯一,通过分数排序)
命令 功能描述 示例
zadd <zset-key> <score> <value> 添加元素(score 为排序依据) zadd zset 1 first
zadd <zset-key> <s1> <v1> <s2> <v2> ... 批量添加元素 zadd zset 2 second 3 third 4 four
zrange <zset-key> <start> <end> 获取指定区间元素(按 score 升序) zrange zset 0 -1
zrangebyscore <zset-key> <min> <max> 按 score 范围获取元素(升序) zrangebyscore zset -inf +inf
zrangebyscore <zset-key> <min> <max> withscores 按 score 范围获取元素并显示分数 zrangebyscore zset -inf +inf withscores
zrangebyscore <zset-key> <min> <max> withscores 按指定 score 范围获取元素 zrangebyscore zset -inf 1 withscores
zrem <zset-key> <value> 移除指定元素 zrem zset four
zcard <zset-key> 获取有序集合元素个数 zcard zset
zrevrange <zset-key> <start> <end> 按 score 降序获取指定区间元素 zrevrange zset 1 2

redis分布式锁

一、先明确分布式锁的核心诉求

在分布式系统中,多台机器 / 进程同时操作同一资源(比如扣库存、创建订单),需要分布式锁保证:

  • 互斥性:同一时刻只有 1 个客户端能拿到锁

  • 安全性:不能误删别人的锁,也不能自己的锁被别人删

  • 容错性:客户端宕机 / 网络中断,锁必须能自动释放,避免死锁

  • 可用性:获取锁失败时,能合理重试,符合业务场景

二、加锁逻辑(核心)

1.加锁的核心命令拆解

加锁的核心是执行 Redis 命令:

bash 复制代码
SET lock_key unique_value NX PX 30000

对应python代码:

python 复制代码
redis_client.set("lock_key", "unique_value", nx=True, px=30000)
部分 作用 为什么必须这么设计?
lock_key 锁的标识(比如order:create:1001,对应订单 1001 的创建锁) 业务维度隔离,不同业务用不同 key,避免锁冲突
unique_value 每个客户端的唯一标识(通常是 UUID / 雪花 ID / 客户端 IP + 进程 ID) 解锁时要验证这个值,确保 "谁加的锁,谁才能解",防止误删别人的锁
NX 仅当 key 不存在时才设置成功(等价于 Python 的nx=True 核心互斥逻辑:如果 key 已存在(有人拿了锁),则设置失败,保证同一时刻只有 1 个客户端拿到锁
PX 30000 设置锁的过期时间为 30000 毫秒(30 秒,等价于 Python 的px=30000 容错性:如果客户端拿到锁后宕机,Redis 会自动删除过期的 key,释放锁,避免死锁
2.加锁的完整流程(带细节)

我用流程图展示加锁的完整逻辑,再用文字解释:
成功(返回OK)
失败(返回nil)


客户端发起加锁请求
生成唯一标识unique_value
执行SET命令:SET lock_key unique_value NX PX expire_ms
命令执行结果?
加锁成功,执行业务逻辑
进入重试逻辑
是否还有重试次数?
等待指定间隔(如500ms)
加锁失败,返回业务失败

关键细节补充:

  1. unique_value 的生成规则(示例):

    python 复制代码
    import uuid
    import os
    # 组合客户端IP+进程ID+UUID,确保绝对唯一,伪代码
    unique_value = f"{client_ip}_{os.getpid()}_{uuid.uuid4()}"

    为什么不只用 UUID?------UUID 本身已足够唯一,但加客户端 / 进程信息,便于排查问题(比如日志里能看到哪个客户端拿了锁)。

  2. 过期时间的选择

    • 不能太短:如果业务逻辑执行需要 20 秒,锁只设 10 秒,会导致 "锁提前释放",其他客户端拿到锁,出现并发问题;
    • 不能太长:如果客户端宕机,锁过期时间太长会导致 "锁迟迟不释放",影响业务可用性;
    • 建议值:基于业务压测结果,取 "平均执行时间 + 冗余时间"(比如平均执行 5 秒,设 10 秒)。
  3. 加锁失败的直接后果:如果不加重试,直接返回失败,在高并发场景(比如秒杀)会导致 "用户点击一次,直接提示失败",体验差;但重试也不能无限制,否则会导致大量请求阻塞。

三、解锁逻辑:最容易踩坑的一步,原子性是关键

1.错误的解锁方式(新手最容易犯的错)

新手可能会这么写解锁代码:

python 复制代码
# 错误示例!!!
def wrong_release(redis_client, lock_key, unique_value):
    # 步骤1:先查锁的value
    current_value = redis_client.get(lock_key)
    # 步骤2:如果匹配,就删除
    if current_value == unique_value:
        redis_client.delete(lock_key)
    return True

为什么错?------ 没有原子性,会导致 "误删别人的锁"

模拟一个并发场景:

时间 客户端 A 客户端 B Redis 状态
T1 拿到锁,设置过期时间 10 秒 - lock_key = A 的 value,过期时间 10 秒
T9 get (lock_key) lock_key = A 的 value,过期时间 1秒
T10 业务还在执行,但锁已过期 - Redis 自动删除 lock_key
T11 delete(lock_key) 执行 SET 命令,拿到锁 lock_key = B 的 value,过期时间 10 秒
T12 执行 get (lock_key),返回 B 的 value?不!啥也没有 - 没有锁

核心问题:在你get和delete 的中间是有一个延迟误差的,有可能其他的客户端用户就在这个延迟误差中间获取到了对应的锁,而这一次的delete操作就直接将这把新的锁给删了。

2.正确的解锁方式:Lua 脚本保证原子性

Lua 脚本的作用是让 "判断 value + 删除 key" 变成一个原子操作(Redis 执行 Lua 脚本时,不会被其他命令打断)。

Lua 脚本代码拆解:

lua 复制代码
-- 解锁的Lua脚本
if redis.call('get', KEYS[1]) == ARGV[1] then
    -- 步骤1:判断value是否匹配(自己的锁)
    return redis.call('del', KEYS[1])  -- 步骤2:匹配则删除,返回1
else
    return 0  -- 不匹配则返回0,不删除
end

对应 Python 代码:

python 复制代码
def release_lock(redis_client, lock_key, unique_value):
    # 定义Lua脚本
    unlock_script = """
    if redis.call('get', KEYS[1]) == ARGV[1] then
        return redis.call('del', KEYS[1])
    else
        return 0
    end
    """
    # 执行Lua脚本(原子操作)
    # KEYS[1] = lock_key,ARGV[1] = unique_value
    result = redis_client.eval(
        unlock_script,
        keys=[lock_key],  # 传KEYS数组
        args=[unique_value]  # 传ARGV数组
    )
    # result=1:解锁成功;result=0:解锁失败(锁已过期/不是自己的锁)
    return result == 1
3.解锁的完整流程

1(删除成功)
0(删除失败)
客户端执行完业务逻辑
调用解锁函数
执行Lua脚本:判断value是否等于自己的unique_value
脚本返回值?
解锁成功,锁释放
解锁失败(原因:锁已过期/被别人拿了)

关键细节补充:

  1. 解锁必须放在 finally 块:

    无论业务逻辑是否抛出异常,都要尝试解锁,否则会导致锁被长时间持有(直到过期)。示例:

    python 复制代码
    try:
        if lock.acquire():
            # 执行业务逻辑(可能抛异常)
            do_business()
    finally:
        # 无论是否异常,都解锁
        lock.release()
  2. 解锁失败的处理:

    如果解锁返回 0(失败),不需要 panic,大概率是 "锁已过期自动释放",只需记录日志即可,不影响业务。

四、重试逻辑:平衡用户体验和系统压力

1.重试逻辑的设计原则

重试不是 "无脑循环",要满足:

  • 有上限:不能无限重试,否则会导致大量请求阻塞,拖垮系统;
  • 有间隔:重试间隔不能太短(比如 1ms),否则会疯狂请求 Redis,造成 Redis 压力过大;
  • 可配置:重试次数和间隔要能根据业务调整(比如秒杀场景重试次数多,间隔短;普通场景重试次数少,间隔长)。
2.重试逻辑的优化(进阶)
  1. 指数退避重试:

    重试间隔随次数增加而变长(比如第一次 0.5 秒,第二次 1 秒,第三次 2 秒),减少 Redis 压力:

    python 复制代码
    # 指数退避示例
    retry_interval = 0.5 * (2 ** retry_count)
    # 限制最大间隔(比如不超过5秒)
    retry_interval = min(retry_interval, 5)
  2. 非阻塞重试:如果是异步场景(比如消息队列消费),可以把重试逻辑放到异步任务中,不阻塞当前请求。

五、完整的分布式锁实现

python 复制代码
import redis
import uuid
import time
import os


class RedisDistributedLock:
    """Redis分布式锁(兼容redis-py 4.x+版本)"""

    def __init__(
            self,
            redis_client: redis.Redis,
            lock_key: str,
            expire_ms: int = 30000,
            retry_times: int = 3,
            retry_interval: float = 0.5,
            use_exponential_backoff: bool = True  # 是否开启指数退避重试
    ):
        self.redis_client = redis_client
        self.lock_key = lock_key
        self.expire_ms = expire_ms
        self.retry_times = retry_times
        self.retry_interval = retry_interval
        self.use_exponential_backoff = use_exponential_backoff
        self.unique_value = self._generate_unique_value()  # 生成唯一标识
        self.locked = False  # 标记是否已拿到锁

    def _generate_unique_value(self) -> str:
        """生成客户端唯一标识(便于排查问题)"""
        process_id = os.getpid()  # 进程ID
        uuid_str = str(uuid.uuid4())  # UUID
        return f"{process_id}_{uuid_str}"

    def acquire(self) -> bool:
        """获取锁(带重试)"""
        retry_count = 0

        while retry_count < self.retry_times:
            # 执行加锁命令(兼容redis-py 4.x+的写法)
            result = self.redis_client.set(
                name=self.lock_key,
                value=self.unique_value,
                nx=True,
                px=self.expire_ms
            )

            if result:
                self.locked = True
                print(f"获取锁成功(锁key:{self.lock_key},唯一标识:{self.unique_value})")
                return True

            # 加锁失败,计算重试间隔
            retry_count += 1
            if retry_count >= self.retry_times:
                break

            # 指数退避或固定间隔
            if self.use_exponential_backoff:
                current_interval = min(self.retry_interval * (2 ** (retry_count - 1)), 5)
            else:
                current_interval = self.retry_interval

            print(f"获取锁失败,{current_interval:.2f}秒后重试(剩余次数:{self.retry_times - retry_count})")
            time.sleep(current_interval)

        print(f"获取锁失败(重试{self.retry_times}次,锁key:{self.lock_key})")
        return False

    def release(self) -> bool:
        if not self.locked:
            print("未持有锁,无需释放")
            return False

        # 解锁Lua脚本(逻辑不变)
        unlock_script = """
        if redis.call('get', KEYS[1]) == ARGV[1] then
            return redis.call('del', KEYS[1])
        else
            return 0
        end
        """

        try:
            # eval(script, numkeys, *keys_and_args)
            # numkeys=1 表示KEYS数组长度为1,后续先传KEYS元素,再传ARGV元素
            result = self.redis_client.eval(
                unlock_script,
                1,  # numkeys:KEYS的长度
                self.lock_key,  # KEYS[1]
                self.unique_value  # ARGV[1]
            )

            if result == 1:
                self.locked = False
                print(f"释放锁成功(锁key:{self.lock_key})")
                return True
            else:
                print(f"释放锁失败:锁已过期或不是当前客户端持有(锁key:{self.lock_key})")
                return False
        except Exception as e:
            print(f"释放锁异常:{e}")
            return False

    # 优化上下文管理器:获取锁失败时抛出异常,避免进入with块
    def __enter__(self):
        if not self.acquire():
            raise RuntimeError(f"获取锁失败(锁key:{self.lock_key})")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.release()


# ------------------- 使用示例 -------------------
if __name__ == "__main__":
    # 初始化Redis客户端
    redis_client = redis.Redis(
        host="localhost",
        port=6379,
        db=0,
        decode_responses=True,
        # 如果Redis有密码,添加password参数
        # password="your_redis_password",
        # 连接超时设置
        socket_timeout=5,
        socket_connect_timeout=5
    )

    # 创建锁实例(开启指数退避重试)
    lock = RedisDistributedLock(
        redis_client=redis_client,
        lock_key="stock:deduct:1001",  # 扣减库存的锁
        expire_ms=10000,
        retry_times=5,
        retry_interval=0.2,
        use_exponential_backoff=True
    )

    # 使用with语句(自动加锁/解锁,获取失败会抛异常)
    try:
        with lock:
            print("执行业务逻辑:扣减库存...")
            time.sleep(3)
    except RuntimeError as e:
        print(e)
        print("无法执行库存扣减操作")

Redis缓存的业务逻辑设计非常丰富,我来详细解释几种典型的业务场景和实现逻辑。

缓存业务存在的核心问题

缓存业务存在的三个核心问题:

问题类型 核心定义 发生场景 核心危害
缓存穿透 请求缓存和数据库都不存在的 key,请求直接打到数据库 恶意攻击(如批量请求不存在的用户 ID)、业务 bug 数据库被大量无效请求压垮
缓存击穿 热点 key突然过期,大量请求瞬间打到数据库 秒杀商品、首页热点数据的缓存过期 数据库瞬时压力骤增,可能宕机
缓存雪崩 大量 key同时过期,或 Redis 集群宕机 缓存 key 集中设置相同过期时间、Redis 主从切换 / 集群故障 数据库被海量请求压垮,系统雪崩

一、缓存穿透:防护方案(从易到难)

1. 核心思路

  • 拦截无效请求:让不存在的 key "止步于缓存层",不打到数据库;
  • 避免空值缓存的内存浪费:设置短期过期时间。

2. 防护方案(按优先级排序)

方案 1:缓存空值(最简单,适用于大部分场景)

  • 逻辑 :数据库查询不到数据时,往缓存中写入一个 "空值"(如None),并设置短期过期时间(如 60 秒);
  • 优点:实现简单,无需额外组件;
  • 缺点:会缓存大量空值,占用少量内存(短期过期可缓解)。

方案 2:布隆过滤器(高效拦截,适用于海量 key 场景)

  • 逻辑:提前将所有有效 key 存入布隆过滤器,请求先通过布隆过滤器判断 key 是否存在,不存在则直接返回;
  • 优点:空间效率极高,查询速度快;
  • 缺点:存在误判率(可接受),不支持删除(需用 Counting Bloom Filter)。

二、缓存击穿:防护方案(针对热点 key)

1.核心思路

  • 避免热点 key 过期:要么永不过期,要么过期时 "串行更新";
  • 防止大量请求同时打到数据库:用分布式锁控制更新缓存的并发。

2.防护方案(按优先级排序)

方案 1:热点 key 永不过期(最简单,推荐)

  • 逻辑:热点 key 不设置过期时间,由后台定时任务主动更新缓存;
  • 优点:彻底避免过期导致的击穿;
  • 缺点:需维护定时任务,缓存数据可能有短暂不一致(可接受)。

方案 2:互斥锁更新缓存(分布式锁)

  • 逻辑:缓存未命中时,只有一个请求能获取锁并更新缓存,其他请求等待或降级;
  • 优点:无需修改过期策略,适配所有场景;
  • 缺点:增加分布式锁的开销,需处理锁失败的降级逻辑。

方案 3:缓存预热 + 过期时间随机化

  • 逻辑:提前加载热点 key 到缓存,且过期时间加随机值(如 3600±600 秒),避免集中过期;
  • 优点:辅助方案,降低击穿概率;
  • 缺点:无法完全避免(如热点 key 突然被大量访问)。

三、缓存雪崩:防护方案(系统性防护)

核心思路

  • 避免大量 key 同时过期:过期时间随机化;
  • 提高 Redis 可用性:主从复制、哨兵、集群;
  • 降级兜底:Redis 故障时,限制请求流量,保护数据库。

防护方案(组合使用)

方案 1:过期时间随机化(核心)

  • 逻辑:给每个 key 的过期时间增加随机值(如 3600±600 秒),避免集中过期;

  • 代码示例:

    python 复制代码
    import random
    # 设置缓存时,过期时间加随机值
    expire_seconds = 3600 + random.randint(-600, 600)
    redis_client.setex(cache_key, expire_seconds, json.dumps(data))

方案 2:Redis 高可用架构(必须)

  • 主从复制:主库写,从库读,主库故障时手动切换;
  • 哨兵(Sentinel):自动监控主从状态,故障时自动切换;
  • Redis Cluster:分片存储,单节点故障不影响整体服务。

方案 3:多级缓存(本地缓存 + Redis)

  • 逻辑:在应用层增加本地缓存(如 Caffeine、Guava Cache),Redis 故障时,优先读取本地缓存;
  • 优点:降低 Redis 压力,提高可用性;
  • 缺点:本地缓存数据可能不一致(短期可接受)。

方案 4:熔断降级(最终兜底)

  • 逻辑:使用 Sentinel/Hystrix 等组件,当 Redis 故障或数据库压力过高时,触发熔断,返回默认值或提示 "服务繁忙";
  • 核心指标:QPS、响应时间、错误率。

缓存的基本业务逻辑

一、 读缓存流程(Cache-Aside Pattern)

读缓存的图示逻辑:








开始:接收查询请求(user_id)
构造缓存key:user:{user_id}
查询Redis缓存
缓存是否命中?
返回缓存中的用户数据
结束
构造分布式锁key:lock:user:{user_id}
尝试获取分布式锁
获取锁是否成功?
直接查询数据库
返回数据库查询结果
双重检查:再次查询Redis缓存
缓存是否命中?
查询数据库
数据库是否存在该用户?
缓存空值(expire=60s)
返回None
释放分布式锁
将用户数据写入Redis(expire=3600s)
返回用户数据

python 复制代码
def get_user_info(user_id: int) -> dict:
    """获取用户信息(缓存穿透/击穿/雪崩防护)"""
    cache_key = f"user:{user_id}"
    
    # 1. 先查缓存
    cached_data = redis_client.get(cache_key)
    if cached_data:
        print(f"缓存命中:{cache_key}")
        return json.loads(cached_data)
    
    print(f"缓存未命中:{cache_key}")
    
    # 2. 缓存未命中,查数据库(分布式锁防止缓存击穿)
    lock_key = f"lock:user:{user_id}"
    try:
        with RedisDistributedLock(redis_client, lock_key, expire_ms=3000):
            # 2.1 双重检查(Double Check)
            cached_data = redis_client.get(cache_key)
            if cached_data:
                return json.loads(cached_data)
            
            # 2.2 查询数据库
            user_data = db.query("SELECT * FROM users WHERE id = %s", user_id)
            if not user_data:
                # 缓存空值防止缓存穿透
                redis_client.setex(cache_key, 60, json.dumps(None))
                return None
            
            # 2.3 写入缓存
            redis_client.setex(
                cache_key, 
                3600,  # 1小时过期
                json.dumps(user_data)
            )
            return user_data
    except RuntimeError:
        # 获取锁失败,直接查数据库(降级策略)
        print("获取锁失败,降级查数据库")
        return db.query("SELECT * FROM users WHERE id = %s", user_id)

二、 写缓存流程

在读写分离架构中,存在主从同步延迟:

text 复制代码
主库(Master)--同步延迟--> 从库(Slave)
     ↓写数据           ↓读数据

写缓存流程示例图:


开始:接收更新请求(user_id, data)
更新数据库(UPDATE users)
数据库更新是否成功?
返回更新失败
结束
构造缓存key:user:{user_id}
立即删除Redis缓存(第一次删除)
启动异步线程执行延迟删除
异步线程:延迟500ms
异步线程:再次删除Redis缓存(第二次删除)
异步线程结束
返回更新成功

python 复制代码
def update_user_info(user_id: int, data: dict) -> bool:
    """更新用户信息(双写策略)"""
    # 1. 更新数据库
    success = db.update("UPDATE users SET ... WHERE id = %s", user_id, data)
    if not success:
        return False
    
    # 2. 更新缓存(延迟双删策略)
    cache_key = f"user:{user_id}"
    
    # 2.1 先删除缓存
    redis_client.delete(cache_key)
    
    # 2.2 更新数据库(上面已做)
    
    # 2.3 延迟再次删除(异步任务)
    def delayed_delete():
        time.sleep(0.5)  # 延迟500ms
        redis_client.delete(cache_key)
    
    threading.Thread(target=delayed_delete, daemon=True).start()
    
    return True
相关推荐
汉堡go1 小时前
python_chapter3
开发语言·python
Wang's Blog1 小时前
Kafka: 动态配置刷新与分布式配置管理深度实践
分布式·kafka
MoonBit月兔2 小时前
海外开发者实践分享:用 MoonBit 开发 SQLC 插件(其三)
java·开发语言·数据库·redis·rust·编程·moonbit
天呐草莓2 小时前
企业微信运维手册
java·运维·网络·python·微信小程序·企业微信·微信开放平台
灵感菇_2 小时前
Android图片加载框架 Glide全面解析
android·缓存·glide
2501_921649492 小时前
股票 API 对接, 接入德国法兰克福交易所(FWB/Xetra)实现量化分析
后端·python·websocket·金融·区块链
lbb 小魔仙2 小时前
Python 读取 Excel 文件:openpyxl 与 pandas 实战对比
python·excel·pandas
热爱生活的五柒2 小时前
两个电脑(windows和linux之间)如何快速传输文件(亲测可用,方便快捷)
python·共享文件夹
@淡 定2 小时前
缓存原理详解
java·spring·缓存