如何解决由触发器导致 MySQL 内存溢出?

MySQL深入原理 客户端通过连接器连接 MySQL 服务;连接成功后向 SQL 接口发送 SQL 语句请求;SQL 接口接收到 SQL 查询语句会先去缓存查询,如果命中返回给客户端,否则交给解析器;解析器在拿到 SQL 语句后会判断语法是否正确,正确会生成 SQL 语法树交给优化器,否则报错给客户端;优化器会根据 SQL 语法树生成一个最优的执行计划交给执行器执行;执行器拿到执行计划调用存储引擎来获取数据响应给客户端;完成!!!在执行前需要:① 连接器连接数据库; 阅读详情

由触发器导致得 OOM 案例分析过程和解决方式。

作者:龚唐杰,爱可生 DBA 团队成员,主要负责 MySQL 技术支持,擅长 MySQL、PG、国产数据库。

爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。

本文约 1500 字,预计阅读需要 5 分钟。

问题现象

一台从库服务器的内存使用率持续上升,最终导致 MySQL 服务被 kill 了。

内存监控视图如下:

内存使用率 92.76%

从图中可以看出,在 00:00 左右触发了 kill,然后又被 mysqld_safe 进程拉起,然后内存又会持续上升。

排查过程

基本信息

  • 数据库版本:MySQL 5.7.32
  • 操作系统版本:Ubuntu 20.04
  • 主机配置:8C64GB
  • innodb_buffer_pool_size:8G

由于用户环境未打开内存相关的监控,所以在 my.cnf 配置文件中配置如下:

performance-schema-instrument = 'memory/% = COUNTED'

打开内存监控等待运行一段时间后,相关视图查询如下:

从上述截图可以看到,MySQL 的 buffer pool 大小分配正常,但是 memory/sql/sp_head::main_mem_root 占用了 8GB 内存。

查看 源代码 的介绍:

sp_head:sp_head represents one instance of a stored program.It might be of any type (stored procedure, function, trigger, event).

根据源码的描述可知,sp_head 表示一个存储程序的实例,该实例可能是存储过程、函数、触发器或者定时任务。

查询当前环境存储过程与触发器数量:

当前环境存在大量的触发器与存储过程。

查询 MySQL 相关 bug,这里面提到一句话:

Tried to tweak table_open_cache_instances to affect this?

查询此参数描述:

A value of 8 or 16 is recommended on systems that routinely use 16 or more cores. However, if you have many large triggers on your tables that cause a high memory load, the default setting for table_open_cache_instances might lead to excessive memory usage. In that situation, it can be helpful to set table_open_cache_instances to 1 in order to restrict memory usage.

根据官方的解释可以了解到,如果有许多大的触发器,参数 table_open_cache_instances 的默认至可能会造成内存使用过多。

比如 table_open_cache_instances 设置为 16,那么表缓存会划分为 16 个 table instance*。当并发访问大时,最多的情况下一个表的缓存信息会出现在每一个 *table instance 里面。

再由于每次将表信息放入表缓存时,所有关联的触发器都被放入 memory/sql/sp_head::main_mem_root 中,table_open_cache_instances 设置的越大其所占内存也就越大,以及存储过程也会消耗更多的内存,所以导致内存一直上升最终导致 OOM。

下面简单验证一下触发器对内存的影响。

当 table_open_cache_instances 为 8 时:
#清空缓存

mysql> flush tables;
Query OK, 0 rows affected (0.00 sec)

[root@test ~]# cat test.sh
for i in `seq 1 1 8`
do
mysql -uroot -p test -e "select * from test;"
done

[root@test ~]# sh test.sh

mysql> show variables like '%table_open_cache_instances%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| table_open_cache_instances | 8 |
+----------------------------+-------+
1 row in set (0.00 sec)

mysql> SELECT current_alloc FROM sys.memory_global_by_current_bytes WHERE event_name='memory/sql/sp_head::main_mem_root';
+---------------+
| current_alloc |
+---------------+
| 119.61 KiB |
+---------------+
1 row in set (0.00 sec)

