文章轉(zhuǎn)載自公眾號(hào)??架構(gòu)師修行之路?,?作者 菜v菜
本文作者:菜v菜 | 觀點(diǎn)整理:徐劉根
一、存儲(chǔ)過程是什么?
存儲(chǔ)過程(Stored Procedure)是在大型數(shù)據(jù)庫系統(tǒng)中,一組為了完成特定功能的SQL 語句集,它存儲(chǔ)在數(shù)據(jù)庫中,一次編譯后永久有效,用戶通過指定存儲(chǔ)過程的名字并給出參數(shù)(如果該存儲(chǔ)過程帶有參數(shù))來執(zhí)行它。存儲(chǔ)過程是數(shù)據(jù)庫中的一個(gè)重要對(duì)象。
二、優(yōu)勢(shì)
1、可以減少程序在調(diào)用DB時(shí)候的信息傳輸量(其實(shí)減少的只有Request的時(shí)候)
2、存儲(chǔ)過程是預(yù)先優(yōu)化和預(yù)編譯的,節(jié)省每次運(yùn)行編譯的時(shí)間,所以一般情況下認(rèn)為存儲(chǔ)過程的性能是優(yōu)于sql語句的。
3、對(duì)調(diào)用者可以隱藏?cái)?shù)據(jù)庫的復(fù)雜性,將數(shù)據(jù)組裝的過程封裝。
4、參數(shù)化的存儲(chǔ)過程可以防止SQL注入式攻擊,而且可以將Grant、Deny以及Revoke權(quán)限應(yīng)用于存儲(chǔ)過程。
5、如果業(yè)務(wù)開發(fā)中,數(shù)據(jù)人員和業(yè)務(wù)代碼人員是分離的,業(yè)務(wù)人員可以不用關(guān)心數(shù)據(jù),直接調(diào)用存儲(chǔ)過程,更加面向分層開發(fā)設(shè)計(jì)理念。
三、存儲(chǔ)過程的劣勢(shì)
1、存儲(chǔ)過程這種“一次優(yōu)化,多次使用”的策略節(jié)省了每次執(zhí)行時(shí)候編譯的時(shí)間,但也是該策略導(dǎo)致了一個(gè)致命的缺點(diǎn):可能會(huì)使用錯(cuò)誤的執(zhí)行計(jì)劃。
2、存儲(chǔ)過程難以調(diào)試,雖然有些DB提供了調(diào)試功能,但是一般的賬號(hào)根本就沒有那種權(quán)限,更何況線上的數(shù)據(jù)庫不可能會(huì)給你調(diào)試權(quán)限的,再進(jìn)一步就算能調(diào)試效果也比程序的調(diào)試效果要差很多。
3、可移植性差,當(dāng)碰到切換數(shù)據(jù)種類的時(shí)候,存儲(chǔ)過程基本就會(huì)歇菜。
4、如果業(yè)務(wù)數(shù)據(jù)模型有變動(dòng),存儲(chǔ)過程必須跟著業(yè)務(wù)代碼一起更改,如果是大型項(xiàng)目,這種改動(dòng)是空前的,是要命的。
四、為什么不推薦存儲(chǔ)過程
以上存儲(chǔ)過程的優(yōu)缺點(diǎn),你隨便一下網(wǎng)絡(luò)就可能查到,表面看來存儲(chǔ)過程的優(yōu)勢(shì)還是不少的,這也說明為什么老一輩程序員有很多喜歡寫存儲(chǔ)過程。但是隨著軟件行業(yè)業(yè)務(wù)日益復(fù)雜化,存儲(chǔ)過程現(xiàn)在在復(fù)雜業(yè)務(wù)面前其實(shí)有點(diǎn)有心無力。
作者在業(yè)務(wù)中并不推薦使用存儲(chǔ)過程。
1、采用存儲(chǔ)過程操作數(shù)據(jù)在網(wǎng)絡(luò)數(shù)據(jù)量傳輸上確實(shí)比直接使用sql語句要少很多,但這通常并不是操作數(shù)據(jù)系統(tǒng)性能的瓶頸,在一次操作數(shù)據(jù)的過程中,假設(shè)用時(shí)100毫秒,采用存儲(chǔ)過程節(jié)省數(shù)據(jù)傳輸時(shí)間0.5毫秒(就算是5毫秒),我覺得這點(diǎn)時(shí)間基本可以忽略。
2、存儲(chǔ)過程是只優(yōu)化一次的,這有時(shí)候恰恰是個(gè)缺陷。有的時(shí)候隨著數(shù)據(jù)量的增加或者數(shù)據(jù)結(jié)構(gòu)的變化,原來存儲(chǔ)過程選擇的執(zhí)行計(jì)劃也許并不是最優(yōu)的了,所以這個(gè)時(shí)候需要手動(dòng)干預(yù)或者重新編譯了,而什么時(shí)候執(zhí)行計(jì)劃不是最優(yōu)的了這個(gè)平衡點(diǎn),預(yù)先無法知曉,這就導(dǎo)致了有些應(yīng)用突然會(huì)變慢,程序員處于懵逼的狀態(tài)。
3、存儲(chǔ)過程確實(shí)可以對(duì)調(diào)用方隱藏?cái)?shù)據(jù)庫的細(xì)節(jié),但是這種業(yè)務(wù)代碼人員和數(shù)據(jù)庫設(shè)計(jì)人員是兩個(gè)團(tuán)隊(duì)的情況又有多少呢,如果真是兩個(gè)團(tuán)隊(duì),那業(yè)務(wù)就需要兩個(gè)團(tuán)隊(duì)來理解和溝通,我想溝通的成本也一定很高,而且分歧更容易產(chǎn)生。
作者認(rèn)為數(shù)據(jù)庫就應(yīng)該做它最擅長的事情:存儲(chǔ)相關(guān)。我不止一次的看過把業(yè)務(wù)寫在存儲(chǔ)過程的情況,程序代碼層面真是薄薄的貧血層,就是一個(gè)數(shù)據(jù)的透傳。我不贊同這種寫法,因?yàn)槲揖徒邮诌^這樣的程序,令我頭疼的不是業(yè)務(wù),而是看著好幾千行的存儲(chǔ)過程熟悉業(yè)務(wù),關(guān)鍵還沒有調(diào)試的權(quán)限(線上更不能調(diào)試)。
一個(gè)業(yè)務(wù)系統(tǒng)的設(shè)計(jì)往往需要你從數(shù)據(jù)庫的層面抽離出來,把主要精力放在業(yè)務(wù)模型的設(shè)計(jì)上,在程序?qū)用骟w現(xiàn)業(yè)務(wù)邏輯,而不是把業(yè)務(wù)邏輯都交給數(shù)據(jù)層面的管理者。
前幾天我排查過一個(gè)“Bug”:存儲(chǔ)過程是輸入?yún)?shù)是一個(gè)主鍵id的列表字符串,長度居然是 nvarchar(max),主要功能是根據(jù)id列表查詢數(shù)據(jù)。我想說的是就算你是max的長度,也有超長的可能性發(fā)生,因?yàn)闃I(yè)務(wù)方傳輸什么參數(shù),參數(shù)什么長度是你DB無法控制的,所以這類的業(yè)務(wù)一定要放在程序中做處理,而不是懷著僥幸心里丟給DB。
如果是抱著存儲(chǔ)過程性能高的心態(tài)的話,我到時(shí)覺你這是誤入歧途,作者認(rèn)為存儲(chǔ)過程從來都不是提高性能的關(guān)鍵,反而系統(tǒng)的架構(gòu),緩存的設(shè)計(jì),數(shù)據(jù)一致性更是系統(tǒng)關(guān)鍵問題。
存儲(chǔ)過程通常是一種解決方案,但是通常情況下不是唯一的解決方案,在選擇存儲(chǔ)過程作為方案前,請(qǐng)確保他們是正確的選擇。
五、其他看法
除了本文作者的觀點(diǎn)之外,小編還給大家整理了一下其他關(guān)于是否要使用從存儲(chǔ)過程的觀點(diǎn):
觀點(diǎn)一:
觀點(diǎn)二:
觀點(diǎn)三:
觀點(diǎn)四:
觀點(diǎn)五:
觀點(diǎn)六:
觀點(diǎn)七:
觀點(diǎn)八:
最后,你覺得哪?歡迎一起交流,說一下你平時(shí)使用存儲(chǔ)過程中的坑或者有哪些更好的建議。
