溫立輝
(河源職業技術學院 電子與信息工程學院,廣東 河源 517000)
觸發器是關系數據庫進行數據維護的一種重要機制[1],其能實現比關系數據自身攜帶CHECK函數更加強大的功能,目前主流的關系型數據庫均支持這一重要的數據維護機制,觸發器的應用存在于各種各樣的業務場景中。
觸發器作為一種數據維護機制,既不同于關系數據庫自身所擁有的函數,也不同于關系數據庫中的存儲過程,而是一種基于事件與時間作為監測點的功能代碼塊。文獻[2-6]描述了觸發器的底層原理及相關的實現機制,闡述了觸發器的功能作用及應用場景。從廣泛意義上來說,觸發器也是屬于關系數據庫中的功能函數,用戶可以自行定義相關的觸發器函數,但此類型的自定義函數不能帶有任何的參數,這是與一般函數的重要區別。同樣,從廣義上來說,觸發器也是關系數據庫中一種特殊的存儲過程,但與一般存儲過程有一個非常重要的區別就是,存儲過程需要顯式的被調用,而觸發器則是無須被系統或用戶顯式調用,只要當相關事件發生時就能自動觸發執行相關觸發器的功能代碼語句,從這個意義上來說觸發器是自動被執行,不需要手動調用。
觸發器存在于關系數據庫服務器端[7],其功能除了數據完整性約束之外,還能實現數據審計,數據表級聯操作等功能。所謂的數據審計是指跟蹤關系數據庫業務表中數據的變化過程,以審核數據變化過程是否合法,是否存在不規范的操作,以保證業務數據的正當、準確、合理。數據表級聯操作是指業務數據表之間是一個一個統一的有機整體,而不是獨立、分散的個體,當某張數據表變化時會引起與此表相關聯的相關數據表的同時變化。如,在一個用戶模塊中,當從用戶表刪除一條用戶記錄時,也必需從其他的業務表中刪除此用戶訂閱的相關業務記錄,否則業務系統會出異常。
觸發器是作用于數據表上的一種操作監測機制[1],在關系數據庫中存在多種類型,一般來說常見的是關系數據庫管理語言中(Data Manipulation Language,DML)中的插入、刪除、更新3種操作類型的觸發器,在對數據表進行insert,delete,update3種操作會觸發對應類型的觸發器。
觸發器的觸發時間點有之前(before)與之后(after)兩種,當定義了觸發時間點為before時,則會在對數據表的操作發生之前先行觸發數據表上的觸發器,觸發器功能代碼執行完畢后才能執行增、刪、改的數據操作;after類型則恰好相反,在對數據表執行完增、刪、改操作后才觸發數據表上的觸發器功能代碼。
不言而喻觸發器是一種強大的數據維護機制,在關系數據庫的底層其采用的是逐行檢查以及臨時表的方式實現其強大的數據維護功能。
逐行檢查觸發器的一個基本原則,當一個數據表的某個字段做了某項自定義約束時,如會員表的年齡字段為必須大于18,當某個業務操作要修改會員表中某一條記錄的年齡字段值時,則觸發器會檢查整個會員表中每一行記錄的年齡字段的值是否有小于18的情況,如有回滾此次操作。這一原則當然能保證數據約束的完整功能,但同時也會引入其他的問題,如性能問題;在業務表中數據量比較大的情況下,逐行檢查嚴重影響數據庫管理系統性能。
使用臨時表是觸發器另一重要原則,當對業務表進行增、刪、改的操作進而觸發相應的觸發器時,觸發器會對操作將觸動的業務數據在臨時表中進行備份,以供后面所需的回滾、恢復操作。觸發器中存在有插入臨時表(insert表)與刪除臨時表(delete表)兩種。
當外部要往數據表中插入新的數據時,觸發器首先會往insert臨時表中插入新數據,然后再往目標數據表插入相關數據,新數據進入目標數據表后再檢查新插入的數據是否合法,如合法則直接刪除insert臨時表中的數據,如不合法則按insert臨時表保存的數據去刪除目標數據表中新進入的數據。
當外部要從業務數據表刪除數據時,觸發器則會先把要刪除的舊數據插入delete臨時表進行數據備份,然后再從目標數據表刪除相關數據,最后檢查刪除操作是否合法,如果合法則直接從delete臨時表中刪除之前備份的數據,如不合法則用備份好的數據恢復到原目標數據表中。
當外部要從業務數據表修改某條業務數據時,觸發器則會往兩張臨時表中備份數據。首先,把目標數據表中要被修改的舊數據備份到delete臨時表;然后,再把修改后的數據同時寫入目標數據表與insert臨時表;最后,檢查操作是否合法,如果合法則刪除兩張臨時表中的數據,反之如果不合法,則按insert臨時表的數據去刪除目標數據表數據,并把delete臨時表備份數據恢復到目標數據表中。
從逐行檢查與使用臨時表兩個原則中可知,觸發器在一定程度會影響到數據庫性能,因而在使用觸發器前一定要嚴格謹慎,如果有其他更好的解決方案則盡可能不使用觸發器。
凡事物都是具有兩面性的,雖然觸發器的使用會在一定程度影響系統性能,但是觸發器還是非常廣泛地應用于很多的業務場景中,只要使用得當,考慮周全,觸發器有非常廣闊的天地,如以下兩個場景中,就可以考慮使用觸發器。
在一個電子商城平臺中需開發一個秒殺搶購功能,按常規的思維,Web應用服務先從數據表中讀取庫存數據,再通過判斷其是否大于0進而結論是否成功秒殺到。在秒殺過程中當庫存數剩余為1時,因為平臺上有多個Web節點,所以可能多個線程同時讀取庫存數為1并多個線程同時下結論成功搶到,但1件商品是不可能同時被多個人搶到的,因而常規思維走不通。
此時就要從后臺數據庫入手,可考慮在數據庫的商品表的庫存數量字段加個約束讓其不能小于0,一般的數據庫中可考慮使用CHECK函數進行約束就可以了,但MySQL中不支持CHECK函數功能,因而無法使用這一方式,必須使用觸發器來對這個庫存數量字段加一個不能小于0的約束,當秒殺開始時對庫存數量的修改使用如下的累減原子操作SQL語句“UPDATE 商品表 SET 庫存數量=庫存數量-1”即可解決上面的問題。
交通違章管理系統記錄著所有車輛交通違章信息,只有內部人員能操作系統中的相關數據,但如果有工作人員違規私下修改或刪除系統里面數據,那就是一個很大的漏洞。
針對這一問題就可以考慮使用觸發器對相關的業務數據表作數據的跟蹤審計,記錄下數據變化全過程,比如,哪個用戶在什么時間基于什么理由修改了什么數據,這樣業務系統中整個數據變化過程就能正常的追蹤,堵塞了管理上的重大漏洞。
觸發器作為一種獨立的機制,其強大功能與優勢是顯而易見的,缺少觸發器機制的關系數據庫是不完整的。觸發器雖然如此強大與重要,但決不能亂用,更不能濫用,如果使用不當極容易使數據庫系統變得性能、效率低下。使用觸發器需堅持一個原則,那就是:必需、必要、數據量小。