Skip to content

KingBaseES 慢查询风暴

慢查询风暴概述

慢查询风暴是指数据库中突然出现大量慢查询,导致数据库性能急剧下降,甚至不可用的现象。慢查询风暴通常具有以下特点:

  • 短时间内出现大量慢查询
  • 查询执行时间明显延长
  • 系统资源(CPU、内存、I/O)使用率急剧上升
  • 数据库响应时间显著增加
  • 可能导致数据库崩溃或不可用

慢查询的定义

慢查询是指执行时间超过预设阈值的查询,KingBaseES 中通过 log_min_duration_statement 参数定义慢查询阈值:

sql
-- 查看慢查询阈值
SHOW log_min_duration_statement;

-- 设置慢查询阈值为 1000 毫秒(1秒)
ALTER SYSTEM SET log_min_duration_statement = 1000;

-- 重载配置
SELECT sys_reload_conf();

慢查询风暴的影响

系统层面影响

  • CPU 使用率飙升:大量慢查询同时执行,导致 CPU 资源耗尽
  • 内存耗尽:复杂查询可能占用大量内存,导致内存不足
  • I/O 压力剧增:大量全表扫描或索引扫描导致磁盘 I/O 使用率过高
  • 连接数耗尽:慢查询占用连接资源,导致新连接无法建立
  • 数据库崩溃:严重情况下可能导致数据库实例崩溃

业务层面影响

  • 应用响应延迟:数据库响应缓慢,导致应用响应时间延长
  • 业务中断:严重情况下可能导致业务完全中断
  • 用户体验下降:应用响应缓慢,导致用户体验变差
  • 业务损失:交易延迟或中断可能导致直接或间接的业务损失

慢查询风暴的原因

应用层面原因

  • 应用代码问题

    • 不当的 SQL 书写
    • 缺少必要的索引
    • 大量的复杂查询
    • 循环查询或嵌套查询
  • 应用架构问题

    • 缺少缓存机制
    • 连接池配置不当
    • 读写分离失效
    • 批量操作不当
  • 业务突发增长

    • 突发流量高峰
    • 数据量急剧增加
    • 业务逻辑变更

数据库层面原因

  • 索引问题

    • 索引缺失
    • 索引失效
    • 索引设计不合理
    • 索引碎片过多
  • 统计信息问题

    • 统计信息过时
    • 统计信息不准确
    • 缺少统计信息
  • 执行计划问题

    • 执行计划错误
    • 绑定变量问题
    • 直方图缺失
  • 资源配置问题

    • 内存配置不足
    • 工作内存过小
    • 并行查询配置不当
  • 数据库版本问题

    • 版本 bug
    • 优化器差异
    • 功能限制

慢查询风暴的诊断

实时监控

生产环境实时监控脚本

bash
#!/bin/bash
# KingBaseES 慢查询实时监控脚本

LOG_FILE="/opt/Kingbase/logs/slow_query_monitor_$(date +%Y%m%d).log"
ALERT_EMAIL="dba@example.com"
SLOW_THRESHOLD=5  # 慢查询阈值(秒)
CONNECTION_THRESHOLD=100  # 活跃连接数阈值

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}

# 确保日志目录存在
mkdir -p $(dirname $LOG_FILE)

log "开始慢查询实时监控"

while true; do
    # 检查活跃连接数
    ACTIVE_CONNECTIONS=$(ksql -U system -d test -t -c "SELECT count(*) FROM sys_stat_activity WHERE state = 'active';")
    
    log "当前活跃连接数:$ACTIVE_CONNECTIONS"
    
    if [ $ACTIVE_CONNECTIONS -gt $CONNECTION_THRESHOLD ]; then
        log "警告:活跃连接数已达 $ACTIVE_CONNECTIONS,超过阈值 $CONNECTION_THRESHOLD"
        
        # 查看慢查询
        SLOW_QUERIES=$(ksql -U system -d test -t -c "SELECT pid, usename, datname, query_start, now() - query_start AS duration, query FROM sys_stat_activity WHERE state = 'active' AND now() - query_start > interval '$SLOW_THRESHOLD seconds' ORDER BY duration DESC LIMIT 10;")
        
        log "慢查询详情:"
        log "$SLOW_QUERIES"
        
        # 发送告警邮件
        # echo "KingBaseES 慢查询告警:活跃连接数 $ACTIVE_CONNECTIONS,慢查询数量 $(echo "$SLOW_QUERIES" | wc -l)" | 
        # mail -s "KingBaseES Slow Query Alert" $ALERT_EMAIL
    fi
    
    # 检查系统资源使用情况
    CPU_USAGE=$(ksql -U system -d test -t -c "SELECT pg_stat_get_cpu_usage();")
    MEMORY_USAGE=$(ksql -U system -d test -t -c "SELECT pg_stat_get_memory_usage();")
    
    log "CPU 使用率:$CPU_USAGE%,内存使用率:$MEMORY_USAGE%"
    
    # 每 30 秒检查一次
    sleep 30
