mysql触发器可以在数据写入操作执行的前后自动触发自定义逻辑,借助这个特性,我们可以记录写入操作的执行耗时,将慢写入的相关信息存储到诊断表中,实现轻量级的慢写入监控。

一、创建慢写入诊断表
首先需要创建一个用于存储慢写入记录的表,该表需要包含写入操作的基本信息、执行耗时、触发时间等字段,方便后续排查问题。
-- 创建慢写入诊断表
CREATE TABLE IF NOT EXISTS slow_write_diagnosis (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT '记录ID',
table_name VARCHAR(100) NOT NULL COMMENT '写入操作的表名',
operation_type VARCHAR(20) NOT NULL COMMENT '操作类型:INSERT/UPDATE/DELETE',
execute_time DECIMAL(10,3) NOT NULL COMMENT '执行耗时,单位毫秒',
trigger_time DATETIME NOT NULL COMMENT '触发器触发时间',
record_data TEXT COMMENT '写入的数据内容'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='慢写入诊断记录表';
二、实现慢写入监控触发器
我们可以通过BEFORE和AFTER类型的触发器配合,计算写入操作的执行耗时,当耗时超过阈值时,将记录写入诊断表。
1. 创建BEFORE触发器记录开始时间
在写入操作执行前,将当前时间存储到用户变量中,作为耗时的起始时间。
-- 为测试表user_info创建INSERT前触发器
DELIMITER //
CREATE TRIGGER before_user_insert BEFORE INSERT ON user_info
FOR EACH ROW
BEGIN
-- 设置用户变量存储操作开始时间,精确到微秒
SET @write_start_time = NOW(6);
END //
DELIMITER ;
2. 创建AFTER触发器计算耗时并记录
写入操作执行完成后,再次获取当前时间,计算与起始时间的差值,若超过设定的慢写入阈值(比如100毫秒),则将记录插入诊断表。
-- 为测试表user_info创建INSERT后触发器
DELIMITER //
CREATE TRIGGER after_user_insert AFTER INSERT ON user_info
FOR EACH ROW
BEGIN
DECLARE cost_time DECIMAL(10,3);
DECLARE slow_threshold DECIMAL(10,3) DEFAULT 100.000; -- 慢写入阈值,单位毫秒
-- 计算执行耗时,转换为毫秒
SET cost_time = TIMESTAMPDIFF(MICROSECOND, @write_start_time, NOW(6)) / 1000;
-- 如果耗时超过阈值,插入诊断表
IF cost_time > slow_threshold THEN
INSERT INTO slow_write_diagnosis (table_name, operation_type, execute_time, trigger_time, record_data)
VALUES ('user_info', 'INSERT', cost_time, NOW(), CONCAT('id:', NEW.id, ',name:', NEW.name));
END IF;
END //
DELIMITER ;
3. 更新和删除操作的触发器适配
对于UPDATE和DELETE操作,只需要调整触发器的触发时机和操作的表、记录的内容即可,核心的耗时计算逻辑一致。
-- UPDATE操作的BEFORE触发器
DELIMITER //
CREATE TRIGGER before_user_update BEFORE UPDATE ON user_info
FOR EACH ROW
BEGIN
SET @write_start_time = NOW(6);
END //
DELIMITER ;
-- UPDATE操作的AFTER触发器
DELIMITER //
CREATE TRIGGER after_user_update AFTER UPDATE ON user_info
FOR EACH ROW
BEGIN
DECLARE cost_time DECIMAL(10,3);
DECLARE slow_threshold DECIMAL(10,3) DEFAULT 100.000;
SET cost_time = TIMESTAMPDIFF(MICROSECOND, @write_start_time, NOW(6)) / 1000;
IF cost_time > slow_threshold THEN
INSERT INTO slow_write_diagnosis (table_name, operation_type, execute_time, trigger_time, record_data)
VALUES ('user_info', 'UPDATE', cost_time, NOW(), CONCAT('id:', NEW.id, ',old_name:', OLD.name, ',new_name:', NEW.name));
END IF;
END //
DELIMITER ;
三、方案注意事项
- 触发器会增加写入操作的额外开销,建议只在需要监控的表上创建,不要全库开启。
- 用户变量
@write_start_time是会话级别的,不同连接的写入操作不会互相干扰,但在高并发场景下需要确认mysql的变量隔离机制符合预期。 - 诊断表需要定期清理历史数据,避免占用过多存储空间,可以设置定时任务删除超过7天的记录。
- 该方案只能记录单条写入操作的耗时,批量写入的场景下,触发器的执行次数会和批量条数一致,需要结合业务场景调整阈值。
四、慢写入记录查询示例
当需要排查慢写入问题时,可以直接查询诊断表获取相关信息。
-- 查询最近100条慢写入记录,按耗时降序排列 SELECT id, table_name, operation_type, execute_time, trigger_time, record_data FROM slow_write_diagnosis ORDER BY execute_time DESC LIMIT 100;