Redis 缓存使用的实践

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
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缓存。记住:缓存设计没有银弹,需要根据具体场景选择合适的策略。

相关文章
|
25天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
27天前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构
|
20天前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
36 5
|
1月前
|
缓存 NoSQL 中间件
redis高并发缓存中间件总结!
本文档详细介绍了高并发缓存中间件Redis的原理、高级操作及其在电商架构中的应用。通过阿里云的角度,分析了Redis与架构的关系,并展示了无Redis和使用Redis缓存的架构图。文档还涵盖了Redis的基本特性、应用场景、安装部署步骤、配置文件详解、启动和关闭方法、systemctl管理脚本的生成以及日志警告处理等内容。适合初学者和有一定经验的技术人员参考学习。
155 7
|
1月前
|
存储 缓存 监控
利用 Redis 缓存特性避免缓存穿透的策略与方法
【10月更文挑战第23天】通过以上对利用 Redis 缓存特性避免缓存穿透的详细阐述,我们对这一策略有了更深入的理解。在实际应用中,我们需要根据具体情况灵活运用这些方法,并结合其他技术手段,共同保障系统的稳定和高效运行。同时,要不断关注 Redis 缓存特性的发展和变化,及时调整策略,以应对不断出现的新挑战。
64 10
|
1月前
|
缓存 监控 NoSQL
Redis 缓存穿透的检测方法与分析
【10月更文挑战第23天】通过以上对 Redis 缓存穿透检测方法的深入探讨,我们对如何及时发现和处理这一问题有了更全面的认识。在实际应用中,我们需要综合运用多种检测手段,并结合业务场景和实际情况进行分析,以确保能够准确、及时地检测到缓存穿透现象,并采取有效的措施加以解决。同时,要不断优化和改进检测方法,提高检测的准确性和效率,为系统的稳定运行提供有力保障。
50 5
|
2月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(一)
数据的存储--Redis缓存存储(一)
92 1
|
2月前
|
存储 缓存 NoSQL
数据的存储--Redis缓存存储(二)
数据的存储--Redis缓存存储(二)
51 2
数据的存储--Redis缓存存储(二)
|
2月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
78 6
|
2月前
|
缓存 NoSQL 关系型数据库
redis和缓存及相关问题和解决办法 什么是缓存预热、缓存穿透、缓存雪崩、缓存击穿
本文深入探讨了Redis缓存的相关知识,包括缓存的概念、使用场景、可能出现的问题(缓存预热、缓存穿透、缓存雪崩、缓存击穿)及其解决方案。
197 0
redis和缓存及相关问题和解决办法 什么是缓存预热、缓存穿透、缓存雪崩、缓存击穿

相关产品

  • 云数据库 Tair(兼容 Redis)