Redis缓存应用场景总结
Redis作为目前最流行的内存数据库,以其极高的性能和丰富的数据结构,在现代Web应用中扮演着越来越重要的角色。从简单的缓存到复杂的分布式系统组件,Redis都能发挥巨大的价值。本文将系统地介绍Redis的各种应用场景,并通过实际代码示例展示如何在项目中正确使用Redis。
一、Redis核心数据结构
Redis提供了五种基础数据结构,每种都有其特定的应用场景:
1. String(字符串)
// 缓存用户信息
SET user:10001 "{name:'张三',age:25}"
GET user:10001
// 计数器
INCR article:1001:views
GET article:1001:views
// 分布式锁
SET lock:order:1001 "uuid" NX EX 30
// 限速器
INCR rate:api:192.168.1.1
EXPIRE rate:api:192.168.1.1 60
2. Hash(哈希)
// 存储对象属性
HMSET user:10001 name "张三" age 25 email "zhangsan@example.com"
HGET user:10001 name
HGETALL user:10001
HINCRBY user:10001 age 1
3. List(列表)
// 消息队列
LPUSH queue:emails "email_content_1"
RPOP queue:emails
// 最新消息列表
LPUSH timeline:user:10001 "post:5001"
LRANGE timeline:user:10001 0 9 // 获取最新10条
4. Set(集合)
// 标签系统
SADD article:1001:tags "PHP" "MySQL" "Redis"
SMEMBERS article:1001:tags
// 共同关注
SINTER user:1001:follows user:1002:follows
// 抽奖系统
SADD lottery:20240101 user:1 user:2 user:3
SRANDMEMBER lottery:20240101 3 // 随机抽取3人
5. Sorted Set(有序集合)
// 排行榜
ZINCRBY leaderboard:score 100 "player:1001"
ZREVRANGE leaderboard:score 0 9 WITHSCORES // TOP10
// 延迟队列
ZADD delay:queue 1700000000 "task:order_timeout:1001"
ZRANGEBYSCORE delay:queue 0 {current_timestamp}
二、缓存策略
1. 缓存穿透解决方案
当查询一个一定不存在的数据时,每次都会打到数据库:
// 方案1:缓存空值
GET user:99999
// 如果不存在,缓存空值,设置短过期时间
SET user:99999 "NULL" EX 60
// 方案2:布隆过滤器(RedisBloom模块)
BF.ADD users user:10001
BF.EXISTS users user:99999 // 返回0,一定不存在
2. 缓存击穿解决方案
热点key过期瞬间,大量请求涌入数据库:
// 使用分布式锁,只让一个请求去加载
function getWithLock(key) {
let value = redis.get(key);
if (value) return value;
let lock = redis.set("lock:" + key, "1", "NX", "EX", 10);
if (lock) {
value = db.query(key);
redis.set(key, value, "EX", 3600);
redis.del("lock:" + key);
return value;
} else {
sleep(100);
return getWithLock(key); // 重试
}
}
3. 缓存雪崩解决方案
大量key同时过期,导致数据库压力骤增:
// 过期时间加随机值,避免同时失效
expire = baseExpire + random(0, 300)
SET key value EX expire
三、分布式锁实现
class RedisLock {
private redis;
private lockKey;
private lockValue;
private expireTime;
function tryLock(timeout = 10) {
this.lockValue = uuid();
let endTime = now() + timeout;
while (now() < endTime) {
let result = redis.set(
this.lockKey,
this.lockValue,
"NX", "EX", this.expireTime
);
if (result) return true;
sleep(100);
}
return false;
}
function unlock() {
// Lua脚本保证原子性
let script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
redis.eval(script, [this.lockKey, this.lockValue]);
}
}
四、Session共享
在分布式系统中,使用Redis存储Session实现多服务器间共享:
// PHP配置
ini_set("session.save_handler", "redis");
ini_set("session.save_path", "tcp://127.0.0.1:6379?database=1");
// 或使用Redis扩展直接操作
$redis = new Redis();
$redis->connect("127.0.0.1", 6379);
$redis->setex("session:" . $sessionId, 1800, serialize($sessionData));
五、Redis性能优化建议
- 使用Pipeline批量执行命令,减少网络往返
- 大key拆分,避免单个value超过10KB
- 合理设置过期时间,避免内存溢出
- 使用SCAN代替KEYS命令遍历键
- 开启持久化时选择AOF的everysec模式
- 使用连接池管理Redis连接
- 在生产环境建议使用Redis Cluster或哨兵模式
Redis的应用场景远不止本文所列,它的灵活性使得开发者可以根据具体需求创造出各种各样的解决方案。掌握Redis的核心数据结构和应用模式,是成为高级后端开发者的必备技能。