把業(yè)務(wù)邏輯寫入應(yīng)用程序,而不是數(shù)據(jù)庫(kù)
我們都有過(guò)這樣的經(jīng)歷,從SQL數(shù)據(jù)庫(kù)中提取數(shù)據(jù),而需要提取數(shù)據(jù)的查詢是一個(gè)復(fù)雜的查詢,有多個(gè)表連接、過(guò)濾條件和復(fù)雜的WHERE語(yǔ)句。像MySQL和PostgreSQL這樣的數(shù)據(jù)庫(kù),非常擅長(zhǎng)執(zhí)行復(fù)雜的連接、過(guò)濾和排序,以便從查詢中準(zhǔn)確獲得所需的數(shù)據(jù)。
如果在數(shù)據(jù)庫(kù)中執(zhí)行這些操作,所做的實(shí)際上是將業(yè)務(wù)邏輯從應(yīng)用程序里抽出,并將其移到數(shù)據(jù)庫(kù)邏輯當(dāng)中。最終你會(huì)使用更多的數(shù)據(jù)庫(kù)資源和更少的應(yīng)用服務(wù)器資源,來(lái)實(shí)現(xiàn)所期望的數(shù)據(jù)檢索結(jié)果。
在實(shí)現(xiàn)數(shù)據(jù)插入要求時(shí),也是如此。在執(zhí)行數(shù)據(jù)插入之前,不必驗(yàn)證所插入的數(shù)據(jù)是否有效、無(wú)沖突且在應(yīng)用程序中完全正確,而是讓數(shù)據(jù)庫(kù)執(zhí)行驗(yàn)證,將沖突標(biāo)準(zhǔn)放入數(shù)據(jù)庫(kù)模式需求中,并讓數(shù)據(jù)庫(kù)在插入的數(shù)據(jù)中拋出錯(cuò)誤。
應(yīng)用程序資源更容易擴(kuò)展
在某些情況下,你需要數(shù)據(jù)庫(kù)做這些類型的驗(yàn)證和檢查。并不是說(shuō),不要讓你的數(shù)據(jù)庫(kù)做任何應(yīng)用邏輯。而是,應(yīng)該讓數(shù)據(jù)庫(kù)盡可能少地做應(yīng)用業(yè)務(wù)邏輯,盡可能多地在應(yīng)用程序代碼本身中執(zhí)行應(yīng)用業(yè)務(wù)邏輯。
一般來(lái)說(shuō),擴(kuò)展應(yīng)用服務(wù)器資源比擴(kuò)展數(shù)據(jù)庫(kù)服務(wù)器資源要容易得多。
對(duì)于大多數(shù)網(wǎng)絡(luò)應(yīng)用程序而言,當(dāng)流量增加時(shí),通??梢暂p松地增加應(yīng)用服務(wù)器,來(lái)處理增加的負(fù)載。但是,除非數(shù)據(jù)庫(kù)也能擴(kuò)展到處理增加的負(fù)載,否則額外的應(yīng)用服務(wù)器并沒(méi)有什么幫助。
觀察應(yīng)用程序的可用資源。您應(yīng)該將數(shù)據(jù)庫(kù)看作是由稀缺的計(jì)算資源組成的,而應(yīng)用程序代碼層(包括服務(wù))是由隨時(shí)可用的計(jì)算資源組成的,這些計(jì)算資源可以很容易地?cái)U(kuò)展。
數(shù)據(jù)庫(kù)資源是稀缺的,計(jì)算資源隨時(shí)可用。
一旦你能理解這種思維方式,你就會(huì)意識(shí)到在應(yīng)用程序?qū)又?,放置盡可能多的邏輯,將有助于更容易地?cái)U(kuò)展。而將業(yè)務(wù)邏輯放在數(shù)據(jù)庫(kù)層會(huì)極大地限制該業(yè)務(wù)邏輯的可伸縮性。
讓數(shù)據(jù)庫(kù)處理復(fù)雜的過(guò)濾問(wèn)題
不可能總是把所有的業(yè)務(wù)邏輯放在應(yīng)用層。有時(shí),你必須把某些查詢放在數(shù)據(jù)庫(kù)里。這樣做的一個(gè)有效原因,可能是為了控制返回結(jié)果的數(shù)量。假設(shè)一個(gè)查詢?cè)谝粋€(gè)非常大的數(shù)據(jù)庫(kù)表上有一個(gè)復(fù)雜的過(guò)濾條件:

在大型數(shù)據(jù)集上,執(zhí)行復(fù)雜的過(guò)濾查詢后,預(yù)期的結(jié)果可能只有表中的幾條記錄。*將從這幾行返回所有數(shù)據(jù)。但是,如果希望在應(yīng)用層,而不是數(shù)據(jù)庫(kù)中,執(zhí)行復(fù)雜的過(guò)濾查詢,則通常需要先從數(shù)據(jù)庫(kù)中檢索所有的數(shù)據(jù)。你最終可能會(huì)出現(xiàn)這樣的結(jié)果:

這將把mytable中所有數(shù)據(jù)返回給應(yīng)用層。然后,應(yīng)用層將扔掉任何沒(méi)有通過(guò)它在數(shù)據(jù)上執(zhí)行的過(guò)濾條件的行。這樣做的問(wèn)題是,為了執(zhí)行這個(gè)需求,mytable的全部?jī)?nèi)容都必須被轉(zhuǎn)移到應(yīng)用層。對(duì)于大型數(shù)據(jù)集來(lái)說(shuō),這是不可接受的。
在許多情況下,簡(jiǎn)單地重構(gòu)一個(gè)查詢或一系列查詢,可以避免此類問(wèn)題,并允許你在應(yīng)用程序中執(zhí)行更多的邏輯,而不會(huì)產(chǎn)生不必要的數(shù)據(jù)流量。做到這一點(diǎn)的一個(gè)方法是將數(shù)據(jù)過(guò)濾與數(shù)據(jù)檢索分離開(kāi)來(lái)。
過(guò)濾與檢索分離
我們經(jīng)常把過(guò)濾結(jié)果和檢索結(jié)果的概念合并到單個(gè)查詢中。特別是當(dāng)我們查看有很多數(shù)據(jù)的大表時(shí),編寫一個(gè)查詢,執(zhí)行所有的過(guò)濾和規(guī)范,選擇我們需要的行,然后讓查詢從選擇的行中,返回需要的所有數(shù)據(jù),這是非常方便的。

就其本身而言,這似乎沒(méi)什么問(wèn)題。但是,當(dāng)查詢涉及到復(fù)雜的連接或其他復(fù)雜的操作時(shí),它就會(huì)給數(shù)據(jù)庫(kù)帶來(lái)不小的負(fù)荷,使數(shù)據(jù)庫(kù)資源緊張。
解決這個(gè)問(wèn)題的方法是執(zhí)行一個(gè)初始查詢,只簡(jiǎn)單地從所有行中返回過(guò)濾查詢所需的字段,然后在應(yīng)用程序中執(zhí)行過(guò)濾邏輯。讓我們假設(shè)field1和field2是上述<復(fù)雜的過(guò)濾查詢>中實(shí)際涉及的字段。所以,讓我們?cè)诔跏疾樵冎兄猾@得這些數(shù)據(jù):

然后,我們可以在你的應(yīng)用程序代碼中對(duì)field1和field2執(zhí)行復(fù)雜過(guò)濾器查詢邏輯。這將導(dǎo)致我的表格中符合復(fù)雜查詢的行的ID列表。一旦你有了匹配的ID列表,你就可以執(zhí)行后續(xù)的查詢,從預(yù)過(guò)濾的行中獲取實(shí)際數(shù)據(jù):

這兩個(gè)查詢都是非常簡(jiǎn)單的查詢,不需要在數(shù)據(jù)庫(kù)中進(jìn)行復(fù)雜的操作。選擇返回?cái)?shù)據(jù)的必要業(yè)務(wù)邏輯是在應(yīng)用層執(zhí)行的,但需要從數(shù)據(jù)庫(kù)傳輸?shù)綉?yīng)用的無(wú)關(guān)數(shù)據(jù)很少。通過(guò)將查詢分割成獨(dú)立的過(guò)濾和數(shù)據(jù)檢索查詢,你已經(jīng)重構(gòu)了你的請(qǐng)求,并允許復(fù)雜的、資源密集的業(yè)務(wù)邏輯在應(yīng)用程序中執(zhí)行,而不是數(shù)據(jù)庫(kù)。
避免對(duì)返回的結(jié)果進(jìn)行操作
將業(yè)務(wù)邏輯從數(shù)據(jù)庫(kù)移到應(yīng)用程序?qū)拥牧硪环N簡(jiǎn)單方法是避免對(duì)數(shù)據(jù)庫(kù)中返回的結(jié)果執(zhí)行計(jì)算;而是在應(yīng)用程序中執(zhí)行它們。因此,不要做這樣的事情。

你可以這樣做:

然后,在應(yīng)用程序中對(duì)返回的結(jié)果執(zhí)行POWER(SQRT(field1)*SIN(field2),5)的等效計(jì)算。其結(jié)果是,執(zhí)行計(jì)算所需的所有計(jì)算都利用了現(xiàn)成的應(yīng)用程序資源,而不是稀缺的數(shù)據(jù)庫(kù)資源。
將連接轉(zhuǎn)移到應(yīng)用層
復(fù)雜的連接是需要大量數(shù)據(jù)庫(kù)資源。與其在數(shù)據(jù)庫(kù)中連接數(shù)據(jù),不如將盡可能多地連接邏輯轉(zhuǎn)移到應(yīng)用層中。以這種方式重構(gòu)代碼,可以顯著減少數(shù)據(jù)庫(kù)的負(fù)載,同時(shí)提高可伸縮性。
當(dāng)然,不可能總是以這些方式重構(gòu)查詢。有時(shí),只需要在數(shù)據(jù)庫(kù)本身中執(zhí)行一個(gè)復(fù)雜的查詢。但是,通過(guò)盡可能多地刪除這些復(fù)雜的查詢,你可以減少對(duì)稀缺的數(shù)據(jù)庫(kù)資源的依賴,增加對(duì)高可用的應(yīng)用級(jí)資源的依賴。
因此,下次當(dāng)你看到使用多個(gè)連接和復(fù)雜過(guò)濾邏輯的大型、長(zhǎng)且復(fù)雜的查詢時(shí),不要為此感到困難。相反,考慮用更簡(jiǎn)單的查詢(可能是多個(gè)查詢)和在應(yīng)用程序?qū)又袌?zhí)行的業(yè)務(wù)邏輯替換它的方法。
隨著應(yīng)用程序的擴(kuò)展,你會(huì)感受到改進(jìn)的靈活性。