Mở đầu sê-ri bốn mươi phần về Redis. Bài này không giới thiệu Redis bằng lời quảng cáo mà đo nó cạnh PostgreSQL trên đúng cùng một bài toán.
Cùng một bài toán, 50 kết nối
Bài toán đơn giản nhất có thể: đọc và ghi khoá–giá trị, giá trị 100 byte, 100.000 khoá.
| Thông lượng | Độ trễ | |
|---|---|---|
Redis SET |
316.456/s | 0,079 ms |
Redis GET |
331.126/s | 0,079 ms |
PostgreSQL INSERT (có WAL) |
77.187/s | 0,648 ms |
PostgreSQL INSERT (unlogged) |
227.510/s | 0,220 ms |
PostgreSQL SELECT |
169.360/s | 0,295 ms |
Redis nhanh hơn 4,1 lần khi ghi và 2,0 lần khi đọc.
Không phải 100 lần. Con số đó xuất hiện khắp nơi và tôi vào bài này trông đợi thấy một khoảng cách lớn hơn nhiều.
Phần lớn khoảng cách là độ bền, không phải kiến trúc
Dòng thứ tư là dòng quan trọng nhất. Cùng PostgreSQL, cùng truy vấn, chỉ khác một chữ UNLOGGED trên bảng:
có WAL 77.187/s
không WAL 227.510/s
Bỏ ghi nhật ký trước là gấp 2,9 lần, và khoảng cách với Redis co lại còn 1,4 lần.
Điều này đổi hẳn cách nên nghĩ về so sánh. Redis nhanh không chủ yếu vì nó "trong bộ nhớ" — PostgreSQL cũng phục vụ phần lớn từ bộ đệm trong bộ nhớ. Redis nhanh chủ yếu vì mặc định nó không hứa gì về độ bền.
Đó là một đánh đổi, không phải một phép màu. Và khi bạn bật độ bền của Redis lên mức tương đương WAL — appendfsync always — thì đánh đổi đó quay lại, phần sau của sê-ri sẽ đo.
Bộ nhớ mỗi bản ghi: gần bằng nhau
Redis 185 byte mỗi khoá (dữ liệu thật ~100 byte)
PostgreSQL 17 MB / 99.996 dòng = ~178 byte mỗi dòng
Tôi trông đợi Redis gọn hơn nhiều. Nó không.
185 byte cho 100 byte dữ liệu là 85% phụ trội — cho con trỏ, bảng băm, tiêu đề đối tượng, và bộ cấp phát. PostgreSQL 178 byte đã gồm cả chỉ mục khoá chính.
Khác biệt thật không nằm ở kích thước mà ở chỗ nằm: 185 byte của Redis phải ở trong RAM, toàn bộ, mọi lúc. 178 byte của PostgreSQL nằm trên đĩa, và chỉ phần đang dùng mới ở trong RAM.
Với 10 triệu bản ghi, đó là 1,85 GB RAM bắt buộc so với 1,78 GB đĩa. Với 1 tỷ bản ghi thì một bên là bất khả thi còn bên kia là một ổ đĩa bình thường.
Chỗ Redis không làm được
Tôi đặt một giá trị đặc biệt vào một khoá rồi đi tìm nó bằng giá trị.
PostgreSQL, không chỉ mục 6,686 ms
PostgreSQL, có chỉ mục 0,573 ms
Redis, quét 63.000 khoá 20 giây vẫn chưa xong
Redis chỉ biết trả lời một câu hỏi: "khoá này có gì". Mọi câu hỏi khác — tìm theo giá trị, lọc theo khoảng, sắp xếp, nối bảng — nó không có khái niệm.
Cách làm trong Redis là tự dựng cấu trúc để trả lời trước: nếu cần tìm theo giá trị thì bạn phải tự tạo thêm một khoá ngược, tự cập nhật nó mỗi lần ghi, và tự lo chuyện hai khoá lệch nhau khi có lỗi giữa chừng.
Trong PostgreSQL, việc đó là CREATE INDEX — một dòng, và cơ sở dữ liệu tự lo phần nhất quán.
Đây là chỗ so sánh "nhanh hơn bao nhiêu lần" trở nên vô nghĩa: hai hệ thống này không làm cùng một việc.
Vậy Redis là gì
Từ ba phép đo trên, một định nghĩa thực dụng: Redis là một bảng băm rất nhanh, sống trong RAM, có sẵn mạng và một số kiểu dữ liệu dựng sẵn.
Nó thắng rõ khi:
- Bạn biết chính xác khoá cần đọc. Không tìm kiếm, không lọc.
- Dữ liệu vừa trong RAM và bạn chấp nhận trả tiền cho RAM đó.
- Mất một phần dữ liệu khi sự cố là chấp nhận được — bộ đệm, phiên đăng nhập, bộ đếm tần suất.
- Bạn cần đúng những thứ nó dựng sẵn: hàng đợi, tập hợp, bảng xếp hạng, khoá phân tán, luồng sự kiện.
Bốn phần sau của sê-ri sẽ đo từng thứ trong danh sách cuối.
Nó không phải:
- Nơi lưu dữ liệu chính, trừ khi bạn đã đo và chấp nhận mô hình bền của nó.
- Một cơ sở dữ liệu để truy vấn.
- Cách rẻ để đi nhanh — RAM đắt hơn đĩa nhiều lần, và đo được ở trên là mỗi bản ghi tốn xấp xỉ nhau.
Sai lầm phổ biến nhất
Không phải "dùng Redis khi không cần". Mà là dùng Redis rồi quên mất nó không bền.
Bộ đệm bị mất thì ứng dụng chậm đi một lúc — đó là thiết kế đúng. Nhưng nếu ứng dụng của bạn ghi cái gì đó chỉ vào Redis, thì một lần khởi động lại là mất thật, và không có bản sao ở đâu để đối chiếu.
Câu hỏi kiểm tra nhanh cho mọi thứ bạn đang cất trong Redis: "Nếu toàn bộ Redis biến mất ngay bây giờ, cái gì không dựng lại được?" Mọi câu trả lời khác "không có gì" đều đáng xem lại.
Thử ba mươi giây
Xem Redis của bạn đang giữ gì và tốn bao nhiêu cho mỗi khoá:
redis-cli info memory | grep -E 'used_memory_human|maxmemory_human|used_memory_rss_human'
redis-cli dbsize
redis-cli --bigkeys
--bigkeys quét toàn bộ và in ra khoá lớn nhất của mỗi kiểu. Nó là cách nhanh nhất tìm ra thứ đang ăn RAM mà bạn không nhớ đã đặt vào — thường là một danh sách không ai xoá, hoặc một tập hợp lớn dần theo thời gian.
Và nếu maxmemory là 0, tức chưa đặt giới hạn, thì Redis sẽ dùng tới lúc hệ điều hành giết nó. Phần sau đo đúng chuyện đó.
Phần tiếp theo: một luồng mà nhanh — đo xem nút thắt thật của Redis nằm ở đâu.