Rails performance bị ảnh hưởng bởi nhiều yếu tố , đặc biệt là việc config của bạn trên server deployment. Tuy nhiên, quá trình code cũng mang lại sức ảnh hưởng lớn và quyết định rằng trang web sẽ tương tác nhanh hay chậm. Trong chủ đề ngày hôm nay, mình chia sẻ vài tips và "best coding practices" để cải thiện performance trong Rails.
1. Tối ưu hoá Ruby code:
Vấn đề hiển nhiên trong mọi dự án, hẳn bạn cũng nhận ra bản chất Rails app chính là Ruby code - thứ sẽ vận hành app. Hãy chắc chắn rằng code của bạn mang lại hiệu quả tốt nhất trên quan điểm Ruby dev. Kiểm tra một vòng quanh code và tự hỏi nếu việc refactor được thực hiện trên các fuction có sẵn dựa trên 2 yếu tố performance và hiệu quả của thuật toán có mang lại sự khác biệt. Sử dụng profiling tools(rack-mini-profiler, DevTrace, Scout, percona(MySQL)) mang lại hiệu quả cao khi xác định phần code gây ảnh hưởng lớn đến performance hoặc dựa vào vài tiêu chí chung được đưa ra:
- Khi classes và methods đã được dựng sẵn, hãy tận dụng nó thay vì việc cố gắng xây lại "rolling your own".
- Sử dụng Regular expression thay vì những vòng lặp tốn kém, khi bạn cần phân tích và xử lý tất cả dữ liệu, ngoại trừ một vài dữ liệu nhỏ lẻ.
- Sử dụng Libxml thay cho Rexml trong trường hợp làm việc với tài liệu XML.
- Đôi lúc cần phải đánh đổi giữa việc đặt biến và tạo method (nếu không thật sự cần thiết) cho tốc độ.
- Cách tốt nhất để giải quyết những vòng lặp tốn kém là loại bỏ chúng nếu có thể. Không phải luôn luôn như vậy, nhưng trong một vài trường hợp có thể loại bỏ vòng lặp bằng cách refactor lại code.
- Đơn giản hoá và giảm thiểu nested if/unless nhiều nhất có thể, thay vào đó sử dụng ||= (a ||= b <=> a || a = b).
- Hashes là một "expensive" cấu trúc dữ liệu. Luôn cân nhắc đến việc lưu trữ giá trị đối với key được cho sẵn trong local variable nếu bạn cần gọi lại vài lần trong một file. Khái quát hơn, lưu trữ dữ liệu thường được sử dụng trong một biến (local, instance hoặc class variable) luôn là ý tưởng tốt.
2. Caching:
Caching có thể làm tăng tốc độ của app một cách đáng kể. Cụ thể:
- Xử lý cache trong model bằng tay hoặc thông qua plugin acts_as_cached.
- Sử dụng MemCacheStore hoặc Redis như nơi lưu trữ Session.
- Cache trang của bạn thông qua fragment cache (extended_fragment_cache) .
3. Sử dụng extent hỗ trợ từ database:
Đừng ngại ngần trong việc sử dụng những feature được cung cấp bởi database, thậm chí việc đó không được hỗ trợ trực tiếp bởi Rails và làm như vậy có nghĩa là bỏ qua ActiveRecord. Ví dụ, định nghĩa store procedures và functions, bạn có thể sử dụng chúng bằng cách giao tiếp trực tiếp với database thông qua driver call, thay vì các phương thức của ActiveRecord. Điều này có thể cải thiện đáng kể hiệu suất của Rails app bị hạn chế bởi ràng buộc dữ liệu.
4. Finders:
Finders rất dễ sử dụng, cung cấp công cụ để bạn code dễ hiểu mà không yêu cầu kiến thức SQL chuyên sâu. Nhưng truy xuất dữ liệu ở cấp độ cao sẽ đi kèm với xử lý tính toán. Để tránh gặp rắc rối với vấn đề xử lý dữ liệu, một vài quy tắc cơ bản đã được thành lập:
- Chỉ lấy thông tin mà bạn thật sự cần giúp tiết kiệm khoảng lớn thời gian thực thi khi lựa chọn dữ liệu không thực sự cần thiết. Khi sử dụng các công cụ tìm kiếm khác nhau, hãy đảm bảo cung cấp các tùy chọn phù hợp để chỉ chọn các trường bắt buộc (: select) và nếu bạn chỉ cần một tập hợp con các bản ghi được đánh số từ tập kết quả, hãy chỉ định một giới hạn (với tùy chọn: limit và: offset ).
- Đừng giết database của bạn với quá nhiều truy vấn, hãy sử dụng tính năng eager_loading thông qua câu lệnh include: Giả sử model A và B cố mối quan hệ A has_many: Bs, có thể kiểm tra association bằng A.reflections.keys sau đó khi truy vấn model A, trong quá trình xử lý có dính dáng đến model B thì A.include(:Bs) để giảm thiểu số câu queries (giải quyết vấn đề n+1 queries).
- Tránh các công cụ tìm động như
MyModel.find_by_*
. Mặc dù việc sử dụng một cái gì đó nhưUser.find_by_username
rất dễ đọc và dễ dàng, nhưng nó cũng có thể khiến bạn tốn kém rất nhiều tài nguyên. Trên thực tế, ActiveRecord tạo các phương thức này trong method_missing và điều này khá chậm. Một khi phương thức được xác định và gọi, phản ánh với thuộc tính model (username trong ví dụ) đạt được thông qua truy vấn select được tạo trước khi được gửi đến database. Sử dụngMyModel.find_by_sql
trực tiếp hoặc thậm chíMyModel.find
, hiệu quả hơn nhiều. - Đảm bảo sử dụng
MyModel.find_by_sql
bất cứ khi nào bạn cần chạy truy vấn SQL được tối ưu hóa. Không cần phải nói, ngay cả khi câu lệnh SQL cuối cùng kết thúc giống nhau, find_by_sql hiệu quả hơn find tương đương (không cần phải xây dựng chuỗi SQL thực tế từ các tùy chọn khác nhau được truyền cho phương thức). Tuy nhiên, nếu bạn đang xây dựng một plugin cần đa nền tảng, hãy xác minh rằng các truy vấn SQL sẽ chạy trên tất cả các database được Rails hỗ trợ hoặc chỉ sử dụng find. Nói chung, việc sử dụng find dễ đọc hơn và dẫn đến code có thể bảo trì tốt hơn, vì vậy trước khi bắt đầu sử dụng find_by_sql với app, hãy thực hiện một số cấu hình và phân loại các truy vấn chậm có thể cần được tùy chỉnh và tối ưu hóa theo cách thủ công.
5. Group operations in a transaction
ActiveRecord kết thúc việc tạo hoặc cập nhật record trong một transaction duy nhất. Nhiều lần "insert record" sẽ tạo ra nhiều transaction (một lần cho mỗi lần "insert"). Nhóm việc "insert record" trong một transaction duy nhất sẽ đẩy nhanh tiến độ. Ví dụ, rollback tất cả thông tin khi "insert record" bị lỗi.
ActiveRecord::Base.transaction do
Post.each do |p|
Comment.create!(name: p.name)
end
end
6. Control your controllers:
Filters khá tốn tài nguyên, đừng lạm dụng chúng. Ngoài ra, đừng sử dụng quá nhiều instance variables mà view của bạn không thực sự yêu cầu (chúng không nhẹ).
7. Sử dụng HTML cho view:
Trong view của bạn, đừng lạm dụng helpers. Mỗi khi bạn sử dụng form helpers, bạn đang bổ sung một step ngoài lề. Có thực sự cần helpers để viết HTML cho một đường link, text box hoặc một form cho bạn không? (Bạn thậm chí có thể làm cho designer của mình, những người không biết Ruby, hạnh phúc!)
8. Ghi log:
Cấu hình các app của bạn để chúng chỉ ghi lại những thông tin hoàn toàn quan trọng đối với bạn. Ghi log là một hoạt động tốn kém và cấp độ không phù hợp (ví dụ: Logger :: DEBUG) có thể làm tê liệt production của bạn.
9. Patch the GC:
Không hẳn là vấn đề về code, Ruby’s Garbage Collection được khuyên dùng và sẽ cải thiện đáng kể tốc độ của các app Ruby và Rails của bạn.
10. Final note:
Không ủng hộ việc tối ưu hóa quá sớm, nhưng nếu bạn có thể, hãy làm việc trên code của bạn với những nguyên tắc này (nhưng cũng đừng lạm dụng nó). Có thể thực hiện các thay đổi và chỉnh sửa vào phút cuối nhưng ít được khuyến khích so với kiểu viết code với ý thức về “performance aware”. Lập profile cho app của bạn, đánh giá chúng (benchmark) và có trải nghiệm thú vị.
Hi vọng sau bài viết này mọi người sẽ rút ra kinh nghiệm cho riêng mình. Happy coding!