Ý chính: Không nên tạo một giao dịch cơ sở dữ liệu cho mỗi tín hiệu tiến độ mà trình phát video gửi lên.
Thuật ngữ trong bài: Write-behind: gom thay đổi rồi ghi xuống cơ sở dữ liệu theo lô; checkpoint: mốc dữ liệu đã lưu an toàn; idempotent: xử lý lặp lại vẫn cho cùng kết quả.
Bài toán thực tế
Một trình phát có thể gửi tiến độ sau mỗi 15 giây. Với hàng nghìn người học, số lần ghi nhỏ này tạo áp lực lớn lên khóa, chỉ mục và cơ chế sao chép dữ liệu. Giá trị nghiệp vụ lại không tăng tương ứng.
Điểm dễ bị bỏ qua
Tiến độ học là dữ liệu có tần suất cập nhật cao nhưng giá trị nằm ở trạng thái tổng hợp, không nằm ở từng lần ghi riêng lẻ. Điều đó cho phép hệ thống gom nhiều cập nhật trước khi ghi bền vững, miễn là quy tắc hợp nhất không làm tiến độ đi lùi.
Khó khăn thật sự không phải giảm số câu lệnh ghi, mà là xử lý đúng khi người học dùng nhiều thiết bị, mạng chập chờn hoặc worker dừng giữa chừng.
Luồng xử lý
Phát tín hiệu tiến độ định kỳ→Redis
Gộp tiến độ mới nhất→Worker xử lý theo lô
Ghi xuống cơ sở dữ liệu theo chu kỳ→Cơ sở dữ liệu
Lưu mốc tiến độ bền vững
Thiết kế và triển khai
Xác định mô hình tiến độ trước
Tách trạng thái hoàn thành đơn điệu khỏi vị trí xem tiếp hiện tại.
Gộp nguyên tử trong Redis
Một Lua script hoặc transaction duy nhất sẽ so sánh sequence, cập nhật trạng thái rút gọn và đánh dấu người học là dirty (đã thay đổi nhưng chưa ghi xuống cơ sở dữ liệu) trong cùng một thao tác.
Ghi xuống bằng upsert (ghi mới hoặc cập nhật nếu đã tồn tại) an toàn khi xử lý lặp
Worker nhận một batch có giới hạn, ghi mốc lưu an toàn kèm điều kiện phiên bản, và chỉ xóa những mục có phiên bản Redis vẫn khớp.
Đi sâu vào thiết kế
Mô hình hóa thứ tự trước khi tối ưu ghi
Client sequence chỉ có ý nghĩa trong phạm vi một session. Cài lại ứng dụng, mở thiết bị khác hoặc phát lại offline queue đều có thể tạo ra counter chồng lấn.
Thiết kế ghi xuống protocol như một máy trạng thái
Worker nhận một nhóm dirty khóa có giới hạn bằng lease (quyền xử lý tạm thời, có hạn), đọc phiên bản hiện tại, rồi conditional upsert xuống cơ sở dữ liệu. Sau commit, nó chỉ xóa khóa nếu Redis vẫn giữ đúng phiên bản vừa ghi xuống; nếu đã có sự kiện mới hơn thì khóa vẫn ở trạng thái dirty.
Cách triển khai
- Chỉ phản hồi thành công sau khi tiến độ đã được hợp nhất an toàn vào Redis.
- Dùng quy tắc hợp nhất có kết quả xác định để yêu cầu gửi lại không làm tiến độ đi lùi.
- Ghi xuống cơ sở dữ liệu theo lô định kỳ và chỉ thử lại những bản ghi thất bại.
Mã minh họa: Hợp nhất theo phiên bản và ghi mốc tiến độ bền vững
incoming = { userId, lessonId, position, completed, seq }
state = redis.HGET(progressKey)
if incoming.seq <= state.seq then return state end
next.position = incoming.position
next.completed = state.completed OR incoming.completed
next.seq = incoming.seq
redis.HSET(progressKey, next)
redis.ZADD(dirtyKey, now(), progressKey)
DB UPSERT ... WHERE stored_seq < next.seqĐiểm số trong tập chờ ghi nên là thời điểm thay đổi đầu tiên chưa được lưu, nhờ đó worker ưu tiên đúng dữ liệu đã chờ lâu.
Rủi ro cần tính trước
- Redis nhận cập nhật nhưng khởi động lại trước khi dữ liệu được ghi bền vững.
- Một bản ghi lỗi liên tục vì vi phạm lược đồ hoặc khóa ngoại và chặn cả lô.
- Hai thiết bị dùng bộ đếm không cùng phạm vi nên trạng thái không thể so sánh trực tiếp.
Nên theo dõi gì?
| Tín hiệu | Điều tín hiệu cho biết |
|---|---|
| Tuổi của cập nhật chưa được ghi lâu nhất | Đo độ trễ lưu bền vững tệ nhất mà người học đang gặp. |
| Số người học chờ ghi và tốc độ cập nhật mới | Cho biết worker có theo kịp tải và cần thêm bao nhiêu năng lực. |
| Số xung đột và lần thử lại khi ghi | Phản ánh cập nhật đồng thời hoặc điều kiện phiên bản chưa đúng. |
| Độ bền Redis và độ trễ sao chép | Định lượng khoảng tiến độ có nguy cơ mất khi hạ tầng gặp sự cố. |
Cách kiểm chứng
- Tạo tín hiệu duy trì sai thứ tự và trùng lặp từ hai session, rồi so trạng thái cuối với merge model đã công bố.
- Dừng worker sau khi cơ sở dữ liệu commit nhưng trước khi xóa dirty marker; lần ghi xuống lặp lại phải cho ra cùng một mốc lưu an toàn.
- Dừng Redis trước khi sự kiện đã xác nhận được lưu bền vững, và kiểm tra lượng mất nằm trong RPO đã cam kết.
- Chèn một bản ghi sai vào batch, và xác nhận người học hợp lệ vẫn được xử lý còn mục lỗi đi tới dead-letter.
- Kiểm thử tải cả tín hiệu duy trì ở giờ cao điểm lẫn lưu lượng phục hồi sau sự cố gián đoạn, không chỉ trạng thái ổn định.
Đưa vào production từng bước
Trong giai đoạn đầu, giữ luồng ghi hiện tại làm chuẩn và cho write-behind chạy song song. So sánh mốc tiến độ theo bài học, phiên ứng dụng và thiết bị; chỉ chuyển hoàn toàn khi các sai lệch đã được giải thích và kiểm thử phục hồi đã đạt.
Checklist trước khi vận hành
- Cấu hình độ bền của Redis quyết định khoảng tiến độ gần nhất có nguy cơ bị thất thoát.
- Chu kỳ ghi lô là sự đánh đổi giữa việc giảm tải cơ sở dữ liệu và độ chính xác khi khôi phục dữ liệu.
- Giám sát thời gian lưu đệm, số lượng học viên chưa lưu tiến độ và tỷ lệ lỗi khi ghi xuống.
Điều cần nhớ
Write-behind chỉ đáng dùng khi quy tắc hợp nhất dữ liệu có kết quả xác định, tiến độ không thể đi lùi và độ trễ ghi xuống đã được đo rõ.