在该表上创建一个触发器。

mysql> \d|
mysql> CREATE TRIGGER trigger_test BEFORE INSERT ON test FOR EACH ROW BEGIN SIGNAL SQLSTATE '45000' SET message_text='Very long string. MySQL stores table descriptors in a special memory buffer, calle
'> at holds how many table descriptors MySQL should store in the cache and table_open_cache_instances t
'> hat stores the number of the table cache instances. So with default values of table_open_cache=4000
'> and table_open_cache_instances=16, you will have 16 independent memory buffers that will store 250 t
'> able descriptors each. These table cache instances could be accessed concurrently, allowing DML to u
'> se cached table descriptors without locking each other. If you use only tables, the table cache doe
'> s not require a lot of memory, because descriptors are lightweight, and even if you significantly increased the value of table_open_cache, it would not be so high. For example, 4000 tables will take u
'> p to 4000 x 4K = 16MB in the cache, 100.000 tables will take up to 390MB that is also not quite a hu
'> ge number for this number of open tables. However, if your tables have triggers, it changes the gam
'> e.'; END|
Query OK, 0 rows affected (0.00 sec)

#清空缓存

mysql> flush tables;
Query OK, 0 rows affected (0.00 sec)

然后访问表,查看缓存。

[root@test ~]# cat test.sh
for i in `seq 1 1 8`
do
mysql -uroot -p test -e "select * from test;"
done

[root@test ~]# sh test.sh

mysql> SELECT current_alloc FROM sys.memory_global_by_current_bytes WHERE event_name='memory/sql/sp_head::main_mem_root';
+---------------+
| current_alloc |
+---------------+
| 438.98 KiB |
+---------------+
1 row in set (0.00 sec)

可以发现 memory/sql/sp_head::main_mem_root* 明显增长较大。如果有很多大的触发器,那么所占内存就不可忽视(现场环境触发器里面很多是调用了存储过程)。

当 table_open_cache_instances 为 1 时:
mysql> flush tables;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%table_open_cache_instances%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| table_open_cache_instances | 1 |
+----------------------------+-------+
1 row in set (0.00 sec)

SELECT current_alloc FROM sys.memory_global_by_current_bytes WHERE event_name='memory/sql/sp_head::main_mem_root';
+---------------+
| current_alloc |
+---------------+
| 119.61 KiB |
+---------------+
1 row in set (0.00 sec)

mysql> #访问表

mysql> system sh test.sh

mysql> SELECT current_alloc FROM sys.memory_global_by_current_bytes WHERE event_name='memory/sql/sp_head::main_mem_root';
+---------------+
| current_alloc |
+---------------+
| 159.53 KiB |
+---------------+
1 row in set (0.00 sec)

可以发现 memory/sql/sp_head::main_mem_root 所占内存增长较小。

由于大量触发器会导致表缓存和 memory/sql/sp_head::main_mem_root 占用更多的内存,根据实际环境,尝试把该从库的 table_open_cache_instances 修改为 1 后观察情况。

可以看到内存值趋于稳定,未再次出现内存使用率异常的问题。

总结

  1. MySQL 中不推荐使用大量的触发器以及复杂的存储过程。
  2. table_open_cache_instances 设置为 1 时,在高并发下会影响 SQL 的执行效率。本案例的从库并发量不高,其他场景请根据实际情况进行调整。
  3. 触发器越多会导致 memory/sql/sp_head::main_mem_root 占用的内存越大,存储过程所使用的内存也会越大。
  4. 本文只是给出了解决内存溢出的一个方向,具体的底层原理请自行探索。

先清空缓存再访问表,查看缓存

更多技术文章,请访问:https://opensource.actionsky.com/

关于 SQLE

SQLE 是一款全方位的 SQL 质量管理平台,覆盖开发至生产环境的 SQL 审核和管理。支持主流的开源、商业、国产数据库,为开发和运维提供流程自动化能力,提升上线效率,提高数据质量。