done

实时监控 SQL 语句

sql
-- 查看当前活跃查询,按执行时间排序
SELECT 
    pid,
    usename,
    datname,
    application_name,
    client_addr,
    query_start,
    now() - query_start AS query_duration,
    state,
    wait_event_type,
    wait_event,
    query
FROM 
    sys_stat_activity
WHERE 
    state = 'active'
ORDER BY 
    query_duration DESC
LIMIT 20;

-- 查看慢查询数量
SELECT count(*) FROM sys_stat_activity WHERE state = 'active' AND now() - query_start > interval '1 second';

-- 查看资源使用情况
SELECT 
    (SELECT count(*) FROM sys_stat_activity) AS current_connections,
    (SELECT pg_size_pretty(pg_total_relation_size('sys_stat_activity'))) AS table_size,
    (SELECT pg_stat_get_cpu_usage()) AS cpu_usage_percent,
    (SELECT pg_stat_get_memory_usage()) AS memory_usage_percent;

慢查询日志分析

生产环境慢查询日志分析脚本

bash
#!/bin/bash
# KingBaseES 慢查询日志分析脚本

LOG_DIR="/opt/Kingbase/ES/V8/data/sys_log"
OUTPUT_FILE="/opt/Kingbase/reports/slow_query_analysis_$(date +%Y%m%d).txt"

# 确保输出目录存在
mkdir -p $(dirname $OUTPUT_FILE)

echo "KingBaseES 慢查询日志分析报告" > $OUTPUT_FILE
echo "生成时间:$(date)" >> $OUTPUT_FILE
echo "=====================================" >> $OUTPUT_FILE
echo >> $OUTPUT_FILE

# 统计慢查询总数
echo "1. 慢查询总数:" >> $OUTPUT_FILE
grep -r "duration:" $LOG_DIR --include="*.log" | wc -l >> $OUTPUT_FILE
echo >> $OUTPUT_FILE

# 统计慢查询次数最多的 SQL
echo "2. 慢查询次数最多的 SQL:" >> $OUTPUT_FILE
grep -r "duration:" $LOG_DIR --include="*.log" | 
    awk -F"duration:" '{print $2}' | 
    awk -F"statement:" '{print $2}' | 
    sort | uniq -c | sort -nr | head -10 >> $OUTPUT_FILE
echo >> $OUTPUT_FILE

# 统计平均执行时间最长的 SQL
echo "3. 平均执行时间最长的 SQL:" >> $OUTPUT_FILE
grep -r "duration:" $LOG_DIR --include="*.log" | 
    awk -F"duration:" '{print $1, $2}' | 
    awk -F"statement:" '{print $1, $2}' | 
    awk '{sum[$3] += $2; count[$3] += 1} END {for (sql in sum) print sum[sql]/count[sql], count[sql], sql}' | 
    sort -nr | head -10 >> $OUTPUT_FILE
echo >> $OUTPUT_FILE

# 统计慢查询时间分布
echo "4. 慢查询时间分布:" >> $OUTPUT_FILE
grep -r "duration:" $LOG_DIR --include="*.log" | 
    awk -F"duration:" '{print $2}' | 
    awk '{if ($1 < 1000) print "0-1s"; else if ($1 < 5000) print "1-5s"; else if ($1 < 10000) print "5-10s"; else print "10s+"}' | 
    sort | uniq -c | sort -k2 >> $OUTPUT_FILE
echo >> $OUTPUT_FILE

echo "详细报告已生成:$OUTPUT_FILE" >> $OUTPUT_FILE

echo "慢查询日志分析完成,请查看报告:$OUTPUT_FILE"

慢查询日志配置示例

sql
-- 编辑 kingbase.conf 文件
log_min_duration_statement = 1000  -- 慢查询阈值(毫秒)
log_destination = 'csvlog'  -- 日志格式
logging_collector = on  -- 启用日志收集器
log_directory = 'sys_log'  -- 日志目录
log_filename = 'kingbase_%Y-%m-%d_%H%M%S.log'  -- 日志文件名格式
log_rotation_age = 1d  -- 日志轮换周期
log_rotation_size = 100MB  -- 日志文件大小限制
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '  -- 日志前缀
log_statement = 'none'  -- 不记录所有语句
log_slow_extra = on  -- 记录慢查询额外信息

