news 2026/10/15 3:13:40

DooTask 性能优化实战:系统变慢、卡顿、CPU 100% 的排查与调优指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DooTask 性能优化实战:系统变慢、卡顿、CPU 100% 的排查与调优指南
  • 后端
  • 前端
  • 协同办公
  • 即时通讯
  • 企业应用

【免费下载链接】dootask

DooTask是一款开源在线项目任务管理工具,提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM,文件管理等工具;同时消息功能使用非对称加密技术让你的沟通更安全。

项目地址:https://gitcode.com/gh_mirrors/do/dootask
点击查看免费下载

本文面向 DooTask 部署管理员,系统梳理「打开任务列表 / 消息会话 / 仪表盘明显变慢、多人操作卡顿、CPU / 内存占用高」等性能问题的根因定位与解决方案。读完你可以掌握一套从量化观测(docker stats)到分层调优(全文搜索、MySQL、Swoole、Redis、nginx、硬件扩容)的完整实战流程,并理解为什么 DooTask 的 Swoole 常驻内存架构不能套用 PHP-FPM 时代的优化经验。

问题现象与排查前提

当 DooTask 出现以下现象时,基本都属于本文讨论的性能问题范畴:

  • 打开任务列表、消息会话、仪表盘加载时间明显变长;
  • 多人同时在线操作时整体卡顿、白屏或请求超时;
  • 主机 CPU、内存长期高位,甚至出现 CPU 100%;
  • 消息发送延迟、任务列表打开缓慢。

排查此类问题前需要两个前提:一是拥有部署主机的 root 权限,能够执行 docker 相关命令与修改配置文件;二是理解 DooTask 的运行时架构——它的后端并不是传统 PHP-FPM 短生命周期进程模型,而是基于 Swoole 的常驻内存协程模型(LaravelS 框架),因此很多 PHP-FPM 时代的优化经验(例如无脑调大pm.max_children)在这里不适用。

从部署形态看,DooTask 的 Docker 编排包含 php(Swoole 常驻进程)、nginx(反向代理与静态资源)、redis(缓存 / 队列 / Session)、mariadb(主数据库)和 appstore(应用商店容器),见 docker-compose.yml。性能问题的定位顺序也应当按这条链路逐层排查。

第一步:先量化,再动手

不要凭感觉调参。第一步永远是用数据定位瓶颈容器:

docker stats

观察哪个容器吃满 CPU 或内存:

  • php 容器吃满 → 进入 Swoole 进程模型调优(下文第三节);
  • mariadb 容器吃满 → 进入 MySQL 慢查询排查(第二节);
  • redis 容器内存逼近上限 → 进入 Redis 容量限制(第四节);
  • nginx 容器连接数 / 带宽吃紧 → 进入反向代理优化(第五节)。

DooTask 官方对硬件的最低建议是 2 核 4G 以上(见 README_CN.md),正常规模建议 4 核 8G 起步。当活跃用户数超过 100 人后,建议给 MySQL 分配独立机器;超过 200 人时,建议给 MySQL / Redis 单独分配资源,避免多容器争抢同一台主机的 IO 与内存。

数据量大是首要根因:启用 Manticore 全文搜索

对绝大多数「越用越卡」的实例而言,真正的瓶颈往往不是硬件,而是消息、任务、文件等大表在百万级数据量下的检索能力。

DooTask 的全站搜索(全局搜索、消息搜索、任务搜索、文件搜索)默认依赖 Manticore 全文搜索服务,前提是必须在应用商店安装 search 插件(入口:AppStore → search)。只有安装后,索引与查询链路才会切换到 Manticore;否则系统会静默降级为 MySQLLIKE模糊查询。

这一点在源码中有明确的分支实现。例如消息搜索模块 app/Module/Manticore/ManticoreMsg.php 中,searchDialogs()会先判断Apps::isInstalled("search"),未安装时调用searchDialogsByMysql()走 MySQL 回退搜索;文件搜索模块 app/Module/Manticore/ManticoreFile.php 同样在未安装 Manticore 时降级到 MySQLLIKE。

MySQLLIKE无法利用倒排索引,百万级messages/tasks表上做模糊匹配会带来全表扫描和极高的 CPU 开销,这正是「消息发送延迟、任务列表打开慢」的直接来源。因此:

  1. 打开 AppStore,安装并启用 search 插件;
  2. 确认 Manticore 服务可达。Manticore 的主机与端口在 config/dootask.php 中配置:默认SEARCH_HOST=search、SEARCH_PORT=9306(Manticore 的 MySQL 协议端口),可通过.env覆盖;
  3. 等待索引同步。项目提供了一组 Manticore 同步命令与任务,例如 app/Console/Commands/SyncMsgToManticore.php、app/Console/Commands/SyncTaskToManticore.php 以及常驻同步任务 app/Tasks/ManticoreSyncTask.php,负责把业务数据持续写入 Manticore 索引。

