上一篇
去年双十一,我负责的电商平台经历了一场惊心动魄的秒杀活动,当秒杀按钮亮起的瞬间,系统监控面板上的QPS曲线像火箭般垂直上升,数据库连接池迅速耗尽,整个系统几乎崩溃,就在这千钧一发之际,我们预先设计的Redis缓存策略发挥了关键作用,成功扛住了每秒数十万次的请求,这次经历让我深刻体会到,在当今高并发场景下,合理使用Redis进行缓存优化是多么重要。
缓存雪崩是指大量缓存同时失效,导致请求直接打到数据库上的现象,我们采用分层失效+本地缓存的组合方案:
def get_product_info(product_id): # 先检查本地缓存 local_cache = get_from_local_cache(product_id) if local_cache: return local_cache # Redis缓存读取 redis_data = redis_client.get(f"product:{product_id}") if redis_data: # 设置本地缓存,过期时间比Redis短 set_local_cache(product_id, redis_data, expire=30) return redis_data # 数据库查询 db_data = db.query_product(product_id) if db_data: # 设置Redis缓存,采用随机过期时间避免同时失效 random_expire = 3600 + random.randint(0, 300) redis_client.setex( f"product:{product_id}", random_expire, db_data ) return db_data return None
对于不存在的商品查询,我们采用布隆过滤器+空值缓存的组合拳:
def get_product_detail(product_id): # 先检查布隆过滤器 if not bloom_filter.exists(product_id): return None # 正常缓存查询流程 data = redis_client.get(f"product_detail:{product_id}") if data == "NULL": # 空值缓存 return None if data: return data # 数据库查询 db_data = db.query_product_detail(product_id) if db_data: redis_client.setex( f"product_detail:{product_id}", 3600, db_data ) else: # 设置空值缓存,过期时间较短 redis_client.setex( f"product_detail:{product_id}", 300, "NULL" ) return db_data
我们开发了实时热点发现系统,基于Redis的HyperLogLog和ZSET实现:
def track_hot_items(item_id): # 使用HyperLogLog统计独立访问量 redis_client.pfadd(f"item_access:{item_id}", request.ip) # 使用ZSET维护热点排行榜 redis_client.zincrby("hot_items", 1, item_id) # 定期分析热点数据 if random.random() < 0.01: # 1%的采样率 hot_items = redis_client.zrevrange("hot_items", 0, 99) for item in hot_items[:10]: # 前10名超级热点 # 实施特殊处理策略 preload_to_local_cache(item) replicate_to_edge_nodes(item)
在秒杀场景中,我们采用RedLock算法实现分布式锁:
def acquire_lock(lock_name, acquire_timeout=10, lock_timeout=30): identifier = str(uuid.uuid4()) lock_key = f"lock:{lock_name}" end = time.time() + acquire_timeout while time.time() < end: # 尝试获取锁 if redis_client.setnx(lock_key, identifier): redis_client.expire(lock_key, lock_timeout) return identifier elif not redis_client.ttl(lock_key): redis_client.expire(lock_key, lock_timeout) time.sleep(0.001) return False def release_lock(lock_name, identifier): lock_key = f"lock:{lock_name}" with redis_client.pipeline() as pipe: while True: try: pipe.watch(lock_key) if pipe.get(lock_key) == identifier: pipe.multi() pipe.delete(lock_key) pipe.execute() return True pipe.unwatch() break except redis.exceptions.WatchError: pass return False
我们采用Hash结构存储购物车,既节省空间又方便操作:
def add_to_cart(user_id, product_id, quantity): cart_key = f"cart:{user_id}" redis_client.hincrby(cart_key, product_id, quantity) # 设置购物车过期时间为7天 redis_client.expire(cart_key, 604800) def get_cart_items(user_id): cart_key = f"cart:{user_id}" return redis_client.hgetall(cart_key) def remove_from_cart(user_id, product_ids): cart_key = f"cart:{user_id}" redis_client.hdel(cart_key, *product_ids)
对于游戏积分排行榜,我们采用ZSET实现:
def update_player_score(player_id, score_delta): leaderboard_key = "game_leaderboard" redis_client.zincrby(leaderboard_key, score_delta, player_id) # 每周重置排行榜 if not redis_client.exists("leaderboard_reset_flag"): redis_client.setex("leaderboard_reset_flag", 604800, 1) redis_client.zremrangebyrank(leaderboard_key, 0, -1) def get_top_players(limit=100): return redis_client.zrevrange( "game_leaderboard", 0, limit-1, withscores=True )
在一次用户画像更新中,我们通过Pipeline将性能提升了20倍:
def batch_update_user_tags(user_tags): with redis_client.pipeline() as pipe: for user_id, tags in user_tags.items(): pipe.delete(f"user_tags:{user_id}") pipe.sadd(f"user_tags:{user_id}", *tags) pipe.expire(f"user_tags:{user_id}", 86400) pipe.execute()
我们发现String类型存储JSON数据浪费严重,改为Hash结构后内存节省40%:
# 优化前 redis_client.set("user:1001", json.dumps(user_data)) # 优化后 redis_client.hmset("user:1001", user_data)
根据2025年最新的技术趋势,我们正在探索以下方向:
Redis作为缓存和高并发处理的利器,其价值只会随着数据量的增长而愈发凸显,掌握这些核心模式和实践经验,你将能够构建出既快又稳的系统架构。
本文由 綦文山 于2025-07-31发表在【云服务器提供商】,文中图片由(綦文山)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/492218.html
发表评论