0
0
Lập trình
Admin Team
Admin Teamtechmely

Cách Tối Ưu Truy Vấn Cơ Sở Dữ Liệu Để Không Mất Người Dùng

Đăng vào 1 tuần trước

• 6 phút đọc

Cách Tối Ưu Truy Vấn Cơ Sở Dữ Liệu Để Không Mất Người Dùng

Trong thế giới phát triển ứng dụng, hiệu suất là một yếu tố sống còn. Nếu bạn đang phát triển một sản phẩm như Mylinx, một dịch vụ tương tự Linktree, bạn sẽ hiểu rõ áp lực phải giữ cho mọi thứ hoạt động mượt mà, đặc biệt khi người dùng phụ thuộc vào tính năng phân tích dữ liệu để đưa ra quyết định. Trong bài viết này, chúng ta sẽ cùng khám phá hành trình tối ưu hóa các truy vấn cơ sở dữ liệu để cải thiện hiệu suất và duy trì sự hài lòng của người dùng.

Giới Thiệu Về Mylinx

Mylinx được xây dựng như một dự án cá nhân và nhanh chóng phát triển lên $750 MRR (doanh thu hàng tháng định kỳ). Dịch vụ này cung cấp:

  • Tùy chỉnh hoàn toàn (bố cục, màu sắc, chủ đề, v.v.)
  • Nhúng nhạc/video cho người sáng tạo
  • Mã QR cho quảng bá ngoại tuyến
  • Kiểm soát SEO và hỗ trợ 11+ ngôn ngữ
  • Công cụ rút gọn URL tích hợp

Tuy nhiên, sau một năm hoạt động trơn tru, các vấn đề bắt đầu phát sinh với việc phân tích dữ liệu.

Khi Phân Tích Đơn Giản Trở Nên Khó Khăn

Như nhiều lập trình viên khác, tôi đã xây dựng hệ thống phân tích theo cách đơn giản: hai bảng (HitPageHitPageLink) lưu trữ mọi nhấp chuột và lượt xem dưới dạng dữ liệu thô. Tôi đã nghĩ rằng "mình sẽ tối ưu sau". Nhưng một năm sau:

  • Hơn 150K dòng mới mỗi tháng
  • Truy vấn mất hơn 30 giây để hoàn thành
  • Người dùng phàn nàn về tính năng phân tích không hoạt động
  • Chi phí cơ sở dữ liệu tăng cao

Cuộc gọi đánh thức đến khi người dùng yêu cầu hoàn tiền vì tính năng phân tích không sử dụng được. Việc mất khách hàng vì những vấn đề kỹ thuật có thể khắc phục thật sự đau lòng, đặc biệt khi doanh thu chỉ là $750/tháng.

Rào Cản Định Hình Giải Pháp

Tôi đã có hai hướng đi:

  1. Cách tiếp cận doanh nghiệp: Xây dựng lại với ClickHouse, TimescaleDB và một hạ tầng phân tích riêng biệt.
  2. Cách tiếp cận khởi nghiệp: Làm việc trong các ràng buộc hiện có và tìm kiếm những giải pháp sáng tạo.

Học Từ Những Người Khổng Lồ (Với Ngân Sách Nhỏ)

Tôi đã nghiên cứu cách mà Instagram và các nền tảng khác xử lý dữ liệu phân tích và áp dụng cách tiếp cận lưu trữ phân cấp của họ:

  • Dữ liệu nóng (0–90 ngày): Giữ trong HitPage cho các truy vấn thời gian thực.
  • Dữ liệu ấm (3–12 tháng): Tính toán tổng hợp hàng ngày.
  • Dữ liệu lạnh (hơn 1 năm): Lưu trữ hoặc xóa bỏ.

Điểm quan trọng: hầu hết các truy vấn phân tích không cần dữ liệu từng lần nhấp. Các tổng hợp hàng ngày là đủ cho các xu hướng lịch sử.

Lược Đồ Đã Giúp Tối Ưu Hóa

Tôi đã thêm một bảng HitPageDaily để lưu trữ các chỉ số đã tính toán sẵn:

prisma Copy
model HitPageDaily {
  id             Int      @id @default(autoincrement())
  kyteId         String
  date           DateTime
  hitCount       Int
  uniqueVisitors Int
  countryStats   Json?
  deviceStats    Json?
  referrerStats  Json?
  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt

  @@unique([kyteId, date])
  @@index([kyteId])
  @@index([date])
}

Điều này giúp biến hàng triệu lượt nhấp riêng lẻ thành các dòng hàng ngày có thể quản lý. Một năm dữ liệu giờ trở thành 365 dòng thay vì hàng triệu.

Nhiệm Vụ Tổng Hợp

Tôi đã xây dựng một hàm serverless chạy hàng đêm, xử lý dữ liệu cũ theo lô:

