存儲過程的優劣
存儲過程是一組實現特定功能的SQL語句集合,存儲過程一經編譯便存儲在了服務器上,可以通過調用存儲過程的名字以及傳入相應的參數來使用存儲過程。要高層次的掌握存儲過程,不能覺得依葫蘆畫瓢,覺得造出來的存儲過程能夠跑出結果就OK。一定要站在一定的高度,看清它的全貌:
選擇使用存儲過程的優勢
- 執行效率快; 存儲過程工作於服務器中,距離數據最近,因此對數據的操作快,和一般SQL語句比,它無需網絡通信開銷,解析開銷,和優化器開銷等。
- 實現代碼重用,接口處理邏輯一致; 存儲過程可以理解為一種抽象級比較低或者說比較具體的函數。因此,可以實現供系統多次反復調用,實現了代碼的重用,保證了系統業務處理的一致性。
- 可以簡化代碼的維護和版本更新;存儲過程部署在服務器端,因此可以所有的備份和維護都可以在服務器上進行。而且如果一些業務邏輯做出修正,也可以在服務上修正存儲過程,一定程度上減少了客戶端的升級次數。
- 提升安全,實現更細粒度的權限控制。
使用存儲過程的弊端
- 缺乏行之有效的開發和調試工具;存儲過程的開發和調試與應用程序代碼是無法相提並論的,感覺存儲過程的調試更像單純的使用“打印調試”。因此,開發效率和調試上有一定的折扣。
- 實現業務 邏輯的靈活度和自由度略差;這點主要是和應用程序開發作比較的,存儲過程中沒有應用程序語言中豐富多彩的函數,因此用起來總感覺需要一些“巧妙的設計”在里面,對於發燒的字符串操作和維護,使用起來並沒有編程序言那樣輕松自如。
- 對數據庫服務器增加了額外的壓力,同時數據庫服務器的擴展性也相對較差。
- mysql中並沒有什么選項對存儲過程的資源消耗進行控制,如果存儲過程中出現了一個錯誤,可能會對整個數據庫服務器造成較大的影響。
存儲過程定義細節
存儲過程在定義的時候,有一些關鍵字的含義以及語句還是需要搞明白的好,諸如分隔符的臨時重定義,調用存儲過程的權限控制等。
在sqllog等這些工具中新建一個存儲過程就會出現一下屬性和定義:
DELIMITER $$
CREATE
/*[DEFINER = { user | CURRENT_USER }]*/
PROCEDURE `my_db`.`heat_test1`()
/*LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'*/
BEGIN
END$$
DELIMITER ;
重新定義分隔符"; "
在mysql的客戶端中向服務器傳送存儲過程的時候,如果不重新定義分號,mysql服務器接收到第一個分號的時候,會以為這條語句傳送完畢,因此分割了整個存儲過程;為了能夠完整的將存儲過程傳送到服務器,務必需要暫時定義mysql的分隔符即分號;
使用 delimiter 關鍵字可以重新定義分隔符,比如 mysql> delimiter //;當然可以想使用你喜歡的字符代替“//”,但是不要使用“\”,因為該符號在mysql中是轉移字符。
當整個存儲過程傳送完畢后,一定要記得將分隔符重新定義為分號。
選擇合適的接入控制權限
對於每一個存儲過程,可以有一個“DEFINER”的屬性,這個屬性是一個mysql中的賬戶名,比如root等。這個屬性幫助在調用存儲過程的時候,根據上下文的環境確定其權限。如果這個屬性沒有明確給出,那么mysql默認為創建該存儲過程的用戶為這個屬性的值。
此外,存儲過程中還有一個SQL SECURITY { DEFINER | INVOKER }來決定使用存儲過程的定義者或者是調用者的上下文環境。如果是DEFINER,那么這個存儲過程在調用的時候就會擁有創建者指定賬戶的權限,如果是INVOKER,那么執行這個存儲過程的時候就會擁有調用者的權限。
CREATE DEFINER = 'admin'@'localhost' PROCEDURE p1()
SQL SECURITY DEFINER
BEGIN
UPDATE t1 SET counter = counter + 1;
END;
上面的存儲過程的SQL SECURITY定義的是DEFINER,因此對於任何有權限調用p1的用戶都可以用call來調用該存儲過程,但是在存儲過程執行的過程中,上下文環境將切換到'admin'@'localhost'用戶下,也就是說調用者的權限對此存儲過程無效(調用者可以沒有對t1表updata權限,只要'admin'@'localhost'有即可),在這個存儲過程執行的時候,所擁有的權限是'admin'@'localhost'所擁有的。
CREATE DEFINER = 'admin'@'localhost' PROCEDURE p2()
SQL SECURITY INVOKER
BEGIN
UPDATE t1 SET counter = counter + 1;
END;
當存儲過程p2的SQL SECURITY定義為INVOKER的時候,這個時候存儲執行的上下文環境將在調用者上下文環境中執行,也就是說,這個時候調用者的權限必須要有對t1表的訪問和修改權限。
存儲過程中,DEFINER屬性的設置規則以及提高安全的策略
- 1 你只可以將自己的賬戶名設置為DEFINER的屬性假如沒有超級權限,如果有超級權限才可以設置其他用戶的賬戶名作為該屬性的值。
- 2 應該盡量使用SQL SECURITY INVOKER來定義存儲過程,這樣會規避不必要的越限。
- 3 如果使用SQL SECURITY DEFINER,而且指定的賬戶具有超級權限,再三分析是否必須這樣。
- 4 管理員為了限制用戶隨意指定DEFINER的屬性為超級權限,應該謹慎的分配給用戶超級權限。
通過這樣的權限設置,可以實現更細粒度以及更自由的權限設置。同時,基於安全的問題也要時時考慮到。
參考文獻
[1] 高性能mysql
[2] mysql官方網站
