1. Redis là gì?
Redis (Remote Dictionary Server) là một in-memory data structure store — lưu dữ liệu trực tiếp trên RAM, hỗ trợ persistence xuống disk, dùng như cache, message broker, session store, hoặc database tùy use case.
Đặc điểm cốt lõi:
- Single-threaded I/O (dùng event loop) → không có race condition
- Tốc độ cực cao: ~100,000 ops/giây
- Hỗ trợ nhiều kiểu dữ liệu phong phú
- Persistence tùy chọn (RDB snapshot / AOF log)
- Replication, Sentinel, Cluster sẵn có
2. Kiến trúc tổng quan
flowchart TD
Client["Client (Laravel App)"] -->|TCP / RESP Protocol| Redis["Redis Server (RAM)"]
Redis -->|Snapshot| RDB["RDB File (.rdb)"]
Redis -->|Append log| AOF["AOF File (.aof)"]
Redis -->|Replication| Replica["Replica Node"]
Redis -->|Health check| Sentinel["Redis Sentinel"]
Sentinel -->|Failover| Replica
Luồng xử lý 1 request:
sequenceDiagram
participant App as Laravel App
participant Redis
participant DB as Database
App->>Redis: GET user:1
alt Cache HIT
Redis-->>App: Return cached data
else Cache MISS
Redis-->>App: nil
App->>DB: SELECT * FROM users WHERE id=1
DB-->>App: User data
App->>Redis: SET user:1 {data} EX 3600
App-->>App: Return data
end
3. Các kiểu dữ liệu (Data Structures)
3.1 String
Kiểu đơn giản nhất — lưu text, số, JSON serialized, binary.
SET name "Trung Phan"
GET name → "Trung Phan"
SETEX token 3600 "abc123" # TTL 1 giờ
INCR counter # Atomic increment
Laravel:
Cache::put('user:1', $user, 3600);
Cache::get('user:1');
Cache::increment('api_hits:today');
-
Giải thích đoạn code
Đoạn code trên minh họa cách sử dụng Laravel Cache với Redis làm backend:
Cache::put('user:1', $user, 3600); Cache::get('user:1'); Cache::increment('api_hits:today');Chi tiết từng dòng:
1.
Cache::put('user:1', $user, 3600)- Chức năng: Lưu dữ liệu vào cache với key là
user:1 - Tham số:
'user:1'- Key để định danh cache (convention:resource:id)$user- Dữ liệu cần cache (Laravel tự động serialize object)3600- TTL (Time To Live) = 3600 giây = 1 giờ
- Redis command tương ứng:
SETEX user:1 3600 "{serialized_user_data}" - Khi nào dùng: Cache thông tin user sau khi query từ DB để giảm tải cho các request tiếp theo
2.
Cache::get('user:1')- Chức năng: Lấy dữ liệu từ cache theo key
- Return:
- Trả về dữ liệu đã cache (Laravel tự unserialize) nếu key tồn tại và chưa hết hạn
- Trả về
nullnếu key không tồn tại hoặc đã expire
- Redis command tương ứng:
GET user:1
3.
Cache::increment('api_hits:today')- Chức năng: Tăng giá trị counter lên 1 (atomic operation)
- Use case: Đếm số lượng API request trong ngày, page views, download count...
- Redis command tương ứng:
INCR api_hits:today - Đặc điểm:
- Thread-safe: Redis đảm bảo không bị race condition khi nhiều request cùng increment
- Nếu key chưa tồn tại, Redis tự khởi tạo = 0 rồi mới +1
- Có thể dùng
Cache::increment('key', 5)để tăng 5 đơn vị
Ví dụ thực tế kết hợp:
// Controller public function show($id) { // Tăng counter lượt xem Cache::increment("post:$id:views"); // Lấy bài viết từ cache, nếu không có thì query DB $post = Cache::remember("post:$id", 3600, function () use ($id) { return Post::with('author', 'tags')->findOrFail($id); }); // Lấy số lượt xem $views = Cache::get("post:$id:views", 0); return view('post.show', compact('post', 'views')); }Best Practices:
- Naming convention: Dùng pattern
resource:id:attribute(ví dụ:user:123:profile,post:456:comments) - TTL hợp lý:
- Data ít thay đổi: 3600-86400s (1-24h)
- Data thay đổi thường xuyên: 300-900s (5-15 phút)
- Session data: theo config session lifetime
- Xử lý cache miss: Luôn dùng
Cache::remember()thay vìget()+put()riêng lẻ - Invalidation: Nhớ xóa cache khi update data:
Cache::forget('user:1')sau$user->update()
- Chức năng: Lưu dữ liệu vào cache với key là
3.2 Hash
Lưu object dạng field-value — tương tự row trong DB nhưng trên RAM.
HSET user:1 name "Trung" email "trung@test.com" age 28
HGET user:1 name → "Trung"
HGETALL user:1 → {name, email, age}
HDEL user:1 age
Khi nào dùng Hash thay vì String?
- Khi object có nhiều field cần cập nhật từng phần (không muốn deserialize/reserialize toàn bộ)
- Tiết kiệm memory hơn lưu JSON string
Laravel:
// Dùng Redis facade trực tiếp
Redis::hset('user:1', 'name', 'Trung');
Redis::hgetall('user:1');
3.3 List
Linked list — push/pop hai đầu, dùng cho queue, stack, activity feed.
LPUSH notifications "msg1" # Push trái
RPUSH notifications "msg2" # Push phải
LPOP notifications # Pop trái
LRANGE notifications 0 -1 # Lấy tất cả
LLEN notifications # Độ dài
flowchart LR
LPUSH -->|"msg3"| L["[msg3 | msg1 | msg2]"]
L -->|RPOP| RPOP["msg2"]
Dùng như task queue đơn giản:
Redis::rpush('jobs', json_encode($job));
$job = json_decode(Redis::blpop('jobs', 5)); // blocking pop, timeout 5s
3.4 Set
Tập hợp không trùng lặp, không có thứ tự — dùng cho tags, unique visitors, relationships.
SADD tags:post:1 "php" "laravel" "api"
SMEMBERS tags:post:1 → {php, laravel, api}
SISMEMBER tags:post:1 "php" → 1 (true)
SINTER tags:post:1 tags:post:2 # Intersection
SUNION tags:post:1 tags:post:2 # Union
Use case thực tế:
- Lưu danh sách user đã đọc bài viết
- Unique IP visitors trong ngày
- Online users list
3.5 Sorted Set (ZSet)
Giống Set nhưng mỗi member có score → tự động sắp xếp theo score. Dùng cho leaderboard, rate limiting, scheduled jobs.
ZADD leaderboard 1500 "user:1"
ZADD leaderboard 2200 "user:2"
ZADD leaderboard 1800 "user:3"
ZRANGE leaderboard 0 -1 WITHSCORES # Tăng dần
ZREVRANGE leaderboard 0 2 WITHSCORES # Top 3
ZRANK leaderboard "user:1" # Vị trí rank
Laravel — Rate Limiting với Sorted Set:
// Đếm request trong 60s gần nhất
$key = 'rate:user:' . $userId;
$now = microtime(true);
Redis::zadd($key, $now, $now); // Thêm timestamp
Redis::zremrangebyscore($key, 0, $now - 60); // Xóa quá 60s
$count = Redis::zcard($key); // Đếm request
if ($count > 100) abort(429);
3.6 Tổng kết Data Structures
| Kiểu | Dùng khi | Use case thực tế |
|---|---|---|
| String | Lưu giá trị đơn, counter, token | Cache HTML, session token, feature flag |
| Hash | Lưu object nhiều field | User profile, product detail |
| List | Queue/Stack theo thứ tự | Notification feed, task queue, log buffer |
| Set | Tập hợp unique, kiểm tra membership | Tags, unique visitors, follow list |
| Sorted Set | Xếp hạng, thứ tự theo score | Leaderboard, rate limiting, delay queue |
4. TTL & Key Expiration
Mỗi key có thể được gán TTL (Time To Live) — Redis tự động xóa key khi hết hạn.
SET session:abc "data" EX 1800 # 30 phút
EXPIRE user:1 3600 # Set TTL cho key sẵn có
TTL user:1 # Kiểm tra thời gian còn lại
PERSIST user:1 # Xóa TTL → key không bao giờ hết
Cơ chế xóa key expired:
flowchart TD
A[Key hết TTL] --> B{Redis xóa khi nào?}
B --> C["Lazy deletion:\nXóa khi key được truy cập"]
B --> D["Active expiration:\nBackground scan định kỳ 100ms"]
C --> E[Giải phóng memory]
D --> E
Laravel:
Cache::put('key', $value, now()->addMinutes(30)); // TTL
Cache::forever('key', $value); // Không hết hạn
Cache::forget('key'); // Xóa thủ công
5. Persistence — Lưu dữ liệu xuống disk
Redis là in-memory nhưng có thể persist để tránh mất dữ liệu khi restart.
5.1 RDB (Redis Database Snapshot)
- Tạo snapshot toàn bộ data theo định kỳ (mặc định: 900s/1 change, 300s/10 changes)
- Ưu điểm: File nhỏ, restore nhanh, ít ảnh hưởng performance
- Nhược điểm: Có thể mất data giữa 2 lần snapshot
5.2 AOF (Append Only File)
- Ghi log mọi write command vào file
- Ưu điểm: Ít mất data hơn (có thể config
fsync every second) - Nhược điểm: File lớn hơn, restore chậm hơn
| Tiêu chí | RDB | AOF |
|---|---|---|
| Tốc độ restore | Nhanh | Chậm hơn |
| Rủi ro mất data | Cao hơn (vài phút) | Thấp (tối đa 1 giây) |
| Kích thước file | Nhỏ | Lớn hơn |
| Dùng cho | Backup, cache thuần túy | Dữ liệu quan trọng (queue jobs) |
Khuyến nghị: Dùng cả hai (RDB + AOF) cho production.
6. Eviction Policy — Khi RAM đầy
Khi Redis hết memory, nó sẽ xóa key theo policy được cấu hình (maxmemory-policy):
| Policy | Hành vi | Dùng khi |
|---|---|---|
noeviction |
Trả error khi đầy | Dữ liệu không được mất (queue) |
allkeys-lru |
Xóa key ít dùng gần nhất (toàn bộ) | Cache thuần túy ✅ phổ biến nhất |
volatile-lru |
Xóa key có TTL, ít dùng nhất | Mix cache + persistent data |
allkeys-lfu |
Xóa key ít truy cập nhất (tần suất) | Cache với access pattern không đều |
volatile-ttl |
Xóa key sắp hết hạn sớm nhất | Muốn giữ key dài hạn lâu hơn |
Laravel config:
// config/database.php
'redis' => [
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'password' => env('REDIS_PASSWORD'),
'port' => env('REDIS_PORT', 6379),
'database' => 0,
],
]
7. Redis trong Laravel — 4 Use Cases chính
7.1 Cache
flowchart LR
Request --> Cache{"Cache::get(key)"}
Cache -->|HIT| Response
Cache -->|MISS| DB[(Database)]
DB --> Store["Cache::put(key, data, ttl)"]
Store --> Response
// Cache-aside pattern
$users = Cache::remember('users:all', 3600, function () {
return User::with('roles')->get();
});
// Cache tags (grouping)
Cache::tags(['users', 'admin'])->put('user:1', $user, 600);
Cache::tags(['users'])->flush(); // Xóa tất cả cache liên quan user
// Cache với lock (tránh cache stampede)
$value = Cache::lock('process-report')->get(function () {
return DB::table('orders')->sum('amount');
});
Cache Stampede là gì?
Các trường hợp sử dụng thực tế:
- Cache kết quả query phức tạp — Dashboard báo cáo doanh thu, thống kê theo tháng: query nặng nhiều JOIN, chạy lại mỗi request rất tốn kém
$report = Cache::remember('report:revenue:' . $month, 1800, function () use ($month) {
return Order::whereMonth('created_at', $month)
->with('items.product')
->selectRaw('SUM(total) as revenue, COUNT(*) as orders')
->first();
});
- Cache danh sách dropdown/filter — Danh mục sản phẩm, tỉnh/thành, config hệ thống: ít thay đổi nhưng được gọi ở nhiều nơi
$categories = Cache::remember('categories:tree', 86400, function () {
return Category::with('children')->whereNull('parent_id')->get();
});
// Khi admin thêm/sửa category:
Cache::forget('categories:tree');
- Cache API response từ bên thứ 3 — Tỷ giá tiền tệ, thời tiết, dữ liệu từ external API: tránh gọi quá nhiều lần, giảm chi phí và latency
$exchangeRate = Cache::remember('exchange:USD:VND', 3600, function () {
return Http::get('https://api.exchangerate.host/latest?base=USD')->json();
});
- Cache permission/role của user — Mỗi request cần kiểm tra quyền, query DB mỗi lần rất chậm
$permissions = Cache::remember("user:{$userId}:permissions", 600, function () use ($userId) {
return User::find($userId)->getAllPermissions()->pluck('name');
});
// Khi user được cập nhật quyền:
Cache::forget("user:{$userId}:permissions");
- Page view counter — Đếm lượt xem bài viết, sản phẩm một cách atomic, không cần ghi DB mỗi lần
// Tăng counter (atomic, không race condition)
Cache::increment("post:{$postId}:views");
// Sync vào DB định kỳ (ví dụ mỗi 5 phút bằng scheduler)
$views = Cache::get("post:{$postId}:views", 0);
Post::find($postId)->update(['views' => $views]);
7.2 Queue (Job Queue)
flowchart LR
App["Laravel App"] -->|dispatch| Queue["Redis Queue (List)"]
Queue -->|BLPOP| Worker1["Queue Worker 1"]
Queue -->|BLPOP| Worker2["Queue Worker 2"]
Worker1 -->|success| Done["✅ Done"]
Worker1 -->|fail| Retry["Retry / Dead Letter"]
// .env
QUEUE_CONNECTION=redis
// Dispatch job
SendEmailJob::dispatch($user)->onQueue('emails');
ProcessReportJob::dispatch($data)->delay(now()->addMinutes(5));
// Run worker
// php artisan queue:work redis --queue=emails --tries=3
// Job class
class SendEmailJob implements ShouldQueue
{
public $tries = 3;
public $backoff = [10, 30, 60]; // Retry sau 10s, 30s, 60s
public function handle(): void
{
Mail::to($this->user)->send(new WelcomeMail());
}
public function failed(Throwable $e): void
{
// Ghi log, notify Slack...
}
}
Các trường hợp sử dụng thực tế:
- Gửi email/SMS hàng loạt — Sau khi user đăng ký, hệ thống dispatch job gửi welcome email → user không phải chờ
// Trong controller — trả về response ngay, email gửi nền
user::create($data);
SendWelcomeEmailJob::dispatch($user); // ~0ms từ góc nhìn user
return response()->json(['message' => 'Đăng ký thành công']);
- Xử lý ảnh/video sau upload — Resize ảnh, tạo thumbnail, convert video: tác vụ nặng không nên block request
after upload:
ProcessImageJob::dispatch($imagePath, [
'sizes' => [150, 400, 800],
'format' => 'webp',
])->onQueue('media');
- Tạo báo cáo PDF/Excel — Export dữ liệu lớn mất 5-30 giây, không thể để user chờ
// User bấm "Export" → dispatch job → nhận email khi xong
GenerateReportJob::dispatch($filters, $user->email)
->onQueue('reports')
->delay(now()->addSeconds(2));
return response()->json(['message' => 'Báo cáo đang được tạo, chúng tôi sẽ gửi email cho bạn']);
- Đồng bộ dữ liệu với hệ thống ngoài — Sau khi tạo đơn hàng, sync sang ERP/kế toán/CRM mà không block luồng chính
OrderCreated::dispatch($order);
// Trong listener:
class SyncOrderToErp implements ShouldQueue
{
public function handle(OrderCreated $event): void
{
ERP::syncOrder($event->order); // Gọi API bên ngoài, có thể chậm
}
}
- Delayed job — nhắc nhở / hẹn giờ — Gửi email nhắc nhở 24 giờ sau khi user bỏ giỏ hàng
AbandonedCartReminderJob::dispatch($cart)
->delay(now()->addHours(24));
7.3 Session Storage
// .env
SESSION_DRIVER=redis
// config/session.php
'connection' => 'session', // Dùng Redis connection riêng
// Laravel tự động handle — không cần code thêm
// Session key pattern: laravel_session:{session_id}
Tại sao dùng Redis cho session thay vì file/DB?
- Scalable: Nhiều server chia sẻ cùng 1 Redis → stateless
- Nhanh hơn: Không cần I/O disk hay query DB
- TTL tự động: Session tự hết hạn
Các trường hợp sử dụng thực tế:
- Multi-server deployment — App chạy trên 2+ server sau load balancer: session file chỉ tồn tại trên 1 server, Redis giúp mọi server đều đọc được session
User → Load Balancer → Server A (request 1)
→ Server B (request 2) ← đọc session từ Redis, không bị mất login
- Lưu trạng thái multi-step form — Checkout nhiều bước (địa chỉ → thanh toán → xác nhận): lưu data từng bước vào session
// Bước 1
session(['checkout.address' => $request->address]);
// Bước 2
session(['checkout.payment' => $request->payment_method]);
// Bước 3 — lấy toàn bộ
$checkoutData = session('checkout');
- Flash message — Thông báo sau redirect (success/error): Laravel dùng session để pass message giữa các request
return redirect()->route('posts.index')
->with('success', 'Bài viết đã được tạo thành công!');
// Trong view: session('success')
- Giới hạn thiết bị đăng nhập — Chỉ cho phép 1 thiết bị đăng nhập cùng lúc, invalidate session cũ khi login mới
// Khi user login trên thiết bị mới
$oldSessionId = Cache::get("user:{$userId}:session_id");
if ($oldSessionId) {
// Xóa session cũ
Redis::del('laravel_session:' . $oldSessionId);
}
Cache::put("user:{$userId}:session_id", session()->getId(), 86400);
- Remember Token / Keep me logged in — Lưu long-lived token trong Redis thay vì DB để tăng tốc verify
$token = Str::random(60);
Cache::put("remember_token:{$token}", $userId, now()->addDays(30));
// Cookie gửi về browser chứa $token
7.4 Pub/Sub — Real-time Events
-
Phải setup thêm trên FEĐúng! Laravel Broadcasting bắt buộc phải có JS phía frontend mới nhận được event real-time. Đây là cách nó hoạt động:
Luồng hoàn chỉnh
Backend (PHP) WebSocket Server Frontend (JS) ────────────── ──────────────── ───────────── broadcast(event) ──→ Redis Pub/Sub ←── Laravel Echo subscribe ──→ Soketi/Reverb ──→ nhận event → update UIBackend phát event → WebSocket server chuyển tiếp → JS nhận và xử lý.
Các thành phần cần có
1. Backend (PHP) — bạn đã có:
broadcast(new OrderConfirmed($order));2. WebSocket server — cần cài thêm 1 trong:
- Laravel Reverb (mới nhất, built-in Laravel 11) — khuyến nghị
- Soketi (self-hosted, miễn phí)
- Pusher (cloud service, có free tier)
3. Frontend JS — bắt buộc:
npm install laravel-echo pusher-js// resources/js/bootstrap.js import Echo from 'laravel-echo'; import Pusher from 'pusher-js'; window.Echo = new Echo({ broadcaster: 'reverb', // hoặc 'pusher' key: import.meta.env.VITE_REVERB_APP_KEY, wsHost: import.meta.env.VITE_REVERB_HOST, wsPort: import.meta.env.VITE_REVERB_PORT, }); // Lắng nghe event Echo.channel('orders') .listen('OrderConfirmed', (e) => { alert(`Đơn hàng #${e.order.id} đã được xác nhận!`); });
Nếu không muốn dùng JS riêng?
Nếu dùng Livewire hoặc Inertia.js, chúng tích hợp sẵn với Echo nên ít phải tự viết JS hơn. Nhưng về bản chất vẫn luôn cần JS để duy trì kết nối WebSocket — đây là đặc tính của browser, server không thể tự push đến browser mà không có client-side script.
Tóm lại: Redis chỉ là "ống dẫn" trung gian giữa Laravel và WebSocket server. Còn để browser nhận event thật sự → JS là bắt buộc, không có cách nào tránh khỏi.
sequenceDiagram
participant P as Publisher (App)
participant R as Redis
participant S1 as Subscriber 1 (WebSocket)
participant S2 as Subscriber 2 (Logger)
S1->>R: SUBSCRIBE notifications
S2->>R: SUBSCRIBE notifications
P->>R: PUBLISH notifications "new_order:123"
R-->>S1: "new_order:123"
R-->>S2: "new_order:123"
// Publisher
Redis::publish('notifications', json_encode([
'event' => 'new_order',
'order_id' => 123
]));
// Subscriber (thường chạy trong artisan command)
Redis::subscribe(['notifications'], function ($message) {
$data = json_decode($message);
// Xử lý event...
});
Laravel Broadcasting với Redis:
// .env
BROADCAST_DRIVER=redis
// Event
class OrderCreated implements ShouldBroadcast
{
public function broadcastOn(): Channel
{
return new Channel('orders');
}
}
// Frontend (Laravel Echo)
Echo.channel('orders').listen('OrderCreated', (e) => {
console.log(e.order);
});
Các trường hợp sử dụng thực tế:
- Thông báo đơn hàng real-time — Khi admin xác nhận đơn, user nhận được notification ngay trên web mà không cần F5
// Backend khi admin confirm order
broadcast(new OrderConfirmed($order))->toOthers();
// Frontend
Echo.private(`orders.${userId}`)
.listen('OrderConfirmed', (e) => {
showNotification(`Đơn hàng #${e.order.id} đã được xác nhận!`);
});
- Live chat / Tin nhắn — Hệ thống chat giữa user và support, tin nhắn hiển thị ngay khi gửi
// Khi user gửi tin
broadcast(new MessageSent($message));
// Recipient nhận qua WebSocket (Laravel Echo + Pusher/Soketi)
Echo.private(`chat.${roomId}`).listen('MessageSent', (e) => {
appendMessage(e.message);
});
- Dashboard thống kê live — Màn hình monitoring đơn hàng, số user online, doanh thu cập nhật theo giây
// Scheduler chạy mỗi phút, broadcast stats mới
Redis::publish('dashboard:stats', json_encode([
'online_users' => Cache::get('online_users_count'),
'orders_today' => Order::today()->count(),
'revenue_today' => Order::today()->sum('total'),
]));
- Invalidate cache trên nhiều server — Khi 1 server update data, broadcast cho tất cả server còn lại xóa cache cũ
// Server A update product
$product->update($data);
Cache::forget("product:{$product->id}");
Redis::publish('cache:invalidate', json_encode([
'key' => "product:{$product->id}"
]));
// Server B, C lắng nghe và xóa cache của họ
Redis::subscribe(['cache:invalidate'], function ($message) {
$data = json_decode($message);
Cache::forget($data->key);
});
- Tracking trạng thái online — Hiển thị "user đang online" trong app chat/collaboration
// Khi user active
Cache::put("user:{$userId}:online", true, 60); // TTL 60s
Redis::publish('presence', json_encode(['user_id' => $userId, 'status' => 'online']));
// Middleware tự gia hạn TTL mỗi request
8. Redis Streams — Nâng cao
Redis Streams (từ Redis 5.0) là append-only log — giống Kafka nhưng đơn giản hơn. Khác với List/Pub/Sub:
- Message persist sau khi consumer đọc
- Hỗ trợ Consumer Groups — nhiều consumer đọc song song, mỗi message chỉ xử lý 1 lần
- Message có ID dựa trên timestamp
XADD orders * product_id 101 qty 2 user_id 5
XREAD COUNT 10 STREAMS orders 0
XGROUP CREATE orders processing $
XREADGROUP GROUP processing worker1 COUNT 5 STREAMS orders >
XACK orders processing {message-id}
So sánh Pub/Sub vs List vs Streams:
| Tiêu chí | Pub/Sub | List (Queue) | Streams |
|---|---|---|---|
| Message persistence | ❌ Không | ✅ Đến khi LPOP | ✅ Giữ mãi |
| Consumer groups | ❌ | ✅ Competing consumers | ✅ Named groups |
| Replay messages | ❌ | ❌ | ✅ |
| Phù hợp | Real-time broadcast | Laravel Queue | Event sourcing, audit log |
9. High Availability
9.1 Replication
flowchart LR
Master["Master (Read/Write)"] -->|Async replication| R1["Replica 1 (Read-only)"]
Master -->|Async replication| R2["Replica 2 (Read-only)"]
- Mọi write đến Master, read có thể phân tán sang Replica
- Replica là bản sao real-time của Master
9.2 Sentinel
flowchart TD
S1[Sentinel 1] & S2[Sentinel 2] & S3[Sentinel 3] -->|Monitor| Master
Master -->|Replication| R1[Replica]
Master -- "❌ Xuống" --> Failover[Sentinel bầu chọn]
Failover --> R1
R1 -- "Promote" --> NewMaster[New Master]
9.3 Redis Cluster
Sharding tự động data ra nhiều node — dùng khi data quá lớn cho 1 máy.
- Data chia thành 16384 hash slots
- Mỗi node quản lý 1 phần slots
- Tự động rebalance khi thêm/bớt node
10. Atomic Operations & Lua Scripting
Redis single-threaded nên mọi command đều atomic. Với nhiều command cần thực thi như 1 unit → dùng MULTI/EXEC hoặc Lua script.
# Transaction
MULTI
SET balance:user:1 900
SET balance:user:2 1100
EXEC
-- Lua script: atomic check-and-decrement
local balance = redis.call('GET', KEYS[1])
if tonumber(balance) >= tonumber(ARGV[1]) then
redis.call('DECRBY', KEYS[1], ARGV[1])
return 1
else
return 0
end
// Laravel — Lua script
$result = Redis::eval(
file_get_contents('scripts/decrement.lua'),
1, // số lượng KEYS
'balance:1', // KEYS[1]
100 // ARGV[1]
);
11. Best Practices & Anti-patterns
✅ Nên làm
- Đặt TTL cho mọi cache key — tránh key tồn tại vô thời hạn làm đầy RAM
- Namespace key có cấu trúc:
{app}:{entity}:{id}:{field}(e.g.myapp:user:1:profile) - Dùng connection pool — không tạo mới connection mỗi request
- Dùng SCAN thay KEYS trong production —
KEYS *block Redis - Monitor với
redis-cli --latency,INFO memory,SLOWLOG - Dùng Redis database riêng cho cache vs queue (DB 0 cho cache, DB 1 cho queue)
❌ Tránh
| Anti-pattern | Vấn đề | Giải pháp |
|---|---|---|
KEYS * trong production |
Block Redis hoàn toàn | Dùng SCAN với cursor |
| Lưu object quá lớn (>1MB) | Tăng latency, lãng phí RAM | Compress hoặc lưu S3 |
| Không set TTL cho cache | Memory leak | Luôn set TTL hoặc allkeys-lru |
| Dùng Redis như primary DB | RAM đắt, giới hạn query | Redis là lớp cache, không thay DB |
| Không handle cache miss storm | Cache stampede | Dùng Lock hoặc stale-while-revalidate |
12. Lộ trình học Redis (cho Laravel Dev)
- Bước 1 — Nền tảng: Cài Redis local, thử các command cơ bản với
redis-cli - Bước 2 — Data structures: Thực hành từng kiểu, hiểu khi nào dùng cái nào
- Bước 3 — Laravel Cache: Implement cache-aside pattern trong project thực
- Bước 4 — Laravel Queue: Chuyển một feature sang background job với Redis driver
- Bước 5 — Session & Auth: Cấu hình session Redis, kết hợp với JWT/token cache
- Bước 6 — Performance: Học
MONITOR,SLOWLOG,INFO, đo latency - Bước 7 — Production: Tìm hiểu Sentinel/Cluster, eviction policy, persistence config
- Bước 8 — Advanced: Redis Streams, Lua scripting, atomic operations