事情本身不复杂,线索都在日志里。日志清洗加了一条校验正则,单条正常日志毫秒级完成,遇到一段 8 万字符的堆栈就卡 40 秒,接口直接超时。用 re 与 regex 两个模块都能复现,加 re.ASCII 没有改善,只有把输入截断到 1 万字符才恢复正常。

第一反应是「校验前没有对超长输入做截断或预筛」,验证下来不成立。

原因不止一个,得分开看。嵌套量词在匹配失败时回溯次数呈指数增长。另一种情况是输入里有一段长串空白让引擎反复重试。

还有一种情况,正则跑在主线程里,一卡住接口就整体超时。

量出不同长度输入下的耗时

python -c "import re,time;s='a'*20;t=time.time();re.match(r'^(\\w+\\s?)*$',s);print(round(time.time()-t,3))"

接着改写成不产生嵌套量词的形式

python -c "import re;print(bool(re.fullmatch(r'[\\w ]+','ab c')))"

给校验加上长度上限前置检查

python -c "s='a'*90000;print(len(s) if len(s)<10000 else 'too long')"

末了用支持超时的库兜住不可控的正则

python -c "import regex;print(bool(regex.match(r'\\w+','abc')))"

怎么算真的好了,看的是同一批现象。8 万字符的堆栈在 20ms 内判定为不合法,接口不再超时,正常日志的校验结果与原来一致。

这里还有个坑。排查正则性能先看有没有量词嵌套,简化成字符类的并集比调引擎参数有效,不可信输入还要加长度上限。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。

作者 9823189