华为云RDS全量慢日志的数据清洗与时间截取思路
需求背景说明:
死锁和慢sql是导致数据库故障的两大噩梦,因此我们要特别注意。由于数据库的内容永远都是越来越多,在加上业务功能迭代,慢sql是不可避免会发生的。那么数据库出现慢sql的情况下,我们该如何快速的定位出具体的sql语句呢?以华为云为例,在rds中很好的给我们展示了参数化的慢sql在整体慢sql中占比情况,但是存在的问题是sql语句都被参数化了,我们无法直接分析具体的sql语句,例如有些语句本身是没问题的因为它是涉及出来用来查询1天或者是1个月数据,但是突然业务拿它区查询了1年的数据就导致了慢sql故障。所以获取到具体的慢sql语句用于问题分析是很有必要的。
想要在华为云上获取具体的sql语句只能是通过下载全量日志,然后再慢慢筛选出我们需要分析的语句。但是全量日志的内容是不规范的,大量的换行,大量的空格,大量与慢sql无关的内容被记录在了慢日志中。另一方面,获取我们只需要某个时间点的具体sql,但是全量日志记录的可能是一个月或者是一年的。因为想要获取一条慢sql,我们需要进行清理无用的数据,美化格式,日志范围筛选等一系列毫无意义的工作,这样耗费了我们大量的精力。
问题解决思路:
主要思路为:
我们可以搭建一个服务并对外提供一个请求地址,通过该请求地址我们传入两个参数:1、rds的名称;2、需要想要获取的sql时间范围。之后服务根据我们传入的参数在华为云平台自动下载全日志并对日志进行数据清洗和根据时间访问筛选内容并生成新的文件返回给我们的请求进行下载。
具体的实现步骤为:
- 通过请求传入rds名称和时间范围参数
- 根据rds名称获取到instance id
- 利用instance id下载全量日志
- 对全量日志进行内容清洗和获取执行时间范围的sql语句
- 生成新的文件供请求用户下载
解决思路的几个核心方法(JAVA实现)
//按照传入时间筛选日志内容 //该方法会调用下面的日志内容清洗方法 private List getSql(String path, String data) { List list = new ArrayList(); List fitlist = new ArrayList<>(); list = filterSql(path); int tag = 0; Pattern pattern = Pattern.compile("[0-9]{4}-[0-9]{2}-[0-9]{2}"); for (String s : list) { if (tag == 1) { fitlist.add(s); continue; } if (s.contains("Query")) { Matcher matcher = pattern.matcher(s); if (matcher.find()) { if (DateUtil.parse(data).compareTo(DateUtil.parse(matcher.group(0))) <= 0) { fitlist.add(s); tag = 1; Console.log("传入截取日志时间为:" + data + ",日志实际提取到时间为:" + matcher.group(0)); } } } } Console.log("过滤完成。"); return fitlist; } //内容清洗 private List dataClean(String path) { String s = ""; List list = new ArrayList(); StringBuffer sb = new StringBuffer(); String[] sql_keys = {"SELECT", "select", "update", "UPDATE", "insert", "INSERT", "delete", "DELETE"}; boolean sql_tag = false, query_tag = false; try (BufferedReader br = FileUtil.getReader(path, "UTF-8")) { while ((s = br.readLine()) != null) { if (!s.contains("#")) { for (String key : sql_keys) { if (s.contains(key)) { sql_tag = true; query_tag = false; break; } } if (query_tag) { continue; } if (sql_tag) { sb.append(s); if (s.contains(";")) { list.add(sb.toString().replaceAll("\r\n|\r|\n", "").replaceAll("\t+", " ").replaceAll(" +", " ")); sb.setLength(0); sql_tag = false; } } //过滤sql执行信息 } else if (s.contains("Query")) { list.add(s.replace(": ", ":")); query_tag = true; } } } catch (Exception e) { e.getMessage(); } return list; }
效果展示:
左边是通过日志清洗和时间范围筛选提供下载的日志
右边是云平台提供下载的原生全量日志(通过截图可以看到充斥了大量和慢sql无关的内容且存在大量换行和空格)
本文系作者 @Mr.Lee 原创发布在 维简网。未经许可,禁止转载。