Đối chiếu thực tế: hệ thống bán vé và sàn TMĐT đã làm gì?
Đặt thiết kế của bạn cạnh các sự cố và bài viết kỹ thuật công khai: năm nào, ràng buộc gì, và vì sao họ chọn khác bạn.
TL;DR: Bài này đặt thiết kế của bạn cạnh bảy case công khai (Shopify, Cloudflare, Alibaba, Ticketmaster, Ticketbox). Với mỗi case bạn tách ba thứ: ràng buộc của họ, lựa chọn của họ, trục bị tác động. Lựa chọn khác bạn thường là hệ quả của một ràng buộc không có trong bảng của bạn.
Bạn đã có một thiết kế: 1 SKU, tồn kho 1.000, 200.000 người chờ, tải đỉnh khoảng 50.000 lượt/giây, và một row Postgres chỉ chịu được cỡ 1.200 lượt/giây ở 50 kết nối trên máy tác giả. Câu hỏi quen thuộc là "vậy người ta làm thế nào?". Hỏi vậy dễ thành học thuộc; hỏi xem họ thiếu gì so với bạn thì có ích hơn.
Bài này đi qua bảy case công khai, mỗi case gắn một ràng buộc khác bảng của bạn. Số liệu đều kèm năm và link; nguồn chỉ là báo chí thì bài ghi rõ.
1. Cách đọc một case: tìm ràng buộc khác bạn
Bảng ràng buộc ở bài 01 gồm một SKU, 1.000 sản phẩm, 200.000 người chờ, tải đỉnh 50.000 lượt/giây, không bán quá, phản hồi dưới 1 giây, chỗ giữ 10 phút. Edge là tầng sát người dùng, nơi request đi qua trước khi vào hệ thống chính (load balancer, CDN). Với mỗi case dưới đây, bạn làm ba việc theo thứ tự. Trước hết đoán, tức là nhìn ràng buộc của họ rồi đoán họ chặn cửa hay nâng trần. Sau đó đọc lời giải. Cuối cùng tự hỏi: nếu bảng của tôi mang ràng buộc đó, thiết kế của tôi có đổi không.
Trần của một row là thứ bạn đã đo: UPDATE có điều kiện cỡ 1.200 lượt/giây ở 50 kết nối, thêm kết nối không thêm thông lượng. Mọi case dưới đây đều là cách khác nhau để thoát khỏi con số đó, hoặc để không bao giờ chạm tới nó.
2. Case: nền tảng nhiều cửa hàng, edge không biết tồn kho
Một nền tảng chạy hàng nghìn cửa hàng, edge (load balancer) của họ chỉ được phép không lưu trạng thái, và một đợt bán của một cửa hàng làm nghẽn việc ghi vào cơ sở dữ liệu chung. Họ nên chặn cửa hay nâng trần của hàng nóng? Viết ra lựa chọn và lý do trước khi đọc tiếp.
Shopify 2017 và 2022: phòng chờ không biết còn hàng hay hết
Trong bài kỹ thuật về load balancer lập trình được, phần II (Shopify Engineering, tháng 2/2017), Shopify chặn tải ngay ở edge bằng leaky bucket (thùng rò: chỉ cho một lượng cố định lọt qua mỗi nhịp). Bản đầu cho người vượt hạn mức thăm dò lại ngẫu nhiên (random polling), và bài viết ghi nhận phương sai cao: median và p95 của thời gian chờ chênh nhau rất xa. Bản sửa lưu timestamp lần thử đầu tiên vào một cookie có chữ ký, ai đến trước thì được ưu tiên. Mỗi load balancer tự tính ngưỡng riêng, vì họ muốn giữ edge không lưu trạng thái: theo bài viết, một kho dữ liệu chung sẽ thành điểm lỗi mới đúng lúc tải cao nhất.
Ràng buộc khác bạn nằm ở đây. Bạn có một Postgres duy nhất cầm sự thật về tồn kho, nên có chỗ để đặt cờ hết hàng. Edge của Shopify thì không được phép biết gì về tồn kho, nên chỉ còn công cụ điều tiết lượng người đi vào. Lựa chọn của họ là chặn cửa, và là lựa chọn duy nhất còn lại.
Cái giá hiện ra năm 2022. Phần tóm tắt của bài nói tại Strange Loop 2022 của Shopify, "A Commerce-centric Approach to Queuing Fairly at High Throughput", nêu ví dụ minh hoạ: người dùng có thể chờ 30 phút, rồi phát hiện sản phẩm trong giỏ đã hết từ 20 phút trước. Họ chuyển việc điều tiết từ edge vào tầng ứng dụng Rails để hàng đợi hiểu được nghiệp vụ thương mại. Đối chiếu với bài 05: cờ hết hàng của bạn chính là thứ phòng chờ Shopify 2017 thiếu.

