Answer-first: Kiến trúc Shopee Flash Sale chịu tải hàng triệu TPS nhờ phân tách luồng qua API Gateway, chặn tải 3 lớp (CDN Edge, Redis Token Bucket, Virtual Queue), tải trước kho (pre-heating) và trừ tồn kho nguyên tử bằng Redis Lua script sub-millisecond, sau đó ghi bất đồng bộ (Cache-Ahead) vào MySQL sharded & TiDB HTAP.

🇬🇧 Read the English version of this article on tanhdev.com

Đúng 00:00:00 đêm 11.11, hàng triệu người dùng tại Đông Nam Á đồng loạt nhấn nút mua hàng trên một trang sản phẩm Flash Sale duy nhất (ví dụ: 1,000 suất sản phẩm giảm giá 99%). Trong 10 giây đầu tiên, một sản phẩm duy nhất có thể chịu hàng triệu lượt truy vấn đồng thời. Nếu không có kiến trúc vững chắc, hệ thống sẽ gặp các sự cố nghiêm trọng như bán quá số lượng kho (overselling), sập máy chủ, hoặc tắc nghẽn cơ sở dữ liệu (database deadlock).

Bài viết này phân tích chi tiết kiến trúc kỹ thuật của Shopee giúp hệ thống đứng vững qua các đợt bão truy cập Flash Sale: cơ chế tải trước tồn kho (pre-heating inventory) trên Redis, chiến lược chặn tải nhiều tầng (multi-tier rate limiting), mở rộng cơ sở dữ liệu MySQL/TiDB, và quy trình giám sát thời gian thực.

Để tìm hiểu toàn bộ hệ thống vi dịch vụ của Shopee, hãy tham khảo Chuỗi Bài Kiến Trúc Shopee (Shopee Architecture Series) và bài viết Chương 2: Flash Sale Engine.


Thách Thức Kỹ Thuật Flash Sale: Vì Sao Không Thể Chỉ “Thêm Máy Chủ”

Phản ứng tự nhiên khi gặp bão truy cập là mở rộng máy chủ theo chiều ngang (horizontal scaling): bổ sung thêm máy chủ ứng dụng, thêm MySQL Read Replicas, và đặt phía sau Load Balancer. Phương pháp này hoạt động tốt với lượng tăng trưởng truy cập đều đặn, nhưng sẽ sụp đổ khi gặp sự kiện Flash Sale.

Nguyên nhân chính nằm ở cạnh tranh ghi (write contention). Trong sự kiện Flash Sale, hàng ngàn request đồng thời cố gắng cập nhật (decrement) cùng một dòng tồn kho sản phẩm trong database:

  1. Tranh chấp khóa dòng (Lock contention): Khóa dòng cơ sở dữ liệu (database row lock) trở thành tài nguyên bị tranh chấp dữ dội nhất. Các request phải xếp hàng chờ đợi, thời gian giữ khóa tăng vọt khiến P99 latency bùng nổ.
  2. Rủi ro bán quá số lượng (Oversell risk): Nếu không sử dụng các phép trừ nguyên tử (atomic decrement), hiện tượng race condition sẽ dẫn đến việc bán ra nhiều hơn số lượng hàng thực tế có trong kho.
  3. Hiệu ứng thăng hoa trùng lặp (Thundering herd / Retry storms): Các request thất bại hoặc bị timeout sẽ liên tục thử lại (retry), khiến lượng tải tăng gấp nhiều lần.

Việc thêm máy chủ ứng dụng không giải quyết được vấn đề mà còn làm tình trạng thắt cổ chai tại dòng khóa tồn kho trở nên nghiêm trọng hơn.

Chìa khóa của bài toán: Đẩy tối đa việc lọc và chặn request về phía gần client nhất (Edge/Gateway), chỉ cho phép các lệnh ghi hợp lệ đã qua kiểm duyệt được truy cập vào cơ sở dữ liệu.


