Giới thiệu
Bạn đã cảm nhận được chưa? Sự khó chịu không nói thành lời trong một ứng dụng đang phát triển. Nó bắt đầu như một cái thì thầm—một báo cáo chậm chạp, một nguồn dữ liệu không vừa vặn với ActiveRecord thanh lịch của chúng ta. Rồi một cái nữa. Và một cái nữa.
Chẳng mấy chốc, bạn không chỉ xây dựng các tính năng; bạn đang vật lộn với một con thủy quái của các silo dữ liệu, API bên thứ ba và các hệ thống kế thừa. Monolith Rails đẹp đẽ của bạn bắt đầu kêu rên dưới sức nặng của các truy vấn phân tích và thao tác dữ liệu hàng loạt. Sự thiêng liêng của các mô hình của bạn bị vi phạm bởi những đoạn script một lần, bị lạc trong thư mục lib/, không bao giờ được kiểm tra hoặc nhìn thấy nữa.
Đây, các đồng nghiệp lập trình viên, là lúc nghệ nhân bước vào. Đây là nơi chúng ta dừng việc viết script và bắt đầu tạo ra pipelines. Đây là hành trình từ hỗn loạn đến tổ chức, và phương tiện của chúng ta hôm nay là ETL—Extract, Transform, Load.
Bức tranh: Tại sao chọn active_etl?
Chúng ta có thể ghép nối điều này với các tác vụ Rake thô sơ, các cuộc gọi ActiveRecord, và sức mạnh ý chí. Nhưng một nghệ sĩ lựa chọn công cụ của mình với sự cân nhắc. Chúng ta chọn active_etl, một framework hiểu nhịp điệu của công việc này.
Nó không cung cấp cho chúng ta những ràng buộc cứng nhắc, mà là một cấu trúc—một ngữ pháp cho bản giao hưởng dữ liệu của chúng ta. Nó cho chúng ta các giai đoạn, hooks, logging và xử lý lỗi ngay từ đầu. Nó cho phép chúng ta không nghĩ trong các dòng mã, mà trong quy trình.
Bản phác thảo: Hình dung kiệt tác của chúng ta
Mỗi tác phẩm nghệ thuật vĩ đại bắt đầu với một tầm nhìn. Giả sử chúng ta cần tạo một báo cáo tóm tắt hàng đêm về sự tương tác của người dùng cho đội ngũ lãnh đạo. Dữ liệu của chúng ta rải rác:
- Extract: Lấy dữ liệu thô từ cơ sở dữ liệu
PostgreSQLchính, một bảnguserskế thừa từMySQL, và một API phân tích bên thứ ba (như Segment hoặc Amplitude). - Transform: Làm sạch dữ liệu này. Ánh xạ các ID người dùng khác nhau thành một ID chuẩn. Tính tổng số sự kiện theo ngày. Xử lý các soft-delete và bất thường.
- Load: Chèn những dữ liệu đã được biến đổi, có giá trị vào bảng
daily_user_summaries, sẵn sàng để được tiêu thụ bởi một dashboard mà không ảnh hưởng đến cơ sở dữ liệu OLTP sản xuất của chúng ta.
Đây là câu chuyện của chúng ta. Hãy mang nó đến sự sống.
Bảng màu: Thiết lập xưởng của chúng ta
Đầu tiên, chúng ta thêm gem vào Gemfile và thiết lập không gian làm việc của mình.
# Gemfile
gem 'active_etl'
bundle install
rails generate active_etl:install
Điều này tạo ra cấu trúc—xưởng nơi chúng ta sẽ làm việc:
app/
etl/
processes/ # Các pipeline hoàn chỉnh của chúng ta nằm ở đây
sources/ # Logic trích xuất tái sử dụng
transforms/ # Logic biến đổi tái sử dụng
destinations/ # Logic tải tái sử dụng
Các nét vẽ: Tạo dựng pipeline
Chúng ta không chỉ đơn giản là đổ sơn lên bức tranh. Chúng ta áp dụng những nét vẽ có chủ ý và có lớp. Hãy tạo quy trình của chúng ta: NightlyUserSummaryProcess.
rails generate active_etl:process NightlyUserSummary
Bây giờ, mở tệp được tạo ra. Đây là bức tranh trống của chúng ta.
1. Nét vẽ trích xuất: Thu thập nguyên liệu
Chúng ta trích xuất từ nhiều nguồn. Lưu ý cách chúng ta đặt tên rõ ràng—đây không chỉ là dữ liệu; đây là nguyên liệu thô cho nghệ thuật của chúng ta.
# app/etl/processes/nightly_user_summary_process.rb
class NightlyUserSummaryProcess < ActiveEtl::Base
def extract
# Nét vẽ 1: Trích xuất từ cơ sở dữ liệu PostgreSQL chính (sử dụng ActiveRecord)
source :postgres_events, -> {
Events::PaymentSucceeded
.where(created_at: (Time.current.yesterday.beginning_of_day..Time.current.yesterday.end_of_day))
.select(:user_id, :amount, :created_at)
}
# Nét vẽ 2: Trích xuất từ cơ sở dữ liệu MySQL kế thừa (sử dụng lớp nguồn tùy chỉnh để tái sử dụng)
source :legacy_users, LegacyUserSource.fetch_data
# Nét vẽ 3: Trích xuất từ API bên ngoài
source :analytics_events, -> {
External::AnalyticsApiClient.new.fetch_events(
event_name: 'user_engagement',
date: Date.yesterday.to_s
)
}
# Làm cho dữ liệu này có sẵn cho giai đoạn tiếp theo
self
end
end
2. Nét vẽ biến đổi: Pha trộn và tinh chế
Đây là thuật giả kim. Đây là nơi chúng ta biến dữ liệu thô thành ý nghĩa. Chúng ta chia nhỏ điều này thành các phương thức có thể kiểm tra riêng biệt. Sự tinh tế hơn là sức mạnh thô.
# ... bên trong lớp NightlyUserSummaryProcess
def transform
# Chúng ta làm việc trên dữ liệu mà chúng ta đã lấy, một cách có phương pháp.
transformed_data = map_legacy_user_ids
transformed_data = aggregate_events(transformed_data)
# Lưu trữ dữ liệu đã tinh chế cho giai đoạn cuối cùng
store transformed_data: transformed_data
end
private
def map_legacy_user_ids
# Hãy tưởng tượng một logic ánh xạ phức tạp, nhưng thanh lịch ở đây.
# Chúng ta có thể sử dụng một đối tượng dịch vụ chuyên dụng. Đây là nơi sự phán đoán của kỹ sư cao cấp tỏa sáng.
legacy_user_map = @sources[:legacy_users].each_with_object({}) do |legacy_user, map|
modern_user = User.find_by(legacy_id: legacy_user.id)
map[legacy_user.id] = modern_user.id if modern_user
end
# Ánh xạ dữ liệu cho các sự kiện
@sources[:postgres_events].each do |event|
event.user_id = legacy_user_map[event.user_id] || event.user_id
end
end
def aggregate_events(data)
# Nghệ thuật của sự giảm thiểu. Chúng ta nhóm, chúng ta tổng hợp, chúng ta đếm.
# Chúng ta tạo ra những đối tượng mới, có giá trị từ tiếng ồn.
data.group_by(&:user_id).map do |user_id, events|
{
user_id: user_id,
date: Date.yesterday,
total_payment_amount: events.sum(&:amount),
engagement_count: events.count,
# ... các chỉ số khác
}
end
end
3. Nét vẽ tải: Tạo lớp hoàn thiện cuối cùng
Giai đoạn tải là lớp bảo vệ của chúng ta. Nó bảo vệ và lưu giữ ý nghĩa mà chúng ta đã tạo ra. Chúng ta thực hiện điều này với độ chính xác, thường sử dụng activerecord-import cho các chèn hàng loạt để nhẹ nhàng với cơ sở dữ liệu.
# ... bên trong lớp NightlyUserSummaryProcess
def load
# Lấy kiệt tác của chúng ta từ giai đoạn trước
facts = stored_data[:transformed_data]
# Điều chỉnh thành cấu trúc chính xác cho bảng mục tiêu của chúng ta
records = facts.map do |fact|
DailyUserSummary.new(fact) # Giả sử một mô hình ActiveRecord tồn tại
end
# Một hoạt động hiệu quả, duy nhất. Đây là dấu ấn của một nghệ nhân.
DailyUserSummary.import(records, batch_size: 500, on_duplicate_key_update: [:total_payment_amount])
end
end
Chữ ký: Xử lý lỗi và quan sát
Một nghệ sĩ ký tên vào tác phẩm của mình. Một kỹ sư cao cấp đảm bảo rằng nó bền bỉ và có thể quan sát được. active_etl cung cấp các hooks cho điều này.
class NightlyUserSummaryProcess < ActiveEtl::Base
after_extract :log_extraction_complete
after_load :notify_on_success
on_error :handle_failure
private
def log_extraction_complete
logger.info "Đã trích xuất dữ liệu thành công từ tất cả các nguồn."
end
def notify_on_success
SlackNotifier.etl_success(self.class.name, Time.current)
end
def handle_failure(exception)
logger.error "Quá trình ETL thất bại: #{exception.message}"
SlackNotifier.etl_failure(self.class.name, exception)
# Có thể thử lại với exponential backoff?
end
end
Triển lãm: Thực thi kiệt tác
Chúng ta không chạy điều này; chúng ta thực thi nó. Chúng ta gọi nó từ một bộ lập lịch (như Clockwork hoặc Sidekiq Cron) với sự thanh lịch của một dòng lệnh duy nhất.
# lib/tasks/nightly_etl.rake
namespace :etl do
desc "Chạy tóm tắt người dùng hàng đêm"
task :nightly_summary => :environment do
NightlyUserSummaryProcess.call # .call là dấu gậy của nhạc trưởng
end
end
bundle exec rake etl:nightly_summary
Phê bình: Tại sao đây là nghệ thuật
Bạn thấy đấy, giá trị không chỉ nằm ở pipeline hoạt động. Nó nằm ở các artifact mà chúng ta đã tạo ra:
- Tính đọc được: Bất kỳ lập trình viên nào cũng có thể đọc quy trình này và hiểu được luồng dữ liệu. Đó là một câu chuyện.
- Tính kiểm tra: Mỗi
source,transform, vàdestinationcó thể được kiểm tra đơn vị riêng biệt. Toàn bộ quy trình có thể được kiểm tra tích hợp. - Tính tái sử dụng:
LegacyUserSourceđó giờ có thể được sử dụng trong các quy trình khác. Chúng ta đang xây dựng một bộ công cụ dữ liệu có thể kết hợp. - Tính bảo trì: Khi API phân tích thay đổi, chúng ta biết chính xác nơi cần đến. Khi cần một sự biến đổi mới, chúng ta chỉ cần thêm một nét vẽ; chúng ta không cần vẽ lại toàn bộ bức tranh.
- Tính bền bỉ: Chúng ta có các cơ chế tích hợp để xử lý lỗi, ghi log và quan sát.
Chúng ta đã chuyển từ một thế giới của những script ẩn giấu, mong manh sang một thế giới của các quy trình dữ liệu rõ ràng, mạnh mẽ và trang trọng. Chúng ta không chỉ giải quyết một vấn đề kinh doanh; chúng ta đã nâng cao nghề nghiệp trong mã nguồn của mình.
Đây là nghệ thuật của ETL. Đó là sự chuyển đổi hỗn loạn thành rõ ràng, dữ liệu thành hiểu biết. Và với Rails và active_etl, chúng ta có một bộ công cụ tuyệt vời để thực hành điều đó.
Bây giờ hãy tiến về phía trước, và xây dựng những bản giao hưởng của bạn.