Cloudflare 2021: tách hẳn hàng đợi khỏi tồn kho
Cloudflare Waiting Room (Cloudflare blog, 22/1/2021) là sản phẩm chạy trên edge của Cloudflare với Workers và Durable Objects, ra đời cùng lúc với chương trình Project Fair Shot cho các trang đăng ký tiêm vaccine COVID-19. Ràng buộc khác bạn rất rõ: Cloudflare phục vụ trang của người khác, không hề biết tồn kho hay schema của họ. Tham số cấu hình chính của phòng chờ là số người hoạt động tối đa trên site.
Gần chín tháng sau, bài về hàng đợi ngẫu nhiên (Cloudflare blog, 7/10/2021) trả lời đúng câu hỏi công bằng. FIFO (vào trước ra trước) hợp khi nhu cầu kéo dài, vì thứ tự quen thuộc và ước tính thời gian chờ chính xác. Hàng đợi ngẫu nhiên hợp với đợt bán ngắn, nhiều hype, sản phẩm có hạn: nếu xếp hàng FIFO dài thì sản phẩm có thể hết trước khi người đến sau kịp vào, và lợi thế của người vào sớm bị giảm mạnh. Với đợt bán 2 giây đầu của bạn, bài này gợi ý bạn xem lại thứ tự của phòng chờ ở bài 05: FIFO nghe công bằng nhưng thưởng cho người vào trước, nên ai có mạng tốt và vào sớm thì thắng.
3. Khi nào nâng trần thay vì chặn cửa?
Hai công ty cùng dùng MySQL cho tồn kho. Công ty A tự sửa được nhân cơ sở dữ liệu. Công ty B bỏ Redis đang giữ chỗ để đưa giữ chỗ về chung bảng với sổ cái. Mỗi bên chọn chặn cửa hay nâng trần, và nút cổ chai thật của B nằm ở hàng nóng, ở CPU, hay ở chỗ khác?
Alibaba 2020: gộp ghi trên một row, nhờ sửa được nhân DB
Trong bài "How to Process Hot Spot Data in E-commerce Scenarios" (Alibaba Cloud, 2020), tác giả viết thông lượng của một hàng nóng xấp xỉ 1 giây chia cho thời gian giữ khoá của transaction. Nhớ lại phép nhẩm ở bài 01: giữ khoá 1 ms thì mỗi giây tối đa 1.000 lượt; rút còn 0,1 ms thì 10.000 lượt. Đó là lý do Alibaba tìm cách rút ngắn thời gian giữ khoá (bớt câu SQL, bớt round-trip) và gộp nhiều lượt trừ cùng một row thành một lần ghi. Một transaction hàng nóng điển hình trong bài gồm năm câu SQL và chín lượt trao đổi mạng giữa client và database. Ba hướng họ nêu: rút ngắn thời gian giữ khoá (đưa thông lượng một luồng lên gần 10.000 TPS), gộp commit (tăng thông lượng "nhiều lần"), và group update, nơi nhiều transaction cùng sửa một row được gộp thành một lần ghi (mười transaction cùng trừ được gộp thành một lần ghi). Cú pháp hint Commit On Success, Rollback On Fail, Target Affect Row gộp kiểm tra, ghi và commit vào một câu. Bài viết không công bố con số TPS trước và sau bằng chữ, chỉ có biểu đồ, nên bài này cũng không in số nào ngoài những gì bài viết ghi.
Ràng buộc khác bạn: họ vận hành MySQL phiên bản riêng của Alibaba Cloud, nên thêm được cú pháp mới vào chính database. Trên Postgres managed bạn không thêm được Rollback On Fail, nên con đường gộp ghi trên engine đóng với bạn. Công thức của họ vẫn dạy được bạn một điều: bài 03 đo cho thấy ở 50 kết nối UPDATE một câu (A) và FOR UPDATE hai round-trip (B) gần như ngang nhau (B bằng 0,88 đến 1,00 lần A): round-trip thừa của B nằm trong vùng giữ khoá nên kéo trần xuống cỡ 10%, ngang dải nhiễu. Cái giá thật của B lộ ra khi có việc chen giữa hai câu. Cùng một logic rút ngắn việc giữ khoá, họ đẩy tới cùng cực.
Shopify 2026: bỏ Redis, chia thành row khoá độc lập
Bài "We replaced Redis with MySQL for inventory reservations—and it scaled" (Shopify Engineering, 12/5/2026) mô tả mô hình cũ: giữ chỗ nằm ở Redis, sổ cái nằm ở MySQL, bước chốt cập nhật MySQL rồi dọn Redis bằng các thao tác không thể làm nguyên tử, nên có thể bán quá hoặc bán thiếu. Mô hình mới đưa giữ chỗ vào cùng MySQL với sổ cái để giữ và chốt nằm trong một transaction ACID. Mỗi tổ hợp item và location có một pool row, mỗi row là một đơn vị bán được, pool giới hạn 1.000 row (trần của pool, không phải tồn kho), con số chọn từ tốc độ giữ chỗ đỉnh quan sát trong các đợt flash sale, và SKIP LOCKED cho phép nhiều luồng lấy các row khác nhau mà không chờ nhau.
Shopify để mỗi đơn vị là một row riêng, như những chiếc ghế đánh số, nhưng chỉ giữ một pool tối đa 1.000 ghế còn trống cho mỗi item và location, nạp lại từ sổ cái. Ghế nào đang bị người khác giữ thì SKIP LOCKED bỏ qua để lấy ghế khác. Cái giá: nếu để toàn bộ tồn kho thành row thì việc quét chậm dần, nên pool phải có trần.
Ràng buộc khác bạn: họ có nhiều địa điểm tồn kho và một sổ cái đã nằm sẵn trong MySQL, và điều họ không chịu nổi là hai nơi lưu không nguyên tử. Nhớ lại bài 04: chỗ giữ 10 phút và worker thu hồi chỉ trọn vẹn khi giữ chỗ và đơn hàng cùng một nơi lưu, vì chỉ khi đó transaction mới nguyên tử. Với Shopify, lý do bỏ Redis không phải tốc độ mà là tính nguyên tử giữa giữ chỗ và sổ cái. Phép đo cho thấy độ trễ P90 của giữ chỗ vẫn ổn, CPU chưa chạm trần; cái nghẽn là các thread phải xếp hàng chờ trong MySQL và kết nối cạn ở ProxySQL. Gắn nhãn từng câu SQL theo quy trình nghiệp vụ thì thấy nhiều đoạn khác của checkout giữ kết nối quá lâu, giữ chỗ chỉ là giọt nước làm tràn ly.

