SQL慢查询日志分析正则:实现匹配时忽略单引号内参数值
实现思路
不要硬写单一复杂正则完成所有逻辑,拆分两步处理可维护性更高,还能覆盖绝大多数边界场景:
- 第一步:先从慢查询日志里提取完整SQL正文
对你原有正则做小范围优化即可,原正则对换行符、SET行的数字长度兼容性不足,替换为/(?<=SET timestamp=\d+;\n)[\s\S]+?(?=;)/gmi:- 用
[\s\S]替代.匹配任意字符,兼容不同正则引擎下.不识别换行符的问题 - 补全
SET timestamp=前缀匹配,避免日志里其他带数字加分号的行造成误匹配
- 用
- 第二步:单独做SQL标准化,清空单引号包裹的参数值
用专门的替换正则匹配所有单引号包裹的内容:/'[^'\\]*(?:\\.[^'\\]*)*'/g
这个正则可以兼容SQL里带转义单引号的场景(比如字段值为'user\'s-data'时不会提前截断匹配),把所有匹配到的内容统一替换为'',就能得到你要的标准化SQL结构。
可直接运行的参考代码(JavaScript实现)
// 输入的原始日志内容 const rawLog = `# Time: 2022-06-01T20:00:00.000000Z # User@Host: database[database] @ [10.10.10.10] Id: 8888888 # Query_time: 0.000450 Lock_time: 0.000160 Rows_sent: 1 Rows_examined: 2 SET timestamp=1654715324; SELECT id FROM table_name WHERE field = 'some-data' AND another_field != 'random-stuff' ORDER BY field_2;` // 1. 提取所有SQL语句 const sqlExtractRegex = /(?<=SET timestamp=\d+;\n)[\s\S]+?(?=;)/gmi const rawSqlList = rawLog.match(sqlExtractRegex) || [] // 2. 标准化SQL:清空单引号内的参数值 const stringValueRegex = /'[^'\\]*(?:\\.[^'\\]*)*'/g const normalizedSqlList = rawSqlList.map(sql => { return sql.replace(stringValueRegex, "''").trim() }) // 3. 统计同类SQL出现次数 const sqlCountMap = new Map() normalizedSqlList.forEach(standardSql => { sqlCountMap.set(standardSql, (sqlCountMap.get(standardSql) || 0) + 1) })
优化提示
- 标准化环节可以额外加一步处理:把连续的空白符(换行、多个空格、制表符)替换为单个空格,同时把SQL关键字统一转为大写/小写,避免因为格式差异、大小写不同导致同一条SQL被误判为不同语句。
- 如果业务SQL里存在双引号包裹的字符串值,参照单引号的匹配规则新增一条双引号的替换正则即可,逻辑完全一致。
- 不要强行把提取、替换逻辑合并到同一个正则表达式中,拆分步骤后逻辑清晰,遇到特殊场景调整规则的成本极低。
内容的提问来源于stack exchange,提问作者Rick Beumers
相关产品推荐
相关产品推荐

