轉(zhuǎn)】提高Ruby on Rails性能的幾種技 巧
Ruby on Rails以其高度的易用性和靈活性著稱,不過(guò)這些優(yōu)點(diǎn)的背后還存在著性能的隱患。最近,資深Ruby on Rails作家David Berube提供了幾個(gè)Ruby on Rails性能優(yōu)化的技巧,對(duì)相關(guān)開發(fā)人員具有一定的借鑒意義。
Ruby on Rails以其高度的易用性和靈活性著稱,不過(guò)這些優(yōu)點(diǎn)的背后還存在著性能的隱患。最近,資深Ruby on Rails作家David Berube提供了幾個(gè)Ruby on Rails性能優(yōu)化的技巧,對(duì)相關(guān)開發(fā)人員具有一定的借鑒意義。
David Berube在文章中首先分析了Rails應(yīng)用運(yùn)行緩慢的原因:
Rails總是會(huì)做一些假設(shè)為您加速開發(fā)。通常,這種假設(shè)是正確而有幫助的。不過(guò),它們并不總能有益于性能,并且還會(huì)導(dǎo)致資源使用的效率低下——尤其是數(shù)據(jù)庫(kù)資源。
另一個(gè)顯著的挑戰(zhàn)是N+1問(wèn)題......這會(huì)導(dǎo)致很多小查詢的執(zhí)行,而不是一個(gè)單一的大查詢。例如,ActiveRecord無(wú)從知道一組父記錄中的哪一個(gè)會(huì)請(qǐng)求一個(gè)子記錄,所以它會(huì)為每個(gè)父記錄生成一個(gè)子記錄查詢。由于每查詢的負(fù)荷,這種行為將導(dǎo)致明顯的性能問(wèn)題。
由于ActiveRecord 能夠讓如此眾多的任務(wù)變得輕而易舉,Rails開發(fā)人員常常會(huì)形成 “SQL 不怎樣” 的一種態(tài)度,即便在更適合使用SQL的時(shí)候,也會(huì)避免SQL。創(chuàng)建和處理數(shù)量巨大的ActiveRecord對(duì)象的速度會(huì)非常緩慢,所以在有些情況下,直接編寫一個(gè)無(wú)需實(shí)例化任何對(duì)象的SQL查詢會(huì)更快些。
對(duì)于如何檢測(cè)性能問(wèn)題, David Berube提供了一些建議:
最好的工具之一是Rails開發(fā)日志,它通常位于每個(gè)開發(fā)機(jī)器上的log/development.log文件內(nèi)。它具有各種綜合指標(biāo):響應(yīng)請(qǐng)求所花費(fèi)的總時(shí)間、花費(fèi)在數(shù)據(jù)庫(kù)內(nèi)的時(shí)間所占的百分比、生成視圖所花時(shí)間的百分比等。
在生產(chǎn)期間,通過(guò)查看mysql_slow_log可以找到很多有價(jià)值的信息。
其中一個(gè)最強(qiáng)大也是最為有用的工具是query_reviewer插件。這個(gè)插件可顯示在頁(yè)面上有多少查詢?cè)趫?zhí)行以及頁(yè)面生成需要多長(zhǎng)時(shí)間。并且它還會(huì)自動(dòng)分析 ActiveRecord生成的SQL代碼以便發(fā)現(xiàn)潛在問(wèn)題。例如,它能找到不使用MySQL 索引的查詢,所以如果您忘記了索引一個(gè)重要的列并由此造成了性能問(wèn)題,那么您將能很容易地找到這個(gè)列。此插件在一個(gè)彈出的
(只在開發(fā)模式下可見(jiàn))中顯示了所有這類信息。
針對(duì)N+1查詢問(wèn)題,David Berube舉了一個(gè)未優(yōu)化的代碼示例:- <%@posts = Post.all(@posts).each do |p|%>
-
- <%=p.category.name%>
-
- <%=p.body%>
-
- <%end%>
復(fù)制代碼 David Berube指出,上述代碼生成了一個(gè)查詢外加@posts內(nèi)的每行一個(gè)查詢。由于每查詢的負(fù)荷,這可能會(huì)成為一個(gè)很大的挑戰(zhàn)。罪魁禍?zhǔn)资菍?duì)p.category.name的調(diào)用。這個(gè)調(diào)用只應(yīng)用于該特定的post對(duì)象,而不是整個(gè)@posts數(shù)組。這種情況通過(guò)使用立即加載可以修復(fù)。立即加載(Eager loading)意味著Rails將自動(dòng)執(zhí)行所需的查詢來(lái)加載任何特定子對(duì)象的對(duì)象。Rails將使用一個(gè)JOIN SQL語(yǔ)句或一個(gè)執(zhí)行多個(gè)查詢的策略。不過(guò),假設(shè)指定了將要使用的所有子對(duì)象,那么將永遠(yuǎn)不會(huì)導(dǎo)致N+1的情形,在N+1情形下,一個(gè)循環(huán)的每個(gè)迭代都會(huì)生成額外的一個(gè)查詢。優(yōu)化后的代碼如下:- <%@posts = Post.find(:all, :include=>[:category] @posts.each do |p|%>
-
- <%=p.category.name%>
-
-
- <%=p.body%>
-
- <%end%>
復(fù)制代碼 比較復(fù)雜的情況包括嵌套的立即加載和間接的立即加載。
除了解決N+1問(wèn)題之外,David Berube還提供了一些優(yōu)化建議:
使用Rails提供的分組和聚合(grouping and aggregate)函數(shù)
用Rails定制 SQL
確保獲得cache-money緩存插件
不過(guò)目前動(dòng)態(tài)語(yǔ)言在企業(yè)開發(fā)中的應(yīng)用還不夠廣泛,很多企業(yè)只是用它來(lái)做一些粘合系統(tǒng)的工作,并沒(méi)有承擔(dān)起主力開發(fā)語(yǔ)言的重任。尤其是在底層系統(tǒng)開發(fā)方面,動(dòng)態(tài)語(yǔ)言遠(yuǎn)沒(méi)有在Web開發(fā)方面那么風(fēng)光。在運(yùn)行時(shí)效率和虛擬機(jī)穩(wěn)定性方面的不足,使得動(dòng)態(tài)語(yǔ)言注定無(wú)法與編譯型語(yǔ)言競(jìng)爭(zhēng),并取代它們?cè)诟咝阅茴I(lǐng)域的地位。然而,動(dòng)態(tài)語(yǔ)言也有自己的優(yōu)勢(shì)所在。如何克服自己的劣勢(shì),將優(yōu)勢(shì)發(fā)揚(yáng)光大,便是每一位動(dòng)態(tài)語(yǔ)言開發(fā)者所面臨的機(jī)遇和挑戰(zhàn)。
我所在的團(tuán)隊(duì)用了近兩年的時(shí)間,將一個(gè)電信領(lǐng)域的公司絕大部分的生產(chǎn)系統(tǒng)用動(dòng)態(tài)語(yǔ)言(主要是Python)重寫。包括短/彩信消息網(wǎng)關(guān)、業(yè)務(wù)訂閱服務(wù)、座席查詢系統(tǒng)、銷售支撐系統(tǒng),乃至搜索引擎等多個(gè)核心系統(tǒng),都在重寫之列。重寫的理由很多,一方面原有系統(tǒng)無(wú)論是從性能上,還是從應(yīng)對(duì)需求變化的能力上,都已經(jīng)不能滿足業(yè)務(wù)發(fā)展的需要;另外一方面,動(dòng)態(tài)語(yǔ)言的諸多優(yōu)勢(shì),也是我們重寫的動(dòng)力。這里僅以開發(fā)這些系統(tǒng)時(shí)獲得的經(jīng)驗(yàn),來(lái)談?wù)剟?dòng)態(tài)語(yǔ)言在應(yīng)用時(shí)的優(yōu)缺點(diǎn)。
標(biāo)簽: ruby
|