触发器执行后MySQL服务器重新启动。

12

我在MySQL中有一个触发器,每次被触发时都会导致MySQL服务器重新启动。这是在1天前进行版本8.0.22更新后开始发生的。我的触发器如下:

CREATE TRIGGER max_client_invoice_before_insert
BEFORE INSERT
ON client_invoices FOR EACH ROW

BEGIN

DECLARE vMax int(11);

SELECT IFNULL(max(client_invoice_id),0) from client_invoices where client_operating_unit_id = NEW.client_operating_unit_id INTO vMax;

SET NEW.client_invoice_id = vMax+1;

END

由于最近的更新,我是否错过了任何东西?如果我找不到服务器崩溃原因,有没有更好、更有效的方法来实现相同的目标呢?

我还检查了日志,下面是我找到的内容:

06:03:02 UTC - mysqld got signal 11 ;
Most likely, you have hit a bug, but this error can also be caused by malfunctioning hardware.
Thread pointer: 0x7f08e4921bd0
Attempting backtrace. You can use the following information to find out
where mysqld died. If you see no messages after this, something went
terribly wrong...
stack_bottom = 7f09c8254c70 thread_stack 0x46000
/usr/sbin/mysqld(my_print_stacktrace(unsigned char const*, unsigned long)+0x3d) [0x2194f3d]
/usr/sbin/mysqld(handle_fatal_signal+0x313) [0xff55f3]
/lib64/libpthread.so.0(+0xf630) [0x7f09d5633630]
/usr/sbin/mysqld(Item_splocal::this_item()+0x14) [0x111fce4]
/usr/sbin/mysqld(Item_sp_variable::val_int()+0x13) [0x111fb63]
/usr/sbin/mysqld(Item_func_plus::int_op()+0x1d) [0x11aafdd]
/usr/sbin/mysqld(Item_func_numhybrid::val_int()+0x191) [0x11ad541]
/usr/sbin/mysqld(Item::save_in_field_inner(Field*, bool)+0x125) [0x11259c5]
/usr/sbin/mysqld(Item::save_in_field(Field*, bool)+0x53) [0x113ef03]
/usr/sbin/mysqld(Item_trigger_field::set_value(THD*, sp_rcontext*, Item**)+0x76) [0x113f136]
/usr/sbin/mysqld(sp_instr_set_trigger_field::exec_core(THD*, unsigned int*)+0x90) [0xe38a80]
/usr/sbin/mysqld(sp_lex_instr::reset_lex_and_exec_core(THD*, unsigned int*, bool)+0x60c) [0xe39b1c]
/usr/sbin/mysqld(sp_lex_instr::validate_lex_and_execute_core(THD*, unsigned int*, bool)+0x9a) [0xe3a55a]
/usr/sbin/mysqld(sp_head::execute(THD*, bool)+0x5d3) [0xe311c3]
/usr/sbin/mysqld(sp_head::execute_trigger(THD*, MYSQL_LEX_CSTRING const&, MYSQL_LEX_CSTRING const&, GRANT_INFO*)+0x29d) [0xe31acd]
/usr/sbin/mysqld(Trigger::execute(THD*)+0x10c) [0xfc150c]
/usr/sbin/mysqld(Trigger_chain::execute_triggers(THD*)+0x18) [0xfc28b8]
/usr/sbin/mysqld(Table_trigger_dispatcher::process_triggers(THD*, enum_trigger_event_type, enum_trigger_action_time_type, bool)+0x46) [0xfbc4a6]
/usr/sbin/mysqld(fill_record_n_invoke_before_triggers(THD*, COPY_INFO*, mem_root_deque<Item*> const&, mem_root_deque<Item*> const&, TABLE*, enum_trigger_event_type, int, bool, bool*)+0x3f9) [0xe45ac9]
/usr/sbin/mysqld(Sql_cmd_insert_values::execute_inner(THD*)+0x454) [0x1352464]
/usr/sbin/mysqld(Sql_cmd_dml::execute(THD*)+0x525) [0xf15695]
/usr/sbin/mysqld(mysql_execute_command(THD*, bool)+0x9f0) [0xeb98d0]
/usr/sbin/mysqld(Prepared_statement::execute(String*, bool)+0x8f0) [0xee8160]
/usr/sbin/mysqld(Prepared_statement::execute_loop(String*, bool)+0x117) [0xeec5f7]
/usr/sbin/mysqld(mysqld_stmt_execute(THD*, Prepared_statement*, bool, unsigned long, PS_PARAM*)+0x181) [0xeecba1]
/usr/sbin/mysqld(dispatch_command(THD*, COM_DATA const*, enum_server_command)+0x1712) [0xebfbf2]
/usr/sbin/mysqld(do_command(THD*)+0x19c) [0xec101c]
/usr/sbin/mysqld() [0xfe69e0]
/usr/sbin/mysqld() [0x272fc3e]
/lib64/libpthread.so.0(+0x7ea5) [0x7f09d562bea5]
/lib64/libc.so.6(clone+0x6d) [0x7f09d3a0e8dd]