javascript Copy
const FETCH_BATCH_SIZE = 10000;
const BATCH_SIZE = 100;

const oldHitPageData = await prisma.hitPage.findMany({
  where: { timestamp: { lt: cutoffDate } },
  take: FETCH_BATCH_SIZE,
  skip
});

// tổng hợp vào các bucket hàng ngày
oldHitPageData.forEach(hit => {
  const key = `${hit.kyteId}-${format(hit.timestamp, 'yyyy-MM-dd')}`;
  if (!dailyAggregates.has(key)) {
    dailyAggregates.set(key, {
      kyteId: hit.kyteId,
      date: startOfDay(hit.timestamp),
      hitCount: 0,
      uniqueVisitors: new Set(),
    });
  }
  const aggregate = dailyAggregates.get(key)!;
  aggregate.hitCount++;
  aggregate.uniqueVisitors.add(`${hit.ip}-${hit.device}`);
});

// lưu kết quả
await prisma.hitPageDaily.upsert({
  where: { kyteId_date: { kyteId: agg.kyteId, date: agg.date } },
  update: { hitCount: agg.hitCount, uniqueVisitors: agg.uniqueVisitors.size },
  create: { kyteId: agg.kyteId, date: agg.date, hitCount: agg.hitCount, uniqueVisitors: agg.uniqueVisitors.size }
});

Nhiệm vụ này xử lý dữ liệu theo từng lô, tổng hợp theo ngày, sau đó dọn dẹp dữ liệu thô cũ.

Truy Vấn Kết Hợp Để Trải Nghiệm Liền Mạch

Phần khó khăn là làm cho các truy vấn hoạt động trên cả dữ liệu thô mới nhất và các tổng hợp lịch sử:

javascript Copy
const [hitPageData, dailyData] = await Promise.all([
  prisma.$queryRaw`
    SELECT TO_CHAR("timestamp", 'MM/DD') as date,
           COUNT(DISTINCT CONCAT("ip", '-', "device"))::int as views
    FROM "HitPage"
    WHERE "kyteId" = ${kyteId} AND "timestamp" >= ${daysAgo}
    GROUP BY 1
  `,
  prisma.$queryRaw`
    SELECT TO_CHAR("date", 'MM/DD') as date, "uniqueVisitors" as views
    FROM "HitPageDaily"
    WHERE "kyteId" = ${kyteId} AND "date" >= DATE(${daysAgo})
  `
]);

Điều này cung cấp cho người dùng dữ liệu gần đây với độ phân giải đầy đủ, cộng với bối cảnh lịch sử mà không bị ảnh hưởng bởi hiệu suất.

Kiểm Tra và Triển Khai

Tôi đã thiết lập một môi trường phát triển giống như sản xuất với dữ liệu thực để kiểm tra việc chuyển đổi. Sau khi xác nhận mọi thứ hoạt động như mong đợi, tôi đã triển khai trong giờ ít lưu lượng với kế hoạch quay lại sẵn sàng.

Kết quả sau khi triển khai:

  • Truy vấn phân tích giảm từ hơn 30 giây xuống dưới 2 giây.
  • Kích thước cơ sở dữ liệu giảm khoảng 60%.
  • Không còn phàn nàn từ người dùng.
  • Không xảy ra mất dữ liệu hay vấn đề chính xác.

Những Gì Tôi Đã Học Được

  • "Tôi sẽ tối ưu sau" là đắt đỏ. Những gì tưởng chừng như tối ưu hóa sớm sẽ tiết kiệm được hàng tuần căng thẳng và gần như mất khách hàng.
  • Rào cản tạo ra sự sáng tạo. Ngân sách hạn chế buộc tôi phải tìm ra giải pháp mà những công ty lớn có thể bỏ qua - đôi khi tổng hợp đơn giản lại tốt hơn hạ tầng phức tạp.
  • Lợi thế của lập trình viên độc lập là có thật. Tôi có thể kiểm tra trên dữ liệu thực, triển khai nhanh chóng và lặp lại mà không cần các quy trình phê duyệt.
  • Hiệu suất là một tính năng. Người dùng không quan tâm đến nợ kỹ thuật của bạn - họ chỉ muốn mọi thứ hoạt động nhanh.

Kết Luận

Đây không phải là cách mà Google hay Facebook sẽ giải quyết vấn đề mở rộng phân tích, nhưng không cần phải như vậy. Đối với một lập trình viên độc lập với dự án phụ 750$/tháng, cách tiếp cận thực tế này đã hoạt động hoàn hảo.

Hai tháng sau, hệ thống có thể xử lý gấp ba lần lưu lượng ban đầu với hiệu suất tốt hơn so với ngày đầu. Đôi khi, kiến trúc tốt nhất là thứ bạn có thể xây dựng, triển khai và duy trì một cách độc lập.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào