Hai vấn đề: ghi quá nhiều và đếm sai.
Ghi quá nhiều: mỗi lượt xem là một UPDATE vào cùng một dòng. Các request cùng lúc xếp hàng chờ khoá dòng đó, và mỗi lần cập nhật sinh một phiên bản dòng mới (Postgres) làm bảng phình và index nặng. Một bài viral đủ để làm chậm cả bảng posts.
Đếm sai: bot, prefetch của trình duyệt, và user F5 liên tục đều được tính.
Cách làm thực tế:
1. Đếm trong Redis rồi ghi DB theo lô. INCR post:123:views cực rẻ; một job chạy mỗi 30-60 giây gom các key đã đổi và ghi một UPDATE duy nhất cho mỗi bài. Hiển thị lấy views_in_db + counter_redis.
2. Khử trùng lặp bằng cửa sổ thời gian. Chỉ tính một lượt cho cùng (post, user hoặc IP+user-agent hash) trong 30 phút:
SET view:123:<viewer_hash> 1 NX EX 1800
-- trả về OK => lượt xem mới, cho INCR
-- trả về nil => đã đếm trong cửa sổ, bỏ qua3. Loại bot bằng user-agent và bỏ qua request prefetch; đồng thời chỉ tính khi trang thực sự hiển thị (client gửi beacon) thay vì tính ngay ở server-side render.
Nếu chỉ cần số người xem duy nhất ở quy mô rất lớn và chấp nhận sai số nhỏ, dùng HyperLogLog (PFADD / PFCOUNT): bộ nhớ cố định vài KB cho hàng triệu viewer, sai số khoảng 0,8%. Còn khi số liệu dùng để tính tiền (quảng cáo) thì phải ghi từng sự kiện vào bảng/log rồi tổng hợp, vì lúc đó cần truy vết được.