Caching trong NestJS: Tối ưu hóa hiệu suất với Redis
- Published on
Caching là một trong những kỹ thuật quan trọng nhất trong việc tối ưu hóa hiệu suất ứng dụng. Trong bài viết này, chúng ta sẽ tìm hiểu chi tiết cách tích hợp caching trong NestJS với Redis để tối ưu hóa tốc độ xử lý và nâng cao trải nghiệm người dùng.
Nội dung
- 1. Tại sao cần caching?
- 2. Redis là gì và tại sao chọn Redis?
- 3. Tích hợp caching trong NestJS với Redis
- 4. Chiến lược caching nâng cao
- 5. Ứng dụng thực tế: Hệ thống ngân hàng
- 6. Theo dõi và quản lý dữ liệu trong Redis
- 7. Kết luận
1. Tại sao cần caching?
- Tăng hiệu suất: Giảm thời gian phản hồi khi truy xuất dữ liệu.
- Giảm tải cơ sở dữ liệu: Hạn chế số lượng truy vấn database.
- Tăng trải nghiệm người dùng: Giúp tối ưu hóa tốc độ tải trang.
2. Redis là gì và tại sao chọn Redis?
Redis là một cơ sở dữ liệu NoSQL dưới dạng key-value, hoạt động trên bộ nhớ RAM, giúp truy xuất nhanh chóng.
Điểm nổi bật của Redis:
- Hiệu suất cao: Hỗ trợ hàng triệu yêu cầu mỗi giây.
- Hỗ trợ đa dạng: Lưu trữ nhiều dạng dữ liệu như chuỗi, danh sách, hash, set.
- Phân tán: Hỗ trợ clustering, tối ưu cho hệ thống lớn.
3. Tích hợp caching trong NestJS với Redis
Bước 1: Cài đặt các thư viện
Đầu tiên, cài đặt các thư viện cần thiết:
npm install cache-manager cache-manager-redis-store ioredis
Bước 2: Cấu hình CacheModule
NestJS cung cấp CacheModule để làm việc với caching. Cách tích hợp Redis như sau:
import { Module, CacheModule } from '@nestjs/common';
import * as redisStore from 'cache-manager-redis-store';
@Module({
imports: [
CacheModule.register({
store: redisStore,
host: 'localhost', // Địa chỉ Redis server
port: 6379, // Cổng mặc định của Redis
ttl: 60, // Thời gian tồn tại cache (giây)
}),
],
})
export class AppModule {}
Bước 3: Sử dụng CacheService
Dưới đây là cách lưu và lấy dữ liệu từ cache:
import { Injectable, CacheService } from '@nestjs/common';
@Injectable()
export class MyService {
constructor(private readonly cacheService: CacheService) {}
async getData(key: string): Promise<any> {
// Kiểm tra dữ liệu trong cache
const cachedData = await this.cacheService.get(key);
if (cachedData) {
return cachedData;
}
// Lấy dữ liệu từ database
const data = await this.fetchFromDatabase(key);
// Lưu vào cache
await this.cacheService.set(key, data, { ttl: 120 }); // Cache tồn tại trong 120 giây
return data;
}
private async fetchFromDatabase(key: string): Promise<any> {
// Giả lập truy vấn database
return { key, value: 'Dữ liệu từ database' };
}
}
Bước 4: Sử dụng Decorator @Cacheable()
NestJS hỗ trợ decorator @Cacheable()
để tự động caching.
import { Injectable } from '@nestjs/common';
import { Cacheable } from '@nestjs/cache-manager';
@Injectable()
export class MyService {
@Cacheable({ ttl: 300 }) // Cache trong 300 giây
async fetchData(id: number): Promise<any> {
console.log('Fetching data from database...');
return { id, name: 'Example' };
}
}
4. Chiến lược caching nâng cao
4.1 Cache Invalidation
Cache invalidation là quá trình xóa cache khi dữ liệu gốc thay đổi.
- TTL (Time-To-Live): Quy định thời gian tồn tại của cache, giúp tự động xóa cache khi hết hạn.
- Manual Invalidation: Xóa thủ công khi dữ liệu được cập nhật hoặc xóa. Ví dụ:
await this.cacheService.del('key-to-remove');
4.2 Cache Hierarchy
Sử dụng nhiều lớp cache:
- In-Memory Cache: Lưu dữ liệu thường xuyên được truy cập.
- Distributed Cache (Redis): Lưu dữ liệu cho các máy chủ trong hệ thống phân tán.
4.3 Cache Stampede
Giảm thiểu tình trạng nhiều yêu cầu đồng thời làm quá tải hệ thống cache. Cách tiếp cận:
- Locking: Sử dụng Redis lock để đảm bảo chỉ một yêu cầu được thực thi tại một thời điểm.
- Randomized TTL: Thêm một khoảng thời gian ngẫu nhiên vào TTL để tránh việc tất cả cache hết hạn cùng lúc.
5. Ứng dụng thực tế: Hệ thống ngân hàng
Trong hệ thống ngân hàng, caching đóng vai trò quan trọng trong việc tối ưu hóa hiệu suất và giảm tải cho cơ sở dữ liệu.
Ví dụ: Lấy danh sách chi nhánh ngân hàng
Danh sách chi nhánh ngân hàng thường ít thay đổi và được sử dụng trong nhiều yêu cầu khác nhau, như khi người dùng tìm kiếm chi nhánh gần nhất. Dữ liệu này rất phù hợp để lưu vào Redis.
Cách triển khai:
import { Injectable, CacheService } from '@nestjs/common';
@Injectable()
export class BranchService {
constructor(private readonly cacheService: CacheService) {}
async getBranchList(): Promise<any[]> {
const cacheKey = 'branch-list';
// Kiểm tra cache
const cachedBranches = await this.cacheService.get<any[]>(cacheKey);
if (cachedBranches) {
return cachedBranches;
}
// Truy vấn cơ sở dữ liệu (giả lập)
const branches = await this.queryDatabaseForBranches();
// Lưu vào cache
await this.cacheService.set(cacheKey, branches, { ttl: 3600 }); // Cache 1 giờ
return branches;
}
private async queryDatabaseForBranches(): Promise<any[]> {
// Giả lập truy vấn database
console.log('Querying database for branch list...');
return [
{ id: 1, name: 'Chi nhánh Hà Nội', address: 'Số 1, Đường ABC, Hà Nội' },
{ id: 2, name: 'Chi nhánh TP HCM', address: 'Số 2, Đường XYZ, TP HCM' },
];
}
}
Kết quả:
- Lần gọi đầu tiên: Truy vấn cơ sở dữ liệu và lưu vào cache.
- Các lần sau: Lấy dữ liệu từ Redis, giảm tải cho hệ thống database.
6. Theo dõi và quản lý dữ liệu trong Redis
6.1 Kiểm tra dữ liệu trong Redis bằng Redis CLI
Redis cung cấp một công cụ CLI để kiểm tra dữ liệu:
Kết nối đến Redis server:
redis-cli
Liệt kê các key trong Redis:
keys *
Xem giá trị của một key cụ thể:
get <key>
Xóa một key cụ thể:
del <key>
6.2 Sử dụng RedisInsight để quản lý dữ liệu
RedisInsight là một công cụ GUI cho phép bạn:
- Xem danh sách key: Dễ dàng duyệt qua các key đang lưu trong Redis.
- Kiểm tra giá trị: Click vào một key để xem giá trị chi tiết.
- Xóa hoặc cập nhật dữ liệu: Hỗ trợ thao tác xóa hoặc chỉnh sửa giá trị của key.
6.3 Theo dõi dữ liệu động với lệnh monitor
Redis hỗ trợ lệnh monitor
để xem các thao tác đang diễn ra trên server:
redis-cli monitor
Lệnh này sẽ hiển thị mọi thao tác đọc, ghi, hoặc xóa trên Redis server, rất hữu ích để debug.
7. Kết luận
Caching là một kỹ thuật mạnh mẽ để tối ưu hóa hiệu suất ứng dụng. Khi tích hợp Redis với NestJS, bạn có thể tận dụng khả năng lưu trữ nhanh chóng và linh hoạt của Redis để cải thiện trải nghiệm người dùng.