新聞中心
先說優(yōu)點(diǎn)
畢竟還是有不少人在用,總要有點(diǎn)好處。

SQL 過程化
很少人提及存儲(chǔ)過程的這個(gè)優(yōu)點(diǎn),似乎是認(rèn)為理所當(dāng)然。SQL 的語法要求數(shù)據(jù)處理必須寫成一句,不管嵌套幾層、用多少子查詢,這對復(fù)雜數(shù)據(jù)處理簡直是災(zāi)難。而存儲(chǔ)過程讓 SQL 也能支持分步計(jì)算,雖然是多個(gè)獨(dú)立 SQL 語句拼接、雖然可能要頻繁寫臨時(shí)表、雖然… ,但至少過程化解決了多少人的數(shù)據(jù)處理困難。
但是,過程計(jì)算并不是存儲(chǔ)過程的專利,使用 Java 來指揮 SQL 也一樣可以,僅此一條優(yōu)點(diǎn)并不足以導(dǎo)致程序員偏愛存儲(chǔ)過程。
界面與邏輯分離
界面與邏輯分離是現(xiàn)代應(yīng)用開發(fā)的一個(gè)基本準(zhǔn)則。相對于后臺(tái)數(shù)據(jù)處理邏輯,界面會(huì)有更多樣性的環(huán)境,如 PC、手機(jī)等,而且業(yè)務(wù)穩(wěn)定性也不強(qiáng),經(jīng)常會(huì)改。如果能把兩者分離,開發(fā)和維護(hù)界面時(shí)不必綁著數(shù)據(jù)處理邏輯一起改,成本就低很多。
使用存儲(chǔ)過程能實(shí)現(xiàn)界面與邏輯分離。存儲(chǔ)過程在后臺(tái)數(shù)據(jù)庫中運(yùn)算,只要向前端提供數(shù)據(jù),而不必關(guān)心界面的形式和異動(dòng)。把所有的數(shù)據(jù)處理邏輯都寫成存儲(chǔ)過程,還有利于統(tǒng)一數(shù)據(jù)的出入口,易于實(shí)現(xiàn)數(shù)據(jù)權(quán)限管控。
再說一遍“但是”,實(shí)現(xiàn)界面與邏輯分離也不是存儲(chǔ)過程的專利。只要做一個(gè)數(shù)據(jù)訪問層,所有數(shù)據(jù)的進(jìn)出都通過這個(gè)訪問層,也會(huì)有同樣效果,事實(shí)上也確實(shí)有些應(yīng)用是這么做的,但在微服務(wù)流行以前并不普遍。這個(gè)原因在于開發(fā)復(fù)雜度上,處理結(jié)構(gòu)化數(shù)據(jù)寫 SQL 總比寫 Java 要簡單多得多。而微服務(wù)架構(gòu)強(qiáng)制在 Java 中實(shí)現(xiàn)數(shù)據(jù)處理邏輯的機(jī)制,其實(shí)是犧牲了開發(fā)效率。
性能好
通常,使用存儲(chǔ)過程會(huì)比在庫外用 Java 指揮 SQL 完成數(shù)據(jù)處理性能更好一點(diǎn)。這個(gè)原因主要在于數(shù)據(jù)不出庫。外部程序訪問庫內(nèi)數(shù)據(jù)時(shí)必須通過數(shù)據(jù)庫提供的接口,而這些接口的性能大都不好,特別是面向 Java 程序的 JDBC 接口。每次發(fā)出 SQL 讓數(shù)據(jù)庫執(zhí)行都會(huì)調(diào)用這個(gè)接口,速度就上不去。如果應(yīng)用程序和數(shù)據(jù)庫不在同一臺(tái)物理機(jī)器上時(shí),還會(huì)有一些網(wǎng)絡(luò)延遲,不過和接口的低性能相比并不算嚴(yán)重。在外部計(jì)算時(shí),從數(shù)據(jù)庫獲取數(shù)據(jù)的時(shí)間常常會(huì)超過計(jì)算本身的時(shí)間。
存儲(chǔ)過程的性能好主要得益于數(shù)據(jù)庫低效的訪問接口。
總結(jié)一下。存儲(chǔ)過程的優(yōu)點(diǎn)主要來源于兩方面:一是 SQL 的數(shù)據(jù)處理能力,至少目前來看結(jié)構(gòu)化數(shù)據(jù)處理尤其是復(fù)雜計(jì)算,SQL 仍然比 Java 強(qiáng)很多。二是庫內(nèi)計(jì)算的便利,數(shù)據(jù)不用出庫省下的 IO 成本對數(shù)據(jù)密集型任務(wù)有莫大優(yōu)勢。
再說缺點(diǎn)
傳說阿里有一條軍規(guī),“禁止使用存儲(chǔ)過程,存儲(chǔ)過程難以調(diào)試和擴(kuò)展,更沒有移植性”。我們這就來羅列一下存儲(chǔ)過程的缺點(diǎn)。
移植性差
存儲(chǔ)過程的移植確實(shí)很困難,一般業(yè)務(wù)邏輯復(fù)雜到需要寫存儲(chǔ)過程的地步,總會(huì)不可避免地用到數(shù)據(jù)庫獨(dú)有的特性和語法,更換數(shù)據(jù)庫時(shí)這部分代碼就需要重寫。如果只是簡單地替換函數(shù)名和參數(shù)規(guī)則(如日期轉(zhuǎn)換等),那成本還不高;如果用到了新數(shù)據(jù)庫不支持的某種特性,那還要重新設(shè)計(jì)算法來編寫計(jì)算邏輯;如果還要再兼顧性能因素,有時(shí)候就會(huì)是個(gè)不可能完成的任務(wù)了。
調(diào)試?yán)щy
編輯調(diào)試是個(gè)大問題,存儲(chǔ)過程的開發(fā)一直缺少有效的 IDE 環(huán)境。SQL 本身經(jīng)常很長,調(diào)試式要把句子拆開分別獨(dú)立執(zhí)行,非常麻煩。存儲(chǔ)過程中也常常有大量的長 SQL,當(dāng)然也有同樣的問題。即使是分步的運(yùn)算,因?yàn)闆]有好的 IDE 支持,要看哪一步出錯(cuò),也要把中間結(jié)果輸出才行,仍然是非常麻煩。
存儲(chǔ)過程的調(diào)試功能近年來略有起色,但離流暢實(shí)用始終差距甚遠(yuǎn),數(shù)據(jù)庫廠商似乎也無心解決。這和 Java 等擁有成熟的開發(fā)環(huán)境完全不可同日而語。困難的調(diào)試自然會(huì)導(dǎo)致低下的開發(fā)效率。
體系封閉
說到封閉性,其實(shí)是數(shù)據(jù)庫的問題。數(shù)據(jù)庫有“庫”的概念,外部數(shù)據(jù)只有入庫才能計(jì)算。而現(xiàn)代應(yīng)用數(shù)據(jù)源眾多,臨時(shí)轉(zhuǎn)入的效率很低(因?yàn)閿?shù)據(jù)庫的 IO 成本高),很可能跟不上訪問需求,定時(shí)批量轉(zhuǎn)入又很難獲得最新的數(shù)據(jù),同樣影響計(jì)算結(jié)果的實(shí)時(shí)性。同時(shí),ETL 往往有時(shí)間窗口(如當(dāng)天夜里到第二天凌晨),趕上業(yè)務(wù)繁忙的時(shí)候還可能因?yàn)闀r(shí)間窗口不足無法完成 ETL 工作而影響第二天的業(yè)務(wù)。
不僅如此,把外部數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)庫中,又會(huì)形成眾多中間表,面臨中間表的各種問題。而且有些互聯(lián)網(wǎng)上取過來的數(shù)據(jù)常常是多層的 json 或 XML 格式,在關(guān)系數(shù)據(jù)庫中還要建立多個(gè)關(guān)聯(lián)的表來存儲(chǔ),會(huì)進(jìn)一步加劇中間表的問題,占用過多寶貴的數(shù)據(jù)庫空間。
存儲(chǔ)過程在數(shù)據(jù)庫中運(yùn)算自然繼承了封閉性的特點(diǎn),想混合計(jì)算外部數(shù)據(jù)很不方便。
耦合性高
存儲(chǔ)過程通常是為前端應(yīng)用服務(wù)的,理論上兩者應(yīng)該在一起,從而組成完整的業(yè)務(wù)功能點(diǎn)。但存儲(chǔ)過程與數(shù)據(jù)庫緊密耦合,所以實(shí)踐中存儲(chǔ)過程與前端應(yīng)用是物理分離的,且無法使用統(tǒng)一的技術(shù)路線。對于同一個(gè)功能點(diǎn)的存儲(chǔ)過程和前端應(yīng)用,維護(hù)其中一處,通常就要維護(hù)另一處,但兩者物理上分離,維護(hù)因此變得很困難,而不統(tǒng)一的技術(shù)路線則加劇了這種困難。
存儲(chǔ)過程與數(shù)據(jù)庫緊密耦合,反而與前端應(yīng)用分離,這就容易使同一個(gè)存儲(chǔ)過程被多個(gè)前端應(yīng)用共享。時(shí)間一長,哪個(gè)存儲(chǔ)過程到底被哪些應(yīng)用調(diào)用就變成了謎團(tuán)。如果某個(gè)應(yīng)用的計(jì)算發(fā)生變化,面對謎團(tuán)一般的共享調(diào)用關(guān)系,管理員只能新建存儲(chǔ)過程而不敢修改原存儲(chǔ)過程。這樣惡性循環(huán)下去,存儲(chǔ)過程越來越多,謎團(tuán)越來越大,終將變得不可收拾。
管理困難
存儲(chǔ)過程的目錄是扁平的,而不是文件系統(tǒng)那樣的樹形結(jié)構(gòu),腳本少的時(shí)候還好辦,一旦多起來,目錄就會(huì)陷入混亂??梢韵胂螅鄠€(gè)項(xiàng)目的存儲(chǔ)過程、同一個(gè)項(xiàng)目不同模塊的存儲(chǔ)過程、同一模塊不同年份或不同版本的存儲(chǔ)過程,這些如果混雜在同一個(gè)目錄下,區(qū)分起來會(huì)非常困難,除非加大管理幅度,比如按項(xiàng)目、模塊、年代、版本命名,這顯然又會(huì)影響開發(fā)效率。有些項(xiàng)目管理較弱的團(tuán)隊(duì),在開發(fā)周期緊張時(shí),常常就顧不得這些規(guī)矩了,先趕著讓項(xiàng)目上線再說,而上線之后這些遺留問題又容易被忘掉,結(jié)果常常一直存在很久。
安全性差
很多存儲(chǔ)過程是為查詢分析服務(wù)的,而這類業(yè)務(wù)的需求經(jīng)常在變,由于存儲(chǔ)過程與數(shù)據(jù)庫緊密結(jié)合,所以程序員每次修改存儲(chǔ)過程代碼之后,都要提交給數(shù)據(jù)庫管理員,由管理員編譯并發(fā)布,無疑會(huì)大大增加管理員的工作負(fù)擔(dān)。所以通常的做法是:給程序員賦予高級權(quán)限,至少也是創(chuàng)建存儲(chǔ)過程的權(quán)限,這樣就不用頻繁打擾管理員了。這樣做雖然方便,但存在嚴(yán)重的安全隱患。本來做報(bào)表查詢只需要對數(shù)據(jù)庫有“讀”權(quán)限,而可以編譯存儲(chǔ)過程的權(quán)限就太大了,幾乎可以做一切了。程序員如果失誤,很可能刪除或修改了數(shù)據(jù),造成嚴(yán)重的安全事故。
開源SPL充當(dāng)庫外存儲(chǔ)過程
從羅列的優(yōu)缺點(diǎn)數(shù)量上看,缺點(diǎn)比優(yōu)點(diǎn)多不少,所以就不難理解阿里的軍規(guī)了:存儲(chǔ)過程能不用還是不要用了。當(dāng)然了,用不用存儲(chǔ)過程完全是自己綜合自己的情況考慮,如人飲水冷暖自知。
其實(shí)存儲(chǔ)過程也并非不可替代,開源 SPL 就可以搞定存儲(chǔ)過程的各類缺點(diǎn),同時(shí)延續(xù)其優(yōu)點(diǎn), 實(shí)現(xiàn)“庫外存儲(chǔ)過程”。
集算器 SPL 是一款專業(yè)的開源數(shù)據(jù)計(jì)算引擎,提供不依賴數(shù)據(jù)庫的計(jì)算能力,數(shù)據(jù)庫更換不需要更改 SPL 計(jì)算腳本,解決存儲(chǔ)過程的移植性問題;簡潔易用的 IDE 環(huán)境編輯調(diào)試功能齊全,算法實(shí)現(xiàn)更加簡單;SPL 體系更加開放,可以直接使用多樣數(shù)據(jù)源計(jì)算;“外置存儲(chǔ)過程”不依賴數(shù)據(jù)庫,可隨應(yīng)用存放解決耦合性問題;借助文件系統(tǒng)的樹狀結(jié)構(gòu)進(jìn)一步解決管理問題;SPL 獨(dú)立數(shù)據(jù)庫運(yùn)行,更不會(huì)帶來安全問題。
SPL 在庫外實(shí)現(xiàn)存儲(chǔ)過程,不再依賴數(shù)據(jù)庫,這樣原來綁定數(shù)據(jù)庫帶來的各種問題也就解決了。
直觀易用的開發(fā)環(huán)境
相對存儲(chǔ)過程的編輯調(diào)試?yán)щy,SPL 提供了簡潔易用的開發(fā)環(huán)境,單步執(zhí)行、設(shè)置斷點(diǎn),所見即所得的結(jié)果預(yù)覽窗口…,開發(fā)效率更高。
庫外計(jì)算降低耦合性,提升移植性與安全性
SPL 提供了不依賴數(shù)據(jù)庫的計(jì)算能力,在庫外實(shí)施計(jì)算。原來不得不依賴存儲(chǔ)過程的兩個(gè)能力(計(jì)算和分步)完全可以使用 SPL 替代,實(shí)現(xiàn)“庫外存儲(chǔ)過程”。這樣原本緊耦合在數(shù)據(jù)庫中的計(jì)算邏輯可以完全獨(dú)立到應(yīng)用中,從而降低與數(shù)據(jù)庫的耦合性。
與數(shù)據(jù)庫解耦以后,數(shù)據(jù)庫變化無需修改 SPL 的計(jì)算邏輯,可以做到輕松移植。同時(shí),SPL 實(shí)現(xiàn)的“庫外存儲(chǔ)過程”創(chuàng)建和使用不需要對數(shù)據(jù)庫有寫權(quán)限,使用存儲(chǔ)過程帶來的安全性問題就可以徹底避免。
在運(yùn)維方面,SPL 是解釋執(zhí)行的,天然支持熱切換??梢院芎眠m應(yīng)微服務(wù)架構(gòu)下多變的服務(wù)修改需求,應(yīng)用修改不需要重啟即時(shí)生效。
數(shù)據(jù)處理邏輯位于 SPL 文件(.splx)中,修改后實(shí)時(shí)生效,相對 Java 等編譯型語言需要重啟服務(wù)有很大優(yōu)勢。
在管理方面,SPL 文件使用文件系統(tǒng)管理機(jī)制,樹狀結(jié)構(gòu)可以很清晰地存放各個(gè)應(yīng)用、各個(gè)模塊的計(jì)算邏輯,不會(huì)不知道有哪些模塊在使用的情況,使用和管理都很方便。
多源支持與開放體系
不同于數(shù)據(jù)庫需要數(shù)據(jù)先入庫再計(jì)算,SPL 面對多樣性數(shù)據(jù)源時(shí)可以直接計(jì)算。數(shù)據(jù)入庫不僅時(shí)效性差,也無法保證數(shù)據(jù)的實(shí)時(shí)性。此外不同數(shù)據(jù)源有各自的優(yōu)點(diǎn),文件的 IO 效率很高,NoSQL 可以存儲(chǔ)文檔數(shù)據(jù),RDB 計(jì)算能力較強(qiáng),數(shù)據(jù)入庫就無法享受這些優(yōu)點(diǎn)了。
SPL 提供了開放的數(shù)據(jù)源支持,你聽說過還是沒聽說過的數(shù)據(jù)源幾乎都能支持,不僅可以連接取數(shù),還可以進(jìn)行跨數(shù)據(jù)源混合計(jì)算。SPL 可以充分利用各類數(shù)據(jù)源的優(yōu)點(diǎn)后,再實(shí)施跨源計(jì)算也更加高效。
開放體系下,不再有“庫”的概念,充分利用各類數(shù)據(jù)源的特點(diǎn),發(fā)揮其優(yōu)勢。
支持過程的簡潔代碼提高開發(fā)效率
存儲(chǔ)過程雖然支持過程計(jì)算,但 SQL 本身在實(shí)現(xiàn)復(fù)雜計(jì)算時(shí)就比較困難,這是由 SQL 的特性(缺乏離散性、集合化不徹底等)決定的,比如根據(jù)股票記錄查詢某只股票最長連續(xù)上漲天數(shù),SQL(oracle)的寫法如下:
SELECT code, MAX(ContinuousDays)-1
FROM (
SELECT code, NoRisingDays, COUNT(*) ContinuousDays
FROM (
SELECT code,
SUM(RisingFlag) OVER (PARTITION BY code ORDER BY day) NoRisingDays
FROM (
SELECT code, day,
CASE WHEN price>
LAG(price) OVER (PARTITION BY code ORDER BY day)
THEN 0 ELSE 1 END RisingFlag
FROM stock
)
) GROUP BY NoRisingDays
)
GROUP BY code
可以嘗試讀一下這個(gè) SQL 在算什么。是不是很繞?其實(shí)按照分步解法,只需要 3 步就能搞定,根本不用這么繞來繞去。
而存儲(chǔ)過程是用 SQL 實(shí)現(xiàn)的自然繼承了這個(gè)缺點(diǎn),我們經(jīng)常能在項(xiàng)目中看到上千行、上百 KB 存儲(chǔ)過程的情況,就是因?yàn)?SQL 對復(fù)雜計(jì)算支持不好的緣故。
SPL 不僅支持天然支持過程計(jì)算,數(shù)據(jù)處理可以分多步、按照自然思維一步一步實(shí)施,而且提供了豐富的計(jì)算類庫和敏捷語法,基于 SPL 可以更容易實(shí)現(xiàn)復(fù)雜計(jì)算。
上一段代碼看一下效果:
【計(jì)算目標(biāo)】要找出銷售額占到一半的前 n 個(gè)客戶(大客戶)的訂單情況。
|
A | |
|
1 |
=file(“/opt/ods/orders.csv”).import@tc() |
|
2 |
=A1.groups(customer;sum(amount):amount).sort(amount:-1) |
|
3 |
=A2.sum(amount)/2 |
|
4 |
=0 |
|
5 |
=A2.pselect((A4=A4+amount,A4>=A3)) |
|
6 |
=A2.(customer).to(,A5) |
|
7 |
=A1.select(A6.pos(A1.customer)) |
通過分步的方式,先找到符合條件的大客戶,再查詢這些客戶的詳細(xì)訂單信息。這些計(jì)算都是在庫外完成的,甚至可以使用文件數(shù)據(jù)源。從實(shí)現(xiàn)的過程來看,SPL 的過程計(jì)算比存儲(chǔ)過程更加優(yōu)秀,語法也更為簡潔。
而前面提到的計(jì)算某只股票最長連續(xù)上漲天數(shù),SPL 的實(shí)現(xiàn)是這樣的:
|
A | |
|
1 |
=db.query("select * from stock order by day") |
|
2 |
=A1.group@i(price |
按交易日排好序,將連漲的記錄分到一組,然后求最大值 -1 就是最長連續(xù)上漲天數(shù)了,完全按照自然思維實(shí)現(xiàn),不用繞來繞去。SQL 雖然比 Java 在某些場合方便了點(diǎn),但仍然差很多,SPL 不僅支持過程計(jì)算,實(shí)現(xiàn)復(fù)雜計(jì)算也更簡單,比 SQL 更有優(yōu)勢。
降低數(shù)據(jù)庫負(fù)擔(dān)并獲得更高性能
不過,SPL 是一種庫外計(jì)算引擎,需要將數(shù)據(jù)從數(shù)據(jù)庫中取出來再計(jì)算。這樣在涉及數(shù)據(jù)量較大時(shí),可能會(huì)因?yàn)閿?shù)據(jù)庫 IO 性能低下導(dǎo)致低性能。
這一點(diǎn),SPL 也有應(yīng)對方法,可以實(shí)現(xiàn)數(shù)據(jù)外置。將數(shù)據(jù)庫中大量歷史冷數(shù)據(jù)外置到文件中,使用 SPL 讀取文件直接計(jì)算,這樣不僅可以降低數(shù)據(jù)庫負(fù)擔(dān)(數(shù)據(jù)庫不再需要承擔(dān)過多的數(shù)據(jù)存儲(chǔ)和數(shù)據(jù)計(jì)算工作自然壓力降低),基于文件還可以獲得更高的 IO 效率。
將大歷史數(shù)據(jù)外置后,借助 SPL 的多源混算能力,還很容易實(shí)現(xiàn) T+0 查詢。從數(shù)據(jù)庫中讀取當(dāng)期熱數(shù)據(jù),從文件中讀取歷史冷數(shù)據(jù),二者混合計(jì)算完成 T+0 全量實(shí)時(shí)數(shù)據(jù)查詢。
此外,使用 SPL 還能獲得更高的運(yùn)算性能。
SPL 提供了眾多高性能數(shù)據(jù)存儲(chǔ)和高性能算法機(jī)制,SQL 中很難實(shí)現(xiàn)的高性能算法及存儲(chǔ)方案用 SPL 卻可以輕松實(shí)現(xiàn),而軟件提高性能關(guān)鍵就在于算法和存儲(chǔ)。
例如,SPL 可以把 TopN 理解為聚合運(yùn)算,這樣可以將高復(fù)雜度的排序轉(zhuǎn)換成低復(fù)雜度的聚合運(yùn)算,而且很還能擴(kuò)展應(yīng)用范圍。
|
A |
||
|
1 |
=file(“data.ctx”).create().cursor() |
|
|
2 |
=A1.groups(;top(10,amount)) |
金額在前 10 名的訂單 |
|
3 |
=A1.groups(area;top(10,amount)) |
每個(gè)地區(qū)金額在前 10 名的訂單 |
相比之下,SQL 描述 TopN 會(huì)涉及大排序,性能非常低下,只能寄希望于數(shù)據(jù)庫的優(yōu)化。但在稍復(fù)雜的情況(比如 A3 中伴隨分組運(yùn)算)數(shù)據(jù)庫優(yōu)化器就會(huì)失效。
總體看來,SPL 可以作為存儲(chǔ)過程很好的替代和延伸。
分享名稱:傳說阿里禁止使用存儲(chǔ)過程?
文章來源:http://fisionsoft.com.cn/article/dpoojdj.html


咨詢
建站咨詢