执行计划分析

生产环境执行计划分析脚本

bash
#!/bin/bash
# KingBaseES 执行计划分析脚本

SQL_FILE="$1"
DB_NAME="test"

if [ -z "$SQL_FILE" ]; then
    echo "用法:$0 <sql_file>"
    exit 1
fi

if [ ! -f "$SQL_FILE" ]; then
    echo "错误:文件 $SQL_FILE 不存在"
    exit 1
fi

echo "KingBaseES 执行计划分析报告" > execution_plan_report.txt
echo "生成时间:$(date)" >> execution_plan_report.txt
echo "=====================================" >> execution_plan_report.txt
echo >> execution_plan_report.txt

while IFS= read -r sql; do
    if [ -n "$sql" ] && [[ ! $sql =~ ^-- ]]; then
        echo "SQL 语句:$sql" >> execution_plan_report.txt
        echo "=====================================" >> execution_plan_report.txt
        ksql -U system -d $DB_NAME -c "EXPLAIN ANALYZE $sql" >> execution_plan_report.txt 2>&1
        echo >> execution_plan_report.txt
        echo "=====================================" >> execution_plan_report.txt
        echo >> execution_plan_report.txt
    fi
done < "$SQL_FILE"

echo "执行计划分析完成,请查看报告:execution_plan_report.txt"

执行计划分析 SQL

sql
-- 查看查询执行计划
EXPLAIN ANALYZE SELECT * FROM table_name WHERE column_name = 'value';

-- 查看查询计划缓存
SELECT 
    planid,
    queryid,
    query,
    calls,
    total_time,
    min_time,
    max_time,
    mean_time,
    stddev_time
FROM 
    sys_stat_plan_cache
ORDER BY calls DESC
LIMIT 10;

-- 查看索引使用情况
SELECT 
    relname,
    indexrelname,
    idx_scan,
    idx_tup_read,
    idx_tup_fetch
FROM 
    sys_stat_user_indexes
ORDER BY idx_scan DESC
LIMIT 10;

-- 查看未使用的索引
SELECT 
    relname,
    indexrelname,
    idx_scan
FROM 
    sys_stat_user_indexes
WHERE idx_scan = 0
ORDER BY relname, indexrelname;

慢查询风暴的处理

紧急处理措施

生产环境紧急处理脚本

bash
#!/bin/bash
# KingBaseES 慢查询风暴紧急处理脚本

LOG_FILE="/opt/Kingbase/logs/slow_query_emergency_$(date +%Y%m%d).log"
KB_BIN="/opt/Kingbase/ES/V8/Server/bin"

# 日志函数
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}

# 确保日志目录存在
mkdir -p $(dirname $LOG_FILE)

log "开始慢查询风暴紧急处理"

# 1. 终止长时间运行的查询
log "1. 终止运行时间超过 5 分钟的查询"
$KB_BIN/ksql -U system -d test -c "SELECT sys_terminate_backend(pid) FROM sys_stat_activity WHERE state = 'active' AND now() - query_start > interval '5 minutes';"
log "终止查询完成"

# 2. 设置慢查询超时
log "2. 设置慢查询超时为 30 秒"
$KB_BIN/ksql -U system -d test -c "ALTER SYSTEM SET statement_timeout = '30000';"
log "设置慢查询超时完成"

# 3. 临时增加工作内存
log "3. 临时增加工作内存至 256MB"
$KB_BIN/ksql -U system -d test -c "ALTER SYSTEM SET work_mem = '256MB';"
log "增加工作内存完成"

# 4. 重载配置
log "4. 重载配置"
$KB_BIN/ksql -U system -d test -c "SELECT sys_reload_conf();"
log "重载配置完成"

# 5. 查看当前状态
log "5. 查看当前状态"
$KB_BIN/ksql -U system -d test -c "SELECT count(*) FROM sys_stat_activity WHERE state = 'active';"
$KB_BIN/ksql -U system -d test -c "SELECT pg_stat_get_cpu_usage(), pg_stat_get_memory_usage();"

log "慢查询风暴紧急处理完成,请查看详细日志:$LOG_FILE"
echo "慢查询风暴紧急处理完成,请查看详细日志:$LOG_FILE"