Kiến Trúc Vi Dịch Vụ Shopee: Phân Lập Và Cách Ly Tải

Shopee phân chia hệ thống thành các vi dịch vụ theo domain độc lập:

flowchart LR
    Client --> GW[Cổng API Gateway]
    GW --> RLS[Dịch Vụ Rate Limit]
    RLS --> FLQ[Hàng Đợi Ảo Flash Sale]
    FLQ --> INV[Dịch Vụ Tồn Kho]
    FLQ --> ORDER[Dịch Vụ Tạo Đơn]
    INV --> REDIS[("Cụm Redis Cluster")]
    INV --> DB[("Hầm MySQL / TiDB")]
    ORDER --> MQ[Message Queue]
    MQ --> PAY[Dịch Vụ Thanh Toán]
    MQ --> NOTIFY[Dịch Vụ Thông Báo]

Các dịch vụ được triển khai và mở rộng độc lập. Đặc biệt, Dịch vụ Tồn Kho (Inventory Service)Dịch vụ Tạo Đơn (Order Service) được phân lập hoàn toàn khỏi dịch vụ phục vụ duyệt sản phẩm (storefront service). Nhờ đó, lượng truy cập xem sản phẩm không làm ảnh hưởng đến khả năng ghi và tạo đơn.

Tại tầng API Gateway, luồng Flash Sale được chuyển hướng sang cụm Gateway riêng (dedicated gateway cluster) với connection pool và cấu hình rate limiting độc lập, đảm bảo cách ly hoàn toàn với các luồng mua sắm thông thường.


Động Cơ Flash Sale: Tải Trước Tồn Kho Và Trừ Tồn Kho Nguyên Tử Với Redis Lua

Trái tim của hệ thống Flash Sale Shopee là tầng tồn kho ưu tiên Redis (Redis-first inventory layer), xử lý toàn bộ lưu lượng ghi trước khi đưa về cơ sở dữ liệu quan hệ.

Tải Trước Tồn Kho Vào Redis (Pre-heating)

30 phút trước khi Flash Sale diễn ra, Flash Sale Engine chủ động nạp số lượng tồn kho của các sản phẩm tham gia vào Redis Cluster:

SET flash:inventory:{product_id} {stock_quantity}
EXPIRE flash:inventory:{product_id} 86400

Khi sự kiện bắt đầu, thao tác trừ tồn kho được thực thi nguyên tử (atomically) trên Redis bằng kịch bản Lua Script:

-- Lua script trừ tồn kho nguyên tử
local key = KEYS[1]
local quantity = tonumber(ARGV[1])
local current = tonumber(redis.call('GET', key))

if current == nil then
    return -1  -- Sản phẩm không thuộc chương trình Flash Sale
end

if current < quantity then
    return 0   -- Hết hàng
end

redis.call('DECRBY', key, quantity)
return 1       -- Trừ tồn kho thành công

Đoạn Lua script này được thực thi nguyên tử (atomic) trên một thread duy nhất của Redis, đảm bảo không có bất kỳ lệnh nào khác xen vào giữa bước GETDECRBY. Giải pháp này mang lại cơ chế giữ chỗ tồn kho không cần khóa (lock-free), loại bỏ hoàn toàn race condition với độ trễ sub-millisecond.

Chỉ những request nhận được kết quả 1 từ Lua script mới được tiếp tục chuyển sang bước tạo đơn hàng. Tất cả các request khác nhận ngay phản hồi “Hết hàng” mà không hề chạm đến database.

Mô Hình Ghi Bất Đồng Bộ (Cache-Ahead Write Pattern)

