這幾天在練習寫爬蟲,一開始我的做法是每爬到一筆資料,就把它存到 DB 裡面,但是若爬了一萬筆資料,就表示你要做一萬次 DB transaction,這些時間累積下來也是很可觀的,因此本篇將來探討不同的多筆資料寫入 DB 的方式,會有什麼樣的差異
PS:本文實驗的方式都是從 CSV 讀取資料再存到 DB
方法 A:一次寫入一筆資料
|
|
|
|
可以很清楚的看出,每次寫入一筆資料就要做一次 transaction
方法 B:用 ActiveRecord::Base.transaction 把資料寫入的程式包起來
程式同方法 A,但是用 ActiveRecord::Base.transaction 把它包起來
|
|
|
|
可以看到與方法 A 不同的方在於,雖然一樣有多筆 INSERT,但只有一次 transaction
方法 C:把資料存成 Hash 陣列,再一次寫到 DB 裡
|
|
|
|
可以看的出來,居然跟方法 A 一樣,多筆 INSERT 加多個 transaction,還要另外把資料處理成 Hash Array 囧,如果只看程式很容易以為一次會把全部資料 INSERT 進去阿,這個方法也是實測中最慢的。
方法 D:利用 gem activerecord-import,把資料包成對應 Model 的 object Array 一次存進去
activerecord-import 這個 gem 就是專門用來處理大筆資料 import 的 gem,其實就是對大筆資料輸入的 ORM 語法做優化,他提供了兩種方式 import(請參閱下方連結),所以在這邊我們也都來試一試
Github: zdennis/activerecord-import
Examples
|
|
|
|
一個 SQL 指令就把資料存進去了。
另外實測的結果,若資料沒過 validation,資料並不會被存進去,安心使用。
他也提供 validate
選項,讓你決定是否要執行 validate
方法 E:利用 gem activerecord-import,把 CSV 處理成 Array 一次存進去
利用 CSV.read 就可以把 CSV 處理成 Array(請參閱下方連結)
Class: CSV (Ruby 2.0.0)
PS:因為我用的 CSV 有包含 header,所以要另外處理掉,避免把 header 也存進資料庫。
|
|
|
|
結果跟方法 D 差不多。
方法 F:跟方法 E 一樣,但是不做 validate
|
|
|
|
看起來跟方法 D 差不多。
速度比較
再回顧一下每個方法
方法 A:一次寫入一筆資料
方法 B:用 ActiveRecord::Base.transaction 把資料寫入的程式包起來
方法 C:把資料存成 Hash 陣列,再一次寫到 DB 裡
方法 D:利用 gem activerecord-import,把資料包成對應 Model 的 object Array 一次存進去
方法 E:利用 gem activerecord-import,把 CSV 處理成 Array 一次存進去
方法 F:跟方法 E 一樣,但是不做 validate
以五萬筆資料寫入實驗,下面速度比較結果的單位是秒
|
|
小結:如果你已經確保你要 import 的資料都正確無誤,就可以用 F 的方法去做,因為其他方法根本就看不到 F 的車尾燈惹 QQ,另外 D 跟 E 有差了一點點時間,我想那是因為 D 還要額外花一點時間把 CSV 處理成 obj array 吧,所以D 跟 E 其實是差不多的。
程式在這裡:https://github.com/kakas/activerecord_import_example
自己玩:
- clone 下來
- bundle install
- rake db:migrate
- rails console
- 執行
DataImportBenchmarkJob.new.perform
即可
2016/07/23 補充
使用 activerecord_import 並不會執行 callback,所以若是你有 callback 的需求可以參考以下連結
Callbacks · zdennis/activerecord-import Wiki