Hướng dẫn sử dụng gem SQL Tracker
Bài đăng này đã không được cập nhật trong 8 năm
1. Tổng quan
Khi phát triển hay khi tối ưu một hệ thống được viết bằng Rails, một trong những mối quan tâm chính là SQL queries. Lúc đó chúng ta sẽ đặt ra những câu hỏi như:
- Có bao nhiêu câu query được gọi qua mỗi lần resquest tới server?
- Mất bao nhiêu thời gian để chạy xong một query?
- Query nào mất nhiều thời gian nhất?
- Có query nào bị gọi lặp lại ở những chỗ khác nhau hay không?
- Có những query nào được thực hiện nhiều hơn so với những query khác?
Theo cách thông thường, chúng ta đơn giản chỉ kiểm tra trong log file nhưng nếu muốn phân tích một request phức tạp hoặc nếu theo dõi đồng thời nhiều request thì với phương pháp trên chúng ta khó có thể lọc ra được những thông tin chính mà chúng ta quan tâm.
Vậy để trả lời những câu hỏi trên một cách đơn giản nhất, tôi sẽ hướng dẫn các bạn sử dụng gem Sql Tracker
.
2. Cài đặt
thêm vào gem file
group :development, :test do
gem “sql_tracker”
end
Config:
SqlTracker::Config.enable = true
SqlTracker::Config.tracked_paths = %w(app lib)
SqlTracker::Config.tracked_sql_command = %(SELECT INSERT UPDATE DELETE)
SqlTracker::Config.output_path = File.join(Rails.root.to_s, “tmp”)
Sau đó chạy lệnh
$ bundle
3. Phương thức hoạt động
3.1. Theo dõi
sql_tracker
sử dụng http://guides.rubyonrails.org/active_support_instrumentation.html (instrumentation API) để theo dõi các câu query được gọi từ Active Record
bằng cách subscribe tới sql.active_record
:
ActiveSupport::Notifications.subscribe('sql.active_record') do |_name, _start, _finish, _id, payload|
sql_query = payload[:sql]
end
hoặc bạn có thể tạo một object để implement phương thức call ví dụ:
class Handler
def call _name, _start, _finish, _id, payload
sql_query = payload[:sql]
end
end
ActiveSupport::Notifications.subscribe(“sql.active_record”, Handler.new)
sql_tracker
sẽ được khởi tạo và bắt đầu theo dõi các query khi mà hệ thống Rails chạy và dừng khi mà hệ thống thoát
3.2. Lọc
Có thể bạn không muốn theo dõi toàn bộ các query. Ví dụ bạn chỉ muốn theo dõi các câu SELECT
hoặc bạn muốn theo dõi các query được gọi từ môt vùng nhất định.
Mặc định sql_tracker
ghi lại tất các query SELECT, INSERT, UPDATE, DELETE nhưng nó cho phép bạn thay đổi bằng cách sử dụng tracker_sql_command
:
SqlTracker::Config.tracked_sql_command = %w(SELECT)
Bạn cũng có thể thay đổi thư mục bạn muốn theo dõi bằng các sử dụng tracked_paths
:
SqlTracker::Config.tracked_paths = %w(app/controllers/api)
Mặc định sql_tracker
theo dõi toàn bộ forder app
và lib
sql_tracker
sử dụng phương thức caller
của Ruby và backtrace_cleaner
của Rails để lọc theo đường dẫn folder. caller
trả về một array
của strack hiện tại và backtrace_cleaner
giúp dọn dẹp thông tin của stack sử dụng regex
.
Rails.backtrace_cleaner.add_silencer do |line|
line !~ %r{^(#{tracked_paths.join('|')})\/}
end
cleaned_trace = Rails.backtrace_cleaner.clean(caller)
return false if cleaned_trace.empty?
3.3. Nhóm query
sql_tracker
cố gắng nhóm các query giống nhau bằng cách thay thế các giá trị cụ thể với xxx
. Ví dụ bạn thực hiện một request thì query sẽ được thực hiện :
SELECT users.* FROM users WHERE users.id = 1
Một ví dụ khác :
SELECT users.* FROM users WHERE users.id = 2
sql_tracker
sẽ nhóm chúng thành một query:
SELECT users.* FROM users WHERE users.id = xxx
sql_tracker
suer dụng các biểu thức thông thường để biên tập lại các query. Ví dụ cố gắng tìm và thay thê các giá trị sau khi so sánh như : =
, <
, >
, <>
hoặc thậm chí BETWEEN
và AND
…
Sau khi được nhóm lại thì sẽ dễ dàng hơn để tổng hợp các query và tính toán tổng số thời gian cũng như thời gian trung bình …
3.4. Lưu trữ
sql_tracker
lưu lại tất cả các dữ liệu theo dõi vào bộ nhớ và chuyển nó thành một file JSON có dạng:
{
key1: {
sql: 'SELECT users.* FROM users ...',
count: 1,
duration: 0.34,
source: ['apps/model/user.rb:57:in ...', ...]
},
key2: { ... },
... ...
}
Mặc định file ày sẽ được lưu tại folder tmp
nhưng bạn có thể config lại:
SqlTracker::Config.output_path = File.join(Rails.root.to_s, "my_folder")
Nếu server của bạn sử dụng puma
và set nhiều worker
thì sẽ xuất ra nhiều hơn 1 file JSON vì mỗi một worker
sẽ theo dõi và lưu trữ riêng.
3.5. Báo cáo
Cuối cùng sql_tracker
hỗ trợ chúng ta tạo ra một báo cáo từ file JSON ở trên
sql_tracker tmp/sql_tracker-*.json
Báo cáo sẽ có dạng :
Total Unique SQL Queries: 24
Count | Avg Time (ms) | SQL Query | Source |
---|---|---|---|
8 | 0.33 | SELECT users .* FROM users WHERE users .id = xxx LIMIT 1 |
app/controllers/users_controller.rb:125:in `create' |
. | . | . | app/controllers/projects_controller.rb:9:in `block in update' |
4 | 0.27 | SELECT projects .* FROM projects WHERE projects .user_id = xxx AND projects .id = xxx LIMIT 1 |
app/controllers/projects_controller.rb:4:in `update' |
2 | 0.27 | UPDATE projects SET updated_at = xxx WHERE projects .id = xxx |
app/controllers/projects_controller.rb:9:in `block in update' |
2 | 1.76 | SELECT projects.* FROM projects WHERE projects.priority BETWEEN xxx AND xxx ORDER BY created_at DESC | app/controllers/projects_controller.rb:35:in `index' |
... ...
Tổng số, thời gian trung bình chạy và dòng code gọi mỗi câu query. Từ báo cáo này bạn có thể nắm bắt được query nào chạy nhiều nhất và từ đó lên ý tưởng để có thể tối ưu code chạy tốt hơn.
4. Tổng kết
Thông qua bài viết trên tôi mong giới thiệu cho các bạn một công cụ tuy đơn giản nhưng rất hữu dụng trong việc. Các bạn có thể tìm hiểu thêm thông tin tại: https://github.com/steventen/sql_tracker
All rights reserved