Phép đo chia N shard của bạn (trên máy tác giả) tăng dưới tuyến tính: N bằng 64 chỉ đạt khoảng 8,3 lần so với một row. Shopify gặp cùng quy luật ở dạng khác, vì sau khi chia nhỏ thì khoá trên từng row không còn là nút cổ chai, kết nối mới là thứ cạn trước.
4. Case: đợt bán có bot và người không có mã
Trong đợt bán vé có cả người thật lẫn bot, phòng chờ FIFO hay ngẫu nhiên có đủ không? Nếu chưa đủ, bạn thêm bước chặn ở đâu: trước phòng chờ, trong phòng chờ, hay ở bước thanh toán?
Ticketmaster, Eras Tour, 15/11/2022: xác minh trước cả phòng chờ
Số liệu dưới đây là theo chính Ticketmaster trong thông cáo "Taylor Swift | The Eras Tour Onsale Explained" (19/11/2022). Họ ghi nhận 3,5 tỉ system request, gấp 4 lần đỉnh trước đó. Hơn 3,5 triệu người đăng ký Verified Fan (cơ chế đăng ký trước rồi phát mã bán trước cho một phần người đăng ký), khoảng 1,5 triệu người được gửi mã, 2 triệu người còn lại vào danh sách chờ (waitlist). Hơn 2 triệu vé bán ra ngày 15/11. Ticketmaster đổ lỗi một phần cho số lượng lớn các vụ tấn công của bot, cùng những người không có mã vẫn vào trang. Theo CBS News (17-18/11/2022), đợt mở bán chung bị huỷ vì nhu cầu quá cao đè lên hệ thống bán vé trong khi lượng vé còn lại không đủ.
Ràng buộc khác bạn: bảng của bạn không có bot và không có định danh, mỗi người tối đa một sản phẩm là hết. Họ phải thêm một tầng chặn cửa trước phòng chờ: đăng ký và xác minh trước để loại bot, rồi cấp mã theo đợt để hạn chế số người vào cùng lúc. Chặn cửa vẫn chưa đủ, và lý do do chính Ticketmaster nêu: bot tấn công và người không có mã vẫn vào trang. Phiên điều trần của Thượng viện Mỹ ngày 24/1/2023 với đại diện Ticketmaster và SeatGeek, theo Semafor (23/1/2023), diễn ra sau sự cố này và xoay quanh độc quyền và thiếu cạnh tranh của ngành bán vé.
Ticketbox, 12/11/2024: chỉ quan sát được từ ngoài
Nguồn duy nhất của case này là báo chí, và bài này không suy đoán kiến trúc nội bộ. Tuổi Trẻ, theo PLO (12/11/2024) viết rằng, theo các diễn đàn, hệ thống Ticketbox sập ngay lúc mở bán concert "Anh trai vượt ngàn chông gai" tại Hà Nội, khoảng 150.000 người chờ, và vé hết chỉ sau 40 phút. Những gì suy ra được từ hành vi quan sát: lượng người chờ cùng bậc với 200.000 trong bảng của bạn, và người dùng chạm vào lỗi trước khi biết còn vé hay hết. Bài báo không nói lỗi nằm ở tầng nào, nên bạn cũng đừng gán nó cho kho hay cho kết nối. Cái đáng mang về là câu hỏi: nếu phản hồi "còn hay hết" của bạn không nằm ở một chỗ rẻ và không phụ thuộc kho, người dùng sẽ trả lời thay bạn bằng cách bấm lại.
5. Bảng tổng hợp: ràng buộc nào đổi lựa chọn nào

