1. Hiệu suất và tối ưu hóa
1.1. Tránh N+1 Query với Eager Loading
Nếu không dùng eager loading, Rails sẽ chạy N+1 query khi truy vấn dữ liệu có liên kết.
Ví dụ:
# N+1 Query (Không tối ưu)
users = User.all
users.each do |user| puts user.posts.count # Mỗi lần gọi .posts.count sẽ thực hiện một truy vấn SQL riêng
end
Giả sử có 100 users, điều này sẽ chạy 1 truy vấn lấy users + 100 truy vấn lấy posts.
🔹 Giải pháp: Dùng includes
để eager load dữ liệu.
# Tối ưu hóa với Eager Loading
users = User.includes(:posts).all
users.each do |user| puts user.posts.count # Không truy vấn SQL lặp lại
end
Rails sẽ chỉ chạy 2 query:
- Lấy tất cả users
- Lấy tất cả posts liên quan
Bạn có thể dùng Bullet gem để phát hiện lỗi N+1:
# Gemfile
gem 'bullet', group: :development
Sau đó, cấu hình Bullet để log cảnh báo:
# config/environments/development.rb
config.after_initialize do Bullet.enable = true Bullet.alert = true Bullet.bullet_logger = true
end
1.2. Caching nâng cao
Cache Fragment (Lưu trữ từng phần HTML)
<% cache @user do %> <%= @user.name %> <%= @user.bio %>
<% end %>
Rails sẽ lưu kết quả vào cache để không cần truy vấn lại DB nếu dữ liệu chưa thay đổi.
Low-level Caching (Lưu trữ dữ liệu trong bộ nhớ)
Rails.cache.fetch("top_articles", expires_in: 1.hour) do Article.order(views: :desc).limit(10)
end
🔹 Redis cache được dùng để lưu trữ dữ liệu nhanh hơn:
# Cấu hình Redis
config.cache_store = :redis_cache_store, { url: "redis://localhost:6379/0" }
2. Cấu trúc dự án và kiến trúc phần mềm
2.1. Service Objects (Tách logic ra khỏi Model & Controller)
Trước đây (Viết hết trong Controller - Code lộn xộn)
class UsersController < ApplicationController def create @user = User.new(user_params) if @user.save UserMailer.welcome_email(@user).deliver_later redirect_to @user else render :new end end
end
🔹 Tối ưu với Service Object:
# app/services/user_creator.rb
class UserCreator def initialize(user_params) @user_params = user_params end def call user = User.create(@user_params) UserMailer.welcome_email(user).deliver_later if user.persisted? user end
end
Controller gọi service:
class UsersController < ApplicationController def create @user = UserCreator.new(user_params).call if @user.persisted? redirect_to @user else render :new end end
end
2.2. Query Objects (Tách truy vấn phức tạp ra khỏi Model)
Thay vì viết query dài trong Model:
class User < ApplicationRecord scope :active, -> { where(active: true) }
end
Dùng Query Object để quản lý:
# app/queries/active_users_query.rb
class ActiveUsersQuery def initialize(relation = User.all) @relation = relation end def call @relation.where(active: true) end
end
Gọi như sau:
users = ActiveUsersQuery.new.call
3. API và GraphQL
3.1. GraphQL API
Dùng graphql-ruby
để định nghĩa API linh hoạt hơn REST.
Tạo một query:
class Types::QueryType < Types::BaseObject field :user, Types::UserType, null: false do argument :id, ID, required: true end def user(id:) User.find(id) end
end
Gọi API:
query { user(id: 1) { id name email }
}
4. Bảo mật nâng cao
4.1. Rate Limiting (Giới hạn số request tránh DDoS)
Sử dụng Rack::Attack để giới hạn request:
class Rack::Attack throttle("req/ip", limit: 100, period: 1.minute) do |req| req.ip end
end
5. Tối ưu Active Record
5.1. Partitioning Tables
Chia bảng theo tháng giúp truy vấn nhanh hơn.
CREATE TABLE orders ( id SERIAL PRIMARY KEY, created_at TIMESTAMP NOT NULL
) PARTITION BY RANGE (created_at);
Tạo bảng con cho từng tháng:
CREATE TABLE orders_2025_03 PARTITION OF orders
FOR VALUES FROM ('2025-03-01') TO ('2025-04-01');
6. Quản lý công việc lớn và batch processing
6.1. Batch Processing với Sidekiq
Dùng Sidekiq để xử lý job nặng:
class ImportCsvJob include Sidekiq::Worker def perform(csv_file_path) CSV.foreach(csv_file_path, headers: true) do |row| User.create!(row.to_h) end end
end
Gọi job từ controller:
ImportCsvJob.perform_async("/path/to/file.csv")
7. DevOps và CI/CD
7.1. Dockerizing Rails
Tạo Dockerfile để deploy Rails app:
FROM ruby:3.2
WORKDIR /app
COPY . .
RUN bundle install
CMD ["rails", "server"]
Chạy app với Docker Compose:
version: "3"
services: web: build: . ports: - "3000:3000"
8. WebSockets và Real-time Features
8.1. ActionCable (Realtime Chat)
Rails hỗ trợ WebSockets với ActionCable:
class ChatChannel < ApplicationCable::Channel def subscribed stream_from "chat_#{params[:room]}" end
end
Frontend kết nối WebSocket:
App.cable.subscriptions.create({ channel: "ChatChannel", room: "general" }, { received(data) { console.log(data); }
});
Tóm tắt
✅ Tránh N+1 query với Eager Loading
✅ Sử dụng Service Object và Query Object
✅ Tăng tốc API với GraphQL và Redis cache
✅ Bảo mật với JWT, Rate Limiting
✅ Dùng Sidekiq, Docker, WebSockets cho hiệu suất cao