Redis đảm nhận xử lý tốc độ cao, nhưng cơ sở dữ liệu quan hệ (MySQL/TiDB) mới là nơi lưu trữ dữ liệu chính thức (system of record). Shopee áp dụng mô hình Cache-Ahead Write:

  1. Redis Lua script thực thi trừ tồn kho nguyên tử.
  2. Đưa yêu cầu tạo đơn hàng vào Message Queue.
  3. Các worker bất đồng bộ (consumer) lấy tin nhắn từ Queue để tạo đơn hàng và cập nhật MySQL trong vòng < 200ms.
  4. Xử lý thanh toán và thông báo kết quả cho người dùng.

Mô hình Cache-Ahead Write này là chuẩn mực cho các hệ thống tồn kho chịu tải cao. Để tìm hiểu thêm về cách phối hợp các dịch vụ bất đồng bộ, hãy tham khảo bài viết Kiến Trúc Hướng Sự Kiện Với Dapr Pub/SubKiến Trúc TMĐT 21 Vi Dịch Vụ Với Golang.


Tấm Khiên Phòng Thủ Nhiều Lớp: Edge CDN, Gateway Token Bucket Và Hàng Đợi Ảo

Tầng Redis xử lý bài toán tranh chấp tồn kho, nhưng tầng Rate Limiting sẽ chặn đứng sóng thần request ngay từ cửa ngõ.

Lớp 1: Chặn Kết Nối Tại CDN Edge (Edge Throttling)

Trang thông tin sản phẩm Flash Sale được lưu bản sao tĩnh (cache) tại mạng lưới CDN. Tại tầng CDN Edge, các kết nối dạng HTTP flood hoặc bot bị sàng lọc và chặn ngay lập tức, ngăn không cho truy cập sâu vào máy chủ ứng dụng.

Lớp 2: Giới Hạn Tần Suất Thuật Toán Token Bucket Tại API Gateway

Tại API Gateway, mỗi người dùng bị giới hạn tần suất truy vấn theo thuật toán Token Bucket lưu trên Redis:

  • Mỗi tài khoản người dùng được cấp 1 token/giây.
  • Dung lượng xô tối đa cho phép bùng nổ (burst) là 3 tokens.
  • Mỗi thao tác mua hàng tiêu tốn 1 token; nếu xô rỗng, hệ thống trả về mã lỗi HTTP 429 (Too Many Requests).

Ở tầng IP và vân tay thiết bị (device fingerprinting), nếu phát hiện > 10 requests/giây từ cùng một IP, hệ thống sẽ kích hoạt thử thách CAPTCHA hoặc chặn kết nối.

Lớp 3: Hàng Đợi Ảo Điều Tiết Tải (Virtual Queue Load Leveling)

Đối với lượng request hợp lệ vượt quá khả năng xử lý của backend, hệ thống sử dụng mô hình hàng đợi ảo (Virtual Queue):

  1. Các request vượt qua cửa kiểm tra tồn kho Redis được đưa vào Virtual Queue.
  2. Worker backend lấy yêu cầu từ queue theo tốc độ ổn định (ví dụ: 50,000 đơn/giây).
  3. Giao diện người dùng hiển thị trạng thái “Đang xử lý đơn hàng…”.
  4. Ngay khi đơn hàng tạo xong, kết quả được gửi lại client qua WebSocket hoặc Push Notification.

Mô hình hàng đợi ảo giúp Shopee điều tiết lưu lượng truy cập mượt mà mà không làm sập các dịch vụ tạo đơn backend. Để tìm hiểu thêm về thuật toán điều tiết giá theo thời gian thực, xem bài viết Kiến Trúc Đẩy Surge Pricing & Băm Không Gian.


Mở Rộng Cơ Sở Dữ Liệu: MySQL Sharding Và TiDB HTAP

Redis chịu trách nhiệm xử lý tồn kho tạm thời, nhưng các thao tác nghiệp vụ chính — tạo đơn hàng, cập nhật tài khoản người bán, nhật ký thanh toán — phải được lưu trữ bền vững vào cơ sở dữ liệu quan hệ.

Phân Mảnh MySQL Cho Dữ Liệu Đơn Hàng (MySQL Sharding)

