Redis 是一款高性能的内存数据存储系统,常被用于缓存、分布式锁、限流、排行榜、消息队列等场景。在实际项目中,合理使用 Redis 可以显著提升系统的性能和可用性。
本文将从三个方面深入解析:
Redis 的高性能并非偶然,其核心原因主要有以下几点:
Redis 将数据保存在内存中,而非磁盘,读写速度接近纳秒级(10⁻⁹ 秒),比传统的磁盘数据库快几个数量级。
Redis 使用单线程处理所有请求(从 Redis 6 开始 IO 多线程),避免了线程上下文切换带来的开销,同时内部使用非阻塞的事件循环处理请求,简洁高效。
Redis 底层采用 epoll
(Linux)或 select
等 I/O 多路复用技术,高效监听并处理大量并发连接,能应对高并发请求。
Redis 使用了大量专为内存优化的数据结构,比如:
类型 | 底层结构 | 用途示例 |
---|---|---|
String | 简单动态字符串 SDS | 缓存对象、计数器 |
Hash | ziplist / hashtable | 用户对象、购物车 |
List | quicklist(双向链表压缩) | 消息队列 |
Set | hashtable / intset | 标签、好友列表 |
ZSet | skiplist + hashtable | 排行榜、积分榜 |
虽然 Redis 是内存数据库,但仍提供 AOF、RDB 机制持久化数据,保障宕机恢复能力。
原理:
def get_user_profile(user_id):
profile = redis.get(f"user:{user_id}")
if profile:
return profile # 命中缓存
profile = db.query_user(user_id)
redis.set(f"user:{user_id}", profile, ex=3600) # 缓存 1 小时
return profile
ZINCRBY leaderboard 100 "user:1"
ZREVRANGE leaderboard 0 9 WITHSCORES
count = redis.incr("req:ip:127.0.0.1")
if count == 1:
redis.expire("req:ip:127.0.0.1", 60)
if count > 100:
return "请求频率过高"
SET session:token123 user_json EX 3600
LPUSH task_queue "task:123"
BRPOP task_queue 0
if not bloom_filter.exists(key):
return "数据不存在"
GEOADD locations 116.397128 39.916527 "user:123"
GEORADIUS locations 116.397128 39.916527 5 km
描述:
缓存和数据库都没有数据,频繁访问数据库,甚至被恶意攻击。
解决方案:
if not bloom_filter.contains(user_id):
return "非法请求"
if user_data is None:
redis.set("user:123", "", ex=60)
描述:
某个热点 key 失效,瞬间大量请求访问数据库,数据库压力骤增。
解决方案:
if redis.get("hot:data") is None:
if acquire_lock("hot:lock"):
data = db.query()
redis.set("hot:data", data, ex=60)
描述:
大量缓存 key 在同一时间过期,或 Redis 整体宕机,造成数据库压力骤增,系统雪崩。
解决方案:
ttl = 3600 + random.randint(0, 300)
redis.set("key", value, ex=ttl)
问题类型 | 描述 | 危害 | 解决方案 |
---|---|---|---|
穿透 | 请求数据缓存和数据库都不存在 | 请求直达数据库 | 布隆过滤器、缓存空值 |
击穿 | 热点 key 突然失效被大量请求打击 | 瞬间压力激增 | 互斥锁、逻辑过期 + 异步刷新 |
雪崩 | 大量 key 同时过期或 Redis 崩溃 | 系统整体宕机雪崩 | 随机 TTL、本地缓存、限流、服务降级保护 |
Redis 作为高性能的内存数据库,具有极高的读写效率与灵活的数据结构。它不仅适用于缓存场景,还支持多种高并发业务逻辑如排行榜、限流、队列、GEO 计算等。理解其核心原理与典型问题防御策略,有助于我们构建更加健壮、高可用的系统架构。