SQLE 获取

类型地址
版本库https://github.com/actiontech/sqle
文档https://actiontech.github.io/sqle-docs/
发布信息https://github.com/actiontech/sqle/releases
数据审核插件开发文档https://actiontech.github.io/sqle-docs/docs/dev-manual/plugins/howtouse
MySQL 故障诊断:MySQL 占用 CPU 过高问题定位及优化 数据库 阅读详情

相关推荐

SQL四把手术刀:触发器、PARTITION BY、LIMIT双参、COALESCE实战精要

SQL不仅是查询语言,更是数据质量治理与分析工程的核心工具。理解触发器的同步校验原理,可实现在事务内阻断脏数据写入;掌握PARTITION BY与窗口函数的协同机制,能保留原始行级粒度完成分组内排序与累计计算;善用LIMIT offset, count的游标式分页逻辑,可规避大数据量下的性能断崖;而COALESCE作为标准SQL空值传递链,提供类型安全、短路求值的多源兜底能力。这些技术共同构成数据科学家在电商风控、用户归因、AB实验等高频场景中稳定交付的关键基础设施。

weixin_30460489的博客 353

mysql 内存一直增长(memory/sql/thd::main_mem_root)

memory/sql/thd::main_mem_root

qq_44989936的博客 876

解决MySQL内存溢出问题

MySQL是一个常用的关系型数据库管理系统,但在处理大型数据集或复杂查询时,可能会遇到内存溢出(OOM)的问题。在本文中,我们将探讨一些解决MySQL内存溢出问题的方法,并提供相应的源代码示例。优化查询语句、调整MySQL配置和分批处理数据是常用的解决方案,具体的实施方法可以根据实际情况进行调整。请注意,以上提供的代码示例仅供参考,您需要根据自己的数据库结构和查询需求进行适当的修改。通过优化查询语句,可以减少内存的使用量并提高查询的性能。调整MySQL的配置参数也是解决内存溢出问题的重要措施。

带你成为别人眼中的大佬! 871

MySQL查看线程内存占用情况

在无法添加索引时,增加join_buffer_size的值,以获得更快的完全连接。这里会话参数调整后,需同时调整/etc/my.cnf的配置,下次服务启动永久生效,另外之前连接的会话线程由于已分配了该buffer大小,调整后内存并不会马上释放。该命令是在线打开内存统计,所以只会统计打开后新增的内存对象,打开前的内存对象不会统计,建议您打开后等待一段时间再执行后续步骤,便于找出内存使用高的线程。内存使用情况,决定着MySQL的性能,内存使用率过高会使系统响应时间变长,严重时内存耗尽还会出现OOM的情况。

wangleshisei的博客 1899

【MySQL 系列】MySQL 架构篇

在我们开始了解 MySQL 核心功能之前,首先我们需要站在一个全局的视角,来看 SQL 是如何运作执行的。通过这种方式,我们可以在头脑中构建出一幅 MySQL 各组件之间的协同工作方式,有助于我们加深对 MySQL 服务器的理解。

栗筝i的博客 6352

保姆级教程:在Windows上用Docker Desktop一键部署Dify 1.0.1(附国内镜像加速)

本文提供在Windows系统上使用Docker Desktop快速部署Dify 1.0.1的详细教程,包括环境准备、国内镜像加速配置、Dify容器部署实战及常见问题解决方案。特别针对国内用户优化,确保快速完成部署并提升开发效率。

weixin_33738555的博客 281

【20180611】MySQL OOM

关于MySQL OOM的排查思路 服务器发生内存泄露 如何确认服务器发生内存泄漏: 一般执行free -m就查看内存的使用情况就可以了。假如cached和used的值相差特别大的话,安么这个时候我们可以认为发生了内存泄漏。(一般在CentOS6的版本上面可以这么认为,但是这个说法暂时还没有一个比较可信的依据) buffer和cache的区别: buffer: 缓冲,为了提高内存和硬盘之间的数...

