169 lines
8.6 KiB
Markdown
169 lines
8.6 KiB
Markdown
# MySQL 与首页请求保护修复记录
|
||
|
||
**17:42 更新:此前 Nginx 每 IP 和首页总量频率限制已撤回;目前只针对已确认的异常请求直接关闭连接,另有 84 个异常来源临时拦截 15 分钟。数据库索引与 PHP 缓存保留。当前访问与到期边界详见 [网络拥塞处理记录](./网络拥塞处理-2026-08-31.md)。下文 15:32 的限速测试为历史记录,不代表现配置。**
|
||
|
||
服务器:47.106.181.28。完成时间:2026-08-31 15:32,北京时间。
|
||
|
||
已直接部署到生产环境。修复范围为 `www.txiaw.com` 的首页请求保护、首页文章列表缓存,以及共享数据库 `xiaxia.gxl_news` 的查询索引。
|
||
|
||
## 已完成的修改
|
||
|
||
### 1. 数据库索引
|
||
|
||
已执行:
|
||
|
||
```sql
|
||
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 文件:
|
||
|
||
```text
|
||
/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` 这个虚拟主机引用,不对其他站点启用。
|
||
|
||
变更文件:
|
||
|
||
```text
|
||
/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 首页文章列表缓存
|
||
|
||
新增:
|
||
|
||
```text
|
||
/www/wwwroot/www.txiaw.com/Lib/HomeNewsCache.php
|
||
```
|
||
|
||
修改首页模板:
|
||
|
||
```text
|
||
/www/wwwroot/www.txiaw.com/Tpl/icp/gxl_index.html
|
||
```
|
||
|
||
该列表仍调用原来的文章查询函数、返回原来的 100 条数据,但增加:
|
||
|
||
- 固定缓存键,URL 的随机参数不参与缓存键。
|
||
- **60 秒**有效期。
|
||
- 文件锁避免正常并发刷新时重复查询。
|
||
- 正在刷新时可使用最近 **300 秒**内的旧值,降低刷新期间的请求排队。
|
||
- 冷缓存等待最多约 200 毫秒;缓存设施故障时回退原查询,避免因缓存文件故障直接让页面不可用。新索引和异常特征拦截继续保留,目前不依赖首页频率限制。
|
||
- 同目录临时文件加重命名写入,避免读到半份 JSON。
|
||
|
||
实际缓存位于:
|
||
|
||
```text
|
||
/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):
|
||
|
||
```text
|
||
/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`;不需要删除旧索引或恢复整张表。
|
||
|
||
不要为了撤销索引而直接导入整表备份,这会有覆盖修复后新增业务数据的风险。
|
||
|
||
## 参考资料
|
||
|
||
- [Nginx 请求速率限制](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html):请求键、突发限制及拒绝状态码。
|
||
- [MySQL InnoDB 在线 DDL](https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl.html):`ALGORITHM` / `LOCK` 约束的用途;本次同时以服务器实际 MySQL 5.6.50 的执行结果验证支持情况。
|
||
- [PHP flock](https://www.php.net/manual/en/function.flock.php):非阻塞文件锁。
|
||
|
||
本记录、部署脚本和本地副本均未保存登录密码或数据库密码。
|