注意:如果 search 插件未安装,仅靠调大机器配置无法根治搜索慢的问题;这也是本文「先检查数据量与大表检索」优先于「加 CPU / 内存」的原因。

MySQL 慢查询:开慢日志、按报告补索引

DooTask 自带数据库镜像已经默认开启了慢查询日志。在 docker/mysql/conf.d/docker.cnf 中可以确认:

slow_query_log=1 slow-query-log-file=/var/lib/mysql/mysql-slow.log long_query_time=3

也就是说执行超过 3 秒的 SQL 都会被记录。排查时进入 mariadb 容器读取慢日志,例如:

docker exec dootask-mariadb-<APP_ID> tail -f /var/lib/mysql/mysql-slow.log

拿到慢 SQL 后,按报告针对messages、tasks、project_task_users、web_socket_dialog_msg_reads这类高频大表补充联合索引(仓库迁移中已有大量索引优化先例,如 2024_03_17_160641_add_index_some_20240317.php、2025_12_26_000000_add_indexes_to_web_socket_dialog_msgs.php 可供参考)。

另一个 MySQL 关键参数是 InnoDB 缓冲池:

innodb_buffer_pool_size = 4096M

该值决定 InnoDB 能在内存中缓存多少数据页,官方镜像默认 4G(docker.cnf)。对于数据量大、内存充足的机器,建议调到物理内存的 50% 左右;如果 MySQL 已拆到独立机器,则可按独立机器内存重新评估。另外注意 docker.cnf 中max_connections = 500、table_open_cache = 2048等连接与表缓存参数,当活跃用户增多导致连接耗尽时,也应同步检查。

Swoole 进程模型调优:worker_num 与 task_worker_num

DooTask 后端通过 LaravelS 把 Laravel 跑在 Swoole 常驻进程中(php 容器由 supervisor 拉起bin/laravels start,见 docker/php/php.conf 与 bin/auto)。核心进程参数集中在 config/laravels.php:

'reactor_num' => env('LARAVELS_REACTOR_NUM', swoole_cpu_num()), 'worker_num' => env('LARAVELS_WORKER_NUM', swoole_cpu_num()), 'task_worker_num' => env('LARAVELS_TASK_WORKER_NUM', swoole_cpu_num() * 2), 'max_request' => env('LARAVELS_MAX_REQUEST', 100000), 'dispatch_mode' => 2, 'enable_coroutine' => true,

关键点:

  • worker_num默认等于 CPU 核数(swoole_cpu_num()),负责处理 HTTP 请求。多核机器上应确认该值确实生效、所有核都被用到;如果容器被限制在单核或核数探测异常,并发能力会大打折扣。
  • task_worker_num默认是核数的 2 倍,负责处理异步任务(消息推送、通知、Manticore 同步等)。如果偏小,后台任务会积压,表现为「消息发送延迟、通知收不到」,此时需要按任务量调大task_worker_num。
  • max_request默认 100000,即每个 worker 处理满 10 万请求后自动重启,用于规避常驻进程的内存泄漏累积。
  • enable_coroutine => true表示启用 Swoole 协程,这也是「PHP-FPM 经验不适用」的根本原因:协程并发模型下,盲目增加进程数并不会线性提升吞吐,反而会加大内存与上下文切换开销。

这些参数均可通过.env中的LARAVELS_WORKER_NUM、LARAVELS_TASK_WORKER_NUM、LARAVELS_REACTOR_NUM、LARAVELS_MAX_REQUEST覆盖(参考 .env.docker 中LARAVELS_LISTEN_IP/LARAVELS_LISTEN_PORT的写法)。

改完任何 Swoole 相关配置,必须重启 PHP 服务才能生效:./cmd php restart(底层逻辑见 cmd 的restart_php(),通过supervisorctl restart php拉起常驻进程)。否则新参数不会加载,误以为调优生效而实际仍在旧配置下运行。

Redis 容量:防缓存撑爆

DooTask 的缓存、队列、Session 默认全部走 Redis(.env.docker:CACHE_DRIVER=redis、QUEUE_CONNECTION=redis、SESSION_DRIVER=redis)。docker-compose 中的 redis 容器 docker-compose.yml 默认没有 maxmemory 限制,大用户量下缓存与队列数据累积,会把 Redis 撑爆,进而拖垮整个系统。

建议为 Redis 显式设置容量上限与淘汰策略:

maxmemory 2gb maxmemory-policy allkeys-lru
  • maxmemory 2gb:按实际可用内存设定硬上限,防止 Redis 无界增长吃光内存;
  • maxmemory-policy allkeys-lru:达到上限后按 LRU 淘汰最久未使用的键,保证热数据常驻、系统稳定。

由于 docker-compose 中的 redis 镜像没有挂载自定义配置文件,实际操作时可以在容器内通过redis-cli CONFIG SET临时调整,或自定义 redis.conf 挂载到/usr/local/etc/redis/redis.conf后重启容器(注意 redis_data 卷会持久化数据,配置调整后需同步保证重启不丢失)。

