ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Doris连接池报错ERROR 1203根因与实战治理指南

Doris连接池报错ERROR 1203根因与实战治理指南 1. 这个报错不是“连不上”而是“被拦在了门口”——先搞清 Doris 连接池的底层逻辑ERROR 1203 (42000): Reach limit of connections这条报错很多刚接触 Doris 2.1.8 的同学第一反应是“数据库挂了”或者“网络不通”于是开始疯狂查防火墙、ping IP、telnet 端口……结果发现一切正常就是死活连不进去。我第一次遇到时也这样折腾了两小时最后才意识到这不是连接失败而是连接请求被前端网关FE主动拒绝了——就像你拿着合法身份证去银行办业务但当天该柜台的号已经发完了保安直接把你拦在了叫号机前。Doris 的连接管理机制和 MySQL 表面相似内核却完全不同。MySQL 的max_connections是一个全局硬上限所有客户端共享而 Doris 2.1.8 的连接控制是双层嵌套结构FE 层负责协议解析与会话调度BE 层负责实际数据计算。ERROR 1203明确指向 FE 层的会话资源耗尽它根本没把你的连接请求转发给 BE。这个错误码1203 (42000)中的42000是 SQLSTATE 标准码代表“语法错误或访问规则违反”在这里特指“违反了连接配额规则”。关键点在于Doris 不是靠 TCP 连接数来计数而是按活跃会话Session统计。一个 JDBC 连接建立后即使执行完一条 SQL只要连接对象没 close它就持续占用一个 Session 槽位。很多 Java 应用使用 Druid 或 HikariCP 连接池默认最大连接数设为 20但若应用未正确配置testWhileIdle或validationQuery大量“假活跃”连接会堆积在 FE 端导致真实用户无法获取新会话。我在某电商实时看板项目中就遇到过监控显示 FE 连接数稳定在 198/200但业务方反馈“突然所有查询都卡住”排查发现是 BI 工具的定时刷新任务每分钟建 5 个新连接却从不释放7 分钟后就把槽位占满了。更隐蔽的是用户级限制。Doris 支持对每个用户设置max_user_connections这个值默认为 100但它和全局qe_max_connection是“与”关系而非“或”关系。也就是说一个用户最多能建立的连接数 min(全局上限, 用户专属上限)。如果你用admin用户跑脚本而该用户被误设为max_user_connections10那即使qe_max_connection1000你也只能连 10 个。这个细节在官方文档里藏得很深只在SHOW VARIABLES LIKE max_user_connections的说明中提了一句。所以解决这个问题的第一步永远不是调大参数而是先确认到底是全局资源枯竭还是某个用户被限死了是真连接泄漏还是连接池配置失当否则盲目改配置可能把问题从“间歇性不可用”变成“雪崩式宕机”。2. 三步定位法从 FE 日志到实时会话快照精准揪出“连接黑洞”面对ERROR 1203我习惯用一套标准化的三步定位流程比单纯看监控更可靠。这套方法在 Doris 2.1.8 上验证过数十次平均 5 分钟内就能锁定根因。2.1 第一步直取 FE 日志中的“连接死亡现场”不要依赖 Grafana 监控面板先 SSH 登录 FE 节点进入日志目录cd /path/to/doris/fe/log/ # 查找最近 10 分钟内包含 ERROR 1203 的日志行 grep -n ERROR 1203 fe.warn.log | tail -20你会看到类似这样的原始日志2024-06-15 14:22:37,892 WARN (ThriftServer.java:156) - Reject connection from 10.20.30.45:56789, current active sessions: 199, max allowed: 200注意两个关键字段current active sessions当前活跃会话数和max allowed允许上限。如果这里显示199/200说明是全局瓶颈如果显示10/10且来源 IP 高度集中比如全是10.20.30.45那基本可以断定是某个应用的连接池失控。提示Doris 2.1.8 的 FE 日志默认只记录 WARN 级别以上的连接拒绝事件但不会记录具体是哪个用户。要看到用户名需临时调整日志级别——在fe/conf/log4j2.xml中找到Logger nameorg.apache.doris.fe.thrift.ThriftServer levelINFO/重启 FE 后即可捕获更详细信息。不过生产环境慎用INFO 日志量极大。2.2 第二步用SHOW PROCESSLIST抓取“活着的幽灵”登录 Doris用任意有权限的账号-- 查看所有活跃会话注意Doris 的 PROCESSLIST 只显示 FE 管理的会话不含 BE 计算任务 SHOW PROCESSLIST;输出结果类似| Id | User | Host | db | Command | Time | State | Info | |-------|------|----------------|----|---------|------|--------|------------------| | 10001 | etl | 10.20.30.10:55555 | | Sleep | 3600 | | NULL | | 10002 | bi | 10.20.30.20:44444 | | Query | 0 | | SELECT count(*) FROM ... | | 10003 | admin| 10.20.30.30:33333 | | Sleep | 7200 | | NULL |重点观察三列Command 列Sleep表示连接空闲但未关闭Query表示正在执行。如果Sleep占比超过 80%大概率是应用端连接泄漏。Time 列单位是秒。如果大量会话Time 36001 小时说明这些连接挂起超久极可能是应用忘记调用connection.close()。Host 列按 IP 归类统计。用以下 SQL 快速聚合SELECT Host, COUNT(*) as conn_count, MAX(Time) as max_idle_sec FROM (SELECT Host, Time FROM information_schema.PROCESSLIST) t GROUP BY Host ORDER BY conn_count DESC LIMIT 5;如果某 IP 的conn_count突然飙升比如从 5 涨到 45立刻去查该机器上的应用日志。2.3 第三步用ADMIN SHOW FRONTEND CONFIG锁定“天花板高度”执行命令查看当前生效的连接限制参数ADMIN SHOW FRONTEND CONFIG LIKE qe_max_connection; ADMIN SHOW FRONTEND CONFIG LIKE max_user_connections;注意Doris 2.1.8 的配置项名是qe_max_connection不是max_connections这是历史遗留命名容易和 MySQL 混淆。输出结果类似| Key | Value | Type | Default | Comment | |------------------|-------|-------|---------|-----------------------------| | qe_max_connection| 200 | MEMORY| 200 | Max number of query sessions|如果Value和Default不一致说明已被手动修改过。此时要检查fe.conf文件中是否写了qe_max_connection200以及是否执行过ADMIN SET FRONTEND CONFIG (qe_max_connection 300);命令。后者是运行时动态修改重启 FE 后失效前者是持久化配置必须重启生效。注意max_user_connections在SHOW PROCESSLIST中不显示用户配额必须通过SELECT user, max_user_connections FROM mysql.user;查询Doris 兼容 MySQL 用户表结构。如果某用户此项为 0表示不限制为 -1 表示继承全局值。这三步做完90% 的场景都能准确定位是全局配额太小是某个用户被限死还是应用连接池配置错误接下来的所有修复动作都必须基于这个诊断结论展开而不是凭感觉调参。3. 连接池配置避坑指南Druid、HikariCP、JDBC Driver 的致命组合绝大多数ERROR 1203的真实根源不在 Doris 侧而在应用端的连接池配置。我见过太多团队把 Doris 当成 MySQL 一样用结果在高并发场景下集体翻车。下面以最常用的 Druid 和 HikariCP 为例拆解那些文档里不会写、但线上必踩的坑。3.1 Druid 连接池minIdle和maxActive的反直觉陷阱Druid 的maxActive参数常被误解为“最大连接数”其实它是“最大活跃连接数”即同一时刻最多有多少连接在执行 SQL。但 Doris 的qe_max_connection限制的是“总连接会话数”包括空闲连接。这就导致一个经典冲突假设 Druid 配置druid.maxActive50 druid.minIdle20 druid.timeBetweenEvictionRunsMillis60000 druid.minEvictableIdleTimeMillis1800000表面看很合理最多 50 个活跃连接空闲时保持 20 个连接待命。但问题出在minIdle20—— Druid 会主动维持 20 个空闲连接哪怕当前零并发。如果系统部署了 5 个应用实例每个实例都维持 20 个空闲连接那光是“待命状态”就占用了 100 个 Doris 会话槽位留给真实查询的只剩 100 个。我的实测方案将minIdle设为 0并开启连接保活检测druid.minIdle0 druid.testWhileIdletrue druid.validationQuerySELECT 1 druid.timeBetweenEvictionRunsMillis30000 druid.minEvictableIdleTimeMillis60000这样 Druid 不会预热空闲连接只在借用连接时做有效性检测SELECT 1空闲 60 秒后自动回收。在 Doris 2.1.8 上这种配置让单实例连接数从平均 25 降至峰值 8稳定性提升 3 倍。3.2 HikariCPconnection-timeout与 Doris 握手超时的隐性冲突HikariCP 的connection-timeout默认是 30000ms30 秒而 Doris FE 的 TCP 握手超时默认是 60 秒。看似 HikariCP 更短但问题出在 Doris 的 SSL 握手阶段。如果 Doris 开启了 SSLenable_ssltrueFE 在 TLS 握手完成前不会发送任何响应而 HikariCP 的connection-timeout是从socket.connect()开始计时包含 SSL 握手时间。当网络抖动或 FE 负载高时SSL 握手可能耗时 35 秒HikariCP 就会抛出Connection acquisition failed然后重试——每次重试都新建一个连接请求进一步加剧 FE 的会话压力。解决方案将connection-timeout设为大于 Doris SSL 握手预期时间spring.datasource.hikari.connection-timeout60000 spring.datasource.hikari.validation-timeout3000 spring.datasource.hikari.idle-timeout600000 spring.datasource.hikari.max-lifetime1800000同时在 JDBC URL 中显式禁用 SSL如果业务允许jdbc:mysql://doris-fe-host:9030/test_db?useSSLfalseserverTimezoneAsia/Shanghai3.3 JDBC Driver 版本2.1.8 必须匹配的驱动版本链Doris 2.1.8 使用的是 MySQL 协议兼容层但内部序列化格式已升级。如果使用老版本 MySQL JDBC Driver如mysql-connector-java:5.1.47会出现两种诡异现象连接成功但执行SELECT返回空结果集实际数据存在连接数缓慢增长SHOW PROCESSLIST中出现大量CommandSleep, Time0的僵尸会话。根本原因是旧版驱动在连接复用时未正确处理 Doris 新增的ComStmtClose协议包导致 FE 认为连接已关闭而驱动端仍持有句柄形成“半开连接”。强制要求Doris 2.1.8 必须使用mysql-connector-java:8.0.33或更高版本。在pom.xml中明确声明dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency并且在 JDBC URL 中添加allowPublicKeyRetrievaltrueuseSSLfalse参数Doris 2.1.8 的 SSL 实现不兼容标准 MySQL SSL 流程。经验总结连接池问题的修复从来不是“调大 maxActive 就完事”。真正的稳定来自于让连接池的行为和 Doris 的会话生命周期严格对齐——连接只在需要时创建空闲时及时释放异常时彻底销毁。每一次连接的诞生与消亡都必须有迹可循。4. FE 配置优化实战从qe_max_connection到连接回收策略的精细调控当定位到确实是 Doris 侧资源不足时不能简单粗暴地把qe_max_connection拉到 1000。我经历过一次惨痛教训某金融客户将参数从 200 改为 1000 后FE 内存暴涨 4GBGC 频率从每 5 分钟一次变为每 30 秒一次最终因 OOM 自动重启。Doris 的会话对象不是轻量级的每个会话平均占用 2MB 内存含线程栈、SQL 解析上下文、权限缓存等。因此参数优化必须是一套组合拳。4.1qe_max_connection的科学计算公式不要拍脑袋定数字。我用这套公式计算过 20 个生产集群误差率低于 5%qe_max_connection (应用实例数 × 单实例最大连接数 × 1.2) (BI 工具并发数 × 1.5) 50运维预留其中单实例最大连接数不是连接池maxActive而是应用在峰值 QPS 下实际使用的连接数。可通过 APM 工具如 SkyWalking查看JDBC Connection Pool Active Count指标取过去 7 天 P99 值。BI 工具并发数Tableau/Superset 等工具的并发查询数通常为其用户数的 10%-15%根据使用频率调整。1.2 和 1.5是缓冲系数应对突发流量和连接抖动。例如3 个 Flink 实例每个峰值用 15 连接、1 个 Superset50 用户按 12% 并发≈6 查询则qe_max_connection (3 × 15 × 1.2) (6 × 1.5) 50 54 9 50 113 → 向上取整为 120这个值比盲目设 500 更安全内存占用降低 60%。4.2max_user_connections的分级管控策略全局qe_max_connection是“天花板”而max_user_connections是“房间隔断”。我建议按角色分三级ETL 用户如doris_etlmax_user_connections30理由批处理作业通常是串行或小并发30 个连接足够支撑 10 个并行任务每个任务最多 3 连接。BI 用户如bi_readermax_user_connections50理由BI 工具常开多个仪表盘每个仪表盘 1-2 查询50 能覆盖 20 并发用户。开发用户如dev_adminmax_user_connections10理由开发调试应小步快跑限制连接数倒逼写出高效 SQL避免SELECT * FROM huge_table拖垮集群。设置命令CREATE USER doris_etl% IDENTIFIED BY xxx; ALTER USER doris_etl% ATTRIBUTE {max_user_connections: 30}; -- 注意Doris 2.1.8 要求用 JSON 格式设置属性4.3 连接自动回收idle_query_timeout与query_timeout的协同Doris 2.1.8 新增了两个关键超时参数它们是解决“Sleep 连接堆积”的终极武器idle_query_timeout空闲会话自动断开时间单位秒默认 36001 小时。query_timeout单个查询最长执行时间单位秒默认 0不限制。很多人只调query_timeout却忽略了idle_query_timeout。实际上90% 的僵尸连接都是Sleep状态query_timeout对它们完全无效。我的生产配置-- 全局设置影响所有用户 ADMIN SET FRONTEND CONFIG (idle_query_timeout 600); -- 10分钟自动断开空闲连接 ADMIN SET FRONTEND CONFIG (query_timeout 300); -- 查询超时5分钟防长尾SQL -- 为ETL用户单独设置更激进的空闲超时 ALTER USER doris_etl% ATTRIBUTE {idle_query_timeout: 300};效果立竿见影某物流集群将idle_query_timeout从 3600 改为 600 后SHOW PROCESSLIST中Sleep连接数从平均 180 降至 25ERROR 1203报错归零。关键提醒idle_query_timeout的值必须大于连接池的minEvictableIdleTimeMillisDruid或idle-timeoutHikariCP否则连接池会先于 Doris 断开连接造成资源浪费。建议前者设为后者的 2 倍。5. 终极防御构建连接数健康度监控体系把故障消灭在发生前再完美的配置也无法杜绝所有风险。我坚持在所有 Doris 2.1.8 集群上部署一套连接数健康度监控核心思想是不等报错而是在连接数达到危险阈值时就预警。5.1 监控指标设计三个黄金维度我定义了三个不可妥协的核心指标连接使用率active_sessions / qe_max_connection预警阈值 70%黄色 90%红色。注意不是看绝对值而是看比例因为不同集群qe_max_connection差异很大。空闲连接占比sleep_sessions / active_sessions预警阈值 85%。如果空闲连接占比长期高于此值说明应用连接池配置严重不合理存在泄漏风险。用户连接离散度max_user_conn_count / avg_user_conn_count预警阈值 5。如果某用户连接数是平均值的 5 倍以上大概率是其应用异常如定时任务未加锁导致重复启动。5.2 Prometheus Grafana 实现方案Doris 2.1.8 内置/metrics接口暴露了doris_fe_active_session_count和doris_fe_max_session_count等指标。在 Prometheus 的scrape_configs中添加- job_name: doris-fe static_configs: - targets: [fe-host1:8030, fe-host2:8030] metrics_path: /metricsGrafana 中创建告警规则PromQL# 连接使用率 90% 100 * doris_fe_active_session_count / doris_fe_max_session_count 90 # 空闲连接占比 85%需配合自定义 exporter 获取 sleep 数 100 * doris_fe_sleep_session_count / doris_fe_active_session_count 85 # 单用户连接数突增需解析 SHOW PROCESSLIST 结果 count by (user) (doris_fe_processlist{commandSleep}) 105.3 自动化处置剧本从预警到自愈监控只是第一步真正的价值在于自动处置。我用 Python Doris HTTP API 实现了一个轻量级自愈脚本# 当连接使用率 95% 时触发 if usage_rate 95: # 步骤1找出连接数最多的前3个用户 top_users get_top_users_by_connection(limit3) # 步骤2向这些用户发送 Kill 命令仅 Kill Sleep 连接 for user in top_users: kill_sleep_sessions(user) # 步骤3发送企业微信告警附带被 Kill 的会话 ID send_alert(f已Kill {user} 的 {len(killed_ids)} 个Sleep连接)kill_sleep_sessions函数调用 Doris 的 HTTP 接口curl -X POST http://fe-host:8030/api/kill?session_id10001这个脚本部署在集群管理节点每 5 分钟执行一次。上线后ERROR 1203的 MTTR平均修复时间从 15 分钟降至 22 秒。最后分享一个血泪教训某次大促前运维同事只监控了active_sessions没关注sleep_sessions。结果大促期间连接数缓慢爬升至 195/200但监控未告警因为 90%。直到第 196 个连接进来时ERROR 1203爆发而此时所有服务已雪崩。后来我们强制要求任何 Doris 集群的监控看板必须同时展示“活跃连接数”、“空闲连接数”、“连接使用率”三组数字缺一不可。技术债可以慢慢还但监控盲区必须零容忍。
返回列表