Redis 缓存使用的实践

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 《Redis缓存最佳实践指南》涵盖缓存更新策略、缓存击穿防护、大key处理和性能优化。包括Cache Aside Pattern、Write Through、分布式锁、大key拆分和批量操作等技术,帮助你在项目中高效使用Redis缓存。

Redis缓存最佳实践指南

一、缓存更新策略

1. Cache Aside Pattern(旁路缓存)

这是最常用的缓存策略,适合读多写少的场景。

class UserService:
    def get_user(self, user_id):
        # 1. 先查询缓存
        user = redis_client.get(f"user:{user_id}")
        if user:
            return json.loads(user)

        # 2. 缓存未命中,查询数据库
        user = db.query(f"SELECT * FROM users WHERE id = {user_id}")

        # 3. 写入缓存
        if user:
            redis_client.setex(
                f"user:{user_id}",
                3600,  # 过期时间1小时
                json.dumps(user)
            )
        return user

    def update_user(self, user):
        # 1. 更新数据库
        db.execute(
            "UPDATE users SET name = %s WHERE id = %s",
            (user.name, user.id)
        )

        # 2. 删除缓存
        redis_client.delete(f"user:{user.id}")

2. Write Through(读写穿透)

适合写多的场景,确保缓存与数据库的一致性。

def write_through_update(user):
    # 1. 更新数据库
    db.execute(
        "UPDATE users SET name = %s WHERE id = %s",
        (user.name, user.id)
    )

    # 2. 更新缓存
    redis_client.setex(
        f"user:{user.id}",
        3600,
        json.dumps(user)
    )

二、缓存击穿防护

1. 分布式锁实现

class RedisLock:
    def __init__(self, key, expire=60):
        self.key = f"lock:{key}"
        self.expire = expire

    def acquire(self):
        # SET NX = set if not exists
        return redis_client.set(
            self.key,
            'locked',
            ex=self.expire,
            nx=True
        )

    def release(self):
        redis_client.delete(self.key)

def get_hot_data(key):
    # 1. 查询缓存
    data = redis_client.get(key)
    if 
        return json.loads(data)

    # 2. 缓存未命中,使用分布式锁防止击穿
    lock = RedisLock(key)
    if lock.acquire():
        try:
            # 双重检查
            data = redis_client.get(key)
            if 
                return json.loads(data)

            # 查询数据库
            data = db.query_hot_data(key)

            # 写入缓存
            redis_client.setex(key, 3600, json.dumps(data))
            return data
        finally:
            lock.release()
    else:
        # 等待100ms后重试
        time.sleep(0.1)
        return get_hot_data(key)

三、大key处理方案

1. 大key识别

def scan_big_keys(pattern="*", min_size=1024*1024):
    """扫描大于指定大小的key"""
    cursor = 0
    big_keys = []

    while True:
        cursor, keys = redis_client.scan(
            cursor,
            match=pattern,
            count=1000
        )

        for key in keys:
            key_type = redis_client.type(key)
            key_size = get_key_size(key, key_type)

            if key_size > min_size:
                big_keys.append({
   
                    'key': key,
                    'type': key_type,
                    'size': key_size
                })

        if cursor == 0:
            break

    return big_keys

def get_key_size(key, key_type):
    if key_type == 'string':
        return len(redis_client.get(key))
    elif key_type == 'hash':
        return len(redis_client.hgetall(key))
    elif key_type == 'list':
        return redis_client.llen(key)
    elif key_type == 'set':
        return redis_client.scard(key)
    elif key_type == 'zset':
        return redis_client.zcard(key)

2. 大key拆分

class UserFavorites:
    def __init__(self, user_id):
        self.base_key = f"user:{user_id}:favorites"

    def add(self, item_id):
        # 使用取模方式拆分大key
        shard = item_id % 10
        key = f"{self.base_key}:{shard}"
        return redis_client.sadd(key, item_id)

    def remove(self, item_id):
        shard = item_id % 10
        key = f"{self.base_key}:{shard}"
        return redis_client.srem(key, item_id)

    def is_favorite(self, item_id):
        shard = item_id % 10
        key = f"{self.base_key}:{shard}"
        return redis_client.sismember(key, item_id)

    def get_all(self):
        # 合并所有分片的结果
        result = set()
        for i in range(10):
            key = f"{self.base_key}:{i}"
            result.update(redis_client.smembers(key))
        return result

四、性能优化建议

  1. 批量操作:使用pipeline减少网络往返

    def batch_get_users(user_ids):
     pipe = redis_client.pipeline()
    
     # 批量获取
     for user_id in user_ids:
         pipe.get(f"user:{user_id}")
    
     # 一次性执行
     results = pipe.execute()
    
     # 处理结果
     users = []
     for result in results:
         if result:
             users.append(json.loads(result))
     return users
    
  2. 序列化优化:对于频繁访问的数据,考虑使用protobuf等二进制序列化

  3. 内存优化:合理设置过期时间,避免内存泄漏

    def cache_with_variable_ttl(key, value):
     """根据数据热度设置不同的过期时间"""
     access_count = redis_client.incr(f"access:{key}")
    
     if access_count > 1000:
         ttl = 7200  # 热点数据2小时过期
     else:
         ttl = 3600  # 普通数据1小时过期
    
     redis_client.setex(key, ttl, value)
    

通过以上实践指南,您应该能更好地在项目中使用Redis缓存。记住:缓存设计没有银弹,需要根据具体场景选择合适的策略。

相关文章
|
17天前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
145 86
|
4月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
10天前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
71 1
Redis专题-实战篇二-商户查询缓存
|
4月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
708 0
|
10天前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。
|
26天前
|
存储 缓存 监控
Redis分区的核心原理与应用实践
Redis分区通过将数据分散存储于多个节点,提升系统处理高并发与大规模数据的能力。本文详解分区原理、策略及应用实践,涵盖哈希、范围、一致性哈希等分片方式,分析其适用场景与性能优势,并探讨电商秒杀、物联网等典型用例,为构建高性能、可扩展的Redis集群提供参考。
60 0
|
2月前
|
存储 缓存 NoSQL
Redis 核心知识与项目实践解析
本文围绕 Redis 展开,涵盖其在项目中的应用(热点数据缓存、存储业务数据、实现分布式锁)、基础数据类型(string 等 5 种)、持久化策略(RDB、AOF 及混合持久化)、过期策略(惰性 + 定期删除)、淘汰策略(8 种分类)。 还介绍了集群方案(主从复制、哨兵、Cluster 分片)及主从同步机制,分片集群数据存储的哈希槽算法。对比了 Redis 与 Memcached 的区别,说明了内存用完的情况及与 MySQL 数据一致性的保证方案。 此外,详解了缓存穿透、击穿、雪崩的概念及解决办法,如何保证 Redis 中是热点数据,Redis 分布式锁的实现及问题解决,以及项目中分布式锁
|
4月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
182 32
|
4月前
|
缓存 NoSQL Java
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
103 5
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
|
3月前
|
存储 缓存
.NET 6中Startup.cs文件注入本地缓存策略与服务生命周期管理实践:AddTransient, AddScoped, AddSingleton。
记住,选择正确的服务生命周期并妥善管理它们是至关重要的,因为它们直接影响你的应用程序的性能和行为。就像一个成功的建筑工地,工具箱如果整理得当,工具选择和使用得当,工地的整体效率将会大大提高。
156 0

相关产品

  • 云数据库 Tair(兼容 Redis)