Shopee phân mảnh (shard) bảng đơn hàng theo order_id bằng thuật toán Consistent Hashing, giúp phân bổ đều lưu lượng ghi trên nhiều máy chủ MySQL vật lý và loại bỏ các hot shard.

Mọi thao tác ghi đơn hàng mới tuân theo nguyên lý Append-Only (Event Sourcing): chèn dòng sự kiện mới thay vì cập nhật trực tiếp vào dòng dữ liệu cũ, tránh triệt để tranh chấp khóa dòng (row lock contention).

TiDB HTAP Cho Phân Tích Tồn Kho Thời Gian Thực

Trong khi MySQL xử lý các giao dịch OLTP, Shopee sử dụng TiDB HTAP (với engine cột TiFlash) để phục vụ phân tích dữ liệu thời gian thực:

  • Theo dõi tốc độ bán hàng thực tế (Live Sale Velocity) trên Dashboard vận hành.
  • Phát hiện gian lận và đánh giá hành vi bất thường theo thời gian thực.
  • Báo cáo hiệu năng kinh doanh cho các nhà bán hàng (Merchants).

Engine TiFlash của TiDB xử lý các truy vấn phân tích phức tạp mà không làm ảnh hưởng đến hiệu năng xử lý giao dịch OLTP. Để tìm hiểu thêm về kỹ thuật phân mảnh MySQL, xem MySQL Horizontal Scaling: Vitess & GORM Sharding.


Giám Sát Thời Gian Thực Và Trung Tâm Điều Hành Sự Kiện (EOC)

Trong suốt sự kiện 11.11, Trung tâm Điều hành Sự kiện (Event Operations Center - EOC) của Shopee theo dõi các chỉ số quan trọng trên Dashboard real-time:

Các Chỉ Số Quan Trọng Cần Theo Dõi

  • Tốc độ giảm tồn kho (Inventory depletion velocity): Phân biệt nhu cầu thực tế của người dùng và hành vi của bot.
  • Độ trễ tiêu thụ tin nhắn (Message queue consumer lag): Đánh giá tình trạng tắc nghẽn của backend.
  • Độ trễ truy vấn Database P95/P99: Phát hiện sớm tình trạng sụt giảm hiệu năng database.
  • Trạng thái cầu dao cách ly (Circuit breaker state): Theo dõi trạng thái tự động cách ly của các dịch vụ.

Kịch Bản Phản Ứng Sự Cố (Runbooks)

Mọi cảnh báo từ EOC đều có kịch bản phản ứng được viết sẵn (runbook). Kỹ sư không cần chẩn đoán nguyên nhân trong lúc đỉnh tải mà thực thi ngay các bước trong kịch bản:

  • Nếu queue_consumer_lag > 10,000 và tăng > 500/s: Tự động tăng số lượng consumer worker (scale N+2).
  • Nếu redis_inventory_key gặp sự cố: Kích hoạt chế độ fallback kiểm tra tồn kho từ database và điều động DBA trực ban.
  • Nếu circuit_breaker của dịch vụ thanh toán chuyển sang OPEN: Chuyển hướng người dùng sang các phương thức thanh toán dự phòng.

Văn Hóa Mổ Xẻ Sự Cố Không Quy Trách Nhiệm (Blameless Post-Mortem)

Mọi sự cố lớn sau sự kiện 11.11 đều được phân tích kỹ lưỡng trong vòng 48 giờ thông qua cuộc họp mổ xẻ sự cố không quy trách nhiệm (blameless post-mortem):

  1. Tái hiện diễn biến (Timeline reconstruction): Ghi nhận chính xác các mốc thời gian diễn ra sự cố.
  2. Phân tích nguyên nhân gốc rễ (Contributing factors): Tìm hiểu các yếu tố kỹ thuật dẫn đến sự cố.
  3. Đánh giá khoảng trống giám sát (Detection gap analysis): Phân tích lý do tại sao sự cố không được phát hiện sớm hơn.
  4. Hành động khắc phục (Action items): Phân công cụ thể công việc cải tiến kỹ thuật cho các đội ngũ.

