Có khá là nhiều cách chúng ta có thể cải thiện hiệu suất của ứng dụng. Một trong những cách căn bản và dễ nhìn thấy nhất là tối ưu code để xử lý nhanh hơn và tối ưu các câu truy vấn đến database. Để API hoạt động hiệu quả và tối ưu hơn nữa, chúng ta có thể tránh việc xử lý những thao tác có cùng kết quả.

Việc truy cập dữ liệu trong database tốn khá nhiều thời gian. Thêm vào đó chúng ta cũng thực hiện một số thao tác dữ liệu trên nó trước khi trả về người dùng cũng tiêu tốn nguồn tài nguyên. May thay chúng ta có thể cải thiện chúng bằng cách sử dụng cache để tăng tốc độ. Bằng cách tạo một bảng sao dữ liệu vào cache để phản hồi lại nhanh hơn. Như các bạn biết, vì khi cache, dữ liệu được lưu trên memory. Và tốc độ truy xuất trên memory nhanh hơn rất rất nhiều lần so với disk ( database của chúng ta ). Nhưng cũng tránh làm dụng sử dụng cache vì chi phí trên memory đắt hơn nhiều lần so với disk. Bạn có thể theo dõi video mình chia sẽ rõ hơn.

Theo dõi source code tại đâybranch dev nha

Link postman để test cho dễ.

Implementing in-memory cache

Để quản lý Cache ta sử dụng thư viện mà NestJS đã cung cấp:

npm install cache-manager

Để sử dụng bất cứ thứ gì được khai báo thì ta cần phải import vào trong module. Thằng cache này cũng không ngoại lệ:

import { CacheModule, Module } from '@nestjs/common';
...
@Module({
  imports: [
    // CacheModule.register(),
    CacheModule.register({
      ttl: 10,
      max: 100,
    }),
    ...
  ],
  ...
})
export class PostModule {}

Mặc định như khai báo, thời gian mà 1 response được lưu trữ vào memory là 5 giây, sau đó nó sẽ tự động xóa đi. Và số lượng phần tử lưu trong cache là 100. chúng ta có thể tùy chỉnh thông qua phương thức register như mình đã làm.

Automatically caching responses

NestJS được trang bị sẵn CacheInterceptor sẽ tự động xử lý cache mà ta không cần làm gì hơn ngoài việc khai báo nó vào controller.

import {
  Body,
  CacheInterceptor,
  ...
  UseInterceptors,
} from '@nestjs/common';
...
@Controller('post')
export class PostController {
  ...

  @Get(':id/get-with-cache')
  @UseInterceptors(CacheInterceptor)
  async getDetailPostWithCache(@Param() { id }: FindPostDto) {
    console.log('handle');
    return (await this.postService.getPostById(id)).toJSON();
  }
}

Khi sử dụng Postman để call api này 2 lần. Lần đầu chúng ta sẽ lấy log “handle” ở màn hình console. Lần 2 thì không thấy nữa mặc dù kết quả trả về như nhau. Tức lần 2 NestJS không gọi function getDetailPostWithCache mà nó trả về dữ liệu được ưu trữ trong cache.

Using the cache store manually

Giả sử như chúng ta không muốn cache response tự động như ở trên, mà ta muốn cache bất kì thứ gì đó như họ tên hay email thì sao? NestJS cũng hỗ trợ chúng ta qua một số phương thức đơn giản như sau:

Nhưng trước tiên ta cần khai báo cacheManager thì mới dùng được

import {
  CACHE_MANAGER,
  Inject,
  ...
} from '@nestjs/common';
...
import { Cache } from 'cache-manager';

@Injectable()
export class PostService {
  constructor(
    ...
    @Inject(CACHE_MANAGER) private cacheManager: Cache,
  ) {}
  ...
}

Để lấy giá trị từ key:

const value = await this.cacheManager.get('key');

Ngược lại, để set giá trị vào 1 key nào đó:

await this.cacheManager.set('key', 'value');

Thời gian hết hạn của cache được cấu hình trong module, mặc định là 5 giây. Nếu muốn lâu hơn ta có thể truyền thêm thời gian vào tham số thứ 3:

await this.cacheManager.set('key', 'value', 1000);

Hoặc bỏ thời hạn luôn thì truyền 0 vào:

await this.cacheManager.set('key', 'value', 0);

Để xóa 1 key trong cache ta dùng del:

await this.cacheManager.del('key');

Cuối cùng là xóa toàn bộ:

await this.cacheManager.reset();

Cache with Redis

Redis là kho lưu trữ key-value nhanh và đáng tin cậy. Nó cũng lưu trữ dữ liệu trên memory (vì nó là cache mà, hehe). Và cơ chế ghi dữ liệu vào disk mỗi 2 giây có thể tùy chỉnh.

Việc sử dụng Redis có gì khác với Cache thông thường như đã giới thiệu bên trên. Và đương nhiên phải có gì đó thì người ta mới sử dụng Redis khá là phổ biến. Ưu điểm vượt trội ở đây là “Dữ liệu không bị mất khi Restart Server”. Và chỉ cần mỗi nguyên nhân này chúng ta nên dùng redis cho dự án với NestJS – Phải build liên tục mỗi khi cập nhật.

Bạn có thể test với cache thông thường phía trên. Khi set một giá trị vào cache, mặc dù chưa hết hạn ttl thì khi start lại server chúng ta không thể get được giá trị đó. Nhưng khi dùng redis thì khác, nó vẫn được lưu và sử dụng lại 1 cách bình thường. Ở đây mình sẽ không tìm hiểu redis là gì, mà cách tích hợp redis vào NestJS thoi.

Setting up Redis

Việc cài đặt Redis vào server ( máy tính của bạn ) có thể cài trực tiếp hoặc cài đặt thông qua docker vô cùng dễ dàng. Mọi người theo dõi tại trang chủ của redis https://redis.io/docs/getting-started/installation/

Ta cần cài thêm thư viện sau khi cài thư viện cache-manager ở trên hoặc cài cả 2 nếu chưa cài cache-manager:

npm install cache-manager-redis-store

Đầu tiên ta cần khai báo cache with redis vào module để sử dụng nha:

import { CacheModule, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import * as redisStore from 'cache-manager-redis-store';
...
@Module({
  imports: [
    ...
    // CacheModule.register({
    //   ttl: 10,
    // }),
    CacheModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        // isGlobal: true,
        store: redisStore,
        host: configService.get<string>('REDIS_HOST'),
        port: configService.get<number>('REDIS_PORT'),
        password: configService.get<string>('REDIS_PASSWORD'),
      }),
    }),
  ],
  ...
})
export class PostModule {}

Trong file .env khai báo cấu hình cho redis

# redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=

Xong rồi, code tương tự ở trên. Thử set, sau đó restart lại server xem sao, dữ liệu có bị mất hay không nha.

Kết luận

Qua bài này chúng ta có cái nhìn tổng quan hơn về cache và cách sử dụng nó để tối ưu tốc độ truy vấn đến database. Cũng như cách sử dụng cache with redis giúp giải quyết được các bài toán về mất dữ liệu hay cân bằng tải, nhiều service trên 1 hosting cùng sử dụng cache. Các bạn có thể tự tìm hiểu có giải quyết như thế nào nhá. Đọc để hiểu hơn cũng như cách cấu hình. Bài này mình chỉ tích hợp vào NestJS thôi.

Rất mong được sự ủng hộ của mọi người để mình có động lực ra những bài viết tiếp theo.
{\__/}
( ~.~ )
/ > ♥️ I LOVE YOU 3000

JUST DO IT!