Tìm hiểu về Data Migration và áp dụng trong Rails
Bài đăng này đã không được cập nhật trong 9 năm
1. Data migration là gì?
Data Migration (Chuyển đổi dữ liệu) là quá trình di chuyển dữ liệu giữa các hệ thống lưu trữ dữ liệu, các định dạng dữ liệu hay giữa các hệ thống máy tính. Một dự án Data Migration thường được triển khai trong các trường hợp sau:
- Thay thế hoặc nâng cấp máy chủ hay các thiết bị lưu trữ dữ liệu.
- Tích hợp các website.
- Bảo trì máy chủ.
- Di dời trung tâm dữ liệu.
Data Migration có thể gây ảnh hưởng tới các hoạt động nghiệp vụ khi nó kéo dài các khoảng thời gian chết, các vấn đề về hệ thống và khả năng tương thích hệ thống. Vì vậy khi thực hiện dự án data migration, chúng ta cần phải tìm giải pháp để tối thiểu hóa các ảnh hưởng tiêu cực lên hệ thống. Lập kế hoạch, thực thi và kiểm thử là ba bước chính để di chuyển dữ liệu đạt hiệu quả như mong đợi. Lập kế hoạch đòi hỏi phải hiểu rõ được các yêu cầu thiết kế như yêu cầu di chuyển, yêu cầu về phần cứng, yêu cầu về độ lớn và giá trị của dữ liệu. Trước khi thực hiện di chuyển dữ liệu, cần phải cài đặt các phần mềm, phần cứng để đáp ứng yêu cầu của nghiệp vụ.
Sau khi việc di chuyển dữ liệu hoàn tất, cần tiến hành công việc kiểm thử để chắc chắn rằng các phần dữ liệu được di chuyển là chính xác theo yêu cầu thiết kế. Cuối cùng là giai đoạn làm sạch dữ liệu để cải thiện chất lượng dữ liệu, giảm thiểu các phần dữ liệu được lặp đi lặp lại một cách không cần thiết.
2. Ứng dụng trong Rails
Để có thể thực hiện việc di chuyển dữ liệu, trước tiên chúng ta cần phải có tài liệu đặc tả cụ thể cho quá trình này. Ví dụ, chúng ta cần thực hiện việc chuyển dữ liệu từ bảng User
của hệ thống A sang bảng Employee
của hệ thống B. Trước hết chúng ta cần phải biết trường nào của bảng User
sẽ được chuyển đổi thành trường nào của bảng Employee
. Giả sử ta có bảng hướng dẫn chuyển đổi dữ liệu như sau:
|_.User|_.Employee|
|id | id|
|mail|email|
|name| last_name|
||last_name|
|type|role|
Trong bảng trên, chúng ta giả sử kiểu dữ liệu của các trường tương ứng giữa hai bảng User
và Employee
không có gì khác biệt (int -> int, string -> string). Lưu ý rằng trường name
của bảng User sẽ được tách thành hai trường first_name
và last_name
của bảng Employee
.
Thông thường dữ liệu từ hệ thống A sẽ được lấy ra thông qua API và được lưu dưới dạng các file dữ liệu xml hoặc json. Trong trường hợp này chúng ta giả sử file dữ liệu được lấy ra từ bảng User
được lưu dưới dạng xml với nội dung mẫu như sau:
<Item>
<id>1</id>
<mail>user1@gmail.com</mail>
<name>Nguyen Van User1</name>
<type>1</type>
</Item>
<Item>
<id>2</id>
<mail>user2@gmail.com</mail>
<name>Nguyen Hoang User2</name>
<type>0</type>
</Item>
Để đọc được dữ liệu từ file xml, trong Rails có gem Nokogiri
giúp cho việc đọc file trở nên dễ dàng hơn.
Cài đặt thông qua lệnh
$ gem instal nokogiri
hoặc bạn thêm dòng sau vào Gemfile
gem "nokogiri"
rồi sau đó chạy lệnh
$ bundle install
Một ví dụ đọc file xml với Nokogiri:
xml_content = Nokogiri::XML(open("#{Rails.root}/app/public/data/user.xml"))
items = xml_content.xpath("//item")
items.each do |item|
type = item.xpath("./type").inner_text.to_i
id = item.xpath("./id").inner_text.to_i
name = item.xpath("./name").inner_text.squish
email = item.xpath("./mail").inner_text.squish
end
Với những file dữ liệu chuyển đổi có kích thước nhỏ hoặc số bản ghi bé, chúng ta chỉ cần đọc file, lấy ra từng trường lưu vào các biến tương ứng. Rồi sau đó thực thi lệnh sql insert table là có thể import được dữ liệu cần chuyển đổi vào bảng mới. Tiếp tục hoàn thiện đoạn code phía trên:
xml_content = Nokogiri::XML(open("#{Rails.root}/app/public/data/user.xml"))
items = xml_content.xpath("//item")
items.each do |item|
type = item.xpath("./type").inner_text.to_i
id = item.xpath("./id").inner_text.to_i
name = item.xpath("./name").inner_text.squish
email = item.xpath("./mail").inner_text.squish
ActiveRecord::Base.connection.execute("insert into employees (id, first_name, email, role)
values (#{id}, \"#{name}\", \"#{email}\", #{type})")
end
Tuy nhiên đối với nhứng file dữ liệu có kích thước lớn và số bản ghi lớn (hàng trăm nghìn bản ghi) thì việc áp dụng giải pháp trên không được tối ưu (tốn thời gian và tài nguyên của hệ thống).
Trong Rails có một giải pháp giúp chúng ta có thể dễ dàng giải quyết khó khăn trên - Gem activerecort-import
Để cài đặt, bạn có thể gem dòng sau vào Gemfile
gem "activerecord-import"
Sau đó chạy lệnh
$ bundle install
Hoặc cài đặt trực tiếp gem trên thông qua lệnh
$ gem install activerecord-import
Cuối cùng chúng ta chỉ cần import dữ liệu vào bảng mới thông qua lệnh import. Bạn có thể tham khảo hướng dẫn chi tiết quá trình này sau đây (dựa theo ví dụ về hai bảng User
và Employee
đã được đề cập phía trên)
#xác định các cột cần import dữ liệu
COLUMNS = [:id, :first_name, :last_name, :email, :role]
UPDATE_COLUMNS = [:first_name, :last_name, :email, :role]
xml_content = Nokogiri::XML open "#{Rails.root}/app/public/data/user.xml"
items = xml_content.xpath("//Item") //Đọc file xml
items.inject([]) do |data, item|
# Tách name thành hai thành phần first_name và last_name theo yêu cầu của hệ thống B
name = (format_text(item.xpath("./name").inner_text) || "").split
# Lưu dữ liệu lấy và xử lý được vào mảng data
data << [format_number(item.xpath("./id").inner_text),
name[0],
name[1],
format_text(item.xpath("./mail").inner_text),
format_number(item.xpath("./type").inner_text)]
end
Employee.import COLUMNS, data, validate: false, on_duplicate_key_update: UPDATE_COLUMNS
Chú ý rằng lệnh Employee.import
thực hiện ở trên có điều kiện on_duplicate_key_update: UPDATE_COLUMNS
, tức là khi thực hiện import nếu có id trùng lặp thì Rails chỉ cập nhật các trường được xác định bởi UPDATE_COLUMNS
Ngòai ra với những bài toán cụ thể sẽ có những yêu cầu khác nhau cho việc ánh xạ dữ liệu. Mục đích là để việc ánh xạ dữ liệu giữa các hệ thống chuyển đổi được chính xác hơn. Ví dụ trong trường hợp trên, type của hệ thống A và role của hệ thống B có thể được quy định khác nhau. Khi đó trước khi thực hiện import dữ liệu chúng ta cần có một bước trung gian xác định rằng với những kiểu type nào của A sẽ được chuyển thành role tương ứng của B.
All rights reserved