Skip to content

疑难案例库

复杂问题的分析过程和解决方案

概述

本文档记录了技术支持过程中遇到的疑难案例,包括问题背景、分析过程、解决方案和经验教训。

案例列表

案例一:数据库连接池耗尽导致服务不可用

问题背景

时间: 2026-01-10 影响范围: 生产环境全部用户 持续时间: 约 30 分钟

现象描述

  • 用户反馈系统无法访问
  • 监控告警:接口响应时间飙升至 10 秒以上
  • 错误日志:大量 "Connection pool exhausted" 错误

分析过程

mermaid
graph TD
    A[服务不可用] --> B[检查服务状态]
    B --> C[服务运行正常]
    C --> D[检查数据库连接]
    D --> E[连接池耗尽]
    E --> F[分析连接使用]
    F --> G[发现慢查询]
    G --> H[定位问题SQL]

排查步骤:

  1. 检查服务状态

    bash
    systemctl status app-service
    # 服务运行正常
  2. 检查数据库连接

    bash
    # MySQL 连接数
    mysql -e "SHOW STATUS LIKE 'Threads_connected';"
    # Threads_connected: 500 (达到上限)
  3. 分析慢查询

    sql
    SELECT * FROM mysql.slow_log 
    WHERE start_time > '2026-01-10 14:00:00'
    ORDER BY query_time DESC 
    LIMIT 10;
  4. 定位问题

    • 发现一条未加索引的查询语句
    • 该查询在数据量增大后执行时间超过 30 秒
    • 大量请求堆积,耗尽连接池

解决方案

临时方案:

sql
-- 终止慢查询
KILL QUERY <query_id>;

-- 重启应用释放连接
systemctl restart app-service

根本方案:

sql
-- 添加索引
ALTER TABLE large_table 
ADD INDEX idx_created_at (created_at);

-- 优化查询语句
-- 原查询:SELECT * FROM large_table WHERE created_at > '2025-01-01'
-- 优化后:SELECT id, name FROM large_table WHERE created_at > '2025-01-01' LIMIT 1000

经验教训

经验总结

  1. 监控告警:增加慢查询监控,超过阈值及时告警
  2. 索引管理:定期审查表索引,特别是大表
  3. 连接池配置:合理设置连接池大小和超时时间
  4. 查询优化:避免全表扫描,限制返回数据量

案例二:缓存穿透导致数据库压力激增

问题背景

时间: 2026-02-20 影响范围: 部分查询接口 持续时间: 约 15 分钟

现象描述

  • 数据库 CPU 使用率飙升至 95%
  • Redis 缓存命中率异常下降
  • 大量请求直接打到数据库

分析过程

  1. 检查缓存状态

    bash
    redis-cli info stats
    # keyspace_hits: 1000
    # keyspace_misses: 50000
    # 命中率极低
  2. 分析请求日志

    • 发现大量请求查询不存在的 key
    • 这些请求每次都绕过缓存直接查数据库

解决方案

实现缓存空值:

python
# 缓存空值防止穿透
def get_data(key):
    # 先查缓存
    data = redis.get(key)
    if data is not None:
        return data
    
    # 查数据库
    data = db.query(key)
    
    # 即使为空也缓存,设置较短的过期时间
    if data is None:
        redis.setex(key, 60, "NULL")
        return None
    
    redis.setex(key, 3600, data)
    return data

增加布隆过滤器:

python
# 使用布隆过滤器预先判断 key 是否存在
from pybloom_live import ScalableBloomFilter

bloom = ScalableBloomFilter(initial_capacity=1000000)

# 初始化时加载所有有效 key
for key in db.get_all_keys():
    bloom.add(key)

def get_data(key):
    # 布隆过滤器判断
    if key not in bloom:
        return None
    
    # 后续逻辑...

经验教训

  1. 对于可能不存在的数据,也要缓存空值
  2. 考虑使用布隆过滤器减少无效查询
  3. 监控缓存命中率,异常时及时告警

案例记录模板

发现新问题后,按以下模板记录:

markdown
### 案例编号:CASE-XXX

#### 问题背景
- 时间:
- 影响范围:
- 持续时间:

#### 现象描述
[描述问题的具体表现]

#### 分析过程
[详细记录排查步骤]

#### 解决方案
[记录临时方案和根本方案]

#### 经验教训
[总结经验,避免类似问题]

相关文档

数字化发展中心 · IT共享服务中心