Giới thiệu

Trong các dự án fullstack sử dụng Prisma làm ORM cho MySQL, việc tối ưu truy vấn là yếu tố quyết định tốc độ phản hồi của API. Bài viết sẽ phân tích chi tiết các kỹ thuật tối ưu schema, cách viết truy vấn hiệu quả và cách quản lý kết nối database để giảm thiểu latency.

Tối ưu schema Prisma cho MySQL

Định nghĩa index phù hợp

MySQL chỉ sử dụng index khi các cột được khai báo trong @@index hoặc @id. Khi thiết kế schema, hãy xác định các cột thường dùng trong where hoặc orderBy và tạo index cho chúng.

model Product {
  id          Int      @id @default(autoincrement())
  name        String
  price       Decimal
  categoryId  Int
  createdAt   DateTime @default(now())

  @@index([price])               // Tìm kiếm theo giá
  @@index([categoryId, price])   // Lọc theo danh mục + giá
}

Sử dụng selectinclude một cách hợp lý

Việc lấy toàn bộ trường của một bảng sẽ gây lãng phí băng thông và bộ nhớ. Thay vào đó, chỉ chọn những trường thực sự cần thiết.

const products = await prisma.product.findMany({
  where: { price: { gt: 100 } },
  select: {
    id: true,
    name: true,
    price: true,
    // Không lấy các trường không cần thiết như description, images
  },
});

Batching và transaction để giảm round‑trip

Batch queries với prisma.$transaction

Khi cần thực hiện nhiều truy vấn liên tiếp, việc gọi riêng lẻ sẽ tạo nhiều kết nối tới MySQL. Sử dụng prisma.$transaction để gộp chúng thành một lần round‑trip.

await prisma.$transaction([
  prisma.order.create({ data: { userId, total: 0 } }),
  prisma.orderItem.createMany({
    data: items.map(item => ({
      orderId: /* order id sẽ được trả về từ query trên */,
      productId: item.id,
      quantity: item.qty,
    })),
  }),
]);

Kiểm soát kết nối và pool trong MySQL

Prisma dựa trên mysql2 driver, cho phép cấu hình pool kết nối. Đặt connection_limit phù hợp với tài nguyên server để tránh quá tải.

# .env
DATABASE_URL="mysql://user:password@localhost:3306/ecommerce?connection_limit=10"

Trong môi trường Docker, bạn cũng nên khai báo max_connections trong my.cnf để đồng bộ với giá trị trên.

Kỹ thuật cache query với TanStack Query

Ở phía client, TanStack Query giúp cache dữ liệu và tự động refetch khi cần. Đặt thời gian staleTime hợp lý để giảm số lần gọi API.

const { data, isLoading } = useQuery(['products', minPrice], async () => {
  const res = await fetch(`/api/products?minPrice=${minPrice}`);
  return res.json();
}, {
  staleTime: 5 * 60 * 1000, // 5 phút
});

Kết luận

Áp dụng các biện pháp trên – tạo index đúng chỗ, giảm trường trả về, batch các truy vấn và cấu hình pool hợp lý – sẽ giúp API của bạn phản hồi nhanh hơn đáng kể. Để nắm bắt toàn bộ quy trình xây dựng một Ecommerce Dashboard từ đầu tới cuối, bạn có thể Tham khảo khóa học "[Full Course] Ecommerce Dashboard Fullstack Clone" tại đây.