weixin_33922670的博客 868

基于Xilinx的I2S Receiver IP核和I2S Transmitter IP核音频验证

基于Xilinx的I2S Receiver IP核和I2S Transmitter IP核音频验证 文章目录前言一、工程构架二、Xilinx IP的配置1.I2S Transmitter IP配置:2.I2S Receiver IP配置:3.WM8960的配置:总结 前言 本实验基于Xilinx ZU4EV平台,也适用于ZYNQ平台。音频验证完成,记录调过程。 ①Xilinx 音频IP只支持主模式,左对齐或右对齐方式,16bit或24bit字长。 ②WM8960器件作为从模式;24bit字长;数据传输格

weixin_45637597的博客 6812

个人经验~ 利用5.7的sys库更好的排查问题

一 简介:今天我们讲讲如何利用5.7的sys新库进行问题的排查二 描述 1 Sys库所有的数据源来自:performance_schema和information_schema。目标是把performance_schema的把复杂度降低,让DBA能更好的阅读这个库里的内容。让DBA更快的了解DB的运行情况。 2 mysql对于系统性能和sql语句状态的收集是非常少的,基本排查手段都要靠经验判...

weixin_33767813的博客 390

timing window file中的S L T指什么意思

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧?

m0_61544122的博客 771

从MySql把表格完整的导入Oracle数据库中(包括注释、触发器等)

之前的时候通过插件的形式,通过Oracle数据库的SQL Developer连接MYSQL数据库并且把mysql数据导入到oracle数据库中,这种方法导入的表格不仅会丢失字段注释还会丢失触发器等数据,并且在导入数据量特别大的表格时会出现内存溢出的错误,从而导出数据导出失败。 下面是采用新的方法把mysql数据导入到oracle中,基本原理: 首先在SQL Developer中连接MYSQL具...

wb175208的专栏 868

Xilinx_JESD204B

Xilinx_JESD204B_AXI配置 说明:通过FPGA的高速数据接口JESD204B对AD9625进行高速采集。 环境:Vivado2018.3。 IP核:JESD204(7.2)。 参考手册:pg066_jesd204 v7.2。 资料以及IP_licence:JESD204B AXI协议资料:AXI 文章目录JESD204B概述JESD204B接口术语JESD204B 层JESD204B_AXI介绍AXI介绍JESD204B__AXI_Lite接口AXI-Lite接口介绍AXI-Lite接口信号

Crazzy_M的博客 1万+

mysql dbvisualizer_mysql - 错误DbVisualizer创建触发器 - 堆栈内存溢出

据我所知,这是错误的:SELECT 01Q00.NEXTVAL INTO :NEW.ID_01 FROM DUAL;因为它是无效的序列名称-应该以字母作为第一个字符,而不是数字。 这是一个例子:SQL> create table t012 (id_01 number,3 fecalt_01 date,4 fecmod_01 date5 );T...

weixin_30366435的博客 841

【存储原理】IO超时处理 块层block-scsi层之间

【存储原理】IO超时处理 块层block-scsi层之间

sinat_37817094的博客 1023

Abaqus数据导出技巧:轻松处理大量XY数据

通过上述三种方法,我们可以解决Abaqus中大量XY数据导出的问题。无论是使用插件、编写Python脚本,还是利用Abaqus的内置报告功能,都可以帮助我们高效地处理数据。希望这篇博客能够帮助到你,提高你在Abaqus使用过程中的数据处理效率。

m0_62153576的博客 1915

Mysql 存储过程 与触发器

两天前同事的一个日志模块事件备份老是报内存溢出错误,正好有点时间研究了一下。日志的备份时查询所有的记录,写到一个文件了。(虽然从实用的角度,数据库备份有很多中非常有效方法,请先不要鄙视这种做法:尊重真理一样尊重需求才是一个好的工程师) Mysql触发器: 当对表格进行某张表格进行插入、更新、删除的之前、之后对数据库进行一些操作,比如:添加用户的时候记录插入一条日志记录到日志表。 Mysq...

