外观
OceanBase 触发器和事件
触发器
触发器类型
- 行级触发器:针对每一行数据的变化触发
- 语句级触发器:针对整个语句触发,无论影响多少行
- BEFORE 触发器:在事件发生前触发
- AFTER 触发器:在事件发生后触发
- INSTEAD OF 触发器:替代原操作执行
触发器事件
- INSERT:插入数据时触发
- UPDATE:更新数据时触发
- DELETE:删除数据时触发
- LOAD:加载数据时触发
创建触发器
sql
-- 创建 BEFORE INSERT 触发器
CREATE TRIGGER trigger_name
BEFORE INSERT ON table_name
FOR EACH ROW
BEGIN
-- 触发器逻辑
SET NEW.created_at = CURRENT_TIMESTAMP;
SET NEW.updated_at = CURRENT_TIMESTAMP;
END;
-- 创建 AFTER UPDATE 触发器
CREATE TRIGGER trigger_name
AFTER UPDATE ON table_name
FOR EACH ROW
BEGIN
-- 触发器逻辑
SET NEW.updated_at = CURRENT_TIMESTAMP;
-- 记录审计日志
INSERT INTO audit_log (table_name, operation, old_value, new_value, operated_at)
VALUES ('table_name', 'UPDATE', OLD.column_name, NEW.column_name, CURRENT_TIMESTAMP);
END;
-- 创建语句级触发器
CREATE TRIGGER trigger_name
AFTER INSERT ON table_name
BEGIN
-- 触发器逻辑
INSERT INTO statistics_table (table_name, insert_count, operated_at)
VALUES ('table_name', ROW_COUNT(), CURRENT_TIMESTAMP);
END;查看触发器
sql
-- 查看所有触发器
SHOW TRIGGERS;
-- 查看特定表的触发器
SHOW TRIGGERS LIKE 'table_name';
-- 查看触发器的详细定义
SELECT * FROM information_schema.triggers WHERE trigger_name = 'trigger_name';修改触发器
sql
-- 修改触发器(需要先删除再创建)
DROP TRIGGER IF EXISTS trigger_name;
CREATE TRIGGER trigger_name
-- 新的触发器定义删除触发器
sql
-- 删除触发器
DROP TRIGGER IF EXISTS trigger_name;
-- 删除特定表的所有触发器
DROP TRIGGER IF EXISTS table_name.*;事件
事件调度器
启用事件调度器:
sqlSET GLOBAL event_scheduler = ON;查看事件调度器状态:
sqlSHOW VARIABLES LIKE 'event_scheduler';
创建事件
sql
-- 创建简单事件
CREATE EVENT event_name
ON SCHEDULE EVERY 1 HOUR
DO
-- 事件逻辑
DELETE FROM log_table WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 DAY);
-- 创建带开始时间和结束时间的事件
CREATE EVENT event_name
ON SCHEDULE EVERY 1 DAY
STARTS '2023-01-01 00:00:00'
ENDS '2023-12-31 23:59:59'
DO
-- 事件逻辑
UPDATE statistics_table SET daily_count = 0 WHERE DATE(stat_date) = DATE(NOW());
-- 创建一次性事件
CREATE EVENT event_name
ON SCHEDULE AT '2023-01-01 00:00:00'
DO
-- 事件逻辑
INSERT INTO holiday_table (holiday_date, description) VALUES ('2023-01-01', 'New Year');查看事件
sql
-- 查看所有事件
SHOW EVENTS;
-- 查看特定数据库的事件
SHOW EVENTS FROM database_name;
-- 查看事件的详细定义
SELECT * FROM information_schema.events WHERE event_name = 'event_name';修改事件
sql
-- 修改事件
ALTER EVENT event_name
ON SCHEDULE EVERY 2 HOUR
ENABLE;
-- 禁用事件
ALTER EVENT event_name DISABLE;
-- 启用事件
ALTER EVENT event_name ENABLE;
-- 修改事件的 SQL 语句
ALTER EVENT event_name
DO
-- 新的事件逻辑
DELETE FROM log_table WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);删除事件
sql
-- 删除事件
DROP EVENT IF EXISTS event_name;
-- 删除特定数据库的所有事件
DROP EVENT IF EXISTS database_name.*;触发器和事件的使用场景
触发器使用场景
- 数据一致性维护:确保相关表之间的数据一致性
- 审计日志记录:自动记录数据的变更历史
- 业务规则 enforcement:强制执行业务规则,如数据格式验证
- 自动计算字段:自动计算派生字段的值
- 级联操作:实现级联更新或删除
事件使用场景
- 定期数据清理:定期删除过期数据
- 统计数据更新:定期更新统计信息
- 数据备份:定期执行数据备份
- 报表生成:定期生成业务报表
- 系统维护:定期执行系统维护任务
触发器和事件最佳实践
触发器最佳实践
- 保持触发器简洁:触发器逻辑应尽量简洁,避免复杂的业务逻辑
- 避免递归触发:防止触发器相互调用导致无限递归
- 使用 AFTER 触发器:优先使用 AFTER 触发器,避免影响原操作性能
- 考虑性能影响:触发器会增加 DML 操作的开销,应谨慎使用
- 测试充分:在生产环境使用前,充分测试触发器的逻辑和性能
事件最佳实践
- 合理设置调度频率:根据业务需求设置合适的事件调度频率
- 考虑系统负载:避免在业务高峰期执行资源密集型事件
- 设置事件超时:为长时间运行的事件设置超时时间
- 记录事件执行日志:记录事件的执行情况,便于调试和监控
- 测试充分:在生产环境使用前,充分测试事件的逻辑和性能
安全性最佳实践
- 严格权限控制:只有具有 CREATE TRIGGER 和 CREATE EVENT 权限的用户才能创建触发器和事件
- 使用最小权限原则:为触发器和事件使用的账户分配最小必要权限
- 定期审计:定期审计触发器和事件,确保其逻辑安全可靠
- 防止 SQL 注入:在触发器和事件中使用参数化查询,防止 SQL 注入
触发器和事件的性能考虑
触发器性能影响
- 增加 DML 操作开销:触发器会在 DML 操作执行前后执行额外的 SQL 语句
- 可能导致锁竞争:复杂的触发器逻辑可能导致锁竞争,影响并发性能
- 影响批量操作性能:对于批量插入、更新或删除操作,触发器会针对每一行执行,影响性能
事件性能影响
- 资源消耗:长时间运行的事件会消耗系统资源
- 调度开销:频繁执行的事件会增加调度器的开销
- 并发问题:多个事件同时执行可能导致资源竞争
性能优化建议
- 优化触发器逻辑:简化触发器逻辑,减少不必要的操作
- 使用存储过程:将复杂逻辑封装到存储过程中,减少触发器的开销
- 调整事件调度频率:根据业务需求调整事件的调度频率
- 优化事件 SQL:优化事件中执行的 SQL 语句,提高执行效率
- 考虑异步执行:对于资源密集型操作,考虑使用异步方式执行
常见问题(FAQ)
Q1: 触发器可以调用存储过程吗?
A1: 是的,触发器可以调用存储过程。在触发器中调用存储过程可以简化触发器逻辑,提高代码的可维护性。
sql
-- 创建存储过程
CREATE PROCEDURE audit_log(
IN p_table_name VARCHAR(50),
IN p_operation VARCHAR(10),
IN p_old_value VARCHAR(255),
IN p_new_value VARCHAR(255)
)
BEGIN
INSERT INTO audit_log (table_name, operation, old_value, new_value, operated_at)
VALUES (p_table_name, p_operation, p_old_value, p_new_value, CURRENT_TIMESTAMP);
END;
-- 创建调用存储过程的触发器
CREATE TRIGGER trigger_name
AFTER UPDATE ON table_name
FOR EACH ROW
BEGIN
CALL audit_log('table_name', 'UPDATE', OLD.column_name, NEW.column_name);
END;Q2: 事件调度器不执行怎么办?
A2: 事件调度器不执行可能有以下原因:
- 事件调度器未启用
- 事件被禁用
- 事件的调度时间未到
- 事件的 SQL 语句执行失败
- 事件的权限不足
sql
-- 检查事件调度器状态
SHOW VARIABLES LIKE 'event_scheduler';
-- 启用事件调度器
SET GLOBAL event_scheduler = ON;
-- 检查事件状态
SELECT event_name, status FROM information_schema.events WHERE event_name = 'event_name';
-- 启用事件
ALTER EVENT event_name ENABLE;Q3: 如何查看触发器的执行日志?
A3: OceanBase 不会直接记录触发器的执行日志,但可以在触发器中添加日志记录语句,将执行情况写入日志表:
sql
-- 创建日志表
CREATE TABLE trigger_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
trigger_name VARCHAR(50),
table_name VARCHAR(50),
operation VARCHAR(10),
gmt_create TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
details TEXT
);
-- 在触发器中添加日志记录
CREATE TRIGGER trigger_name
AFTER INSERT ON table_name
FOR EACH ROW
BEGIN
-- 触发器逻辑
-- 记录日志
INSERT INTO trigger_log (trigger_name, table_name, operation, details)
VALUES ('trigger_name', 'table_name', 'INSERT', CONCAT('Inserted row with id:', NEW.id));
END;Q4: 触发器和事件的区别是什么?
A4: 触发器和事件的主要区别:
- 触发条件:触发器由 DML 操作触发,事件由时间触发
- 执行时机:触发器在 DML 操作执行前后执行,事件在指定时间点执行
- 作用范围:触发器针对特定表,事件针对整个数据库
- 使用场景:触发器用于数据一致性维护、审计日志等,事件用于定期任务
Q5: 如何优化触发器和事件的性能?
A5: 可以通过以下方式优化触发器和事件的性能:
- 简化触发器逻辑,减少不必要的操作
- 使用存储过程封装复杂逻辑
- 调整事件调度频率,避免频繁执行
- 优化 SQL 语句,提高执行效率
- 考虑使用异步方式执行资源密集型操作
- 避免在触发器中执行长时间运行的操作
Q6: 触发器可以在视图上创建吗?
A6: 是的,可以在视图上创建 INSTEAD OF 触发器,用于替代对视图的直接操作:
sql
-- 创建视图
CREATE VIEW view_name AS SELECT * FROM table_name;
-- 创建 INSTEAD OF 触发器
CREATE TRIGGER trigger_name
INSTEAD OF INSERT ON view_name
FOR EACH ROW
BEGIN
-- 替代插入逻辑
INSERT INTO table_name (column1, column2) VALUES (NEW.column1, NEW.column2);
END;