为什么个人站长该认真考虑 Redis
很多人做个人博客,第一反应是"加个 OPcache 就够了"。OPcache 确实能干掉 PHP 编译开销,但它只是在单机内存里缓存字节码,一旦你遇到下面三类问题,OPcache 就完全帮不上忙:
- 数据库被同一条查询反复打——首页文章列表、侧边栏热门文章,每次刷新都查一遍 MySQL;
- 多台机器(或 PHP-FPM 多容器)之间登录状态对不上,用户在前端机登录、在后端机掉线;
- 爬虫或者恶意刷量把动态页面打到 CPU 100%,而你没有任何一层可以挡住读请求。
Redis 解决的正是这三个问题。它常驻内存、单线程无锁、支持过期时间,qps 轻松上万,对个人站来说一台 1C1G 的机器跑 Redis 都绰绰有余。本文只讲个人网站真正用得上的三件事:Session 共享、页面/数据缓存、持久化选型,不铺开集群那套。
一、安装与安全基线(十分钟)
以 Debian 12 为例:
apt update
apt install -y redis-server
systemctl enable --now redis-server
redis-cli ping # 返回 PONG 即正常默认配置有三个坑,必须改。打开 /etc/redis/redis.conf:
# 1. 只监听本机(除非有内网多机共享需求)
bind 127.0.0.1 ::1
# 2. 一定要设密码,否则被扫到就是挖矿肉鸡
requirepass 换成你自己的长随机串
# 3. 关掉危险命令,防止误操作或入侵后被清库
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""改完 systemctl restart redis-server。注意:如果你的 PHP 和 Redis 不在同一台机器(比如 PHP 在应用机、Redis 在另一台),不要把 Redis 直接暴露公网,正确做法是走内网 IP,或者用 SSH 隧道 / WireGuard 打通,再配合防火墙只放行内网段。
验证:redis-cli -a 你的密码 ping。如果报 NOAUTH Authentication required 说明密码已生效。
二、场景一:多机 Session 共享(PHP 站最刚需)
Typecho、WordPress 这类 PHP 站默认把 session 存成 /var/lib/php/sessions/sess_xxx 文件。单机没问题,一旦你上了负载均衡、或者 PHP-FPM 跑在容器里,用户刷新一次换一台机器,登录态就丢了。
把 session 交给 Redis,两步搞定。
方案 A:PHP 配置(推荐,改动最小)
编辑 php.ini(PHP 8.x 通常被 FPM 拆到 /etc/php/8.2/fpm/php.ini):
session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379?auth=你的密码&database=0"
session.gc_maxlifetime = 86400重启 php-fpm 后,去看 Redis:
redis-cli -a 你的密码 --scan --pattern 'PHPREDIS_SESSION:*'能看到一串 key 就说明生效了。注意 session.save_path 里的密码如果含特殊字符(: @ / #)必须 URL 编码,否则 PHP 解析会失败——这是个非常隐蔽的坑,表现为 session 完全写不进去但页面不报错。
方案 B:应用层接管(更灵活)
如果你只想给某几个场景用 Redis 存登录态,可以在代码里直接写:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->auth('你的密码');
$redis->setex('login:uid:' . $uid, 86400, json_encode($userInfo));方案 B 的好处是可控、可观测;坏处是要自己处理序列化与过期。个人站我一般建议先用方案 A,省事。
三、场景二:页面与数据缓存(提升最大的部分)
首页、文章列表、热门推荐这类"读多写少"的页面,最值得缓存。这里给一个不会翻车的思路:缓存的是数据,不是整页 HTML,除非你确定页面里没有任何用户态内容(比如登录后显示的"你好,XX")。
反例(新手常踩):直接把首页整页 HTML 塞 Redis,结果 A 用户看到 B 用户的登录名——这是缓存穿透用户态的经典事故。
正确做法:缓存查询结果,PHP 层再渲染。
function getHotPosts($redis, $db) {
$key = 'cache:hot_posts:v1';
$data = $redis->get($key);
if ($data !== false && $data !== null) {
return json_decode($data, true); // 命中,直接返回
}
// 未命中:查库
$rows = $db->query('SELECT cid,title FROM contents WHERE type="post"
ORDER BY views DESC LIMIT 10')->fetchAll();
$redis->setex($key, 300, json_encode($rows)); // 缓存 5 分钟
return $rows;
}几个必须记住的点:
- 一定要设过期时间。永远不过期的缓存,最后会变成"改了内容前台不更新"的幽灵 bug。个人站 300~600 秒是舒服的区间。
- key 里要带版本号(上面的
v1)。数据结构一变,改 v2 就整体失效,不用一个个删。 - 写操作要主动删缓存。发了新文章后
$redis->del('cache:hot_posts:v1'),否则要等 5 分钟才更新,老板以为你没发出去。 - 加个"缓存空值"防穿透。如果查库结果为空,也要缓存一个短过期(比如 30 秒)的空标记,避免恶意 id 反复穿透到 MySQL。
用这套跑下来,一个日 PV 几千的个人站,MySQL 查询量通常能降 70% 以上,首页 TTFB 从两三百毫秒降到几十毫秒。
四、场景三:持久化怎么选(RDB 还是 AOF)
这是被问得最多的问题。先记住一句话:Redis 是缓存,不是数据库。你的文章数据永远应该在 MySQL 里,Redis 里的是"可以随时重建的副本"。想通这点,持久化就好选了。
RDB(快照):每隔一段时间把内存 dump 到磁盘。文件小、恢复快、对性能影响小。缺点是两次快照之间的数据会丢。个人站的 session 丢了也无非是让用户重登,可以接受——大多数个人站选 RDB 就够了。
save 900 1 # 900 秒内至少 1 次写就存盘
save 300 10
save 60 10000
dbfilename dump.rdb
dir /var/lib/redisAOF(追加日志):把每条写命令追加到日志,重启时重放。默认 appendfsync everysec,最多丢 1 秒数据。数据安全得多,但文件大、重启慢。
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb选型建议:
- 只做页面/数据缓存(本文场景二)→ 纯 RDB,甚至可以直接关掉持久化,重启后缓存自然重建,反而更干净;
- 当 session 存储用 → RDB + 每 60 秒存盘,用户最多被踢一次;
- 如果你拿 Redis 存了"丢了会心痛"的数据 → 那是架构设计错误,应该挪回 MySQL,再上 AOF 也只是补救。
另外强烈建议单独监控 Redis 内存:redis-cli -a 密码 info memory | grep used_memory_human。一旦 maxmemory 被打满,Redis 会按 maxmemory-policy 淘汰 key。缓存场景一般用 allkeys-lru;如果 Redis 里混存了 session 和缓存,就别用 allkeys-*,会误淘汰 session,改用 volatile-lru 并保证所有缓存 key 都设了过期时间。
五、常见排错清单
- PHP 写入 session 后立刻失效:Redis 内存满了触发淘汰,或
session.gc_maxlifetime小于业务需要的登录时长。 - 连接数暴涨报
ERR max number of clients reached:PHP-FPM 每个进程一条长连接,进程数 × 机器数不能超过maxclients(默认 10000)。真到这一步通常是代码没复用连接,每次请求都 new Redis()。 - 命中率上不去:先用
redis-cli info stats | grep keyspace看keyspace_hits/misses。命中率低一般是 key 设计有随机成分(比如把时间戳、随机数拼进 key),等于缓存永远不命中。 - Redis 把内存吃满导致机器 OOM:务必设
maxmemory 512mb之类的硬上限,并且给 Redis 配vm.overcommit_memory=1,否则 fork 做 RDB 时可能失败。 - 改完配置不生效:确认改的是正在用的那个
redis.conf(有些系统默认读/etc/redis/redis.conf,有些读/etc/redis/6379.conf),用redis-cli config get dir反查。
六、这套方案的实际收益
拿一个典型的 Typecho 个人站举例,改造前后对比大致是:
- 首页 TTFB:约 260ms → 约 40ms(数据缓存命中);
- MySQL QPS:峰值约 150 → 约 30;
- 多机部署时登录态丢失问题:彻底消失;
- 新增成本:一台机器上多占 100~200MB 内存,零额外费用。
Redis 对个人站最大的价值不是"高并发",而是用极低的成本把数据库从无意义的重复查询里解放出来。装完、改三行配置、包一层缓存函数,一个下午就能搞定,收益却长期存在。建议按 Session → 数据缓存 → 持久化调优的顺序推进,每步都验证再往下走,别一次性全上。