器查看全攻略:從命令到權(quán)限與排障實(shí)踐)
做MySQL運(yùn)維和開發(fā)這些年我有個(gè)特別深的體會(huì)觸發(fā)器是數(shù)據(jù)庫(kù)里最容易被忽視、又最容易惹禍的東西。表結(jié)構(gòu)、索引、慢查詢都有人盯唯獨(dú)TRIGGERS經(jīng)常被晾在一邊。直到線上數(shù)據(jù)被莫名改動(dòng)、某張表總是自動(dòng)多出記錄、或者刪數(shù)據(jù)刪不掉才想起來(lái)要查查觸發(fā)器??烧娴搅艘榈臅r(shí)候很多人連SHOW TRIGGERS和information_schema.TRIGGERS都分不清更不用說(shuō)8.0之后的一些細(xì)節(jié)變化。這篇文章就把“查看觸發(fā)器”這件事徹底講透。從最基礎(chǔ)的命令姿勢(shì)到權(quán)限坑、主從坑、字符集坑再到面試時(shí)人家到底想問(wèn)什么我都會(huì)結(jié)合實(shí)操經(jīng)驗(yàn)一步步拆開說(shuō)。不管你是剛接觸MySQL的開發(fā)新手還是經(jīng)常上生產(chǎn)環(huán)境排查問(wèn)題的運(yùn)維這篇文章都能讓你少走彎路。哪怕你只是臨時(shí)接手一個(gè)別人的庫(kù)看完也能五秒鐘定位出這張表到底“背著”哪些觸發(fā)器、里面干了什么。1. 繞不開的問(wèn)題觸發(fā)器到底為什么要“查看”1.1 觸發(fā)器是“隱形的代碼”先聊一個(gè)最基本的認(rèn)知觸發(fā)器Trigger是數(shù)據(jù)庫(kù)對(duì)象里的一種它綁定在某張表上當(dāng)表發(fā)生INSERT、UPDATE、DELETE操作時(shí)會(huì)被自動(dòng)執(zhí)行一段SQL邏輯。聽起來(lái)很方便但它有個(gè)很陰險(xiǎn)的屬性——它是隱形的。什么概念就是你給一張表做SELECT * FROM user看不到任何觸發(fā)器你做SHOW CREATE TABLE user默認(rèn)情況下也看不到和觸發(fā)器相關(guān)的信息。但實(shí)際插入一條數(shù)據(jù)的時(shí)候后臺(tái)可能已經(jīng)悄悄改了另一張表、寫了一條日志、更新了一個(gè)統(tǒng)計(jì)字段。很多時(shí)候你查數(shù)據(jù)發(fā)現(xiàn)對(duì)不上根本不是業(yè)務(wù)代碼的問(wèn)題而是觸發(fā)器的“鍋”。所以我一直建議接手任何一套數(shù)據(jù)庫(kù)第一步不是看表結(jié)構(gòu)而是先把觸發(fā)器摸一遍。摸清楚了才知道這套庫(kù)的“隱藏邏輯”在哪才不會(huì)在后面排障的時(shí)候被它坑得體無(wú)完膚。1.2 查看別人的庫(kù)先從觸發(fā)器開始再說(shuō)一個(gè)更實(shí)際的場(chǎng)景。很多開發(fā)同學(xué)在團(tuán)隊(duì)協(xié)作里會(huì)拿到一個(gè)“歷史悠久”的數(shù)據(jù)庫(kù)里面有幾十張表光看表結(jié)構(gòu)已經(jīng)夠累了可你還得知道里面有沒(méi)有觸發(fā)器。為什么因?yàn)橛|發(fā)器會(huì)直接影響你對(duì)業(yè)務(wù)邏輯的判斷。你在改一條UPDATE語(yǔ)句之前如果不清楚這張表上掛了一個(gè)AFTER UPDATE觸發(fā)器可能會(huì)忽略它同步到別的表的副作用。等你上線之后發(fā)現(xiàn)其他表的數(shù)據(jù)變了再回頭看已經(jīng)不是一句“回滾”能解決的。我自己就見過(guò)有人把一張大表的UPDATE操作做成了半小時(shí)級(jí)的長(zhǎng)事務(wù)最后定位出來(lái)是因?yàn)橛|發(fā)器里套了一次全表操作直接把性能拖垮。所以查看觸發(fā)器不是“想起來(lái)了看一眼”的事而是數(shù)據(jù)庫(kù)變更、排障、審計(jì)流程里的一個(gè)前置步驟。理解了這一點(diǎn)再看下面的實(shí)操姿勢(shì)你才會(huì)明白為什么我要分門別類講得這么細(xì)。2. 查觸發(fā)器之前先把四種姿勢(shì)搞清楚查看MySQL觸發(fā)器本質(zhì)上有四類路徑分別適用于不同場(chǎng)景。我先把它們拉出來(lái)對(duì)比后面再逐一說(shuō)透。查看方式核心命令/位置適用場(chǎng)景特點(diǎn)SHOW TRIGGERSSHOW TRIGGERS [FROM db] [LIKE pattern]快速查看當(dāng)前庫(kù)或指定庫(kù)所有觸發(fā)器輸出直觀字段聚合度好但有信息截?cái)囡L(fēng)險(xiǎn)system schema查詢information_schema.TRIGGERS精確篩選、腳本化處理、按任意條件過(guò)濾字段最全可SQL過(guò)濾適合二次加工SHOW CREATE TRIGGERSHOW CREATE TRIGGER trigger_name查看單個(gè)觸發(fā)器的完整定義語(yǔ)句拿到重建腳本可完整復(fù)制客戶端可視化Navicat、MySQL Workbench等GUI日常人工排查直觀但操作效率較低不適合批量處理2.1 SHOW TRIGGERS最直觀但藏著小坑這是大家最常用的一條命令。語(yǔ)法很簡(jiǎn)單SHOW TRIGGERS; SHOW TRIGGERS FROM mydb; SHOW TRIGGERS FROM mydb LIKE log%;不加任何條件的時(shí)候它列出當(dāng)前連接所在數(shù)據(jù)庫(kù)或者叫當(dāng)前schema的所有觸發(fā)器。如果當(dāng)前沒(méi)有選中任何庫(kù)有些客戶端版本會(huì)直接報(bào)No database selected。這一點(diǎn)注意一下就行。它的結(jié)果集有以下關(guān)鍵字段Trigger觸發(fā)器名字。Event觸發(fā)事件顯示為INSERT、UPDATE、DELETE。Table觸發(fā)器所在的表名。Statement觸發(fā)器執(zhí)行的邏輯主體。注意這里顯示的是轉(zhuǎn)換成文本之后的內(nèi)容如果SQL很長(zhǎng)會(huì)被截?cái)嗄J(rèn)不是完整的。TimingBEFORE還是AFTER。Created創(chuàng)建時(shí)間。sql_mode創(chuàng)建觸發(fā)器時(shí)的SQL模式。Definer定義者格式通常是userhost。character_set_client、collation_connection、Database Collation字符集相關(guān)的三件套。這個(gè)命令的好處就是快一眼能看到全貌。但它的“坑”也很明顯Statement字段會(huì)被截?cái)嚅L(zhǎng)觸發(fā)器你在這根本看不到完整邏輯而且篩選能力弱只能按庫(kù)、按名字LIKE過(guò)濾沒(méi)辦法按表名過(guò)濾。比如我只想看orders表上的所有觸發(fā)器用SHOW TRIGGERS FROM mydb LIKE %orders%就只能碰運(yùn)氣了因?yàn)長(zhǎng)IKE匹配的是觸發(fā)器名字不是表名。所以單獨(dú)依賴SHOW TRIGGERS查全量沒(méi)問(wèn)題但定向排查、按表查詢你必須得用下面這種姿勢(shì)。2.2 information_schema.TRIGGERS最靈活批量篩選首選information_schema.TRIGGERS是MySQL官方提供的系統(tǒng)視圖專門存放觸發(fā)器的元數(shù)據(jù)。這里才是查觸發(fā)器的“正規(guī)軍”字段最全、支持任意過(guò)濾條件而且可以通過(guò)SQL方式批量處理。先看它的核心字段結(jié)構(gòu)DESC information_schema.TRIGGERS;重點(diǎn)字段說(shuō)明TRIGGER_CATALOG固定為def了解一下即可。TRIGGER_SCHEMA觸發(fā)器所在的庫(kù)名。TRIGGER_NAME觸發(fā)器名稱。EVENT_MANIPULATION觸發(fā)事件類型INSERT、UPDATE、DELETE。EVENT_OBJECT_SCHEMA綁定的庫(kù)名。EVENT_OBJECT_TABLE綁定的表名。ACTION_ORDER同表同事件多個(gè)觸發(fā)器的執(zhí)行順序。ACTION_CONDITION觸發(fā)條件一般是NULL。ACTION_STATEMENT觸發(fā)器的完整執(zhí)行語(yǔ)句。ACTION_ORIENTATION一般是ROW行級(jí)觸發(fā)器。ACTION_TIMINGBEFORE或AFTER。ACTION_REFERENCE_OLD_TABLE、ACTION_REFERENCE_NEW_TABLE不影響行級(jí)觸發(fā)器通常NULL。ACTION_REFERENCE_OLD_ROW舊值引用通常為OLD。ACTION_REFERENCE_NEW_ROW新值引用通常為NEW。CREATED創(chuàng)建時(shí)間。SQL_MODE創(chuàng)建時(shí)的SQL模式。DEFINER定義者。CHARACTER_SET_CLIENT、COLLATION_CONNECTION、DATABASE_COLLATION字符集相關(guān)信息。為什么說(shuō)它靈活因?yàn)槟憧梢噪S便查-- 查某張表上的所有觸發(fā)器 SELECT TRIGGER_NAME, EVENT_MANIPULATION, ACTION_TIMING, ACTION_STATEMENT FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA mydb AND EVENT_OBJECT_TABLE orders; -- 查所有庫(kù)里名字帶log的觸發(fā)器 SELECT * FROM information_schema.TRIGGERS WHERE TRIGGER_NAME LIKE %log%; -- 查所有庫(kù)的AFTER INSERT觸發(fā)器數(shù)量 SELECT TRIGGER_SCHEMA, COUNT(*) AS cnt FROM information_schema.TRIGGERS WHERE EVENT_MANIPULATION INSERT AND ACTION_TIMING AFTER GROUP BY TRIGGER_SCHEMA;注意一點(diǎn)在MySQL 8.0中information_schema.TRIGGERS的字段還是這些沒(méi)有大的改動(dòng)兼容性很好可以放心用。2.3 SHOW CREATE TRIGGER看定義拿重建腳本SHOW TRIGGERS和information_schema.TRIGGERS雖然能告訴你觸發(fā)器的基本信息和語(yǔ)句但如果你需要拿到一個(gè)可以直接重建的完整腳本就得用SHOW CREATE TRIGGER。SHOW CREATE TRIGGER mydb.trg_order_after_insert;執(zhí)行結(jié)果返回兩列Trigger觸發(fā)器名和SQL Original Statement完整創(chuàng)建語(yǔ)句。注意這里的SQL Original Statement左側(cè)會(huì)有一個(gè)Create Trigger字樣是用于重建的完整DDL定義可以直接復(fù)制出來(lái)作為備份或者遷移腳本。這里我一般會(huì)配合!的快捷方式在mysql客戶端里查看排版避免一行超長(zhǎng)語(yǔ)句看不清楚。當(dāng)然在DBeaver、DataGrip這類工具里可以直接格式化顯示體驗(yàn)會(huì)好很多。除此之外還可以用SHOW CREATE TABLE看觸發(fā)器的存在性。執(zhí)行SHOW CREATE TABLE orders\G在輸出內(nèi)容的最后面會(huì)有TRIGGERS部分但注意它只列出觸發(fā)器名字和事件不會(huì)展示完整定義所以不要指望它能替代SHOW CREATE TRRIGER。2.4 圖形客戶端不提工單也能“可視化”看開發(fā)環(huán)境里我最常用的還是Navicat。操作路徑很簡(jiǎn)單打開連接展開目標(biāo)庫(kù)找到“觸發(fā)器”文件夾雙擊即可看到庫(kù)內(nèi)所有觸發(fā)器列表。點(diǎn)某個(gè)觸發(fā)器右側(cè)會(huì)出現(xiàn)兩部分上半部分是屬性定義者、時(shí)間、事件、表名下半部分是定義SQL。還可以直接右鍵“修改觸發(fā)器”查看完整代碼或者“復(fù)制SQL”拿到重建語(yǔ)句。MySQL Workbench的路徑也類似在左側(cè)的SCHEMAS面板里展開庫(kù)找Tables下面的觸發(fā)器節(jié)點(diǎn)。注意有些舊版本W(wǎng)orkbench的觸發(fā)器列表不顯示需要右鍵表選擇“Alter Table”在里面看Triggers選項(xiàng)卡。圖形客戶端的優(yōu)勢(shì)是直觀劣勢(shì)就是不夠批量。如果觸發(fā)器數(shù)量多或者需要過(guò)濾、統(tǒng)計(jì)還是推薦SQL方式。3. 實(shí)操在真實(shí)環(huán)境里把觸發(fā)器查清楚說(shuō)再多理論不如實(shí)際跑一遍。我?guī)Т蠹易咭惶淄暾膶?shí)操流程大家可以直接對(duì)照著自己庫(kù)里試。3.1 造一張演示表和兩個(gè)觸發(fā)器先建一個(gè)簡(jiǎn)單的訂單表再模擬兩個(gè)常見觸發(fā)器一個(gè)在插入訂單時(shí)自動(dòng)寫一條操作日志一個(gè)在更新訂單金額時(shí)自動(dòng)加一個(gè)校驗(yàn)或者同步動(dòng)作。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 ;注意一下我在觸發(fā)器里用了DELIMITER $$這是因?yàn)樵趍ysql客戶端里默認(rèn)分號(hào)就是語(yǔ)句結(jié)束符如果不臨時(shí)切換分隔符CREATE TRIGGER在第一條分號(hào)處就會(huì)提前結(jié)束導(dǎo)致語(yǔ)法錯(cuò)誤。這個(gè)細(xì)節(jié)看起來(lái)基礎(chǔ)但真的是新手翻車重災(zāi)區(qū)。3.2 按表查、按庫(kù)查、按事件查現(xiàn)在我在demo_db庫(kù)里先用SHOW TRIGGERS看一眼全貌SHOW TRIGGERS FROM demo_db\G注意我用了\G在mysql命令行里這是把輸出結(jié)果轉(zhuǎn)成縱向顯示的關(guān)鍵觸發(fā)器語(yǔ)句長(zhǎng)了以后橫向顯示會(huì)亂成一團(tuán)縱向輸出才是正經(jīng)姿勢(shì)。再試試按表定向查詢。我想知道orders表上都有哪些觸發(fā)器用SHOW TRIGGERS不好按表名過(guò)濾所以直接用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如果只看某個(gè)觸發(fā)器的完整定義SHOW CREATE TRIGGER demo_db.trg_order_after_insert\G你會(huì)發(fā)現(xiàn)輸出的SQL Original Statement中包含完整的CREATE DEFINER...TRIGGER ...語(yǔ)句這其實(shí)是做觸發(fā)器備份、遷移最靠譜的方式。我之前從5.7遷到8.0的時(shí)候就是用一條SQL查出所有觸發(fā)器的定義直接生成遷移腳本比在客戶端里一個(gè)個(gè)復(fù)制粘貼快得多。批量生成所有觸發(fā)器的重建腳本可以這樣寫SELECT CONCAT(SHOW CREATE TRIGGER , TRIGGER_SCHEMA, ., TRIGGER_NAME, ;) FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA demo_db;把結(jié)果逐條執(zhí)行再把每條結(jié)果里的SQL Original Statement收集起來(lái)就是一個(gè)完整的觸發(fā)器備份文件。3.3 把查看結(jié)果變成完整的排障報(bào)告光會(huì)執(zhí)行命令還不夠排障時(shí)要能把結(jié)果串起來(lái)。我通常的做法是先查全量確認(rèn)數(shù)量和總覽再按表篩選確認(rèn)范圍最后對(duì)重點(diǎn)觸發(fā)器看完整定義。順序不能反因?yàn)橄热吭倏磫螚l定位問(wèn)題的路徑才是最優(yōu)的。舉個(gè)例子。有一次生產(chǎn)環(huán)境發(fā)現(xiàn)goods表的數(shù)據(jù)總是一致性異常我先執(zhí)行SELECT TRIGGER_NAME, EVENT_OBJECT_TABLE, ACTION_TIMING, EVENT_MANIPULATION FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_SCHEMA shop;很快就定位到有3個(gè)觸發(fā)器都掛在goods表上其中一個(gè)AFTER UPDATE觸發(fā)器嫌疑最大。接著查它的完整定義SHOW CREATE TRIGGER shop.trg_goods_update_sync\G結(jié)果看到里面有一句UPDATE other_table SET ...也就是每UPDATE一次商品就會(huì)同步更新另一張表的一大堆字段。但問(wèn)題是該表數(shù)據(jù)量大觸發(fā)器執(zhí)行時(shí)間比主操作本身還長(zhǎng)。最后和開發(fā)確認(rèn)這個(gè)觸發(fā)器已經(jīng)不需要了直接禁用掉異常就消失了。這就是觸發(fā)器查看的實(shí)際價(jià)值——不是“看一眼有沒(méi)有觸發(fā)器”這個(gè)動(dòng)作本身而是通過(guò)查看把隱藏的副作用挖出來(lái)給后續(xù)決策提供依據(jù)。4. 踩坑記錄查看觸發(fā)器時(shí)最容易翻車的地方查觸發(fā)器看著簡(jiǎn)單實(shí)際操作里各種坑我基本都踩過(guò)。下面這些是我覺(jué)得最有必要分享的基本都屬于“沒(méi)人告訴你等你碰到再查就要花半天時(shí)間”的類型。4.1 權(quán)限不夠命令白敲我最開始接觸MySQL的時(shí)候用的還是一個(gè)只讀賬號(hào)敲SHOW TRIGGERS直接報(bào)錯(cuò)ERROR 1227 (42000): Access denied; you need (at least one of) the TRIGGER privilege(s) for this operation這里涉及MySQL權(quán)限體系查看觸發(fā)器至少需要TRIGGER權(quán)限。如果你用的是普通只讀賬號(hào)想要查詢information_schema.TRIGGERS可能也會(huì)被限制具體表現(xiàn)為連SELECT都報(bào)權(quán)限拒絕或者能查到一部分庫(kù)但查不到全部。解決方式有兩層讓DBA授權(quán)執(zhí)行GRANT TRIGGER ON demo_db.* TO userhost;。如果是只讀場(chǎng)景也可以嘗試SHOW TRIGGERS看能不能命中已開放的權(quán)限項(xiàng)但實(shí)測(cè)不通用。實(shí)際經(jīng)驗(yàn)是給開發(fā)環(huán)境賬號(hào)直接開TRIGGER權(quán)限問(wèn)題不大生產(chǎn)環(huán)境堅(jiān)持最小權(quán)限原則但要保證DBA自己隨時(shí)能查。如果你連TRIGGER權(quán)限都沒(méi)有又急需查看臨時(shí)讓DBA開個(gè)只讀會(huì)話也可以接受。4.2 8.0之后 mysql.proc 沒(méi)了以前在MySQL 5.7時(shí)代有些老DBA會(huì)通過(guò)直接查詢mysql.proc表來(lái)查看觸發(fā)器、存儲(chǔ)過(guò)程、函數(shù)之類的定義。很多遺留教程也是這么教的。但是MySQL 8.0把mysql.proc表徹底移除了。你再去查SELECT * FROM mysql.proc; -- 8.0直接報(bào)錯(cuò)會(huì)得到很直接的ERROR 1109 (42S02): Unknown table proc in mysql。正確姿勢(shì)就是我用前面講的information_schema.TRIGGERS和SHOW CREATE TRIGGER這兩條路在8.0里都是官方支持的穩(wěn)定可靠。順帶提醒一句如果你從5.7升到8.0遷移工具可能也不會(huì)自動(dòng)處理老觸發(fā)器務(wù)必用SHOW CREATE TRIGGER先備份再遷移否則你升完級(jí)會(huì)發(fā)現(xiàn)觸發(fā)器悄悄丟了。4.3 主從庫(kù)觸發(fā)器不一致問(wèn)題最隱蔽觸發(fā)器在MySQL主從復(fù)制里有個(gè)大坑默認(rèn)情況下觸發(fā)器只在主庫(kù)執(zhí)行不會(huì)在從庫(kù)執(zhí)行。也就是說(shuō)你在主庫(kù)建了觸發(fā)器從庫(kù)對(duì)應(yīng)表上的數(shù)據(jù)可能是對(duì)的因?yàn)閺膸?kù)接收的是主庫(kù)已經(jīng)做了觸發(fā)器處理的binlog但如果你在從庫(kù)單獨(dú)查觸發(fā)器會(huì)發(fā)現(xiàn)也許是空的。反過(guò)來(lái)如果你在主庫(kù)上用binlog_format STATEMENT且觸發(fā)器向其他表寫入數(shù)據(jù)主從復(fù)制可能遇到“從庫(kù)找不到觸發(fā)器定義”之類的異常。這些情況在金絲雀上線、臨時(shí)搭建只讀從庫(kù)時(shí)特別容易踩我之前就被人問(wèn)過(guò)“為什么明明主庫(kù)有觸發(fā)器從庫(kù)數(shù)據(jù)還是會(huì)不一致”追到根因就是兩邊的觸發(fā)器集合沒(méi)有保持一致。所以查看觸發(fā)器時(shí)一定要主庫(kù)從庫(kù)分開查、定期對(duì)比不能默認(rèn)“主庫(kù)有從庫(kù)就有”。工具可以寫個(gè)定期巡檢腳本把主從的觸發(fā)器列表用information_schema.TRIGGERS拉出來(lái)做diff這不麻煩能防很多事故。4.4 字符集顯示亂碼還有一個(gè)容易忽略的點(diǎn)。觸發(fā)器新增數(shù)據(jù)時(shí)可能用到中文常量或者注釋如果你在客戶端查看客戶端連接字符集和觸發(fā)器創(chuàng)建時(shí)的字符集不一致會(huì)出現(xiàn)亂碼。經(jīng)典例子是SHOW TRIGGERS輸出看中文正常用腳本采集時(shí)卻發(fā)現(xiàn)亂碼或者SHOW CREATE TRRIGGER復(fù)制出來(lái)執(zhí)行后中文全變成問(wèn)號(hào)。原因是character_set_client在觸發(fā)器創(chuàng)建會(huì)話里被固化成了當(dāng)時(shí)的連接字符集。查詢時(shí)如果當(dāng)前會(huì)話的字符集不同MySQL會(huì)做隱式轉(zhuǎn)換而轉(zhuǎn)換規(guī)則有時(shí)候并不如你所愿。處理原則是先統(tǒng)一字符集再查看。連接后立刻執(zhí)行SET NAMES utf8mb4;再去做查詢基本能避開大部分亂碼問(wèn)題。如果還是亂碼檢查觸發(fā)器的character_set_client字段和當(dāng)前會(huì)話是否一致。4.5 觸發(fā)器語(yǔ)句太長(zhǎng)默認(rèn)工具顯示不全這是最容易被誤解的一點(diǎn)。很多人用SHOW TRIGGERS看到Statement字段的最后是...以為是觸發(fā)器壞了。其實(shí)不是是顯示截?cái)?。所以在查看較長(zhǎng)觸發(fā)器時(shí)一定要用SHOW CREATE TRIGGER或者查information_schema.TRIGGERS.ACTION_STATEMENT字段。這兩個(gè)位置存儲(chǔ)的是完整語(yǔ)句不會(huì)被截?cái)?。另外很多圖形客戶端的表格控件也會(huì)對(duì)超長(zhǎng)文本做縮略顯示右鍵“復(fù)制單元格內(nèi)容”一般能看到全文。5. 面試番外知道怎么查還不夠觸發(fā)器這玩意兒面試也特別愛(ài)考。很多候選人說(shuō)起觸發(fā)器張口就是“在表上放一段SQL自動(dòng)執(zhí)行”但你說(shuō)到具體怎么查、怎么排查、怎么備份他就卡殼了。只能說(shuō)懂得不夠系統(tǒng)的還是多數(shù)。5.1 高頻三連問(wèn)第一問(wèn)MySQL有哪幾種觸發(fā)器事件答案BEFORE INSERT、AFTER INSERT、BEFORE UPDATE、AFTER UPDATE、BEFORE DELETE、AFTER DELETE為行級(jí)觸發(fā)器每張表同一事件可以存在多個(gè)觸發(fā)器按ACTION_ORDER順序執(zhí)行。第二問(wèn)如何查看某張表上的觸發(fā)器標(biāo)準(zhǔn)回答是先SHOW TRIGGERS FROM 庫(kù)名快速看全量再用SELECT ... FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_TABLE表名做定向排查最后用SHOW CREATE TRIGGER查看完整定義。如果能順帶答出“SHOW TRIGGERS的Statement會(huì)被截?cái)?、需要看ACTION_STATEMENT”這種細(xì)節(jié)面試印象會(huì)好很多。第三問(wèn)觸發(fā)器對(duì)性能有什么影響這個(gè)更能考察理解的深度。答案要點(diǎn)觸發(fā)器在事務(wù)內(nèi)執(zhí)行執(zhí)行業(yè)務(wù)SQL和觸發(fā)器SQL的總時(shí)間會(huì)疊加如果觸發(fā)器里查了別的表行鎖會(huì)持有更久長(zhǎng)觸發(fā)器還會(huì)增加死鎖概率。所以生產(chǎn)環(huán)境觸發(fā)器邏輯一定要短平快重邏輯應(yīng)該丟到應(yīng)用層異步處理。5.2 學(xué)習(xí)建議我個(gè)人建議觸發(fā)器能不用盡量別用尤其在大型分布式系統(tǒng)中會(huì)有維護(hù)成本。但如果你已經(jīng)接手了一套老系統(tǒng)觸發(fā)器滿天飛那想辦法先查清楚它們、做好備份和文檔化就已經(jīng)是很大的價(jià)值輸出。查是所有后續(xù)操作的前提一點(diǎn)虛的都不摻雜。6. 我的實(shí)操體會(huì)總結(jié)最后分享幾個(gè)“如果讓我重新查一遍我一定先做”的小建議。第一善用information_schema別死記硬背SHOW TRIGGERS的選項(xiàng)。我在實(shí)際工作中90%的觸發(fā)器查看都是用information_schema.TRIGGERS完成的因?yàn)樗С秩我庾侄芜^(guò)濾還方便用SQL拼接批量腳本這是SHOW命令不具備的能力。第二備份觸發(fā)器只用SHOW CREATE TRIGGER不要依賴圖形工具導(dǎo)出。我曾經(jīng)嘗試用Navicat的“轉(zhuǎn)儲(chǔ)SQL文件”導(dǎo)出觸發(fā)器結(jié)果發(fā)現(xiàn)它會(huì)帶上DELIMITER和一些版本相關(guān)的特殊字符換庫(kù)執(zhí)行時(shí)容易報(bào)錯(cuò)。直接拿SHOW CREATE TRIGGER的輸出做成一份標(biāo)準(zhǔn)的DDL腳本反而干凈利落。第三查看觸發(fā)器之后順手檢查一下觸發(fā)器里引用的表、字段是否還存在。很多人建完觸發(fā)器后改表結(jié)構(gòu)直接把字段名改掉但觸發(fā)器還在引用舊字段。你平時(shí)不用它沒(méi)事一旦觸發(fā)條件滿足就是一連串報(bào)錯(cuò)。查完觸發(fā)器確認(rèn)一下ACTION_STATEMENT里涉及的相關(guān)對(duì)象都健在能避免很多“莫名其妙”的錯(cuò)誤。細(xì)算下來(lái)我從第一次被觸發(fā)器坑到如今把它玩得明明白白也就是靠多查、多踩、多想這六個(gè)字。這篇文章寫下來(lái)也算是我這些年查觸發(fā)器經(jīng)驗(yàn)的一個(gè)梳理。如果你對(duì)照著操作下來(lái)還有哪里跟你的環(huán)境不一致的多半要把目光放在版本差異和權(quán)限配置上這兩個(gè)變量是最大的“隱形差異項(xiàng)”。希望這篇對(duì)你有用祝大家查庫(kù)愉快少踩坑多省心。