具体紧急处理措施

  1. 终止慢查询

    sql
    -- 终止运行时间超过 5 分钟的查询
    SELECT sys_terminate_backend(pid) FROM sys_stat_activity WHERE state = 'active' AND now() - query_start > interval '5 minutes';
    
    -- 终止特定类型的慢查询
    SELECT sys_terminate_backend(pid) FROM sys_stat_activity WHERE state = 'active' AND query LIKE '%SELECT * FROM large_table%' AND now() - query_start > interval '1 minute';
  2. 临时增加资源

    sql
    -- 临时增加工作内存
    ALTER SYSTEM SET work_mem = '256MB';
    
    -- 临时增加维护工作内存
    ALTER SYSTEM SET maintenance_work_mem = '4GB';
    
    -- 重载配置
    SELECT sys_reload_conf();
  3. 启用慢查询保护

    sql
    -- 设置慢查询超时
    ALTER SYSTEM SET statement_timeout = '30000'; -- 30秒
    
    -- 重载配置
    SELECT sys_reload_conf();

根本原因处理

  1. 优化 SQL 查询

    • 重写复杂查询,简化查询逻辑
    • 减少查询返回的数据量,只查询必要的字段
    • 优化连接方式,避免笛卡尔积
    • 使用合适的聚合函数
    • 避免在 WHERE 子句中使用函数
  2. 优化索引

    sql
    -- 创建缺失的索引
    CREATE INDEX idx_table_column ON table_name(column_name);
    
    -- 为经常一起查询的列创建复合索引
    CREATE INDEX idx_table_col1_col2 ON table_name(column1, column2);
    
    -- 重建失效或碎片化的索引
    REINDEX INDEX index_name;
    
    -- 重建整个表的索引
    REINDEX TABLE table_name;
    
    -- 分析表,更新统计信息
    ANALYZE VERBOSE table_name;
  3. 优化执行计划

    sql
    -- 强制使用特定索引
    SELECT * FROM table_name USE INDEX (idx_table_column) WHERE column_name = 'value';
    
    -- 重置执行计划缓存
    SELECT sys_evict_plan_cache();
    
    -- 设置优化器参数
    SET enable_seqscan = off;  -- 禁用全表扫描(临时)
    SET enable_hashjoin = off;  -- 禁用哈希连接(临时)
  4. 优化数据库配置

    sql
    -- 调整共享缓冲区大小(建议为系统内存的 25%)
    ALTER SYSTEM SET shared_buffers = '16GB';
    
    -- 调整有效缓存大小(建议为系统内存的 75%)
    ALTER SYSTEM SET effective_cache_size = '32GB';
    
    -- 调整随机页面成本(SSD 存储建议设置为 1.1)
    ALTER SYSTEM SET random_page_cost = 1.1;
    
    -- 调整顺序页面成本
    ALTER SYSTEM SET seq_page_cost = 1.0;
    
    -- 调整工作内存
    ALTER SYSTEM SET work_mem = '64MB';
    
    -- 调整 maintenance 工作内存
    ALTER SYSTEM SET maintenance_work_mem = '2GB';

慢查询风暴的预防

监控与告警

生产环境监控配置

  1. 慢查询监控配置

    sql
    -- 配置慢查询日志
    ALTER SYSTEM SET log_min_duration_statement = 1000; -- 1秒
    ALTER SYSTEM SET log_destination = 'csvlog';
    ALTER SYSTEM SET logging_collector = on;
    ALTER SYSTEM SET log_directory = 'sys_log';
    ALTER SYSTEM SET log_filename = 'kingbase_%Y-%m-%d_%H%M%S.log';
    ALTER SYSTEM SET log_rotation_age = 1d;
    ALTER SYSTEM SET log_rotation_size = 100MB;
    ALTER SYSTEM SET log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h ';
    ALTER SYSTEM SET log_slow_extra = on;
    
    -- 重载配置
    SELECT sys_reload_conf();
  2. 资源监控

    • 使用监控工具如 Prometheus + Grafana 监控 CPU、内存、I/O 使用率
    • 监控连接数变化
    • 监控锁等待情况
    • 监控事务执行时间
  3. 执行计划监控

    • 定期分析执行计划缓存
    • 监控索引使用情况
    • 定期更新统计信息