Trying to get some variables.
Some pointers may be invalid and cause the dump to abort.
Query (7f08e5343368): insert into `client_invoices` (`customer_id`, `invoice_date`, `sub_total`, `vat`, `total`, `client_operating_unit_id`, `client_invoice_id`, `invoiced`, `paid`, `created_by`, `updated_by`, `updated_at`, `created_at`) values (459, '2020-10-18 08:03:01', '24202.53', '0', '24202.53', 1, 0, 0, 0, 47, 47, '2020-10-20 08:03:02', '2020-10-20 08:03:02')
Connection ID (thread ID): 743
Status: NOT_KILLED



The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains
information that should help you find out what is causing the crash.
2020-10-20T06:03:04.667817Z 0 [Warning] [MY-011070] [Server] 'Disabling symbolic links using --skip-symbolic-links (or equivalent) is the default. Consider not using this option as it' is deprecated and will be removed in a future release.
2020-10-20T06:03:04.668382Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.22) starting as process 59229
2020-10-20T06:03:04.685040Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
2020-10-20T06:03:07.357601Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
2020-10-20T06:03:08.537376Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
2020-10-20T06:03:08.656148Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2020-10-20T06:03:08.656724Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
2020-10-20T06:03:08.733111Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.22' socket: '/var/lib/mysql/mysql.sock' port: 3306 MySQL Community Server - GPL.

当我运行SHOW TABLE client_invoices;时,我得到的结果如下:

