使用實體變數instance variable增進rails效能
我們曾經在Ruby面試精選30題 - Day12 千變萬化的變數: class variable, class instance variable 與 instance variable討論過實體變數,可用在實體方法instance method
,今天要探討instance variable在rails裡增進效能。
重點摘要:
A. 增進效能前,查詢的記憶體位置一直改變
我們在application.rb
寫一個class method可查詢登入的目前user:
application.rb
:
class ApplicationController < ActionController
def current_user
User.find(session[:user_id])
end
end
這個基本的類別方法想必大家都很熟悉,我也曾在Ruby面試精選30題 - Day04 玩弄Ruby的方法:instance method與class method這篇文章中舉例各自的差別。類別方法的接收者就是類別本身。(相較於類別方法,需要在類別外再new新物件的當變數接收者的是實體方法
。)
這時候去script/console
查詢Database中,User的id屬性為1的單筆資料 (:user_id
:symbol
符號)。儘管我們送出的程式碼都是User.find
,但傳回的User記憶體位置不斷改變。
script/console
>> User.find(1)
=> #<User:0x335c624 @attributes=("name" => "Ting", "id" => "1")>
>> User.find(1)
=> #<User:0x335a900 @attributes=("name" => "Ting", "id" => "1")>
>> User.find(1)
=> #<User:0x335b350 @attributes=("name" => "Ting", "id" => "1")>
而在Development log
內,相同的指令查詢跑了3次,造成效能低落。
Development log
Processing ProjectsController#index (for 127.0.0.1 at 2019-01-17)
Session ID: eb5d28exxxxx
Parameters: {"action"=> "index", "controller"=>"projects"}
Project Load (0.000259) SELECT * FROM projects
Rendering projects/index
Completed in 0.00619 (161 reqs/sec) | Rendering: 0.00071 (11%) | DB: 0.00026 (4%) | 200 OK [http://localhost/projects/]
SQL (0.000134) SET SQL_AUTO_IS_NULL=0
User_Columns (0.001251) SHOW FIELDS FROM users
User Load (0.000346) SELECT * FROM users WHERE (users.id = 1)
User Load (0.000293) SELECT * FROM users WHERE (users.id = 1)
User Load (0.000314) SELECT * FROM users WHERE (users.id = 1)
B. 使用instance variable與or-equals指定運算式增進效能
還記得a ||= b
嗎? 如果a尚未初始化/或為空值nil/或為false,a等於b; 其他情況下,a值不變。(請參考:Ruby面試精選30題 - Day09 Ruby的 or-equals 是什麼意思呢?)
我們將
User.find(session[:user_id])
改寫為
@current_user ||= User.find(session[:user_id])
代表若User.find(session[:user_id])
可以查詢到資料(非空值),則指定給@current_user
實體變數,讓記憶體位置可以維持固定不變。
application.rb
:
class ApplicationController < ActionController
def current_user
@current_user ||= User.find(session[:user_id])
end
end
>> @current_user ||= User.find(1)
#@current_user第一次尚未初始化,因此在資料庫內查詢。
=> #<User:0x335a890 @attributes=("name" => "Ting", "id" => "1")>
>> @current_user ||= User.find(1)
#@current_user已非空值,傳回instance variable屬性,其記憶體位置相同。
=> #<User:0x335a890 @attributes=("name" => "Ting", "id" => "1")>
Development log
SQL (0.000134) SET SQL_AUTO_IS_NULL=0
User_Columns (0.001251) SHOW FIELDS FROM users
User Load (0.000346) SELECT * FROM users WHERE (users.id = 1)
# 僅需查詢一次
總結
剛開始熟悉的rails的應用就綜合了三篇在2018年寫的Ruby面試文章,這不但是一次有趣的複習,也代表以前做過的努力,都不會白費!
Ref: