81、数据库日志分析:慢查询日志解析、错误日志分析、审计日志、自动化分析工具

日志分析这事儿,说简单也简单,说复杂真能让人头大。我做了十几年嵌入式数据库,最怕半夜被电话吵醒——"数据库挂了,快看看怎么回事!" 这时候,日志就是你唯一的救命稻草。

今天咱们就把数据库日志分析这摊事儿捋清楚。慢查询日志、错误日志、审计日志,还有自动化分析工具,一个一个来。

慢查询日志解析

慢查询日志,说白了就是记录那些"跑得慢"的SQL语句。我习惯把慢查询阈值设在100毫秒,超过这个时间的查询,统统记录下来。

核心思路:不是所有慢查询都要优化,但频繁出现的慢查询必须处理。

先看一个典型的慢查询日志条目:

# Time: 2024-01-15T10:23:45.123456Z
# User@Host: root[root] @ localhost []  Id: 12345
# Query_time: 2.345678  Lock_time: 0.000123
# Rows_sent: 1000  Rows_examined: 500000
SET timestamp=1705314225;
SELECT * FROM sensor_data WHERE device_id = 'DEV-001' AND timestamp > '2024-01-01';

这里有几个关键字段:

  • Query_time:查询耗时,2.34秒,明显慢了
  • Lock_time:锁等待时间,0.0001秒,基本没锁问题
  • Rows_examined:扫描了50万行,但只返回1000行

看到这个比例了吗?50万行扫描只返回1000行,说明索引没用好。我在项目中遇到过类似情况,加个复合索引就解决了。

我的习惯:慢查询日志不要一直开着,生产环境开个5分钟采样就够了。一直开着会影响性能,你想想看,日志写入本身也是I/O操作。

错误日志分析

错误日志这东西,很多人只看最后几行。嗯,这其实是个坏习惯。错误日志里藏着很多"预兆",比如磁盘空间不足、连接数超限、死锁等等。

常见的错误日志分类:

错误类型 典型信息 处理建议
连接错误 Can't connect to MySQL server 检查网络、端口、防火墙
权限错误 Access denied for user 检查用户权限配置
存储错误 Disk is full 清理磁盘或扩容
死锁错误 Deadlock found 优化事务顺序,减少锁粒度
崩溃错误 Out of memory 调整内存配置,检查内存泄漏

我曾经遇到过一个诡异的问题:数据库每天凌晨3点准时崩溃。查了三天,最后发现是crontab里有个脚本在凌晨3点跑全表扫描,把内存吃光了。错误日志里其实早就记录了OOM信息,只是没人去看。

注意:错误日志会不断增长,一定要配置日志轮转。我见过一个生产环境,错误日志撑爆了根分区,数据库直接挂了。你说冤不冤?

审计日志

审计日志,说白了就是"谁在什么时候干了什么"。在嵌入式数据库里,审计日志尤其重要——设备上的数据往往涉及用户隐私或商业机密。

审计日志通常记录:

  • 登录/登出时间
  • 执行的SQL语句(特别是DDL和DML)
  • 数据导出操作
  • 权限变更操作

在SQLite中开启审计日志,可以用钩子函数:

#include <sqlite3.h>

static int audit_callback(
    unsigned int event,
    sqlite3 *db,
    sqlite3_stmt *stmt,
    void *user_data
) {
    const char *sql = sqlite3_sql(stmt);
    if (sql) {
        // 记录到审计日志文件
        fprintf(audit_log, "[%ld] SQL: %s\n", time(NULL), sql);
    }
    return 0;
}

// 注册审计回调
sqlite3_audit_hook(db, audit_callback, NULL);

嗯,这里要注意:审计日志本身也会产生I/O,别记录得太细。我建议只记录DDL和敏感表的DML操作,SELECT操作一般不用记。

自动化分析工具

手动翻日志?那是十年前的做法了。现在咱们得用工具。

我常用的几个工具:

  1. pt-query-digest:Percona Toolkit里的慢查询分析神器,能自动归类相似的慢查询,给出统计报告
  2. mysqldumpslow:MySQL自带的慢查询汇总工具,轻量级,适合快速查看
  3. 自研小工具:有时候现成工具不够用,我就写个C程序自己解析

写一个简单的日志解析器,其实不难:

#include <stdio.h>
#include <string.h>

int main() {
    char line[1024];
    double total_time = 0.0;
    int count = 0;
    
    while (fgets(line, sizeof(line), stdin)) {
        if (strstr(line, "Query_time:")) {
            double qt;
            sscanf(line, "%*[^Q]Query_time: %lf", &qt);
            total_time += qt;
            count++;
            
            if (qt > 1.0) {
                printf("慢查询警告: %.3f秒\n", qt);
            }
        }
    }
    
    if (count > 0) {
        printf("平均查询时间: %.3f秒 (共%d条)\n", 
               total_time / count, count);
    }
    
    return 0;
}

这个程序虽然简单,但我在嵌入式设备上用了好几年。你想想看,嵌入式设备资源有限,装不了Percona Toolkit,自己写个轻量级解析器反而更实用。

避坑指南:我曾经写过一个日志分析工具,没考虑日志文件编码问题。结果客户那边日志是GBK编码的,解析出来全是乱码。从那以后,我所有日志工具都默认用UTF-8,同时加一个编码检测功能。

日志分析的整体流程

说了这么多,咱们用一张图把整个流程串起来:

数据库日志分析流程 慢查询日志 Query_time > 阈值 错误日志 Error / Warning / Note 审计日志 谁?什么时间?做了什么? 手动分析 grep / awk / 自研工具 自动化工具 pt-query-digest / 脚本 实时监控 Prometheus + Grafana 慢查询优化 加索引 / 改SQL / 分表 故障定位 找到根因 / 修复配置 安全审计 权限检查 / 异常行为 报告生成 + 告警通知

这张图把整个流程分成了四层:日志来源、解析方式、分析结果、最终输出。我个人习惯把自动化工具放在第二层,因为手动分析太慢了,尤其是日志量大的时候。

好了,日志分析这块就聊到这儿。记住一句话:日志不是用来存的,是用来分析的。别让日志躺在硬盘里睡大觉。


公众号:蓝海资料掘金营,微信deep3321