JumpServer 登录慢问题排查

有人反馈"JumpServer 登录变慢"。这篇是完整的排查 + 修复:怎么排查"登录慢"(把登录分段计时,再用受控实验坐实"缺索引",同一条查询 0.5ms vs 1202ms、差约 2400 倍)、加索引修复;以及更深一层——索引只是放大器,真凶是一套 AI 运维 agent 把登录审计表刷到了十几万、数据极偏,治本要把这个量降下去。

文中主机名、IP、域名、集群、账号一律占位或泛化(someuser / 来源IP / 账号),SQL 与命令可照抄、替换成你自己的即可。

排查与修复链路总览

一、问题表现

  • 主观:有人反馈登录堡垒机变慢。但这条反馈没留下可复现的现场,等我去测时已经不慢了(第二节,~2.5s)——所以本文不是"抓现行修一次慢登录",而是顺着它把"慢会从哪来"用受控实验还原出来。
  • 客观(真正能看到的异常):翻审计,某账号在 audits_userloginlog 里有 13 万+ 条登录记录,失败登录几乎全是「用户名或密码不正确」在刷屏——量大得离谱。

先认清三张表,后面不会看串:

  • audits_userloginlog:登录堡垒机本身(Web / SSH 网关 koko)的日志;
  • terminal_session:登录到目标资产的会话,带 cmd_amount(这条会话跑了几条命令);
  • terminal_command:会话里每条命令。

二、排查方法:先把"登录"分段,定位卡在哪一层

排"慢"的第 0 步永远是把"登录"拆成段计时,不分段直接猜没意义。一次登录可拆成四段,实测一遍(秒级精度):

阶段 实测本段耗时
建连 + SSH 握手 ~1.0s
密码 → OTP 提示 ~1.0s
认证(LDAP/MFA)+ 列资产 → 出菜单 ~0.6s
到资产菜单合计 ~2.5s

这一次测下来不慢(每段都 <1s)。但一次"现在不慢"既证明不了它从来不慢、也说不出它什么时候慢——我们手里始终没有那次"慢"的现场。所以两条路:要么等它再慢时按下表分层抓现行,要么(本文走的)用受控实验把"慢会从哪来"复现出来。先给分层抓法——卡哪段就查哪层:

卡的段 最可能的原因 查什么
建连握手 网络 / koko 负载 mtr 堡垒机、docker stats koko
认证段(输完密码/OTP 到出菜单久) LDAP/AD 域控慢、MFA 慢、core/DB 慢 core 日志 /authentication/tokens/ 耗时、域控响应
列资产慢 该用户资产/授权太多(JumpServer 经典慢点) 可见资产数、授权规则数、core+DB
选资产→连上目标机 目标机 / 网络 单独 ssh 目标机计时

横切三件(任何段慢都顺手看):容器/宿主资源docker stats、宿主 load)、MariaDBSHOW FULL PROCESSLIST、慢查询日志、审计表大小与索引)、RedisSLOWLOG)。

而本文要重点查的,是 DB 这条线——登录的安全检查会去 audits_userloginlog 按"用户 + 状态 + 时间"查这个账号最近的(失败)登录(连续失败锁定那套)。表一大、这条查询又没合适索引,就会拖慢登录。下面就去坐实它。

三、坐实"是不是索引":受控对照实验(不碰生产)

光看 SHOW INDEX / EXPLAIN 只能"指认",要"定罪"得做去索引/加索引的前后对照。生产表索引已在、不能动,所以:建一张临时表、复制真实数据量,在副本上去/加索引跑 ANALYZE(看实际扫了几行、几毫秒),用完即删——全程不碰生产表 audits_userloginlog

-- 副本(不动生产表),灌入真实数据量(实测 39.6 万行)
CREATE TABLE _idxtest LIKE audits_userloginlog;
INSERT INTO _idxtest SELECT * FROM audits_userloginlog;
-- ANALYZE 会真正执行并给出实际扫行/耗时(MariaDB)
ANALYZE FORMAT=JSON
  SELECT COUNT(*) FROM _idxtest
  WHERE username='账号' AND status=0 AND datetime > NOW() - INTERVAL 1 DAY;   -- 防爆破计数
