最准切入点是直接分析 rewrite 规则执行频次和耗时;nginx 正则匹配在请求路由阶段触发 pcre 解析,高并发下易成 cpu 瓶颈;需通过 error.log 统计、临时禁用、debug 日志定位高频耗时规则,并优化为 location 分流、map 映射等低开销方式,最后压测验证与监控闭环。

直接看 rewrite 规则执行频次和耗时,这是最准的切入点。Nginx 的正则匹配发生在请求路由阶段,每条含正则的 rewrite 都会触发 PCRE 引擎解析,高并发下极易成为 CPU 瓶颈。
确认是否是 rewrite 导致的问题
先验证怀疑方向是否正确:
- 用 access.log 统计被 rewrite 的 URL 路径出现频次:
grep "rewrite" /var/log/nginx/error.log | tail -1000—— 查是否有循环重写或大量匹配日志 - 临时关闭所有 rewrite 指令(注释掉),reload Nginx,观察 CPU 是否明显回落;若回落,基本锁定问题源
- 检查 error.log 中是否频繁出现
rewrite or internal redirection cycle,这是典型无限循环标志
定位具体哪条 rewrite 规则最耗资源
不要靠猜,要用数据说话:
- 启用 debug 日志(需编译时带 --with-debug):
在 nginx.conf 的 error_log 行改为:error_log /var/log/nginx/error.log debug;,然后nginx -s reload - 抓取一段高负载时段的日志,过滤 rewrite 相关行:
grep "rewrite.*using" /var/log/nginx/error.log | awk '{print $9}' | sort | uniq -c | sort -nr
输出中排第一的正则表达式,就是最常执行、最可能拖慢系统的那条 - 重点关注嵌套量词(如
.*.*、(a+)+)、模糊匹配(^/.*$)、长字符串捕获(^(.*)/(.*)/(.*)$)——这些极易引发回溯爆炸
优化 rewrite 规则本身
能不用正则就不用,能简化就简化:
- 把
rewrite ^/api/v(\d+)/(.*)$ /v$1/api/$2 break;这类复杂捕获,拆成多个前缀 location 匹配,例如:location /api/v1/ { proxy_pass http://backend; }location /api/v2/ { proxy_pass http://backend; } - 避免在
if块中使用正则:if ($request_uri ~* "^/old/(.*)$") { rewrite ^/old/(.*)$ /new/$1 permanent; }是高危写法,改用 location + rewrite 组合 - 用
map替代高频 rewrite:对简单路径映射(如域名跳转、版本路由),map $host $upstream { example.com backend_v1; api.example.com backend_v2; }执行开销远低于每次匹配正则
验证与长期监控
改完不是终点,得闭环验证:
- 压测对比:用 wrk 或 ab 对相同 URL 跑 1000 QPS,对比优化前后 top 中 nginx worker 的 CPU 占比变化
- 加轻量级监控:在 log_format 中加入
$time_real和$request_time,再配合awk '{if($NF>1) print}'抓出慢请求,看是否集中在某类 rewrite 路径 - 定期 audit:把所有 rewrite 规则导出,用工具(如 regex101)测试其最坏情况回溯步数,超过 1000 步的建议重构