Bài Học Kỹ Thuật Cốt Lõi

  1. Trừ tồn kho nguyên tử bằng Redis Lua script: Loại bỏ hoàn toàn race condition và khóa cơ sở dữ liệu.
  2. Xử lý bất đồng bộ qua hàng đợi (Asynchronous Queueing): Giúp hệ thống nuốt trọn đỉnh tải mà không bị sập.
  3. Phân lập hoàn toàn luồng Flash Sale: Cách ly tài nguyên giữa luồng săn sale và luồng mua sắm thông thường.
  4. Thử nghiệm áp lực tải trên 110% kịch bản: Đảm bảo hệ thống chịu được các đợt bùng nổ tải ngoài dự kiến.
  5. Xây dựng kịch bản phản ứng sự cố (Runbooks) có sẵn: Giúp đội ngũ kỹ sư xử lý nhanh chóng mà không bị hoảng loạn.

Những Câu Hỏi Thường Gặp (FAQ)

Shopee chống bán quá số lượng (overselling) trong Flash Sale như thế nào?

Shopee dùng Redis Lua script để trừ tồn kho nguyên tử trong bộ nhớ in-memory. Do script thực thi đơn luồng nguyên tử trên Redis, không thể xảy ra hiện tượng race condition giữa lệnh kiểm tra và trừ tồn kho. Nếu số lượng tồn kho chạm 0, Lua script trả về kết quả hết hàng và chặn đứng các truy vấn tiếp theo.

Cơ sở dữ liệu nào được sử dụng cho hệ thống Flash Sale?

Tầng tồn kho cấp tốc sử dụng Redis (in-memory, latency sub-1ms). Tầng dữ liệu giao dịch sử dụng MySQL Sharding (phân mảnh theo Order ID) để ghi đơn hàng, và TiDB HTAP (TiFlash) cho phân tích dữ liệu thời gian thực.

Hệ thống Rate Limiting hoạt động ra sao?

Rate Limiting được triển khai thành nhiều tầng: CDN Edge chặn kết nối bất thường, API Gateway áp dụng thuật toán Token Bucket theo User ID và chặn IP/thiết bị bất thường, và hàng đợi ảo (Virtual Queue) điều tiết lưu lượng vào backend.


🤝 Kết nối với tôi

Bạn đang gặp phải những thách thức tương tự về kiến trúc hệ thống, mở rộng quy mô (scaling) hay dịch chuyển (migration)? Hãy kết nối với tôi trên LinkedIn, theo dõi GitHub của tôi, hoặc gửi một email để trao đổi nhé.


❓ Câu Hỏi Thường Gặp (FAQ)

Q1: Kiến Trúc Shopee Flash Sale: Chống Tải Đột Biến & Redis Cache-Ahead giải quyết vấn đề cốt lõi nào trong kiến trúc hệ thống?

Phân tích kiến trúc hệ thống Flash Sale của Shopee: chống tải đột biến với Redis Cache-Ahead, Rate Limiting nhiều tầng và xử lý tồn kho nguyên tử.

Q2: Những lưu ý quan trọng nhất khi triển khai thực tế là gì?

Cần chú trọng phân tầng ranh giới trách nhiệm (bounded context), thiết lập cơ chế fallback dự phòng, và giám sát chặt chẽ qua metrics OpenTelemetry để phát hiện sớm các điểm nghẽn.

Q3: Làm sao để kiểm thử và đánh giá hiệu quả sau khi áp dụng?

Áp dụng kiểm thử tải (load test), benchmark độ trễ P95/P99 trước và sau triển khai, kết hợp tracing phân tán để xác minh tính ổn định dưới tải cao.