Importing data quickly in Ruby on Rails applications
Bài đăng này đã không được cập nhật trong 3 năm
Việc dùng ActiveRecord để insert dữ liệu vào database là việc làm hết sức thường xuyên và quá quen thuộc với các lập trình viên chúng ta.
Vấn đề đặt ra là chúng ta đã có rất nhiều dữ liệu mà cần phải thường xuyên tích hợp vào một ứng dụng cục bộ, nhưng cơ chế của ActiveRecord để tạo các bản ghi là quá chậm. Vậy chúng ta cải thiện tốc độ này bằng cách nào?
Ruby đã cung cấp cho chúng ta một gem activerecord-import
và nó có sẵn trong rubygem, đã trở thành tiêu chuẩn và vô cùng hữu ích cho việc nhập dữ liệu hàng loạt với ActiveRecord một cách hiệu quả.
ActiveRecord Import is a gem that helps you do bulk inserts using ActiveRecong
- Cài đặt
Cho vào gem file và chạy
bundle install
trên command line là chúng ta có thể sử dụng được
#gemfile
gem "activerecord-import"
- Sử dụng
Để hiểu hơn về cách dùng
activerecord-import
chúng ta cùng xem qua ví dụ đơn giản sau: Gỉa dụ rằng, ta có bảng books như sau:
create_table :books do |t|
t.column :name, :string, null: false
t.column :description, :string
end
Bây giờ ta muốn insert 100000 record vào DB: Dưới đây, ta sẽ có cách insert đơn giản nhất là sử dụng ActiveRecord
class Book < ActiveRecord::Base
end
# convert_csv_to_books is a method that converts CSV into an array of Book models, returning an array of attributes
convert_csv_to_book_attributes.each do |attrs|
Book.create!(attrs)
end
Tuy nhiên, đoạn code phía trên, mất ~97s để insert xong, thật sự rất chậm.
Vậy tại sao ActiveRecord lại chậm như thế?
Tốc độ thực hiện chậm và mất nhiều thời gian vì mỗi khi tạo một bản ghi với ActiveRecord, một câu lệnh INSERT đơn sẽ được tạo ra và gửi đến cơ sở dữ liệu. Insert 100000 record tương đương với gửi 100.000 câu lệnh riêng biệt tới cơ sở dữ liệu, cơ sở dữ liệu phải phân tích 100.000 câu lệnh riêng biệt, mở và đóng bảng 100.000 lần để viết, ghi dữ liệu, insert và update các chỉ mục 100.000 lần.
*Chúng ta sẽ thực hiện một thay đổi nhỏ để tăng tốc độ này: *
Tăng tốc độ bằng việc import models với validations: Thay vì dùng create!, chúng ta sẽ build các instance của Book và sau đó sẽ dùng Book.import
books = convert_csv_to_book_attributes.map do |attrs|
Book.new(attrs)
end
Book.import books
Dùng import
chỉ mất ~5s, tức là tăng gấp gần 19 lần. Tốc độ đươc cải thiện một cách đáng kể thật sự đáng kinh ngạc phải không ^^
Tăng performance bằng việc import model không validations:
books = convert_csv_to_book_attributes.map do |attrs|
Book.new(attrs)
end
Book.import books, validate: false
Với việc import mà không validate, tốc độ thực hiện insert 100.000 record trên chỉ mất ~4.6s, tốc độ lại được cải thiện thêm một chút nữa.
Tắng tốc độ bằng việc importing các column và value với validations: Ta sẽ thực hiện như sau:
columns = [:title, :description]
value_arrays = [["title 1", "description1"], ["title 2", "description 2"],...,["title 100000", "description 100000"]]
Book.import columns, value_arrays, validate: true
Việc import trên mất ~7.5s
Tăng tốc độ bằng việc import columns và values bỏ qua validations:
columns = [:title, :description]
value_arrays = [["title 1", "description1"], ["title 2", "description 2"],...,["title 100000", "description 100000"]]
Book.import columns, value_arrays, validate: false
Việc import này giảm thời gian thực hiện xuống còn ~2.5s. Đây là cải tiến hiệu suất lớn nhất - tốc độ tăng lên đến 38 lần.
Ta cùng xem qua hiệu quả hoạt động bị ảnh hưởng như thế nào trên các cơ sở dữ liệu MySQL (InnoDB), PostgreSQL và SQLite3.
Ví dụ ở trên hệ quản trị cơ sở dữ liệu MySQL:
Các ví dụ trên đây là những cách đơn giản nhất để cải thiện tải việc import hàng lọat (batch import) với activerecord-import. Hi vọng bài viết hữu ích. Thanks for your reading!
Bài viết được tham khảo và dịch từ https://www.mutuallyhuman.com/blog/2016/06/28/importing-data-quickly-in-ruby-on-rails-applications
All rights reserved