12/08/2018, 14:06

Insert lượng lớn dữ liệu vào DB sử dụng gem Activerecord-import

Ruby On Rails cung cấp bộ công cụ giúp thao tác với cơ sở dữ liệu một cách dễ dàng, nhưng với những yêu cầu cần insert lượng lớn dữ liệu hàng trăm ngàn bản ghi thì việc insert từng bản ghi sẽ gây mất rất nhiều thời gian. Trong bài viết này giới thiệu giải pháp sử dụng gem "activerecord-import" để ...

Ruby On Rails cung cấp bộ công cụ giúp thao tác với cơ sở dữ liệu một cách dễ dàng, nhưng với những yêu cầu cần insert lượng lớn dữ liệu hàng trăm ngàn bản ghi thì việc insert từng bản ghi sẽ gây mất rất nhiều thời gian. Trong bài viết này giới thiệu giải pháp sử dụng gem "activerecord-import" để tăng tốc insert dữ liệu bằng cách chỉ sử dụng một câu lệnh INSERT.

Nhập 100.000 bản ghi book với thuộc tính như dưới đây

create_table :books do |t|
  t.column :name, :string, null: false
  t.column :description, :string
end

Dưới đây là một giải pháp sử dụng ActiveRecord

class Book < ActiveRecord::Base
end

100_000.times do |n|
   Book.create name: "#book_#{n}", description: "description_#{n}"
end

Mỗi khi bạn tạo một bản ghi với ActiveRecord một câu lệnh INSERT được tạo ra và gửi đến cơ sở dữ liệu. Nghĩa là để tạo 100.000 ngàn bản ghi cần tạo 100.000 câu lệnh INSERT, cơ sở dữ liệu phải phân tích cú pháp câu lệnh, mở và đóng kết nối 100.000 lần, điều đó gây tốn rất nhiều thời gian và tài nguyên hệ thống do phải cấp phát hoặc giải phóng bộ nhớ cho 100.000 block.

Vậy cần giải pháp khác hiệu quả hơn.

Cài đặt

Mở Gemfile và chèn vào dòng dưới đây

gem "activerecord-import", ">= 0.2.0"

Tiếp theo thêm vào file application.rb

require "activerecord-import/base"
ActiveRecord::Import.require_adapter('mysql2')

Chạy bundle install

Tăng tốc insert dữ liệu đồng thời validate model

Thay vì sử dụng create! để thêm từng đối tượng book vào cơ sở dữ liệu, ta tạo một mảng các đối tượng book rồi insert đồng thời cả mảng đó chỉ với môt câu query bằng lệnh Book.import:

books = []
100_000.times do |n|
   Book.new name: "#book_#{n}", description: "description_#{n}"
end

# without validations
Book.import books, :validate => false

# with validations
# Book.import books, :validate => true

# when not specified :validate defaults to true
# Book.import books

Theo mặc định giá trị validate bằng true, phương thức import sẽ lần lượt validate các model book và build thành một câu query tối ưu.

INSERT INTO books ("name","description")
VALUES ("book_1", "description_1"), ("book_2", "description_2"), ..., ("book_n", "description_n");

Tăng tốc insert dữ liệu bỏ qua validate model

Với những dữ liệu đã được kiểm tra và đẩm bảo thỏa mãn validate, khi import ta có thể thêm tùy chọn validate: false, điều này cho tốc độ insert cao hơn khi thực hiện với validate.

books = []
100_000.times do |n|
   Book.new name: "#book_#{n}", description: "description_#{n}"
end

# without validations
Book.import books, :validate => false

Insert bằng tên columns và giá trị tương ứng ỏ qua tạo model

Thay vì khởi tạo, cấp phát bộ nhớ cho các đối tượng rồi thêm vào mảng, ta có thể tạo một mảng các giá trị tương ứng với từng column name.

columns = [:name, :description]

books = [ ['Book #1', 'Good book'], ['Book #2', 'Great Book'], ...]

Book.import columns, books, validate: true

Bảng đánh giá tốc độ insert

Capture.PNG

Ta thấy tốc độ khi sử dụng import cao hơn rất nhiều so với create từng record sử dụng ActiveRecord của rails.

Với những yêu cần cần insert lượng lớn dữ liệu thì giải pháp lần lượt insert từng bản ghi vào cơ sở dữ liệu là không khả thi do tốn nhiều thời gian và tài nguyên hệ thống. Việc sử dụng gem activerecord-import giúp tối ưu tốc độ và hiệu năng, đồng thời cung cấp các lựa chọn validate hoặc không 'validate'.

Các bài viết tham khảo

Wiki activerecord-import

Bài viết chi tiết của tác giả gem activerecord-import

0