nginx 反代瓶颈

nginx 作为唯一入口负责静态资源与 WebSocket 转发(见 docker/nginx/default.conf)。如果确认 nginx 容器 CPU / 连接数吃紧,需要检查:

  • worker 进程数与连接数:默认nginx:alpine镜像配置较为保守,并发高时可在挂载的站点配置中调大worker_processes(建议等于核数)与worker_connections;
  • 静态资源 gzip / cache:public/下存在大量前端静态资源(public/js、public/css),应开启 gzip 压缩与浏览器缓存(expires/Cache-Control),避免每次刷新都回源;
  • upstream 连接复用:default.conf 中已为 php 服务配置keepalive 16与proxy_http_version 1.1,location @laravels与/ws均走该 upstream,若确认此处成为瓶颈,可适当调大 keepalive 数量。

注意/ws的 WebSocket 代理超时已设置为 3600s(default.conf),长连接场景下不应再人为缩短该超时。

硬件扩容与架构拆分

当软件层优化做完后仍不够,才考虑资源层扩容。优先级如下:

  1. 加 CPU / 内存(最简单):先在docker-compose.yml中给 php / mariadb 服务合理分配资源,再考虑整机升级;
  2. MySQL 拆到独立机器:用户超过 100 后,把 mariadb 容器迁移到独立主机,DooTask 通过.env中的DB_HOST、DB_PORT、DB_DATABASE、DB_USERNAME、DB_PASSWORD连接外部数据库(.env.docker),并把DB_HOST从默认的mariadb改为外部主机地址即可;
  3. Redis 单独分配资源:用户超过 200 后,同样可通过REDIS_HOST、REDIS_PORT指向独立 Redis 实例。

迁移外部 DB 时注意保持数据一致性(建议先./cmd mysql backup备份,参考 cmd),并同步检查外部数据库的innodb_buffer_pool_size、慢查询日志等参数配置。

反模式清单:这些操作不要做

  • 不要只加 worker 不加 CPU:worker_num本质是 CPU 绑定型进程数,CPU 不足时盲目加大 worker 只会加剧线程切换与内存占用,系统反而更慢;
  • 生产环境不要保留APP_DEBUG=true:调试模式下日志会爆炸式增长,拖慢磁盘与整体响应。生产必须保持APP_DEBUG=false(.env.docker 默认即为 false;可通过./cmd debug false一键切换,见 cmd);
  • 不要套用 PHP-FPM 调优经验:DooTask 是 Swoole 常驻协程模型,php-fpm.conf那套进程池调优思路不适用。

相关 FAQ 延伸阅读

  • 装了 search 插件仍然搜不到结果:参见 全局搜索无结果 FAQ
  • 磁盘空间耗尽导致系统异常:参见 磁盘满 FAQ
  • 备份 / 升级运维实践:参见 部署备份 FAQ 与 部署升级 FAQ
  • 后端
  • 前端
  • 协同办公
  • 即时通讯
  • 企业应用

【免费下载链接】dootask

DooTask是一款开源在线项目任务管理工具,提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM,文件管理等工具;同时消息功能使用非对称加密技术让你的沟通更安全。

项目地址:https://gitcode.com/gh_mirrors/do/dootask
点击查看免费下载

相关推荐

上一篇:悬浮透明浏览器:3步解锁多任务并行工作新体验
下一篇:如何用3个简单步骤告别窗口切换焦虑:悬浮透明浏览器的效率革命

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 3:11:03

Hyperframes高帧率处理:帧密度、依赖与层级优化实战

1. 从“hyperframes”这个词本身说起第一次看到“hyperframes”这个词&#xff0c;我下意识地把它拆成了两半&#xff1a;hyper 和 frames。hyper 在技术语境里通常意味着“超”“高维”“超越常规”&#xff0c;而 frames 则指向帧、框架、结构。把这两个词拼在一起&#xff0…

作者头像 李华
网站建设 2026/10/15 3:10:12

网络安全工程师成长路线图,从小白到大神分阶段规划

夯实基石&#xff1a;初级工程师的入门必修课对于刚踏入网络安全领域的新人而言&#xff0c;首要任务不是盲目追求高深的攻防技巧&#xff0c;而是构建稳固的地基。这一阶段的核心目标是成为一名合格的初级安全工程师&#xff0c;重点在于对网络原理的深刻理解与 Linux 系统的熟…

作者头像 李华
网站建设 2026/10/15 3:08:26

Rope 免费换脸教程:从安装到导出第一条视频的 10 分钟路径

Rope 免费换脸教程&#xff1a;从安装到导出第一条视频的 10 分钟路径 【免费下载链接】Rope GUI-focused roop 项目地址: https://gitcode.com/GitHub_Trending/ro/Rope Rope 是一款免费的图形化人脸交换工具&#xff0c;基于 insightface 的 inswapper_128&#xff08…

作者头像 李华