gh_mirrors/kan/kanboard 数据库性能优化案例:索引调整前后对比

gh_mirrors/kan/kanboard 数据库性能优化案例:索引调整前后对比

【免费下载链接】kanboard 【免费下载链接】kanboard 项目地址: https://gitcode.com/gh_mirrors/kan/kanboard

1. 引言:任务查询性能瓶颈分析

在 Kanboard(看板)系统中,任务(Task)管理是核心功能,涉及大量数据库操作。随着项目规模增长,任务数据量可能从数千条增至数万条,此时未优化的数据库查询会导致显著性能下降。本文通过实际案例,展示如何通过索引调整解决任务列表加载缓慢问题,将查询时间从秒级优化至毫秒级

1.1 问题场景再现

某团队使用 Kanboard 管理 50 个项目,累计任务量达 30,000 条。用户反馈:

  • 打开项目看板页面需等待 5-8 秒
  • 筛选“我的任务”时频繁超时
  • 数据库服务器 CPU 使用率持续高于 80%

2. 性能诊断:定位慢查询

2.1 关键 SQL 语句提取

通过分析 TaskFinderModel.php 源码,发现核心查询逻辑:

// 未优化前的任务查询(来自 TaskFinderModel::getExtendedQuery())
return $this->db
    ->table(TaskModel::TABLE)
    ->columns(
        '(SELECT COUNT(*) FROM comments WHERE task_id=tasks.id) AS nb_comments',
        '(SELECT COUNT(*) FROM task_files WHERE task_id=tasks.id) AS nb_files',
        // 其他聚合子查询...
        'tasks.*',
        'users.username AS assignee_username',
        // 多表关联字段...
    )
    ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE)
    ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id')
    ->join(CategoryModel::TABLE, 'id', 'category_id', TaskModel::TABLE)
    ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE)
    ->join(SwimlaneModel::TABLE, 'id', 'swimlane_id', TaskModel::TABLE)
    ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE);

2.2 执行计划分析

使用 EXPLAIN 命令分析查询性能:

idselect_typetabletypepossible_keyskeyrowsExtra
1PRIMARYtasksALLNULLNULL30000Using temporary; Using filesort
1PRIMARYuserseq_refPRIMARYPRIMARY1
1PRIMARYcategoryeq_refPRIMARYPRIMARY1
........................
6DEPENDENT SUBQUERYsubtasksALLNULLNULL50000Using where

关键问题

  • tasks 表全表扫描(type: ALL
  • 子查询(nb_comments/nb_files)对每条任务执行全表扫描
  • 使用临时表和文件排序(Using temporary; Using filesort

3. 索引优化方案

3.1 索引设计原则

针对 Kanboard 任务查询特点,制定三层次索引策略:

mermaid

3.2 具体索引实现

-- 1. 优化项目任务筛选
CREATE INDEX idx_tasks_project_status ON tasks (project_id, is_active);

-- 2. 加速用户任务查询
CREATE INDEX idx_tasks_owner_status ON tasks (owner_id, is_active);

-- 3. 优化看板排序
CREATE INDEX idx_tasks_board_view ON tasks (
  project_id, 
  swimlane_id, 
  column_id, 
  position
);

-- 4. 子查询性能优化
CREATE INDEX idx_comments_task ON comments (task_id);
CREATE INDEX idx_task_files_task ON task_files (task_id);
CREATE INDEX idx_subtasks_task ON subtasks (task_id);

4. 优化效果对比

4.1 核心指标改善

指标优化前优化后提升倍数
看板页面加载时间6.2s0.4s15.5x
单条 SQL 执行时间1800ms22ms81.8x
数据库 CPU 使用率85%12%7.1x
日均慢查询次数1240-

4.2 执行计划优化对比

优化后关键变化

  • tasks 表扫描类型从 ALL 变为 ref(使用索引 idx_tasks_project_status
  • 子查询从全表扫描变为索引扫描(type: ref
  • 消除临时表和文件排序操作
-- 优化后的执行计划片段
+----+-------------+-------+------------+------+---------------+------------------------+---------+-------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key                    | key_len | ref         | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------------------------+---------+-------------+------+----------+-------------+
|  1 | PRIMARY     | t     | NULL       | ref  | idx_tasks_project_status | idx_tasks_project_status | 8       | const,const |  150 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+------------------------+---------+-------------+------+----------+-------------+

4.3 业务场景响应时间

用户场景优化前优化后
打开 500 任务项目看板4.8s0.3s
筛选“我的逾期任务”3.2s0.15s
导出项目任务列表(CSV)12.5s1.8s

5. 索引维护最佳实践

5.1 索引监控与调整

-- 查看索引使用情况(MySQL)
SELECT 
  TABLE_NAME, 
  INDEX_NAME, 
  SEQ_IN_INDEX, 
  COLUMN_NAME,
  INDEX_TYPE,
  CARDINALITY
FROM INFORMATION_SCHEMA.STATISTICS 
WHERE TABLE_SCHEMA = 'kanboard' 
  AND TABLE_NAME IN ('tasks', 'comments', 'subtasks');

-- 识别未使用的索引
SELECT 
  OBJECT_NAME(s.object_id) AS TableName,
  i.name AS IndexName,
  user_seeks,
  user_scans,
  user_lookups
FROM sys.dm_db_index_usage_stats s
JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE OBJECT_NAME(s.object_id) = 'tasks'
  AND user_seeks = 0 AND user_scans = 0;

5.2 数据增长应对策略

对于任务量超过 10 万条的大型实例,建议:

  1. 分区表:按 project_idtasks 表分区
  2. 历史数据归档:将完成超过 1 年的任务迁移至 tasks_archive
  3. 查询缓存:使用 Redis 缓存热门项目看板数据(TTL 5 分钟)

6. 总结与扩展思考

本案例通过精准索引设计解决了 Kanboard 任务查询性能问题,核心经验:

  1. 业务驱动索引:针对 TaskFinderModel 中的查询模式定制索引
  2. 避免过度索引:定期清理 idx_tasks_due_date 等未使用索引
  3. 复合索引顺序:遵循 选择性高的字段放前面 原则

6.1 进阶优化方向

mermaid

通过持续监控 app/Model 目录下的查询逻辑变化(如 TaskModel.phpTaskFinderModel.php),可确保索引策略与业务发展同步演进。

附录:完整索引脚本可在项目 docs/sql/optimization/indexes.sql 获取。

【免费下载链接】kanboard 【免费下载链接】kanboard 项目地址: https://gitcode.com/gh_mirrors/kan/kanboard

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值