Files
kefu/im/MySQL修复记录-2026-08-31.md
2026-09-03 08:38:17 +08:00

8.6 KiB
Raw Permalink Blame History

MySQL 与首页请求保护修复记录

17:42 更新:此前 Nginx 每 IP 和首页总量频率限制已撤回;目前只针对已确认的异常请求直接关闭连接,另有 84 个异常来源临时拦截 15 分钟。数据库索引与 PHP 缓存保留。当前访问与到期边界详见 网络拥塞处理记录。下文 15:32 的限速测试为历史记录,不代表现配置。

服务器:47.106.181.28。完成时间:2026-08-31 15:32,北京时间。

已直接部署到生产环境。修复范围为 www.txiaw.com 的首页请求保护、首页文章列表缓存,以及共享数据库 xiaxia.gxl_news 的查询索引。

已完成的修改

1. 数据库索引

已执行:

SET SESSION lock_wait_timeout = 5;
ALTER TABLE xiaxia.gxl_news
  ADD INDEX idx_news_status_addtime (news_status, news_addtime),
  ALGORITHM=INPLACE,
  LOCK=NONE;
  • 执行前检查了现有索引,没有发现前两列为 (news_status, news_addtime) 的适用索引。
  • 保留原有分类列表等索引,没有删除索引或修改业务记录。
  • 先用一致性快照备份目标表,校验 gzip 完整性后才执行 ALTER。
  • 在线创建耗时约 0.49 秒;明确指定并发读写及原地算法,避免自动回退到需要复制表的操作。
  • 新执行计划使用 idx_news_status_addtime,不再出现 Using filesort。
  • 关闭 MySQL 查询缓存后连续测试三次,均返回 100 条,包含客户端启动的耗时分别约 4.7、4.3、4.0 毫秒。
  • 单次查询会话计数:Handler_read_key=1、Handler_read_prev=99、Sort_rows=0、Sort_scan=0。

原故障负载下同一 SQL 的慢日志样本平均为 6.479 秒,每次检查 72,352 行;前后测试处于不同流量条件,不能把全部耗时改善单独归因于索引。

2. PHP 入口保护与 Nginx 首页限速

新增 PHP 文件:

/www/wwwroot/www.txiaw.com/Lib/HomeRequestProtection.php

由网站 index.php 在加载 ThinkPHP 之前引入。对 / 或 /index.php 的 GET/HEAD 请求,若同时带 r 参数且 User-Agent 为空,则返回 429;PHP 响应附带 Retry-After: 60。不依靠客户端提交的 IP 地址,也不读取数据库。

此前 Nginx 同步在请求进入 PHP 之前拦截上述异常特征,并启用了以下首页频率限制(17:18 已撤回):

  • 每个来源 IP:平均 5 次/秒,允许 20 次突发。
  • 该站首页总量:平均 50 次/秒,允许 100 次突发。
  • 超出限制返回 429。
  • 限速键使用原始请求路径;普通 /down/... 等文章路径、静态资源和 POST 请求不在这些首页限速键的范围内。
  • 限速配置只被 www.txiaw.com / txiaw.com 这个虚拟主机引用,不对其他站点启用。

变更文件:

/www/wwwroot/www.txiaw.com/index.php
/www/server/panel/vhost/nginx/www.txiaw.com.conf
/www/server/panel/vhost/nginx/txiaw-home-protection-http.inc
/www/server/panel/vhost/nginx/txiaw-home-protection-server.inc

首次部署时 Nginx 修改前后均通过 nginx -t,只做了一次平滑重新加载。17:18 撤回频率限制、17:36 启用精确连接关闭规则时也分别检查配置并平滑重载。未重启 MySQL、PHP-FPM 或服务器,未终止业务查询。

3. PHP 首页文章列表缓存

新增:

/www/wwwroot/www.txiaw.com/Lib/HomeNewsCache.php

修改首页模板:

/www/wwwroot/www.txiaw.com/Tpl/icp/gxl_index.html

该列表仍调用原来的文章查询函数、返回原来的 100 条数据,但增加:

  • 固定缓存键,URL 的随机参数不参与缓存键。
  • 60 秒有效期。
  • 文件锁避免正常并发刷新时重复查询。
  • 正在刷新时可使用最近 300 秒内的旧值,降低刷新期间的请求排队。
  • 冷缓存等待最多约 200 毫秒;缓存设施故障时回退原查询,避免因缓存文件故障直接让页面不可用。新索引和异常特征拦截继续保留,目前不依赖首页频率限制。
  • 同目录临时文件加重命名写入,避免读到半份 JSON。