CREATE TABLE `client_invoices` (
 `id` bigint unsigned NOT NULL AUTO_INCREMENT,
 `client_invoice_id` bigint unsigned NOT NULL COMMENT 'Unique autoincrementing bigint for this client',
 `client_operating_unit_id` int unsigned NOT NULL,
 `customer_id` int unsigned NOT NULL,
 `invoice_status_id` int unsigned NOT NULL DEFAULT '1',
 `invoice_date` date NOT NULL,
 `sub_total` decimal(14,2) DEFAULT '0.00',
 `vat` decimal(14,2) DEFAULT '0.00',
 `total` decimal(14,2) DEFAULT '0.00',
 `invoiced` tinyint(1) NOT NULL COMMENT 'Invoice sent to customer',
 `paid` tinyint(1) NOT NULL COMMENT 'Invoice been paid',
 `exported` tinyint(1) NOT NULL DEFAULT '0',
 `automatic_invoice` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Invoice has been generated automatically',
 `comments` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `created_by` int unsigned NOT NULL DEFAULT '0',
 `updated_by` int unsigned NOT NULL DEFAULT '0',
 `created_at` timestamp NULL DEFAULT NULL,
 `updated_at` timestamp NULL DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `invoice_id` (`client_operating_unit_id`,`client_invoice_id`),
 KEY `client_invoices_client_operating_unit_id_index` (`client_operating_unit_id`),
 KEY `client_invoices_customer_id_index` (`customer_id`),
 KEY `client_invoices_invoice_status_id_index` (`invoice_status_id`),
 KEY `client_invoices_invoice_date_index` (`invoice_date`),
 CONSTRAINT `client_invoices_client_operating_unit_id_foreign` FOREIGN KEY (`client_operating_unit_id`) REFERENCES `client_operating_units` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
 CONSTRAINT `client_invoices_customer_id_foreign` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
 CONSTRAINT `client_invoices_invoice_status_id_foreign` FOREIGN KEY (`invoice_status_id`) REFERENCES `client_invoice_status` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE=InnoDB AUTO_INCREMENT=60975 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

2
我也遇到了与这个版本的mysql(8.0.22)相同的问题,它是昨天更新的,但在我的情况下是在执行更新时出现的。如果你找到了任何解决方案,请告诉我。 - josemm1790
1
@josemm1790 这就是让我头疼的那个版本。不幸的是,即使在MySQL论坛上,我也还没有收到任何指针。 - Leo Rams
1
目前我重新创建了所有触发器(大约有10个),目前看起来运行了5分钟。我会继续监控情况。 - peterC_
2
@LeoRams 有关于错误报告的任何消息吗? - raphaeldavidf
1
@raphaeldavidf 漏洞报告上没有任何说法或行动,就好像他们根本不在意这件事情发生了一样,真是令人遗憾… - Leo Rams
显示剩余24条评论
8个回答

4
今天(2021年1月18日)发布的MySQL 8.0.23版本应该已经解决了这个问题:
Bug#32045681:触发器Item_splocal::this_item()中的堆使用后释放 该问题需要使用多个后续会话来使用相同的触发器程序,并包含一个本地变量。由于Items仅在服务器上的第一次安装时进行准备,并且Item_splocal的m_thd成员仅在准备时初始化,因此在第二次使用时,m_thd与当前THD不同,从而引发故障。 有至少两种方法可以解决这个问题。一种是在将过程绑定到新会话时为m_thd分配值。然而,该解决方案可能被认为是容易受攻击的,我们可能会冒着出现相同问题的风险。此处选择的策略是删除m_thd成员并改用current_thd。现有代码对此发出了警告,但现在作为线程本地变量访问current_thd已足够快速,并且服务器中还有其他几个地方使用了相同的原则。不过,将THD作为上下文参数传递会更好,但对于错误修复来说太过深入了。 审查者:Dmitry Lenev Dmitry.Lenev@oracle.com
等待Percona发布以进行测试。 https://github.com/mysql/mysql-server/commit/aecc02f8c75beb0f5911b02b8364b4e2ba22a25a

3
谢谢您告诉我们!我刚刚安装了它,似乎已经起作用了,我的测试触发器成功运行,没有导致服务器重启。 - raphaeldavidf
FYI:对我来说还没有解决。我用来重现错误的步骤不再触发崩溃。然而,自那以后我又在生产环境中(当然是运行 MySQL 8.0.23)看到它再次发生。 - rmoestl

1

升级5.7到8.0.22-13(Percona Server)后出现同样的问题。该问题已报告在https://jira.percona.com/browse/PS-7477

我尝试过但没有成功:

  • 关闭复制(禁用从库)
  • 重新创建触发器
  • 重新创建表
  • 从触发器中删除INSERTs

@RealEnder建议问题可能在于触发器内使用本地变量,因此我从触发器中删除了所有变量,并且自那以来没有崩溃。

我仍然需要寻找不使用触发器中的变量的替代方法,这不是解决方案,而是在修复错误之前的解决方法。

更新1:24小时未崩溃

更新2:11天未崩溃


1
在我的情况下,通过删除IFNULL函数,问题得到了解决,而我的触发器仍然使用变量。似乎有不同的方法来找到这个错误的临时解决方案,对于一个人有效的方法可能对另一个人无效。 - Leo Rams

1
我能追踪到问题的根源只在于一个更新触发器(其他触发器似乎都正常工作)。
奇怪的是,这个测试更新触发器却运行良好:
CREATE DEFINER=`root`@`%` TRIGGER `nobreak_status_AFTER_UPDATE` AFTER UPDATE ON `nobreak_status` FOR EACH ROW BEGIN
    DECLARE old_values VARCHAR(2000);
    
    SET old_values := '';
    
    
    IF TRUE THEN
        INSERT INTO cybersecurity.nobreak_status_changelog (`date`, `user`, `type`, result, attribute) VALUES (NOW(), USER(), 'UPDATE', '', '');
    END IF;
END

而这个会随机导致 MySQL 服务器崩溃:

CREATE DEFINER=`root`@`%` TRIGGER `nobreak_status_AFTER_UPDATE` AFTER UPDATE ON `nobreak_status` FOR EACH ROW BEGIN
    DECLARE old_values VARCHAR(2000);
    
    SET old_values := '';
    
    
    IF old_values = '' THEN
        INSERT INTO cybersecurity.nobreak_status_changelog (`date`, `user`, `type`, result, attribute) VALUES (NOW(), USER(), 'UPDATE', '', '');
    END IF;
END

更新:

SHOW CREATE TABLE cybersecurity.nobreak_status_changelog; 返回

CREATE TABLE `nobreak_status_changelog` (
  `date` datetime NOT NULL,
  `user` varchar(100) NOT NULL,
  `type` varchar(45) NOT NULL,
  `result` varchar(1000) NOT NULL,
  `attribute` varchar(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

更新2:刚刚查看了服务器日志,自更新以来至少每天会出现一次以下内容:

Oct 29 08:01:50 SERVER systemd[1]: mysql.service: Main process exited, code=exited, status=2/INVALIDARGUMENT
Oct 29 08:01:50 SERVER systemd[1]: mysql.service: Failed with result 'exit-code'.
Oct 29 08:01:50 SERVER systemd[1]: mysql.service: Service RestartSec=100ms expired, scheduling restart.
Oct 29 08:01:50 SERVER systemd[1]: mysql.service: Scheduled restart job, restart counter is at 12.
Oct 29 08:01:50 SERVER systemd[1]: Stopped MySQL Community Server.
Oct 29 08:01:50 SERVER systemd[1]: Starting MySQL Community Server...
Oct 29 08:01:55 SERVER systemd[1]: Started MySQL Community Server.

请发布SHOW CREATE TABLE cybersecurity.nobreak_status_changelog;的TEXT结果以供分析。 - Wilson Hauck
@WilsonHauck更新了帖子,其中包含SHOW CREATE TABLE返回的内容。 - raphaeldavidf
你是否需要使用TABLE LOCK来向没有PRIMARY key的表中追加数据? - Wilson Hauck
@WilsonHauck 我不这么认为,因为触发器是唯一写入该表的程序,所以不会有任何读/写该数据的竞争条件。 - raphaeldavidf
@raphaeldavidf 我在查找的地方没有看到这个,你运行了什么命令来生成它?我会去看一下。 - MrJ
显示剩余5条评论

1

在升级到8.0.22版本后,我加入了这个错误俱乐部,该版本修复了许多安全问题,我们不得不进行升级。在我的情况下,触发器是在删除之后:

CREATE TRIGGER `TRG_n2d_delete` AFTER DELETE ON `n2d` FOR EACH ROW BEGIN
    DECLARE vn_state tinyint(1);

    SELECT n_state FROM nets WHERE net_id=OLD.net_id INTO vn_state;
    IF (vn_state=0) THEN
        UPDATE dicts SET hits=hits-1 WHERE d_id=OLD.d_id;
        UPDATE nets SET hits=hits-1 WHERE net_id=OLD.net_id;
    END IF;
END

看起来问题出在访问本地变量上,这是一个共同点。

编辑:重写触发器,删除内部变量后,没有崩溃。我当前的代码:

CREATE TRIGGER `TRG_n2d_delete` AFTER DELETE ON `n2d` FOR EACH ROW BEGIN
    IF ((SELECT n_state FROM nets WHERE net_id=OLD.net_id)=0) THEN
        UPDATE dicts SET hits=hits-1 WHERE d_id=OLD.d_id;
        UPDATE nets SET hits=hits-1 WHERE net_id=OLD.net_id;
    END IF;
END

我还没有完成我的测试,但是我正在遵循你的建议“问题在于访问本地变量”,结果非常积极,在从触发器中删除变量后没有崩溃。 - xsbrz

0

你可以尝试更改调用查询的方式。

这个 bug 的名称是:涉及存储程序的预处理语句可能会导致堆使用后释放内存问题。

所以我尝试更改了调用语句的方式,从 prepare 改为单个查询。这应该有所帮助,因为查询不会被执行为“预处理语句”。我不知道现在是否有帮助,但我希望它能帮助一些周,直到 Percona 8.0.23 可用。


0

我将触发器更新为以下内容,这已经阻止了MySQL服务器崩溃(现在已经接近36小时没有重新启动):

CREATE TRIGGER max_client_invoice_before_insert
                        BEFORE INSERT
                           ON client_invoices FOR EACH ROW
                        BEGIN
                           DECLARE vMax int(11);
                           SELECT IF(COUNT(client_invoice_id) > 0, MAX(client_invoice_id) + 1, 1) from client_invoices where client_operating_unit_id = NEW.client_operating_unit_id INTO vMax;
                           SET NEW.client_invoice_id = vMax;
                        END

唯一的区别是去除

SELECT IFNULL(max(client_invoice_id),0)

并将其替换为

SELECT IF(COUNT(client_invoice_id) > 0, MAX(client_invoice_id) + 1, 1)

1
很高兴看到你有解决方法:o) 他们刚刚验证了我提供的内容引起了一个错误。不幸的是,我认为我真的无法修改我的触发器语句 :o( - MrJ
1
@MrJ 请分享您的触发器,希望有人能提供一些解决方法。您也可以分享您的错误报告链接... - Leo Rams

0

经常导致崩溃的一个触发器

CREATE TRIGGER `items_insert`
    AFTER INSERT
        ON `items` FOR EACH ROW
    BEGIN
        DECLARE rowcount INT;
        SET rowcount = (SELECT COUNT(*) from entity_log WHERE global_ref=NEW.global_ref LIMIT 1);
        IF (rowcount=0 AND LEFT(NEW.global_ref, 2)="ZZ") THEN
            INSERT INTO entity_log (global_ref, entity_type, server_updated_at) values (NEW.global_ref, "IL", UTC_TIMESTAMP());
        END IF;
    END
CREATE TABLE `entity_log` (
  `entity_id` int NOT NULL,
  `global_ref` varchar(30) COLLATE utf8mb4_general_ci NOT NULL,
  `entity_type` varchar(2) COLLATE utf8mb4_general_ci NOT NULL,
  `subentity_type` char(2) COLLATE utf8mb4_general_ci NOT NULL,
  `server_updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

不确定如何解决这个问题,因为它非常简单

我的MySQL错误报告在https://bugs.mysql.com/bug.php?id=101342中有所提及,但出于安全原因,MySQL将其与评论中提到的其他报告一样设为私有


0

我也遇到了同样的问题。 在我的情况下,似乎是发生在我有这个选择器的触发器上:

SET vTipoEvento = '';
SELECT TipoEvento
  INTO vTipoEvento
  FROM evexcl
 WHERE eclid = OLD.IdEsito;

这很简单。我甚至不知道该如何重写它......问题始于mysql 8.0.22(ubuntu 20.04.2)

我有很多触发器,在我的情况下,它一直在重新启动


大家好,我看到mysql 8.0.23已经发布了。我还没有尝试过它。有人知道这个bug是否已经修复了吗? - lorife

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接