|
|
很多朋友折腾 Xiuno,一开始都会被“轻量、纯文本缓存、单文件路由”这些点吸引,但真把站点跑到日 UV 5k 以上,性能瓶颈就开始冒头:慢查询、反复加载配置、帖子列表翻页抖动。过去两年里我给两个活跃站做过优化,总结下“缓存与数据库索引”的实践思路,尽量少谈玄学,多说可落地的手段。
先说缓存。Xiuno 自带简单的 filecache,优点是零依赖,缺点是 I/O 抢占明显、并发下锁竞争。我的做法是按数据热度分层:配置与版块信息这类低变更数据,仍用本地文件缓存,TTL 拉长到分钟级;帖子列表、置顶、最后回复等高热数据,切到 Redis,键名按业务域前缀+参数哈希,TTL 控制在 30-120 秒。别迷信“全站缓存”,命中率才是王道。衡量命中率时别只看整体 QPS,要细分到接口维度,比如 thread-list:forum-1,page-1 的命中应高于 page-5,这能指导你是否对深翻页禁用缓存、改为引导搜索。
缓存回收和失效策略同样关键。Xiuno 的发帖/回帖动作是缓存失效的天然触发点,但很多人只删了帖子详情缓存,忘了列表、用户主页、全站最新回帖等聚合视图。我的做法是做一个小的失效映射表:当帖子写入成功后,根据受影响的 forumid、uid、相关分页范围批量失效 key;对于分页,经验规则是只清 page-1 和 page-2,深页让它自然过期,既保证时效又减少删除风暴。还有一个坑:统计类数据(比如帖子数、今日发帖)建议用 Redis 原子自增,定时异步回写 MySQL,避免写放大。
再谈数据库索引。Xiuno 默认表结构够用但不完美,热点表是 thread、post、user。慢查询里最常见的有:按 forumid + lastpid 或 create_date 排序的翻页;按 uid 查帖子或回复;以及模糊搜索命中不到索引。通用优化思路:为高选择性、高频过滤字段建立联合索引,并把排序字段放在索引尾部以覆盖常见的 ORDER BY。举例:forum 帖子列表通常是 WHERE fid=? AND tid IN/ORDER BY lastpid 或 lastdate,实践里我更推荐联合索引 (fid, last_date, tid) 或 (fid, tid, create_date),视你的模板排序而定;用户主页则加 (uid, create_date);回帖表按 (tid, create_date, pid)。注意联合索引的最左前缀原则,SQL 写法要配合索引顺序,别在条件上做函数或隐式类型转换。
覆盖索引能明显降低回表成本。列表页如果只需要 tid、subject、uid、create_date,就尽量让这些列都被联合索引覆盖;需要长文本时再用二次查询按批量 tid 回表,这比单次 SELECT * 全表扫快得多。深分页建议改用“基于游标”的翻页方式:记住上一条的 last_date, tid 作为游标条件而不是 OFFSET N,这能避免越翻越慢。对历史大表,按时间区间做冷/热数据拆分也有效,比如把一年以前的 post 归档到只读表,写路径压力立降。
还有几个容易忽略的细节。第一,统计类的 COUNT(*) 在高并发下很贵,能用近似值就别实时算,或者维护冗余计数列,写时增减。第二,MySQL 配置别掉链子:innodb_buffer_pool_size 至少覆盖热数据集,配合合适的 redo/log 配置,索引再好也救不了 128M 的 buffer。第三,应用层的 N+1 查询要用简单的批量接口消除,比如收集 tid 一次性取作者昵称与头像,避免循环查询 user 表。
最后,用数据说话。每次改动先加埋点:缓存命中率、慢查询分布、单页 SQL 次数。服务端加个简单的 A/B 或灰度,对一部分流量启用新索引或新缓存策略,观察 p95/p99 延迟和错误率。Xiuno 的优点在于简单可控,性能优化不求一步登天,围绕“热数据命中、慢查询消除、写放大减压”三板斧稳步推进,通常就能从卡顿站点打磨到丝滑。 |
|