优化与规范

  1. SQL 开发规范

    • 制定 SQL 书写规范,强制使用绑定变量
    • 限制查询复杂度,避免嵌套查询和循环查询
    • 避免全表扫描,确保查询使用索引
    • 限制单次查询返回的数据量,建议不超过 1000 行
    • 避免在 WHERE 子句中使用函数,如 WHERE DATE(create_time) = '2023-01-01'
    • 禁止使用 SELECT *,只查询必要的字段
    • 限制 JOIN 表数量,建议不超过 5 个表
  2. 索引设计规范

    • 根据查询模式设计索引,优先为 WHERE 子句、JOIN 条件和 ORDER BY 字段创建索引
    • 避免过度索引,每个表索引数量建议不超过 5 个
    • 使用复合索引时,将选择性高的列放在前面
    • 定期审查索引使用情况,及时删除无用索引
    • 定期重建碎片化的索引,当索引碎片率超过 30% 时
    • 考虑使用部分索引和表达式索引,如 CREATE INDEX idx_partial ON table_name(column) WHERE status = 'active'
  3. 数据库配置优化

    • 根据业务特点调整配置参数,如 OLTP 系统适合较小的 work_mem,OLAP 系统适合较大的 work_mem
    • 定期审查配置参数,建议每季度一次
    • 测试不同配置的影响,使用 A/B 测试选择最优配置
    • 保持配置文件的版本控制,记录配置变更历史
    • 配置示例:
      sql
      -- OLTP 系统推荐配置
      ALTER SYSTEM SET shared_buffers = '16GB';          -- 系统内存的 25%
      ALTER SYSTEM SET effective_cache_size = '48GB';   -- 系统内存的 75%
      ALTER SYSTEM SET work_mem = '64MB';               -- 每个工作进程的内存
      ALTER SYSTEM SET maintenance_work_mem = '2GB';     -- 维护操作的内存
      ALTER SYSTEM SET random_page_cost = 1.1;          -- SSD 存储
      ALTER SYSTEM SET seq_page_cost = 1.0;             -- 顺序扫描成本
      ALTER SYSTEM SET log_min_duration_statement = 1000; -- 慢查询阈值
  4. 自动化定期维护脚本

    bash
    #!/bin/bash
    # KingBaseES 定期维护脚本
    # 用于自动更新统计信息、重建索引和清理碎片
    
    KB_HOME="/opt/Kingbase/Server"
    LOG_FILE="/opt/Kingbase/logs/maintenance_$(date +%Y%m%d).log"
    
    # 日志函数
    log() {
        echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
    }
    
    # 确保日志目录存在
    mkdir -p $(dirname $LOG_FILE)
    
    log "开始定期维护任务"
    
    # 1. 更新所有数据库的统计信息
    log "1. 更新统计信息"
    $KB_HOME/bin/ksql -U sysdba -d template1 -c "DO \$DO\$ 
    BEGIN 
      FOR db IN SELECT datname FROM sys_database WHERE datname NOT IN ('template0', 'template1') LOOP 
        EXECUTE 'ANALYZE VERBOSE ' || quote_ident(db) || '.*'; 
      END LOOP; 
    END \$DO\$;"
    
    # 2. 重建碎片化的索引
    log "2. 重建碎片化索引"
    $KB_HOME/bin/ksql -U sysdba -d template1 -c "DO \$DO\$ 
    BEGIN 
      FOR rec IN 
        SELECT 
          schemaname, 
          tablename, 
          indexname 
        FROM 
          sys_stat_user_indexes 
          JOIN sys_indexes ON indexrelid = oid 
          JOIN sys_class ON relid = indrelid 
        WHERE 
          idx_scan > 0 
          AND pg_index_size(indexrelid) > 0 
          AND (pg_index_size(indexrelid) - pg_relation_size(indexrelid)) / pg_index_size(indexrelid)::numeric > 0.3
      LOOP 
        EXECUTE 'REINDEX INDEX ' || quote_ident(rec.schemaname) || '.' || quote_ident(rec.indexname) || ';'; 
      END LOOP; 
    END \$DO\$;"
    
    # 3. 清理表碎片
    log "3. 清理表碎片"
    $KB_HOME/bin/ksql -U sysdba -d template1 -c "DO \$DO\$ 
    BEGIN 
      FOR rec IN 
        SELECT 
          schemaname, 
          tablename 
        FROM 
          sys_stat_user_tables 
        WHERE 
          n_dead_tup > 0 
          AND n_live_tup > 0 
          AND n_dead_tup > n_live_tup * 0.2
      LOOP 
        EXECUTE 'VACUUM FULL ANALYZE ' || quote_ident(rec.schemaname) || '.' || quote_ident(rec.tablename) || ';'; 
      END LOOP; 
    END \$DO\$;"
    
    log "定期维护任务完成"
    
    # 发送维护报告
    MAIL_TO="dba@example.com"
    SUBJECT="KingBaseES 定期维护报告 - $(date +%Y-%m-%d)"
    BODY="KingBaseES 定期维护已完成,请查看日志:$LOG_FILE"
    
    # 发送告警邮件(需要配置 mail 命令)
    # echo "$BODY" | mail -s "$SUBJECT" $MAIL_TO

