上一篇
"小王,咱们的商品详情页接口又超时了!"运维同事急匆匆地跑过来,作为某电商平台的后端开发,小王最近经常被这类问题困扰,每当用户查看热门商品时,MySQL数据库就会不堪重负,响应时间飙升,经过排查,他们决定引入Redis作为缓存层,但如何高效地从Redis查询单条数据,却成了新的挑战...
# 查询字符串类型的值 127.0.0.1:6379> GET user:1001 "{\"name\":\"张三\",\"age\":28}"
这是Redis最基础的单数据查询方式,适用于简单的键值存储,就像查字典一样直接,但只能用于字符串类型的数据。
小王刚开始就这样用,但很快发现了问题:
import redis import json r = redis.Redis(host='localhost', port=6379, db=0) def get_user(user_id): key = f"user:{user_id}" try: data = r.get(key) if data is None: # 模拟从数据库查询 db_user = query_db_for_user(user_id) if db_user: # 设置缓存,过期时间30分钟 r.setex(key, 1800, json.dumps(db_user)) return db_user return json.loads(data) except redis.RedisError as e: print(f"Redis操作失败: {e}") return query_db_for_user(user_id) # 降级处理
这种写法解决了几个问题:
def get_multiple_users(user_ids): keys = [f"user:{uid}" for uid in user_ids] try: cached_data = r.mget(keys) result = [] for i, data in enumerate(cached_data): if data is None: # 处理缓存未命中的情况 db_user = query_db_for_user(user_ids[i]) if db_user: r.setex(keys[i], 1800, json.dumps(db_user)) result.append(db_user) else: result.append(json.loads(data)) return result except redis.RedisError as e: print(f"Redis批量查询失败: {e}") return [query_db_for_user(uid) for uid in user_ids]
批量查询可以显著减少网络往返时间,特别是在需要获取多条数据时。
对于用户对象这种有多字段的数据,使用Hash更合适:
# 存储用户数据 127.0.0.1:6379> HSET user:1001 name "张三" age 28 email "zhangsan@example.com" # 获取所有字段 127.0.0.1:6379> HGETALL user:1001 # 获取单个字段 127.0.0.1:6379> HGET user:1001 name
Python实现示例:
def get_user_profile(user_id): key = f"user:{user_id}" try: if not r.exists(key): # 从数据库获取并存入Redis db_user = query_db_for_user(user_id) if db_user: r.hset(key, mapping=db_user) r.expire(key, 1800) return db_user return r.hgetall(key) except redis.RedisError as e: print(f"Redis Hash查询失败: {e}") return query_db_for_user(user_id)
当需要执行多个命令时,Pipeline可以大幅减少网络开销:
def get_user_with_stats(user_id): key = f"user:{user_id}" stats_key = f"user_stats:{user_id}" try: with r.pipeline() as pipe: pipe.hgetall(key) pipe.hgetall(stats_key) user_data, stats_data = pipe.execute() if not user_data: user_data = query_db_for_user(user_id) if user_data: r.hset(key, mapping=user_data) r.expire(key, 1800) return {**user_data, **stats_data} except redis.RedisError as e: print(f"Redis Pipeline操作失败: {e}") return query_db_for_user(user_id)
使用互斥锁防止缓存击穿:
def get_user_with_lock(user_id): key = f"user:{user_id}" lock_key = f"lock:{key}" try: # 尝试获取缓存 data = r.get(key) if data is not None: return json.loads(data) # 获取锁 lock_acquired = r.set(lock_key, "1", nx=True, ex=10) if not lock_acquired: # 获取锁失败,短暂等待后重试 time.sleep(0.1) return get_user_with_lock(user_id) try: # 再次检查缓存,防止在等待锁期间其他线程已经写入 data = r.get(key) if data is not None: return json.loads(data) # 查询数据库 db_user = query_db_for_user(user_id) if db_user: r.setex(key, 1800, json.dumps(db_user)) return db_user finally: # 释放锁 r.delete(lock_key) except redis.RedisError as e: print(f"Redis操作失败: {e}") return query_db_for_user(user_id)
使用Redis的监控命令发现热点Key:
# 监控命令执行情况(生产环境慎用) 127.0.0.1:6379> MONITOR # 使用redis-cli的热点key发现功能(Redis 5.0+) $ redis-cli --hotkeys
对于热点数据,可以考虑:
数据结构选择:根据场景选择合适的数据类型
缓存策略:
性能优化:
异常处理:
安全考虑:
"现在我们的商品详情页响应时间从原来的2秒降到了200毫秒以内!"小王兴奋地向团队汇报,通过合理使用Redis查询优化技术,他们成功解决了性能瓶颈问题,Redis虽快,但用得不当反而会成为系统瓶颈,从简单到复杂的查询方式,需要根据实际业务场景灵活选择。
本文由 赛雪珍 于2025-08-01发表在【云服务器提供商】,文中图片由(赛雪珍)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/509263.html
发表评论