Bài viết mới nhất

Tổng 2300 bài
Lập trình 22/09/2026 8 phút

Vectorized execution đo thật: DuckDB xử lý theo lô vector thay vì từng dòng, nên tính tổng 10 triệu dòng mất 2,7 ms còn vòng lặp Python mất 83,9 ms (nhanh hơn 31 lần), lọc rồi tổng nhanh hơn 35 lần, và numpy cũng nhanh hơn vòng lặp 70 lần

DuckDB nhanh vì xử lý dữ liệu theo lô vector thay vì từng dòng một. Đo thật trên 10 triệu dòng trong bộ nhớ: tính tổng bằng vòng lặp Python từng dòng mất 83,9 ms, DuckDB vectorized mất 2,7 ms, nhanh hơn 31 lần; lọc rồi tổng WHERE cat=0 nhanh hơn 35 lần; numpy xử lý theo lô mất 1,2 ms, nhanh hơn vòng lặp Python 70 lần dù cùng là Python. Mấu chốt là xử lý theo lô, không phải riêng ngôn ngữ: chi phí điều phối chia đều cho cả vector và dữ liệu cột cho CPU đọc tuần tự dùng SIMD.

Lập trình 22/09/2026 9 phút

Fan-out WebSocket tới 10.000 client đo thật: ghi tuần tự bắt client cuối chờ 91 ms, chia 10 worker còn 31 ms, PreparedMessage bớt 2 ms, một client không đọc làm cả 10.000 client trễ 1,1 giây với ghi tuần tự, 1.000 với shard, không ai với kênh riêng

Server Go phát 40 vòng, mỗi vòng 1 KB tới 10.000 client, ghi timestamp vào payload để client đo độ trễ thật: 400.000 mẫu mỗi cách. Ghi tuần tự: client cuối nhận sau 91 ms, p99 99 ms. Chia 10 worker: 31 ms, p99 43 ms, CPU mỗi vòng tăng từ 90 lên 140 ms. PreparedMessage chỉ giảm 2 đến 3 ms vì ở 1 KB chi phí là syscall chứ không phải copy. Kênh riêng mỗi client: 31 ms nhưng thêm 10.000 goroutine và 52 MB. Lẫn vào một client không đọc với write deadline 1 giây: tuần tự kẹt cả vòng 1.105 ms nên p99 toàn bài thành 1.066 ms, shard kẹt một phần mười số client, kênh riêng không ảnh hưởng ai.

Lập trình 22/09/2026 10 phút

Reconnect WebSocket đo thật: rút cáp 3 giây giữa stream 200 sự kiện mỗi giây, cách ngây thơ mất 615 sự kiện không ai biết, seq cộng ring buffer mất 0, session cộng ack mất 0 nhưng trùng 10, và server ghi thành công 411 sự kiện vào kết nối đã chết

Server Go phát 200 sự kiện mỗi giây có số thứ tự, client ReadDeadline 1 giây nối lại mỗi 100 ms, iptables DROP cắt 3 giây. Ngây thơ: mất 615 sự kiện, dải 964 tới 1578, không trùng, không ai biết. Số thứ tự cộng ring buffer 1.000 cộng resume: mất 0, server replay 818. Ring 256 quá nhỏ: server báo gap, client biết mình thiếu 819. Session cộng ack mỗi 50 cộng resend: mất 0, trùng 10, server resend 827 và còn giữ 142 chưa ack. Trong 3 giây đứt, server ghi thành công 1.222 sự kiện vào kết nối cũ nhưng client chỉ nhận 811: 411 biến mất trong kernel.

Lập trình 22/09/2026 10 phút

Khung WebSocket đọc từng byte: header 2 đến 14 byte so với 260 byte của HTTP, mask tốn 0,29 ms mỗi MB nhưng phân mảnh 4 KB mặc định của gorilla làm chậm gấp đôi, và một thông điệp 1 MB chặn thông điệp nhỏ phía sau 436 ms trên cùng kết nối

Client Go đếm byte trên net.Conn và đọc thô từng khung: payload 10 byte tốn 16 byte đi, 12 byte về, so với 151 và 126 byte của HTTP; 1 MB tốn thêm 14 byte, nhưng WriteBufferSize mặc định 4 KB của gorilla cắt thành 256 khung và trả thêm 2.048 byte. Mask không phải nút cổ chai: XOR 1 MB mất 0,29 ms, chiều có mask với buffer 2 MB đạt 2.213 MB/s; phân mảnh 4 KB kéo xuống 1.151 MB/s. Dưới 20 Mbit/s, một thông điệp 1 MB chặn tick phía sau 436 ms trên cùng kết nối; chia 256 thông điệp 4 KB thì tick đến sau 0,5 ms; kết nối riêng vẫn chờ 24 ms. Text và binary nhanh như nhau: gorilla không kiểm UTF-8.

Lập trình 22/09/2026 8 phút

Phiên bản API: đo thật qua nginx cache — đánh phiên bản bằng header mà quên Vary là 100/200 client nhận sai bản

Ba cách đổi hợp đồng API khi còn client cũ đang gọi: phiên bản trong URL, phiên bản trong header Accept, và không đánh phiên bản mà chỉ thêm trường. Mình đặt nginx proxy_cache trước server Go rồi cho hai client xen kẽ đòi v1 và v2: cách URL và cách header có Vary: Accept đều đúng 200/200, còn header mà quên Vary thì 100/200 client nhận sai phiên bản — cache trả bản v1 cho client v2 mà không có lỗi nào. Đo thêm: client dùng decoder chặt vỡ ngay khi server chỉ thêm một trường, và lớp chuyển đổi v2 sang v1 tốn 14–17 µs, nằm trong nhiễu đo — cái giá của phiên bản không nằm ở CPU.

Lập trình 22/09/2026 9 phút

Scale WebSocket ra nhiều máy bằng Redis pub/sub đo thật: thêm 0,4 ms p50 nhưng p99 tăng 10 lần, node vừa phát local vừa publish làm client nhận trùng 50%, Redis cho mọi client cùng một thứ tự, và presence bằng sự kiện sai mãi sau khi một node chết

Hai node WebSocket Go chung một kênh Redis, 100 client mỗi node, nguồn phát 200 sự kiện mỗi giây. Phát trực tiếp không qua Redis: p50 0,63 ms, p99 1,42. Qua Redis: p50 1,06 ms, p99 13,65, node khác không chậm hơn node phát. Node phát vừa gửi local vừa publish: client của nó nhận mỗi sự kiện hai lần, trùng 50%. Hai node cùng publish: 200 client thấy cùng một thứ tự, một hash duy nhất; phát local trước thì mỗi node một thứ tự. Presence bằng join/leave qua Redis: kill -9 một node, 50 user ma còn mãi tới hết bài; presence bằng SET với khoá alive EX 3 giây: đúng lại sau 3 giây.