| Case | Năm | Ràng buộc khác bạn | Lựa chọn | Trục |
|---|---|---|---|---|
| Shopify, load balancer lập trình được | 2017 | Edge không lưu trạng thái, không có kho chung | Leaky bucket cộng cookie timestamp | Tải đổ vào |
| Shopify, hàng đợi công bằng | 2022 | Hàng đợi ở edge không biết đã hết hàng | Chuyển điều tiết vào tầng Rails hiểu nghiệp vụ | Tải đổ vào, giữ chỗ |
| Cloudflare Waiting Room | 2021 | Phục vụ site người khác, không thấy tồn kho | Phòng chờ ở edge, FIFO hoặc ngẫu nhiên | Tải đổ vào |
| Alibaba, hàng nóng | 2020 | Sửa được nhân MySQL của chính mình | Gộp ghi và gộp commit trên một row | Cơ chế trên một row |
| Shopify, giữ chỗ | 2026 | Nhiều địa điểm, sổ cái đã ở MySQL, cần nguyên tử | Bỏ Redis, pool row cộng SKIP LOCKED | Giữ chỗ |
| Ticketmaster, Eras Tour | 2022 | Bot và định danh, 3,5 triệu người đăng ký | Verified Fan: xác minh trước, cấp mã theo đợt | Tải đổ vào |
| Ticketbox (báo chí) | 2024 | Không rõ, chỉ quan sát được lỗi và 40 phút hết vé | Không suy đoán | Tải đổ vào (quan sát) |
Năm dòng đổ về trục tải đổ vào vì họ đứng ở chỗ không chạm được vào kho (edge, site của người khác), gặp bot mà tồn kho không giải được, hoặc không rõ. Chỉ Alibaba và Shopify 2026 chạm được vào kho, nên chỉ hai nơi này sửa ở cơ chế trên một row và ở giữ chỗ.
6. Tự kiểm tra
- Q1Vì sao Shopify 2026 không giữ Redis cho giữ chỗ, dù Redis vốn là kho đếm trong bộ nhớ?
- Q2Vì sao Alibaba nâng trần ngay trên một row còn Shopify 2017 và Cloudflare chặn cửa, dù cùng gặp hàng nóng hoặc tải dồn?
- Q3Bạn xây phòng chờ cho một nền tảng mà bạn không sở hữu tồn kho của khách. Bạn chọn gì, và nêu cái giá?
- Q4Cloudflare khuyên dùng hàng đợi ngẫu nhiên cho đợt bán ngắn có sản phẩm giới hạn. Với đợt 2 giây đầu của bạn, phòng chờ FIFO có hại ở đâu?
- Q5Đợt bán có bot và người không có mã. Bảng ràng buộc ở bài 01 của bạn thiếu gì, và bạn thêm tầng nào vào thiết kế?
- Q6Hệ thống của bạn giữ chỗ ở Redis và sổ cái ở Postgres, và thỉnh thoảng bán quá hoặc bán thiếu. Theo bài học Shopify 2026, bạn đổi gì, và cái giá là gì?
Tóm tắt
- Ticketbox 2024 chỉ nên đọc như quan sát từ ngoài, không suy ra kiến trúc.
Liên hệ các bài khác
- 02 — Đếm đơn rồi ghi: phương án đếm rồi ghi vỡ ở đó, và không case nào ở trên chọn nó.
- 03 — Trừ tồn kho trên hàng nóng: nền cho thấy vì sao rút ngắn giữ khoá (Alibaba) có tác dụng.
- 04 — Giữ chỗ có hạn: chỗ giữ 10 phút mà Shopify 2026 gộp vào cùng transaction với sổ cái.
- 05 — Tải vượt trần: chặn cửa và nâng trần mà các case ở đây đối chiếu.
Bài tiếp theo tổng kết lại cả module và đối chiếu từng quyết định với ràng buộc ban đầu: Tổng kết module.
Bài này có giúp bạn hiểu bản chất không?
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Tải vượt trần hàng chục lần: chặn cửa hay nâng trần?
Bài tiếpTổng kết case flash sale: thiết kế hoàn chỉnh và các cái giá
Thảo luận & góp ý
Hỏi đáp về bài này