-- 然后 ALTER ... DROP INDEX / ADD INDEX 切换索引状态,重复跑,对比
-- 做完务必 DROP TABLE _idxtest;   ← DROP 只对副本,绝不对生产表

39.6 万行真实数据上,两条登录相关查询、四种索引状态的实测(r_rows = 实际扫描行数):

索引状态 Q1 取最近一条登录 Q2 防爆破·近 1 天失败计数
A 有复合索引 (username,status,datetime) 1 行 / 1.2 ms 1450 行 / 0.5 ms
B 只剩 datetime 单列(原始态) 3 行 / 2.8 ms 4473 行 / 1202 ms ⚠️
C 完全无索引 39.6 万行 / 1439 ms 39.6 万行 / 101 ms
D 加回复合索引 1 行 / 1.7 ms 1450 行 / 0.5 ms

这就是"缺索引导致慢"的因果闭环:防爆破计数查询,从"原始态(只有 datetime 单列)"的 1202 ms → 加上复合索引 0.5 ms,约 2400 倍。去/加前后对照,比任何 EXPLAIN 都硬。

三点必须诚实标注(否则又是"看起来成立"):

  1. 慢的是 Q2(计数),不是 Q1(取最近一条):Q1 即便只有 datetime 单列也才 2.8ms——ORDER BY datetime DESC LIMIT 1 顺时间倒序很快撞到。所以"登录慢是不是这条索引",取决于登录实际跑哪条查询(要读 JumpServer 源码确认,本文未核,属未验证)。
  2. 反直觉:Q2 在"只剩 datetime 单列"(1202ms)比"完全无索引"(101ms)还慢——前者顺 datetime 索引扫一天范围、再回表做了近 2000 次随机页读;后者顺序全表扫反而快。用错索引比没索引更糟,也是实测出来的。
  3. 这 1202ms 的前提是该用户有 13 万行——见下一节。

怎么在你自己环境抓到它(不靠猜):

SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.3;
SET GLOBAL log_queries_not_using_indexes = ON;   -- 关键:没走索引的查询全记
-- 慢登录当下:SHOW FULL PROCESSLIST; 看那条登录检查查询是否在跑、Time 高、Rows_examined 大

慢日志/processlist 抓到那条查询没走索引扫大表 → EXPLAIN 佐证 → 去/加索引前后对照定因。

四、修复 A:加复合索引

出厂只有 datetime 单列索引(服务不了"按用户查"),补一条复合索引:

ALTER TABLE audits_userloginlog
  ADD INDEX idx_userloginlog_username_status_datetime (username, status, datetime);

列顺序是关键:usernamestatus 做等值过滤放前面,datetime 做排序/范围放最后——正好匹配"某用户、某状态、最近一段"。表才几十万行 ALTER 很快;千万级走在线 DDL(ALGORITHM=INPLACEpt-online-schema-change)、挑低峰。加完 EXPLAIN 应见 type=refrows=1

顺带一个常被问的:能不能上分区(按时间 RANGE)替代? 治不了这条查询——它按 username 过滤、没有时间范围谓词,分区按时间切裁剪不掉;且分区索引每分区本地,反而每个分区都探一遍。按用户查靠对的索引;按时间删/查才轮到分区。

五、但索引只是放大器:真凶是数据被刷爆

加索引能救急,可得回答:这表怎么会有 13 万行、还全压在一个用户身上? 数据不这么偏,缺索引也到不了秒级。

先拿对数据时踩了一脚:

SELECT status, COUNT(*) FROM audits_userloginlog WHERE username='someuser';
-- 成功 0、失败 13 万+ ?!

可这账号天天登得进去——现象和数据冲突。对账发现:成功存 姓名(账号)、失败存裸 账号,精确查只命中失败那一半。换 LIKE '%账号%' 一看:成功、失败各 13.6 万。

那谁在高频登录?按来源 IP 聚合——一个 IP 占 98%+terminal_session/terminal_command 显示全是 root、只读磁盘巡检命令(df/du/ls)、几乎每条命令一次会话。顺藤摸到代码:一套基于 LLM 的智能运维 agent,磁盘告警来了自适应下钻,而它执行命令是调一个"登录堡垒机 → 跑一条 → 断开"的脚本。于是:

一条命令 = 一次完整登录。 一次排查十来条命令 = 十来次登录,日积月累 13 万——把表撑大、把单用户行数撑到极偏,这才让第三节那条查询从亚毫秒放大到秒级。