版本差异

V8 R6 与 V8 R7 慢查询处理差异对比

特性V8 R6V8 R7
慢查询日志支持基本的慢查询日志增强了日志格式和内容,包含更多上下文信息(如执行计划、锁信息)
执行计划管理基本支持执行计划查看支持执行计划缓存和管理,可查看计划使用情况、命中次数和执行时间
慢查询保护基本支持全局语句超时设置支持更细粒度的慢查询控制,如按用户、按语句类型、按数据库设置超时
统计信息收集手动触发统计信息收集支持自动统计信息收集和更新,可配置收集策略、频率和阈值
索引管理基本的索引创建和重建支持索引使用情况监控和建议,自动识别无用索引,提供索引优化建议
慢查询分析工具有限的慢查询分析支持支持更强大的慢查询分析工具,提供可视化分析和优化建议
执行计划稳定性执行计划可能频繁变化支持执行计划锁定,确保计划稳定性,防止计划抖动
自适应优化器基本的优化器支持增强的自适应优化器,根据实际数据分布和执行统计调整计划
自适应执行计划不支持支持自适应执行计划,根据实际执行情况动态调整计划
查询监控视图基本的 sys_stat_activity 视图增强的监控视图,包含更多查询执行统计信息和资源使用情况
锁监控基本的锁信息查看增强的锁监控,支持查看锁等待链、锁等待时间和锁持有者信息
执行计划诊断有限的诊断支持支持执行计划诊断,提供计划选择理由和优化建议
自动工作负载仓库不支持支持自动工作负载仓库(AWR),定期收集性能数据,提供性能报告
慢查询自动优化不支持支持慢查询自动优化,自动识别和优化高频慢查询
资源组管理基本支持增强的资源组管理,可按用户、按应用分配资源,限制慢查询资源使用

最佳实践

慢查询监控最佳实践

  • 设置合理的慢查询阈值:根据业务特点设置合适的慢查询阈值,一般建议为 1-5 秒
  • 定期分析慢查询日志:每周至少分析一次慢查询日志,识别高频慢查询
  • 建立慢查询告警机制:设置慢查询数量和执行时间告警,及时发现问题
  • 使用专业的慢查询分析工具:如 KingBaseES 自带的性能分析工具或第三方工具
  • 保存历史慢查询数据:便于趋势分析和问题回溯

索引设计最佳实践

  • 根据查询模式设计索引:优先为 WHERE 子句、JOIN 条件和 ORDER BY 字段创建索引
  • 避免过度索引:每个表索引数量建议不超过 5 个
  • 使用复合索引时注意列顺序:将选择性高的列放在前面
  • 定期审查索引使用情况:删除未使用或使用频率低的索引
  • 考虑索引的维护成本:频繁更新的表应谨慎创建索引

SQL 优化最佳实践

  • 优先优化频繁执行的查询:重点关注执行频率高的慢查询
  • 优化返回大量数据的查询:只查询必要的字段,使用 LIMIT 限制返回行数
  • 优化复杂的连接查询:减少连接表数量,优化连接顺序
  • 避免在查询中使用函数:特别是在 WHERE 子句中
  • 使用绑定变量:避免硬解析,提高缓存命中率

性能测试最佳实践

  • 定期进行性能测试:建议每季度进行一次全面的性能测试
  • 模拟真实的业务场景:使用真实的数据集和查询模式
  • 测试不同负载下的表现:包括正常负载、峰值负载和极限负载
  • 测试数据库的极限能力:了解数据库的最大处理能力
  • 保存性能测试结果:便于对比分析和问题回溯

常见问题(FAQ)

如何快速定位慢查询风暴的原因?

解答:慢查询风暴的快速定位方法:

  1. 查看当前活跃查询:使用 sys_stat_activity 视图找出执行时间最长的查询

    sql
    SELECT pid, usename, datname, query_start, now() - query_start AS duration, query 
    FROM sys_stat_activity 
    WHERE state = 'active' 
    ORDER BY duration DESC 
    LIMIT 10;
  2. 分析慢查询日志:找出出现频率最高和执行时间最长的查询

    bash
    grep -r "duration:" /opt/Kingbase/ES/V8/data/sys_log --include="*.log" | 
        sort -nr | head -20
  3. 查看执行计划:分析慢查询的执行计划,找出异常

    sql
    EXPLAIN ANALYZE SELECT * FROM slow_query;
  4. 检查索引使用情况:找出缺少索引或索引失效的查询

    sql
    SELECT relname, indexrelname, idx_scan FROM sys_stat_user_indexes ORDER BY idx_scan;
  5. 检查系统资源使用情况:找出 CPU、内存或 I/O 瓶颈

    sql
    SELECT pg_stat_get_cpu_usage(), pg_stat_get_memory_usage();

