Các kỹ thuật nâng cao trong Ruby on Rails dành cho **Senior Developer**.
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
All rights reserved