MariaDB 10.11.5频繁内存耗尽问题排查求助
问题背景
我们的MariaDB 10.11.5服务器每隔几天就会出现内存耗尽情况,服务器采用Galera复制架构,通过ProxySQL实现故障转移与连接池功能。服务器总内存32GB,当前MariaDB已占用约24GB。此前怀疑默认malloc库存在内存泄漏,切换为tcmalloc gperftools 2.6.1后内存增长速度有所放缓,但仍会逐渐耗尽。
当前innodb_buffer_pool为默认值(调大后内存耗尽更快),连接池保持300个连接,按配置计算单个连接约占19MB,300个连接仅占5GB,即使按最大连接数883计算也仅为16.8GB,远低于当前24GB的占用量。需明确以下问题:
- 连接内存的计算逻辑是否有误?
- 连接实际占用内存是否远超预期?
- 如何查看表缓存或线程缓存的内存占用量?
- 是否是复制机制导致的内存异常?
配置信息
[mysqld] performance_schema=ON event_scheduler=ON max_connections = 5000 # SSD推荐设置为0 innodb_flush_neighbors=0 # 不使用MyISAM索引时建议设小(默认1G) key_buffer_size=10M [mariadb] log_error=/var/log/mysql/error_log.log log_warnings=4 slow_query_log=1 long_query_time = 5 slow_query_log_file=/var/log/mysql/slow_query.log # 缩短连接休眠时间 wait_timeout = 600
问题解答
1. 连接内存计算逻辑是否有误?
你的计算仅考虑了连接线程的基础内存开销,但MariaDB的内存占用是多组件叠加的,遗漏了大量关键消耗项:
- InnoDB缓冲池(10.11.5版本中,32GB内存服务器的默认值约为8GB,需结合
SHOW VARIABLES确认具体数值) - 表缓存、表定义缓存的内存开销
- 排序缓存、连接缓存、临时表内存——这些是每个连接按需分配的,并非固定每个连接占用19MB
- Galera复制相关的写集缓存、复制线程内存
- Performance Schema、事件调度器等后台组件的内存占用
仅计算连接线程内存会严重低估实际总占用,你的计算逻辑存在明显遗漏。
2. 连接实际占用内存是否远超预期?
单个连接的内存开销并非固定值,取决于实际执行的SQL操作:
- 若连接执行大表排序、无索引关联等操作,会按需分配
sort_buffer_size、join_buffer_size,这些缓存会额外占用内存 - 使用MEMORY引擎的临时表会占用
tmp_table_size范围内的内存,频繁创建大内存临时表会瞬间拉高内存占用 - 线程缓存中的空闲线程也会持续占用内存,即使连接处于空闲状态
可通过以下SQL查看每个线程的内存使用细节(需开启performance_schema):
SELECT thread_id, processlist_id, processlist_user, processlist_host, SUM(current_alloc) AS total_memory FROM performance_schema.memory_summary_by_thread_by_event_name GROUP BY thread_id, processlist_id, processlist_user, processlist_host ORDER BY total_memory DESC;
3. 如何查看表缓存或线程缓存的内存占用量?
表缓存相关内存
-- 查看表缓存内存占用 SELECT SUM(current_alloc) AS table_cache_memory FROM performance_schema.memory_summary_by_event_name WHERE event_name LIKE '%table%cache%'; -- 查看表定义缓存内存占用 SELECT SUM(current_alloc) AS table_def_cache_memory FROM performance_schema.memory_summary_by_event_name WHERE event_name LIKE '%table_definition_cache%';
同时可通过SHOW STATUS LIKE 'Open_tables'查看当前打开的表数量,SHOW VARIABLES LIKE 'table_open_cache'查看配置上限,若Open_tables接近或超过上限,会导致频繁打开/关闭表,间接增加内存碎片。
线程缓存相关内存
SELECT SUM(current_alloc) AS thread_cache_memory FROM performance_schema.memory_summary_by_event_name WHERE event_name LIKE '%thread_cache%';
通过SHOW STATUS LIKE 'Threads_cached'可查看当前缓存的空闲线程数量,若数量过大(如超过几百),会占用大量闲置内存。
4. 是否是Galera复制机制导致的内存异常?
Galera复制确实会产生额外内存开销,需重点排查以下几点:
- wsrep_provider_options中的
gcache.size:Galera全局写集缓存,默认值可能为1GB,高写负载场景下会占用更多内存,若写集无法及时清理会导致内存堆积 - wsrep_slave_threads与wsrep_applier_threads:复制线程与写集应用线程的数量,每个线程会占用类似普通连接的内存开销
- 查看Galera状态变量,确认节点状态与复制健康度:
SHOW STATUS LIKE 'wsrep%';
重点关注wsrep_local_state_comment(节点状态是否正常)、wsrep_flow_control_paused(是否存在流控导致内存堆积)、wsrep_apply_oooe/wsrep_apply_oool(是否有应用延迟导致写集堆积)
若Galera节点处于分裂、延迟等异常状态,写集无法及时应用或清理,会导致内存持续增长。
额外排查建议
- 监控内存变化趋势:使用
vmstat、top或Prometheus+Grafana等工具跟踪MariaDB内存占用的变化,定位是否在高峰负载、批量任务时段出现内存陡增 - 排查内存泄漏:使用gperftools的
pprof工具生成不同时间点的内存快照,对比找出内存增长的核心来源 - 优化配置参数:
- 降低
max_connections(当前设为5000,远高于实际使用的883,会预留过多闲置内存) - 合理设置
innodb_buffer_pool_size:给Galera、连接线程预留8-10GB内存后,剩余资源分配给缓冲池 - 限制
sort_buffer_size、join_buffer_size的大小,避免单个连接占用过多内存
- 降低
- 分析慢查询日志:排查是否存在大量耗时、高内存占用的SQL(如大表排序、无索引关联),这类操作会导致临时内存缓存暴涨
内容的提问来源于stack exchange,提问作者Nate L