如何处理突发的慢查询风暴?

解答:突发慢查询风暴的处理步骤:

  1. 紧急终止长时间运行的查询:减少系统资源占用

    sql
    SELECT sys_terminate_backend(pid) FROM sys_stat_activity WHERE state = 'active' AND now() - query_start > interval '5 minutes';
  2. 临时增加数据库资源配置:如工作内存、维护工作内存等

    sql
    ALTER SYSTEM SET work_mem = '256MB';
    ALTER SYSTEM SET maintenance_work_mem = '4GB';
    SELECT sys_reload_conf();
  3. 启用慢查询保护机制:设置语句超时,防止单个查询占用资源时间过长

    sql
    ALTER SYSTEM SET statement_timeout = '30000'; -- 30秒
    SELECT sys_reload_conf();
  4. 分析慢查询原因:通过日志和执行计划分析,找出根本原因

  5. 进行针对性优化:如添加索引、优化SQL、调整配置等

  6. 监控系统恢复情况:确保系统逐渐恢复正常

如何预防慢查询风暴的发生?

解答:慢查询风暴的预防措施:

  1. 建立完善的慢查询监控和告警机制:及时发现慢查询问题
  2. 制定严格的 SQL 开发规范:包括 SQL 书写、索引使用、绑定变量等
  3. 定期优化数据库和索引:包括统计信息更新、索引重建、无用索引删除等
  4. 定期进行性能测试:模拟真实业务场景,发现潜在问题
  5. 保持数据库版本更新:使用最新的稳定版本,享受优化器改进
  6. 合理配置数据库参数:根据服务器资源和业务特点调整配置
  7. 使用缓存机制:减少数据库直接访问,尤其是热点数据
  8. 实现读写分离:减轻主库压力

如何优化慢查询?

解答:慢查询的优化方法:

  1. 重写 SQL 查询:简化查询逻辑,避免复杂嵌套和循环查询
  2. 添加必要的索引:为 WHERE 子句、JOIN 条件和 ORDER BY 字段创建索引
  3. 优化执行计划:使用合适的索引,调整优化器参数
  4. 调整数据库配置:如增加内存、调整缓存大小等
  5. 考虑使用缓存:对热点数据使用缓存,减少数据库访问
  6. 优化表结构:如分区表、分表等,减少单表数据量
  7. 使用并行查询:对于复杂查询,考虑启用并行执行

不同版本的慢查询处理有什么差异?

解答:V8 R7 相比 V8 R6 在慢查询处理方面的改进:

  1. 增强的慢查询日志:包含更多上下文信息,便于分析
  2. 更细粒度的慢查询控制:可按用户、按语句类型设置不同的超时时间
  3. 执行计划缓存和管理:可查看计划使用情况,锁定执行计划
  4. 自动统计信息收集和更新:无需手动触发,确保统计信息准确性
  5. 索引使用情况监控和建议:自动识别无用索引,提供优化建议
  6. 增强的自适应优化器:根据实际数据分布调整执行计划

如何监控慢查询?

解答:慢查询监控的方法:

  1. 配置慢查询日志:设置合适的慢查询阈值,记录慢查询详细信息
  2. 使用系统视图监控:如 sys_stat_activitysys_stat_plan_cache
  3. 使用第三方监控工具:如 Prometheus + Grafana、Zabbix 等
  4. 设置慢查询告警:根据慢查询数量或执行时间设置告警
  5. 定期分析慢查询日志:每周至少分析一次,识别高频慢查询

如何处理大量慢查询日志?

解答:处理大量慢查询日志的方法:

  1. 设置合理的日志保留策略:定期清理过期日志

    bash
    # 保留最近 7 天的慢查询日志
    find /opt/Kingbase/ES/V8/data/sys_log -name "*.log" -mtime +7 -delete
  2. 使用日志轮换:限制单个日志文件大小,便于管理

  3. 使用日志分析工具:如 ELK Stack、Graylog 等集中管理和分析日志

  4. 提取关键信息:只保存慢查询的关键信息,减少日志量

  5. 定期归档日志:将重要日志归档保存,便于后续分析

