Tăng hiệu suất insert hoặc update khối lượng lớn dữ liệu với gem activerecord-import trong Rails
Giả sử bạn có file dữ liệu chứa khoảng 1000 đối tượng bản ghi cần insert vào hệ thống hoặc update lại nếu đã tồn tại bản ghi. Nếu bạn thực hiện insert hoặc update từng bản ghi, mỗi lần như vậy bạn phải kết nối với Database do ActiveRecord trong rails không hỗ trợ insert hàng loạt record mà phải ...
Giả sử bạn có file dữ liệu chứa khoảng 1000 đối tượng bản ghi cần insert vào hệ thống hoặc update lại nếu đã tồn tại bản ghi. Nếu bạn thực hiện insert hoặc update từng bản ghi, mỗi lần như vậy bạn phải kết nối với Database do ActiveRecord trong rails không hỗ trợ insert hàng loạt record mà phải thực hiện tuần tự. Điều này làm cho chức năng của bạn thực hiện mất nhiều thời gian và giảm hiệu suất của ứng dụng vì phải sinh ra 1000 câu lệnh SQL để thực hiện việc ghi dữ liệu.
ActiveRecord-import là một thư viện dùng để insert một số lượng lớn các bản ghi vào trong cơ sở dữ liệu sử dụng ActiveRecord. Sử dụng ActiveRecord-import cho phép sử dụng 1 câu lệnh insert cùng lúc nhiều values, điều này giúp bạn cải thiện được suất hiệu của chức năng này.
Khai báo gem file
# Gemfile gem "activerecord-import", "~> 0.15.0"
Sau khi đã thêm gem, thực hiện lệnh bundle install để cài gem
Import sử dụng Columns và Arrays
Phương thức import có thể lấy một array các tên cột và array gồm các array khác. Mỗi array con đại diện cho một bản ghi và danh sách các giá trị theo thứ tự như các cột đã chỉ định. Đây là cơ chế import nhanh nhất và cũng nguyên thủy nhất.
columns = [ :name, :address ] values = [ ["Vendor1", "Hanoi"], ["Vendor2", "Danang"] ] Vendor.import columns, values
Import sử dụng Hashes
Phương thức import có thể nhận một array gồm các hash. Các keys tương ứng với tên cột trong cơ sở dữ liệu.
values = [{name: "Vendor1", address: "Hanoi"}, {name: "Vendor2", address: "Danang"}] Vendor.import values
Import sử dụng Hashes và chỉ rõ tên Column
Phương thức import có thể lấy một array các tên cột và một array các đối tượng hash. Các tên cột được sử dụng để xác định các trường cần import vào database. Ví dụ sau sẽ chỉ nhập sách với trường name
vendors = [ {name: "Vendor1", address: "Hanoi"}, {name: "Vendor2", address: "Danang"} ] columns = [ :name ] Vendor.import columns, vendors # Kết quả insert trong table vendors # name | address #---------|-------- # Vendor1 | NULL # Vendor2 | NULL
Import sử dụng ActiveRecord models
Phương thức import có thể lấy một array các object model. Các thuộc tính sẽ được lấy ra từ mỗi model bằng cách so khớp với các thuộc tính của model
vendors = [ Vendor.new(name: "Vendor1", address: "Hanoi"), Vendor.new(name: "Vendor1", address: "Hanoi") ] Vendor.import vendors
Import sử dụng ActiveRecord Models và chỉ rõ tên Column
Phương thức import có thể lấy một array các object model. Các tên cột được sử dụng để xác định các trường cần import vào database.
vendors = [ Vendor.new(name: "Vendor1", address: "Hanoi"), Vendor.new(name: "Vendor1", address: "Hanoi") ] columns = [:name] Vendor.import columns, vendors # Kết quả insert trong table vendors # name | address #---------|-------- # Vendor1 | NULL # Vendor2 | NULL
Trong một số trường hợp bạn muốn cập nhật lại thông tin nếu bản ghi đã tồn tại thì sử dụng phương thức on_duplicate_key_update, bằng cách chỉ rõ các colunm thuộc tính mà bạn cần cập nhật lại dữ liệu vào database. Ví dụ bên dưới sẽ cập nhật thông tin address của các bản ghi vendor trong trường hợp đã trùng name
vendors_update = [] list_name_vendor.each do |name_vendor| vendor = Vendor.find_by name: name_vendor if vendor.present? vendor.address = "Address update" vendors_update << vendor end end # MySQL version Vendor.import vendors_update, on_duplicate_key_update: [:address] # PostgreSQL version Vendor.import vendors_update, on_duplicate_key_update: {conflict_target: [:id], columns: [:address]} # PostgreSQL shorthand version (conflict target must be primary key) Vendor.import vendors_update, on_duplicate_key_update: [:address]
Thiết lập validate khi sử dụng
# Thực hiện import nhưng bỏ qua check validate trong model Vendor.import columns, values, validate: false #Import có thực hiện check validate trong model Vendor.import columns, values, validate: true # Mặc định nếu không khai báo thì sẽ thực hiện check validate trong model Vendor.import columns, values
Thiết lập số lượng bản ghi cần import cho mỗi câu lệnh
Trong trường hợp các bản ghi quá lớn ,để tránh trường hợp quá tải chúng ta có thể sử dụng batch_size để điều khiển số lượng bản ghi được import : Ví dụ sử dụng 2 câu SQL để import 20 bản ghi vendors
Vendor.import vendors, batch_size: 10
Cùng xem kết quả SQL trong trường hợp này:
Callback trong activerecord-import
Có một lưu ý khi bạn sử dụng activerecord-import khi import dữ liệu nó sẽ bỏ qua các method callback liên quan đến create, update và delete bản ghi đó. Sỡ dĩ nó bỏ qua callback là vì với một khối lượng lớn bản ghi được import vào thì việc truy cập ActiveRecord trong bộ nhớ là không cần thiết. Tuy nhiên, trường hợp bạn muốn gọi callback thì có thể tham khảo ví dụ sau:
vendors.each do |vendor| vendor.run_callbacks(:save) {false} vendor.run_callbacks(:create) {false} end Vendor.import(vendors)
Thao tác này sẽ chạy before_create và before_save callbacks cho mỗi bản ghi vendor. Đối số false truyền vào đây mục đích để ngăn chặn việc thực thi after_save, điều này sẽ không có ý nghĩa trước khi nhập hàng loạt.
Bài viết này giới thiệu cho các bạn về gem activerecord-import hi vọng sẽ giúp ích được các bạn trong một vài trường hợp import dữ liệu lớn trong Rails. Nguồn tham khảo: https://github.com/zdennis/activerecord-import