摘要:什么是反模式我很高興地發(fā)現(xiàn)有一個(gè)相當(dāng)全面的關(guān)于反模式的列表,包括來(lái)自編程界及其之外的內(nèi)容。但是要作為一個(gè)反模式,還需要存在替代的解決辦法。
上周我在twitter上討論了ORM,在那以后有人希望我澄清一下。事實(shí)上,我曾經(jīng)寫(xiě)文章討論過(guò)ORM,?但那是時(shí)的上下文是關(guān)于SQL的,我不應(yīng)該把這將兩件事情混為一談。
因此,在本文中我將集中討論ORM本身。同時(shí),我盡力保持簡(jiǎn)潔,因?yàn)槲覐淖约旱腟QL文章中發(fā)現(xiàn):人們一旦讀到讓他們發(fā)怒的內(nèi)容就很容易離開(kāi)(同時(shí)留下一句評(píng)論,而不論他們所關(guān)注的東西是否在后面會(huì)討論到)。
什么是反模式?我很高興地發(fā)現(xiàn)Wikipedia有一個(gè)相當(dāng)全面的關(guān)于反模式的列表,包括來(lái)自編程界及其之外的內(nèi)容。我之所以稱(chēng)ORM為反模式的原因是因?yàn)?,反模式的作者定義了用來(lái)區(qū)分反模式和普通的壞習(xí)慣的兩個(gè)條件,而ORM完全符合這些條件:
它開(kāi)始的時(shí)候看起來(lái)很有用,但是從長(zhǎng)期來(lái)看,壞處要大過(guò)好處
存在已驗(yàn)證并且可重復(fù)的替代方案
由于第一個(gè)因素導(dǎo)致了ORM令人抓狂(對(duì)我來(lái)說(shuō))的流行性:它第一眼看上去像是個(gè)好主意,但是當(dāng)問(wèn)題更加明顯的時(shí)候,已經(jīng)很難離開(kāi)了。
這對(duì)ORM來(lái)說(shuō)是什么意思?我想說(shuō)的主要問(wèn)題在于?ActiveRecord,它由于 Ruby on Rails 而著名,
從那以后已經(jīng)移植到了許多其他語(yǔ)言。然而,這些問(wèn)題同樣存在于其他的ORM層,比如Java的Hibernate和PHP的Doctrine。
ORM的優(yōu)點(diǎn)
簡(jiǎn)單:一些ORM層告訴你它們“消除了對(duì)SQL的要求”。我至今仍然看到這種承諾在傳播。其他一些會(huì)更加現(xiàn)實(shí)地聲稱(chēng)它們可以減少手寫(xiě)SQL的需要,但是仍然允許你在需要的時(shí)候使用它。對(duì)于簡(jiǎn)單的模型以及項(xiàng)目的早期,這確實(shí)是一個(gè)優(yōu)點(diǎn):使用ORM,無(wú)疑你能夠更快地開(kāi)始啟動(dòng)。然而,你將會(huì)走向錯(cuò)誤的方向。
代碼生成:使用ORM從模型中消除用戶(hù)層面的代碼,這一做法開(kāi)啟了通向代碼生成的大門(mén)。通過(guò)對(duì)schema的簡(jiǎn)單描述,“腳手架”模式可以為你的所有表生成一個(gè)可工作的界面。更加具有魔力的是,你可以修改你的schema描述,然后重新生成代碼,從而消除了CRUD。同樣,這在開(kāi)始的時(shí)候確實(shí)是可行的。
性能“足夠好”:我沒(méi)有看到任何ORM層聲稱(chēng)在性能上更加優(yōu)越。很明顯,為了代碼的敏捷性需要付出性能的代碼。如果哪里變慢了,你總是可以用更加有效的手寫(xiě)SQL覆蓋你的ORM方法。不是嗎?
ORM的問(wèn)題不充分的抽象
ORM最明顯的問(wèn)題是它并不能完全從實(shí)現(xiàn)細(xì)節(jié)中抽象出來(lái)。所有主流ORM的文檔中到處都引用了SQL的概念。其中一些介紹的時(shí)候并不會(huì)表明其在SQL中的等價(jià)物,而其他一些則將庫(kù)看作用來(lái)生成SQL的過(guò)程函數(shù)。
抽象的要點(diǎn)在于它應(yīng)該使問(wèn)題得以簡(jiǎn)化。對(duì)SQL進(jìn)行抽象,同時(shí)又要求你懂得SQL,這使得你需要學(xué)習(xí)的東西成倍增加了:首先,你必須理解你正在試圖執(zhí)行的SQL是什么,然后你還要學(xué)習(xí)ORM的API,來(lái)讓它為你編寫(xiě)這些SQL。在Hibernate中,為了完成復(fù)雜的SQL你甚至需要學(xué)第三種語(yǔ)言:HQL,它幾乎就是SQL(但又不完全是),其在幕后被翻譯成SQL。
ORM的支持者會(huì)辯解說(shuō)并非每個(gè)項(xiàng)目都是如此,并非每個(gè)人都需要復(fù)雜的join,并且ORM是一個(gè)"80/20"解決方案,其中80%的用戶(hù)只需要SQL中20%的功能,ORM可以處理這些問(wèn)題。我能說(shuō)的是,我15年來(lái)編寫(xiě)web應(yīng)用的數(shù)據(jù)庫(kù)后端的經(jīng)歷表明,事實(shí)并非如此。只有在項(xiàng)目剛開(kāi)始的時(shí)候你不需要join和本地join。在那之后,你就要優(yōu)化和鞏固你的查詢(xún)。即使80%的用戶(hù)只用到SQL中30%的功能,可是100%的用戶(hù)都需要打破ORM的抽象才能夠完成工作。
不正確的抽象
如果你的項(xiàng)目確實(shí)不需要任何關(guān)系數(shù)據(jù)功能,那么ORM可以非常完美地為你工作。但是接下來(lái)你又遇到另外一個(gè)問(wèn)題:你用錯(cuò)了了數(shù)據(jù)存儲(chǔ)。關(guān)系存儲(chǔ)的額外付出是非常高的;這就是為什么NoSQL數(shù)據(jù)要快得多的重要原因之一。然而,如果你的數(shù)據(jù)是關(guān)系型的,那么額外的付出就是值得的:你的數(shù)據(jù)庫(kù)不僅存儲(chǔ)數(shù)據(jù),它還表達(dá)了你的數(shù)據(jù),并且可以基于關(guān)系概念回答關(guān)于它的問(wèn)題,這比你用過(guò)程代碼能夠做到的要快速得多。
但是,如果你的數(shù)據(jù)不是關(guān)系型的,那么你就是在不適當(dāng)?shù)膱?chǎng)合使用SQL,這為你增加了巨大且不必要的負(fù)擔(dān);為了讓問(wèn)題更加嚴(yán)重,你在其上又增加了一重額外的抽象。
另一方面,如果你的數(shù)據(jù)是關(guān)系型的,那么你的對(duì)象映射最終會(huì)失敗。SQL是關(guān)于關(guān)系代數(shù)的:SQL的輸出不是對(duì)象,而是對(duì)于某個(gè)問(wèn)題的解答。如果你的對(duì)象“是一個(gè)”X的實(shí)例,并且“擁有一些”Y,且每個(gè)Y“屬于”Z,那么對(duì)象在內(nèi)存中正確的表達(dá)形式是什么?
它應(yīng)該是X的屬性,或者全部包含在Y中,或者/并且全部包含在Z中?如果你只得到X的屬性,那么何時(shí)你運(yùn)行查詢(xún)來(lái)獲得Y呢?而且,你是想要其中一個(gè)還是全部?現(xiàn)實(shí)中,答案是依賴(lài)于條件的:這就是為什么我說(shuō)SQL是對(duì)于問(wèn)題的回答。對(duì)象在內(nèi)存中的表達(dá)形式取決于你的意圖,然而面向?qū)ο笤O(shè)計(jì)沒(méi)有依賴(lài)于上下文的表達(dá)這樣的功能。關(guān)系不是對(duì)象;對(duì)象也不是關(guān)系。
多個(gè)查詢(xún)導(dǎo)致失敗
這自然的引出了ORM的另一個(gè)問(wèn)題:效率低下。當(dāng)你獲取一個(gè)時(shí),你需要哪些屬性?ORM并不知道,所以它總是取得全部(或者它要求你告訴它,但是這又打破了抽象)。開(kāi)始的時(shí)候這不成問(wèn)題,但是當(dāng)你一次取出上千條紀(jì)錄的時(shí)候,如果你只需要3個(gè)屬性卻不得不取出全部30列,這時(shí)就產(chǎn)生了嚴(yán)重的性能問(wèn)題。許多ORM層非常不善于推斷join,從而不得不使用分離的查詢(xún)來(lái)獲取關(guān)聯(lián)數(shù)據(jù)。如前所述,許多ORM層明確聲明效率將會(huì)有所犧牲,其中一些提供了某些機(jī)制來(lái)調(diào)整引起問(wèn)題的查詢(xún)。我從過(guò)去的經(jīng)歷中發(fā)現(xiàn)的問(wèn)題表明,很少有只需要調(diào)整單個(gè)“銀彈”查詢(xún)的情況:應(yīng)用的數(shù)據(jù)庫(kù)后端之所以死掉不是因?yàn)槠渲心骋粭l查詢(xún),而是眾多的查詢(xún)引起的。ORM缺少上下文敏感的性質(zhì)意味著它無(wú)法鞏固查詢(xún),相反必須借助cache或其他機(jī)制來(lái)進(jìn)行一定程度的補(bǔ)償。
那么替代方案是什么?希望到這里我已經(jīng)澄清ORM在設(shè)計(jì)上的一些缺陷。但是要作為一個(gè)反模式,還需要存在替代的解決辦法。事實(shí)上有兩個(gè)取代方法:
使用對(duì)象
如果你的數(shù)據(jù)是對(duì)象,那么停止使用關(guān)系數(shù)據(jù)庫(kù)。編程界當(dāng)前正在出現(xiàn)鍵-值對(duì)存儲(chǔ)的浪潮,它允許你以閃電般的速度訪問(wèn)優(yōu)雅的、自我包含的海量數(shù)據(jù)。沒(méi)有法律規(guī)定編寫(xiě)Web應(yīng)用的第一步必須安裝MySQL。對(duì)于對(duì)象的每一種表達(dá)方式都使用關(guān)系數(shù)據(jù)庫(kù)是一種過(guò)度使用,這也是近幾年SQL的名稱(chēng)不太好的原因之一。事實(shí)上,問(wèn)題在于偷懶的設(shè)計(jì)。
在模型中使用SQL
編程中作任何事情都只有一種正確的方式,這是一種危險(xiǎn)的說(shuō)法。然而根據(jù)我的實(shí)踐,在面向?qū)ο蟮拇a中表達(dá)關(guān)系模型的最佳方法仍然是模型層:將你的所有數(shù)據(jù)表示封裝在一個(gè)多帶帶的區(qū)域是一個(gè)好注意。然而,記住模型層的工作簿在于表達(dá)對(duì)象,而在于回答問(wèn)題。提供一個(gè)可以回答你的應(yīng)用程序所包含的問(wèn)題的API,盡量保持簡(jiǎn)潔高效。有時(shí)候,這些回答顯得格格不入,以致于看上去是“錯(cuò)誤的”,甚至對(duì)于資深的OO開(kāi)發(fā)者也是如此。但是,你可以根據(jù)經(jīng)驗(yàn)來(lái)更好地找到其中的普遍性,從而允許你將多個(gè)查詢(xún)方法重構(gòu)為單個(gè)。
類(lèi)似的,有時(shí)候輸出會(huì)是單個(gè)對(duì)象X,它很容易表達(dá)。但是也有時(shí)候輸出是聚合的對(duì)象表格,或者單個(gè)整數(shù)值。你要忍住將這些內(nèi)容用過(guò)多抽象來(lái)包裝的誘惑,用對(duì)象自身的術(shù)語(yǔ)來(lái)描述。首要的是,不要相信OO能夠表達(dá)任何對(duì)象和所有對(duì)象。OO本身是一種優(yōu)美和靈活的抽象,但關(guān)系數(shù)據(jù)在其范圍之外,把它不能表達(dá)的東西偽裝成對(duì)象是ORM的核心與真正的問(wèn)題。
總結(jié)ORM最初比編寫(xiě)基于SQL的模型代碼更快,也更容易理解
它在任何項(xiàng)目早期都是足夠有效的
不幸的是,這些優(yōu)點(diǎn)在項(xiàng)目復(fù)雜性提升的時(shí)候就消失了:抽象被打破,開(kāi)發(fā)者被迫使用并理解SQL
完全是非正式的聲明,我認(rèn)為ORM對(duì)抽象的破壞不是僅僅涉及20%的項(xiàng)目,而是幾乎100%。
對(duì)象并不足以充分表達(dá)關(guān)系查詢(xún)的結(jié)果。
關(guān)系查詢(xún)映射到對(duì)象的不充分性導(dǎo)致了ORM后端應(yīng)用的效率低下,這些問(wèn)題普遍分布在應(yīng)用的各處,并且除了完全放棄ORM之外,沒(méi)有簡(jiǎn)單的解決辦法。
不要對(duì)任何問(wèn)題都使用關(guān)系存儲(chǔ)與ORM,而是更加仔細(xì)地思考你的設(shè)計(jì)
如果你的數(shù)據(jù)天生就是對(duì)象,那么請(qǐng)使用對(duì)象存儲(chǔ)("NoSQL")。它們要比關(guān)系數(shù)據(jù)庫(kù)快得多。
如果你的數(shù)據(jù)天生就是關(guān)系型的,那么關(guān)系數(shù)據(jù)庫(kù)帶來(lái)的開(kāi)銷(xiāo)是值得的。
把你的關(guān)系查詢(xún)封裝在模型層中,設(shè)計(jì)你的API從而為應(yīng)用提供數(shù)據(jù)訪問(wèn)支持;拒絕過(guò)分泛化的誘惑。
面向?qū)ο鬅o(wú)法以有效的形式表達(dá)關(guān)系數(shù)據(jù);這是面向?qū)ο笤O(shè)計(jì)的一個(gè)基本限制,ORM無(wú)法修復(fù)它。
原文 ORM is an anti-pattern
轉(zhuǎn)自 nowamagic.net
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/17435.html
摘要:當(dāng)時(shí),還飽受微軟和太陽(yáng)間的訴訟的影響,該訴訟涉及到和間的兼容性。開(kāi)發(fā)者們都在討論哪個(gè)平臺(tái)或者框架能夠勝出還是微軟新發(fā)布的。能為您提供端到端的應(yīng)用性能解決方案,我們支持所有常見(jiàn)的框架及應(yīng)用服務(wù)器,助您快速發(fā)現(xiàn)系統(tǒng)瓶頸,定位異常根本原因。 【編者按】關(guān)注 NoSQL 的動(dòng)態(tài)發(fā)展很重要。NoSQL 的好處并不僅限于新的應(yīng)用開(kāi)發(fā)。在某些案例中,你可以見(jiàn)識(shí)到重新訪問(wèn)現(xiàn)有的、傳統(tǒng)的框架帶來(lái)的積極效...
摘要:微服務(wù)架構(gòu)的風(fēng)險(xiǎn)微服務(wù)架構(gòu)將應(yīng)用程序邏輯移動(dòng)到服務(wù),并使用網(wǎng)絡(luò)層在它們之間進(jìn)行通信。在微服務(wù)架構(gòu)中,服務(wù)依賴(lài)于彼此。您始終只能部署其中一個(gè),并且在驗(yàn)證新版本是否符合預(yù)期之后才,將負(fù)載均衡器指向新的。 [譯] 設(shè)計(jì)一個(gè)容錯(cuò)的微服務(wù)架構(gòu) 摘要:本文屬于原創(chuàng),歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)保留出處:https://github.com/jasonGeng88/blog 原文地址 https://blog....
摘要:優(yōu)雅的服務(wù)降級(jí)微服務(wù)架構(gòu)最大的優(yōu)點(diǎn)之一就是當(dāng)組件出現(xiàn)故障時(shí),能隔離這些故障并且能做到優(yōu)雅地服務(wù)降級(jí)。 本文首先介紹微服務(wù)架構(gòu)存在的風(fēng)險(xiǎn),然后針對(duì)如何避免微服務(wù)架構(gòu)的故障,提出了多種有效的微服務(wù)架構(gòu)中的方法和技術(shù),其中例如服務(wù)降級(jí)、變更管理、健康檢查和修復(fù)、斷路器、限流器等。 目錄 1、微服務(wù)架構(gòu)的風(fēng)險(xiǎn) 2、優(yōu)雅的服務(wù)降級(jí) 3、變更管理 4、健康檢查和負(fù)載均衡 5、自我修復(fù) 6、故障轉(zhuǎn)移...
閱讀 3893·2021-09-27 13:36
閱讀 4627·2021-09-22 15:12
閱讀 3072·2021-09-13 10:29
閱讀 1840·2021-09-10 10:50
閱讀 2374·2021-09-03 10:43
閱讀 528·2019-08-29 17:10
閱讀 453·2019-08-26 13:52
閱讀 3265·2019-08-23 14:37