如何识别和处理无用索引?

解答:识别和处理无用索引的方法:

  1. 识别无用索引

    sql
    SELECT 
        relname,
        indexrelname,
        idx_scan
    FROM 
        sys_stat_user_indexes
    WHERE idx_scan = 0
    ORDER BY relname, indexrelname;
  2. 处理无用索引

    • 对于确认无用的索引,直接删除:`DROP INDEX index_name;
    • 对于不确定的索引,可先重命名,观察一段时间后再决定是否删除
    • 定期审查索引使用情况,每季度至少一次

如何优化执行计划?

解答:优化执行计划的方法:

  1. 添加或调整索引:确保查询使用合适的索引
  2. 更新统计信息:确保优化器获得准确的表和索引统计信息
  3. 调整优化器参数:如 enable_seqscanenable_hashjoin
  4. 使用执行计划锁定:对于关键查询,锁定执行计划,确保稳定性
  5. 重写 SQL 查询:简化查询逻辑,便于优化器生成更好的计划
  6. 升级数据库版本:享受优化器改进带来的性能提升

如何处理执行计划不稳定的问题?

解答:处理执行计划不稳定的方法:

  1. 使用执行计划锁定:锁定关键查询的执行计划
  2. 调整优化器参数:减少执行计划的变化
  3. 更新统计信息:确保优化器获得准确的统计信息
  4. 使用绑定变量:避免硬解析,提高缓存命中率
  5. 简化复杂查询:将复杂查询拆分为多个简单查询
  6. 使用提示(Hint):强制优化器使用特定的执行计划

案例分析

电商平台慢查询风暴案例

背景:某电商平台在促销活动期间,突然出现大量慢查询,导致数据库响应时间从正常的 50 毫秒延长到 5 秒以上,严重影响用户体验。

问题分析

  1. 查看当前活跃查询,发现大量查询集中在 order
  2. 分析慢查询日志,发现主要是 SELECT * FROM order WHERE user_id = ? 语句
  3. 查看执行计划,发现该查询没有使用索引,进行了全表扫描
  4. 检查索引使用情况,发现 order 表缺少 user_id 索引
  5. 检查数据量,发现 order 表数据量已达到 1000 万行

解决方案

  1. 紧急创建索引CREATE INDEX idx_order_user_id ON order(user_id);
  2. 终止长时间运行的查询SELECT sys_terminate_backend(pid) FROM sys_stat_activity WHERE query LIKE '%SELECT * FROM order WHERE user_id%' AND now() - query_start > interval '1 minute';
  3. 优化查询:修改应用代码,只返回必要的字段:SELECT order_id, order_time, status FROM order WHERE user_id = ?
  4. 增加缓存机制:对热点订单数据添加缓存
  5. 调整数据库配置:增加共享缓冲区和工作内存

效果

  • 数据库响应时间从平均 5 秒降至 50 毫秒
  • 慢查询数量从每秒 1000+ 降至 0
  • CPU 使用率从 95% 降至 30%
  • 应用响应时间恢复正常

金融系统慢查询风暴案例

背景:某金融系统在每日对账期间,出现大量慢查询,导致数据库资源耗尽,影响核心业务处理。

问题分析

  1. 查看当前活跃查询,发现大量 GROUP BYJOIN 操作
  2. 分析慢查询日志,发现主要是对账相关的复杂查询
  3. 查看执行计划,发现执行计划异常,使用了不当的连接顺序
  4. 检查统计信息,发现统计信息过时,最后更新时间是 30 天前
  5. 检查数据库配置,发现工作内存过小,仅为 16MB

解决方案

  1. 更新统计信息ANALYZE VERBOSE ALL;
  2. 优化执行计划SELECT sys_evict_plan_cache();
  3. 调整工作内存ALTER SYSTEM SET work_mem = '256MB';
  4. 优化对账查询:将复杂的对账查询拆分为多个简单查询
  5. 调整对账时间:将对账时间从业务高峰期调整到凌晨

效果

  • 对账时间从 4 小时降至 30 分钟
  • 数据库资源使用率恢复正常
  • 核心业务不受影响
  • 系统稳定性显著提高

通过以上诊断方法和解决方案,可以有效处理 KingBaseES 慢查询风暴问题。DBA 需要根据具体情况,结合实时监控、日志分析和执行计划分析,快速定位问题,并采取相应的解决方案。同时,通过合理的监控告警、SQL 开发规范和定期优化,可以预防慢查询风暴的发生,提高系统的可靠性和可用性。