附带一个隐蔽现象:每次成功登录都精确配一条「用户名或密码不正确」的失败。看 koko 日志(同一连接)真相是——SSH 客户端默认先甩本地公钥,没在 JumpServer 注册 → koko 调 core 返 password_failed 记成一条失败 → 再回退密码+OTP 成功。所以那 13 万"失败"不是攻击、是公钥认证的副产物,还把审计里真正该警惕的失败登录淹没了

koko 登录认证流程:那条假失败的来历,以及禁公钥后的变化

六、修复 B(治本):把登录量降下去

加索引治标,降量才治本。两处:

1)禁公钥,消掉假失败 —— 登录脚本的 ssh 命令别甩公钥:

ssh -o PubkeyAuthentication=no -o PreferredAuthentications=keyboard-interactive,password -p 2222 user@bastion

改完来源 IP 失败登录归零(koko 日志只剩 none → keyboard-interactive),审计那一半失败噪声不再增长。

2)会话复用,砍掉重复登录 —— 一次排查的多条命令复用一个登录会话(给登录脚本加 --session:登录到资产后循环读命令、逐条执行、打完成标记)。工程上挂开关、默认关、灰度放量、会话异常自动回退一条一登录,先上线零风险再开。效果看 terminal_session.cmd_amount

改之前 改之后
一次排查登录数 ≈ 命令数(十来次) 1~2
cmd_amount 1 11(一个登录跑 11 条命令)
terminal_command(命令审计) 每条一记 不变

命令审计一条不少,被压下去的只是"登录/会话"这两张被刷爆的表。延迟上也直观:首条命令 ~22s(含那一次登录握手),之后每条 ~1s(不再重登)。

七、几个对账教训

  1. 现象与数据矛盾先对账:第五节那个"成功 0 次"——字段口径差异,差点得出相反结论。
  2. "段慢" ≠ "那条查询慢" ≠ "没索引":分段计时只能定位到"认证段慢",必须下钻到 DB 抓现行 + EXPLAIN + 前后对照,才能把"慢"归到索引上。别让"听起来成立"替代"亲手抓到"。
  3. 索引可能只是放大器:缺索引到秒级,往往还叠加了"数据异常偏斜"。修索引的同时要回头看数据为什么这么多。
  4. 别据 commit message / 日志措辞想当然:一度看提交写了"换模型版本"就认定是它,实查配置根本没换。配置和代码才是事实。
  5. HTTP 200 ≠ 真跑通:做模型对照测速,跑出"对方更快",一看返回体是 {"code":...,"code_msg":"...no longer provided..."} 的错误信封、根本没产生内容。别拿"快速报错的耗时"当数据。

快速参考

  • 三张表分工audits_userloginlog(登录堡垒机本身);terminal_session(登录目标资产的会话,有 cmd_amount);terminal_command(每条命令)。
  • 排"登录慢"先分段:建连 / 认证 / 列资产 / 连资产,卡哪段查哪层;多数"慢"在 LDAP/MFA 或列资产,不一定是 DB。
  • 坐实"缺索引导致慢"= 三件套:慢查询日志(开 log_queries_not_using_indexes)抓到没走索引扫大表 → EXPLAIN 佐证 → 去/加索引前后对照计时(最硬)。
  • 加索引ALTER TABLE audits_userloginlog ADD INDEX idx_...(username,status,datetime);(列顺序:等值在前、排序/范围在后)。
  • username 口径坑:成功存 姓名(账号)、失败存裸 账号;按 LIKE '%账号%' 查,别精确查裸账号。
  • "密码不正确"假失败:SSH 甩了未注册公钥,koko 记成 password_failed;消除:ssh -o PubkeyAuthentication=no -o PreferredAuthentications=keyboard-interactive,password
  • 看认证细节docker logs <koko 容器> | grep -E "auth method|password_failed"
  • 受控验证索引(不碰生产)CREATE TABLE _t LIKE ...; INSERT ... SELECT ...; 在副本上 ANALYZE + 去/加索引对照,完事 DROP TABLE _t(DROP 只对副本)。
posted @ 2026-06-23 10:17  Hello_worlds  阅读(13)  评论(0)    收藏  举报