尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

MySQL触发器查看全攻略:从命令到权限与排障实践

MySQL触发器查看全攻略:从命令到权限与排障实践 做MySQL运维和开发这些年我有个特别深的体会触发器是数据库里最容易被忽视、又最容易惹祸的东西。表结构、索引、慢查询都有人盯唯独TRIGGERS经常被晾在一边。直到线上数据被莫名改动、某张表总是自动多出记录、或者删数据删不掉才想起来要查查触发器。可真到了要查的时候很多人连SHOW TRIGGERS和information_schema.TRIGGERS都分不清更不用说8.0之后的一些细节变化。这篇文章就把“查看触发器”这件事彻底讲透。从最基础的命令姿势到权限坑、主从坑、字符集坑再到面试时人家到底想问什么我都会结合实操经验一步步拆开说。不管你是刚接触MySQL的开发新手还是经常上生产环境排查问题的运维这篇文章都能让你少走弯路。哪怕你只是临时接手一个别人的库看完也能五秒钟定位出这张表到底“背着”哪些触发器、里面干了什么。1. 绕不开的问题触发器到底为什么要“查看”1.1 触发器是“隐形的代码”先聊一个最基本的认知触发器Trigger是数据库对象里的一种它绑定在某张表上当表发生INSERT、UPDATE、DELETE操作时会被自动执行一段SQL逻辑。听起来很方便但它有个很阴险的属性——它是隐形的。什么概念就是你给一张表做SELECT * FROM user看不到任何触发器你做SHOW CREATE TABLE user默认情况下也看不到和触发器相关的信息。但实际插入一条数据的时候后台可能已经悄悄改了另一张表、写了一条日志、更新了一个统计字段。很多时候你查数据发现对不上根本不是业务代码的问题而是触发器的“锅”。所以我一直建议接手任何一套数据库第一步不是看表结构而是先把触发器摸一遍。摸清楚了才知道这套库的“隐藏逻辑”在哪才不会在后面排障的时候被它坑得体无完肤。1.2 查看别人的库先从触发器开始再说一个更实际的场景。很多开发同学在团队协作里会拿到一个“历史悠久”的数据库里面有几十张表光看表结构已经够累了可你还得知道里面有没有触发器。为什么因为触发器会直接影响你对业务逻辑的判断。你在改一条UPDATE语句之前如果不清楚这张表上挂了一个AFTER UPDATE触发器可能会忽略它同步到别的表的副作用。等你上线之后发现其他表的数据变了再回头看已经不是一句“回滚”能解决的。我自己就见过有人把一张大表的UPDATE操作做成了半小时级的长事务最后定位出来是因为触发器里套了一次全表操作直接把性能拖垮。所以查看触发器不是“想起来了看一眼”的事而是数据库变更、排障、审计流程里的一个前置步骤。理解了这一点再看下面的实操姿势你才会明白为什么我要分门别类讲得这么细。2. 查触发器之前先把四种姿势搞清楚查看MySQL触发器本质上有四类路径分别适用于不同场景。我先把它们拉出来对比后面再逐一说透。查看方式核心命令/位置适用场景特点SHOW TRIGGERSSHOW TRIGGERS [FROM db] [LIKE pattern]快速查看当前库或指定库所有触发器输出直观字段聚合度好但有信息截断风险system schema查询information_schema.TRIGGERS精确筛选、脚本化处理、按任意条件过滤字段最全可SQL过滤适合二次加工SHOW CREATE TRIGGERSHOW CREATE TRIGGER trigger_name查看单个触发器的完整定义语句拿到重建脚本可完整复制客户端可视化Navicat、MySQL Workbench等GUI日常人工排查直观但操作效率较低不适合批量处理2.1 SHOW TRIGGERS最直观但藏着小坑这是大家最常用的一条命令。语法很简单SHOW TRIGGERS; SHOW TRIGGERS FROM mydb; SHOW TRIGGERS FROM mydb LIKE log%;不加任何条件的时候它列出当前连接所在数据库或者叫当前schema的所有触发器。如果当前没有选中任何库有些客户端版本会直接报No database selected。这一点注意一下就行。它的结果集有以下关键字段Trigger触发器名字。Event触发事件显示为INSERT、UPDATE、DELETE。Table触发器所在的表名。Statement触发器执行的逻辑主体。注意这里显示的是转换成文本之后的内容如果SQL很长会被截断默认不是完整的。TimingBEFORE还是AFTER。Created创建时间。sql_mode创建触发器时的SQL模式。Definer定义者格式通常是userhost。character_set_client、collation_connection、Database Collation字符集相关的三件套。这个命令的好处就是快一眼能看到全貌。但它的“坑”也很明显Statement字段会被截断长触发器你在这根本看不到完整逻辑而且筛选能力弱只能按库、按名字LIKE过滤没办法按表名过滤。比如我只想看orders表上的所有触发器用SHOW TRIGGERS FROM mydb LIKE %orders%就只能碰运气了因为LIKE匹配的是触发器名字不是表名。所以单独依赖SHOW TRIGGERS查全量没问题但定向排查、按表查询你必须得用下面这种姿势。2.2 information_schema.TRIGGERS最灵活批量筛选首选information_schema.TRIGGERS是MySQL官方提供的系统视图专门存放触发器的元数据。这里才是查触发器的“正规军”字段最全、支持任意过滤条件而且可以通过SQL方式批量处理。先看它的核心字段结构DESC information_schema.TRIGGERS;重点字段说明TRIGGER_CATALOG固定为def了解一下即可。TRIGGER_SCHEMA触发器所在的库名。TRIGGER_NAME触发器名称。EVENT_MANIPULATION触发事件类型INSERT、UPDATE、DELETE。EVENT_OBJECT_SCHEMA绑定的库名。EVENT_OBJECT_TABLE绑定的表名。ACTION_ORDER同表同事件多个触发器的执行顺序。ACTION_CONDITION触发条件一般是NULL。ACTION_STATEMENT触发器的完整执行语句。ACTION_ORIENTATION一般是ROW行级触发器。ACTION_TIMINGBEFORE或AFTER。ACTION_REFERENCE_OLD_TABLE、ACTION_REFERENCE_NEW_TABLE不影响行级触发器通常NULL。ACTION_REFERENCE_OLD_ROW旧值引用通常为OLD。ACTION_REFERENCE_NEW_ROW新值引用通常为NEW。CREATED创建时间。SQL_MODE创建时的SQL模式。DEFINER定义者。CHARACTER_SET_CLIENT、COLLATION_CONNECTION、DATABASE_COLLATION字符集相关信息。为什么说它灵活因为你可以随便查-- 查某张表上的所有触发器 SELECT TRIGGER_NAME, EVENT_MANIPULATION, ACTION_TIMING, ACTION_STATEMENT FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA mydb AND EVENT_OBJECT_TABLE orders; -- 查所有库里名字带log的触发器 SELECT * FROM information_schema.TRIGGERS WHERE TRIGGER_NAME LIKE %log%; -- 查所有库的AFTER INSERT触发器数量 SELECT TRIGGER_SCHEMA, COUNT(*) AS cnt FROM information_schema.TRIGGERS WHERE EVENT_MANIPULATION INSERT AND ACTION_TIMING AFTER GROUP BY TRIGGER_SCHEMA;注意一点在MySQL 8.0中information_schema.TRIGGERS的字段还是这些没有大的改动兼容性很好可以放心用。2.3 SHOW CREATE TRIGGER看定义拿重建脚本SHOW TRIGGERS和information_schema.TRIGGERS虽然能告诉你触发器的基本信息和语句但如果你需要拿到一个可以直接重建的完整脚本就得用SHOW CREATE TRIGGER。SHOW CREATE TRIGGER mydb.trg_order_after_insert;执行结果返回两列Trigger触发器名和SQL Original Statement完整创建语句。注意这里的SQL Original Statement左侧会有一个Create Trigger字样是用于重建的完整DDL定义可以直接复制出来作为备份或者迁移脚本。这里我一般会配合!的快捷方式在mysql客户端里查看排版避免一行超长语句看不清楚。当然在DBeaver、DataGrip这类工具里可以直接格式化显示体验会好很多。除此之外还可以用SHOW CREATE TABLE看触发器的存在性。执行SHOW CREATE TABLE orders\G在输出内容的最后面会有TRIGGERS部分但注意它只列出触发器名字和事件不会展示完整定义所以不要指望它能替代SHOW CREATE TRRIGER。2.4 图形客户端不提工单也能“可视化”看开发环境里我最常用的还是Navicat。操作路径很简单打开连接展开目标库找到“触发器”文件夹双击即可看到库内所有触发器列表。点某个触发器右侧会出现两部分上半部分是属性定义者、时间、事件、表名下半部分是定义SQL。还可以直接右键“修改触发器”查看完整代码或者“复制SQL”拿到重建语句。MySQL Workbench的路径也类似在左侧的SCHEMAS面板里展开库找Tables下面的触发器节点。注意有些旧版本Workbench的触发器列表不显示需要右键表选择“Alter Table”在里面看Triggers选项卡。图形客户端的优势是直观劣势就是不够批量。如果触发器数量多或者需要过滤、统计还是推荐SQL方式。3. 实操在真实环境里把触发器查清楚说再多理论不如实际跑一遍。我带大家走一套完整的实操流程大家可以直接对照着自己库里试。3.1 造一张演示表和两个触发器先建一个简单的订单表再模拟两个常见触发器一个在插入订单时自动写一条操作日志一个在更新订单金额时自动加一个校验或者同步动作。CREATE DATABASE IF NOT EXISTS demo_db DEFAULT CHARACTER SET utf8mb4; USE demo_db; CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, amount DECIMAL(10,2) NOT NULL DEFAULT 0, status TINYINT NOT NULL DEFAULT 0, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB; CREATE TABLE order_logs ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, action VARCHAR(64) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; DELIMITER $$ CREATE TRIGGER trg_order_after_insert AFTER INSERT ON orders FOR EACH ROW BEGIN INSERT INTO order_logs(order_id, action) VALUES (NEW.id, INSERT); END$$ DELIMITER ; DELIMITER $$ CREATE TRIGGER trg_order_before_update BEFORE UPDATE ON orders FOR EACH ROW BEGIN IF NEW.amount OLD.amount THEN INSERT INTO order_logs(order_id, action) VALUES (NEW.id, CONCAT(AMOUNT_CHANGE_, OLD.amount, _TO_, NEW.amount)); END IF; END$$ DELIMITER ;注意一下我在触发器里用了DELIMITER $$这是因为在mysql客户端里默认分号就是语句结束符如果不临时切换分隔符CREATE TRIGGER在第一条分号处就会提前结束导致语法错误。这个细节看起来基础但真的是新手翻车重灾区。3.2 按表查、按库查、按事件查现在我在demo_db库里先用SHOW TRIGGERS看一眼全貌SHOW TRIGGERS FROM demo_db\G注意我用了\G在mysql命令行里这是把输出结果转成纵向显示的关键触发器语句长了以后横向显示会乱成一团纵向输出才是正经姿势。再试试按表定向查询。我想知道orders表上都有哪些触发器用SHOW TRIGGERS不好按表名过滤所以直接用information_schemaSELECT TRIGGER_NAME, EVENT_MANIPULATION, ACTION_TIMING, ACTION_STATEMENT FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA demo_db AND EVENT_OBJECT_TABLE orders\G如果只看某个触发器的完整定义SHOW CREATE TRIGGER demo_db.trg_order_after_insert\G你会发现输出的SQL Original Statement中包含完整的CREATE DEFINER...TRIGGER ...语句这其实是做触发器备份、迁移最靠谱的方式。我之前从5.7迁到8.0的时候就是用一条SQL查出所有触发器的定义直接生成迁移脚本比在客户端里一个个复制粘贴快得多。批量生成所有触发器的重建脚本可以这样写SELECT CONCAT(SHOW CREATE TRIGGER , TRIGGER_SCHEMA, ., TRIGGER_NAME, ;) FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA demo_db;把结果逐条执行再把每条结果里的SQL Original Statement收集起来就是一个完整的触发器备份文件。3.3 把查看结果变成完整的排障报告光会执行命令还不够排障时要能把结果串起来。我通常的做法是先查全量确认数量和总览再按表筛选确认范围最后对重点触发器看完整定义。顺序不能反因为先全量再看单条定位问题的路径才是最优的。举个例子。有一次生产环境发现goods表的数据总是一致性异常我先执行SELECT TRIGGER_NAME, EVENT_OBJECT_TABLE, ACTION_TIMING, EVENT_MANIPULATION FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA shop;很快就定位到有3个触发器都挂在goods表上其中一个AFTER UPDATE触发器嫌疑最大。接着查它的完整定义SHOW CREATE TRIGGER shop.trg_goods_update_sync\G结果看到里面有一句UPDATE other_table SET ...也就是每UPDATE一次商品就会同步更新另一张表的一大堆字段。但问题是该表数据量大触发器执行时间比主操作本身还长。最后和开发确认这个触发器已经不需要了直接禁用掉异常就消失了。这就是触发器查看的实际价值——不是“看一眼有没有触发器”这个动作本身而是通过查看把隐藏的副作用挖出来给后续决策提供依据。4. 踩坑记录查看触发器时最容易翻车的地方查触发器看着简单实际操作里各种坑我基本都踩过。下面这些是我觉得最有必要分享的基本都属于“没人告诉你等你碰到再查就要花半天时间”的类型。4.1 权限不够命令白敲我最开始接触MySQL的时候用的还是一个只读账号敲SHOW TRIGGERS直接报错ERROR 1227 (42000): Access denied; you need (at least one of) the TRIGGER privilege(s) for this operation这里涉及MySQL权限体系查看触发器至少需要TRIGGER权限。如果你用的是普通只读账号想要查询information_schema.TRIGGERS可能也会被限制具体表现为连SELECT都报权限拒绝或者能查到一部分库但查不到全部。解决方式有两层让DBA授权执行GRANT TRIGGER ON demo_db.* TO userhost;。如果是只读场景也可以尝试SHOW TRIGGERS看能不能命中已开放的权限项但实测不通用。实际经验是给开发环境账号直接开TRIGGER权限问题不大生产环境坚持最小权限原则但要保证DBA自己随时能查。如果你连TRIGGER权限都没有又急需查看临时让DBA开个只读会话也可以接受。4.2 8.0之后 mysql.proc 没了以前在MySQL 5.7时代有些老DBA会通过直接查询mysql.proc表来查看触发器、存储过程、函数之类的定义。很多遗留教程也是这么教的。但是MySQL 8.0把mysql.proc表彻底移除了。你再去查SELECT * FROM mysql.proc; -- 8.0直接报错会得到很直接的ERROR 1109 (42S02): Unknown table proc in mysql。正确姿势就是我用前面讲的information_schema.TRIGGERS和SHOW CREATE TRIGGER这两条路在8.0里都是官方支持的稳定可靠。顺带提醒一句如果你从5.7升到8.0迁移工具可能也不会自动处理老触发器务必用SHOW CREATE TRIGGER先备份再迁移否则你升完级会发现触发器悄悄丢了。4.3 主从库触发器不一致问题最隐蔽触发器在MySQL主从复制里有个大坑默认情况下触发器只在主库执行不会在从库执行。也就是说你在主库建了触发器从库对应表上的数据可能是对的因为从库接收的是主库已经做了触发器处理的binlog但如果你在从库单独查触发器会发现也许是空的。反过来如果你在主库上用binlog_format STATEMENT且触发器向其他表写入数据主从复制可能遇到“从库找不到触发器定义”之类的异常。这些情况在金丝雀上线、临时搭建只读从库时特别容易踩我之前就被人问过“为什么明明主库有触发器从库数据还是会不一致”追到根因就是两边的触发器集合没有保持一致。所以查看触发器时一定要主库从库分开查、定期对比不能默认“主库有从库就有”。工具可以写个定期巡检脚本把主从的触发器列表用information_schema.TRIGGERS拉出来做diff这不麻烦能防很多事故。4.4 字符集显示乱码还有一个容易忽略的点。触发器新增数据时可能用到中文常量或者注释如果你在客户端查看客户端连接字符集和触发器创建时的字符集不一致会出现乱码。经典例子是SHOW TRIGGERS输出看中文正常用脚本采集时却发现乱码或者SHOW CREATE TRRIGGER复制出来执行后中文全变成问号。原因是character_set_client在触发器创建会话里被固化成了当时的连接字符集。查询时如果当前会话的字符集不同MySQL会做隐式转换而转换规则有时候并不如你所愿。处理原则是先统一字符集再查看。连接后立刻执行SET NAMES utf8mb4;再去做查询基本能避开大部分乱码问题。如果还是乱码检查触发器的character_set_client字段和当前会话是否一致。4.5 触发器语句太长默认工具显示不全这是最容易被误解的一点。很多人用SHOW TRIGGERS看到Statement字段的最后是...以为是触发器坏了。其实不是是显示截断。所以在查看较长触发器时一定要用SHOW CREATE TRIGGER或者查information_schema.TRIGGERS.ACTION_STATEMENT字段。这两个位置存储的是完整语句不会被截断。另外很多图形客户端的表格控件也会对超长文本做缩略显示右键“复制单元格内容”一般能看到全文。5. 面试番外知道怎么查还不够触发器这玩意儿面试也特别爱考。很多候选人说起触发器张口就是“在表上放一段SQL自动执行”但你说到具体怎么查、怎么排查、怎么备份他就卡壳了。只能说懂得不够系统的还是多数。5.1 高频三连问第一问MySQL有哪几种触发器事件答案BEFORE INSERT、AFTER INSERT、BEFORE UPDATE、AFTER UPDATE、BEFORE DELETE、AFTER DELETE为行级触发器每张表同一事件可以存在多个触发器按ACTION_ORDER顺序执行。第二问如何查看某张表上的触发器标准回答是先SHOW TRIGGERS FROM 库名快速看全量再用SELECT ... FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_TABLE表名做定向排查最后用SHOW CREATE TRIGGER查看完整定义。如果能顺带答出“SHOW TRIGGERS的Statement会被截断、需要看ACTION_STATEMENT”这种细节面试印象会好很多。第三问触发器对性能有什么影响这个更能考察理解的深度。答案要点触发器在事务内执行执行业务SQL和触发器SQL的总时间会叠加如果触发器里查了别的表行锁会持有更久长触发器还会增加死锁概率。所以生产环境触发器逻辑一定要短平快重逻辑应该丢到应用层异步处理。5.2 学习建议我个人建议触发器能不用尽量别用尤其在大型分布式系统中会有维护成本。但如果你已经接手了一套老系统触发器满天飞那想办法先查清楚它们、做好备份和文档化就已经是很大的价值输出。查是所有后续操作的前提一点虚的都不掺杂。6. 我的实操体会总结最后分享几个“如果让我重新查一遍我一定先做”的小建议。第一善用information_schema别死记硬背SHOW TRIGGERS的选项。我在实际工作中90%的触发器查看都是用information_schema.TRIGGERS完成的因为它支持任意字段过滤还方便用SQL拼接批量脚本这是SHOW命令不具备的能力。第二备份触发器只用SHOW CREATE TRIGGER不要依赖图形工具导出。我曾经尝试用Navicat的“转储SQL文件”导出触发器结果发现它会带上DELIMITER和一些版本相关的特殊字符换库执行时容易报错。直接拿SHOW CREATE TRIGGER的输出做成一份标准的DDL脚本反而干净利落。第三查看触发器之后顺手检查一下触发器里引用的表、字段是否还存在。很多人建完触发器后改表结构直接把字段名改掉但触发器还在引用旧字段。你平时不用它没事一旦触发条件满足就是一连串报错。查完触发器确认一下ACTION_STATEMENT里涉及的相关对象都健在能避免很多“莫名其妙”的错误。细算下来我从第一次被触发器坑到如今把它玩得明明白白也就是靠多查、多踩、多想这六个字。这篇文章写下来也算是我这些年查触发器经验的一个梳理。如果你对照着操作下来还有哪里跟你的环境不一致的多半要把目光放在版本差异和权限配置上这两个变量是最大的“隐形差异项”。希望这篇对你有用祝大家查库愉快少踩坑多省心。
返回列表