origin 专栏 234

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Spring AOP 和 MyBatis插件 优化指数统计,代替MySQL触发器

首先定义一个MyBatis拦截器,拦截并缓存最近执行的SQL和参数,这样可以在AOP通知中访问这些信息。})// 定义一个线程安全的变量用于存储最近的SQL和参数@Override// 获取StatementHandler并解析SQL语句// 缓存SQL// 执行原方法@Override@Override在Spring AOP切面中,使用环绕通知来拦截Mapper方法,获取SQL语句并在SQL执行后调用额外的业务逻辑。@Aspect。

csdn_life18的博客 939

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

待解决问题

1, java内存溢出的问题产生的原因及解决办法? 2,

u013305082的专栏 479

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

MySQL Workbench隐藏功能实战:从数据导出到触发器配置,一份给中级开发者的效率手册

本文深入探讨MySQL Workbench的隐藏功能,包括数据导出转换、外键约束维护、触发器自动化等高级技巧,帮助中级开发者提升数据库管理效率。特别介绍了如何利用Workbench的中文指南优化工作流程,实现从基础操作到高效开发的跃迁。

weixin_33739646的博客 285

mysql触发器如何获取当前表名_MySQL整体架构是什么样?点开这里告诉你!

点击蓝字,关注更多精彩内容MySQL 在整体架构上分为 Server 层和存储引擎层。其中 Server 层,包括连接器、查询缓存、分析器、优化器、执行器等,存储过程、触发器、视图和内置函数都在这层实现。数据引擎层负责数据的存储和提取,如 InnoDB、MyISAM、Memory 等引擎。在客户端连接到 Server 层后,Server 会调用数据引擎提供的接口,进行数据的变更。01连接...

weixin_39616056的博客 543

mysql 触发器 宕机_mysql意外宕机现象分析

最近在云上的博客系统总是数据库宕机导致服务异常。登到服务器上重启mysql有时也启动不了,查看mysql的日志,发现如下错误:InnoDB: mmap(137363456 bytes) failed; errno 12InnoDB: Cannot allocate memory for the buffer pool搜索“InnoDB: mmap(137363456 bytes) failed; ...

weixin_39853843的博客 184

Flink 实时去重三部曲:HashSet → 布隆过滤器 → 布隆 + 窗口触发器渐进式优化实战

布隆过滤器是空间利用率极高的概率型二进制数据结构,底层本质是一个超长 bit 位数组,数组内元素只有01两种值。方案一 HashSet:窗口等待所有数据齐了再遍历迭代器统计,超大流量下迭代器积攒海量对象,堆内存暴涨极易 OOM;方案二 Redis 布隆过滤器:依然依赖窗口攒齐全部数据再统一遍历计算,迭代器数据积压问题没有解决;同时逐条落地 MySQL 频繁创建关闭连接,IO 压力大、写入性能极差。针对性优化思路: 自定义窗口触发器,每来一条数据立刻触发窗口计算、用完立即清空窗口数据,不让迭代器堆积数据。

weixin_73593530的博客 521

zabbix 内存溢出 解决

管理界面下 Zabbix server is running 是NO 就去看了下log 发现了几个错误 解决 打开zabbix_server.conf 找到 Option: CacheSize 把原来的 # CacheSize=8M 前面的#注释去掉,将8M修改为1024,这个1024根据服务器性能修改。 ###Option:CacheSize #...

Linux和Eda技术分享 1016

MySQL 进阶 - 2 ( 15000 字详解)

MySQL 进阶 15000 字详解第二篇

weixin_73232539的博客 1148
上一篇: Google 在 MySQL 中推进矢量搜索,在 LLM 支持方面超越 Oracle
下一篇: MySQL 核心模块揭秘 | 09 期 | 二阶段提交 (3) flush、sync、commit 子阶段
爱可生开源社区
博客等级 码龄8年 1936粉丝 1004原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值