实际缓存位于:

/www/wwwroot/www.txiaw.com/Runtime/TxiawHomeNews/news-list-v1.json

只备份并清理了命中这条旧首页查询的 1 个编译模板缓存文件,没有清空全站 Runtime、会话或其他业务数据。

验证结果

以下为首次修复的历史验证,当前网络验证见上方更新记录。

已通过以下验证:

  • PHP 5.6 语法检查。
  • 11 个请求保护边界用例,包括正常首页、浏览器请求、文章路径、POST 和证书验证路径。
  • 缓存命中、过期刷新、持锁时读取旧值、模拟数据库异常回退、损坏缓存恢复。
  • 8 个同时到达的冷缓存请求取得相同数据,数据加载函数只执行 1 次。
  • 首页加入随机参数仍复用同一个新鲜缓存,实际缓存包含 100 条数据。
  • 30 次、最多 4 路并发的短突发 HTTP 检查:24 次 200、6 次 429;等待 5 秒后首页恢复 200。
  • 首页限速期间,同站文章页和 www.xxiaw.com 首页仍为 200。

实际页面检查:

请求 结果
无 User-Agent 的 www.txiaw.com/?r=... 429
正常 www.txiaw.com/ 200
www.txiaw.com/down/184778.html 200
www.xxiaw.com/ 200
m.bchongw.com/ 301,保留站点原跳转行为

15:30 的数据库采样:

指标 故障期间 修复后采样
Threads_running 79 1
Threads_connected 80 7
慢查询增长 约 12.4 次/秒 5 秒采样内 0 次
MySQL CPU 约 136%(3 秒样本) 约 18.33%(3 秒样本)
系统 1 分钟负载 修改前约 86.31 15:32 约 1.51

这些是短时采样,不是长期服务承诺。5/15 分钟负载仍包含此前故障窗口,会滞后回落。

剩余压力与边界

异常请求尚未停止:验证时最近 3,000 条、以及后续最近 1,000 条该站访问记录均返回 429,说明仍在持续拦截。15:32 的两秒整机 CPU 样本空闲约 15%、I/O 等待约 1%;入口处理仍消耗资源。此次已经消除已定位的 MySQL 排序热点并限制首页流量,但没有部署云端 WAF/CDN,也未宣称能够抵御任意规模或任意路径的攻击。

后续可在云侧增加 CC 防护,减轻源站的连接与 TLS 处理压力。若站点以后接入代理/CDN,需要同步核对可信真实 IP 配置和合法流量,再调整当前每 IP 阈值。

备份与回滚

服务器备份目录(权限 0700):

/www/backup/txiaw-mysql-fix-20260831/

主要文件:

  • originals/original-index.php:原 PHP 入口。
  • originals/original-www.txiaw.com.conf:原 Nginx 站点配置。
  • originals/original-gxl_index.html:原首页模板。
  • compiled-home-before/:本次清理的首页编译模板备份。
  • gxl_news-before.sql.gz:完整目标表一致性快照,压缩后 15,245,809 字节,已做 gzip 完整性校验。
  • gxl_news-schema-before.txt:修改前表结构和索引。
  • index-result.json、cache-result.json、verification-result.json、final-summary.json:实际执行与验证记录。
  • stage/:此次部署的源码与测试脚本。

回滚时先核对文件是否又被别人修改,避免覆盖后续变更。建议按所需范围单独回滚:

  1. 仅撤回 PHP 缓存:恢复原首页模板,保留其当前属主和权限;只重新生成该首页的编译模板缓存。缓存辅助 PHP 文件可先保留,避免在途请求的已编译模板调用失效。无需导入数据库备份。
  2. 撤回请求保护:恢复原 PHP 入口和原 Nginx 站点配置,先执行 nginx -t,成功后再 nginx -s reload。两个 .inc 文件可保留为未引用文件,不必急于删除。撤回后当前刷请求会再次进入 PHP,应先有替代保护。
  3. 确需撤回新索引:确认没有其他新依赖后,使用短元数据锁等待时间,单独删除 idx_news_status_addtime;不需要删除旧索引或恢复整张表。

不要为了撤销索引而直接导入整表备份,这会有覆盖修复后新增业务数据的风险。

参考资料

本记录、部署脚本和本地副本均未保存登录密码或数据库密码。