Redis cung cấp một hệ sinh thái phong phú gồm 9 loại cấu trúc dữ liệu, mỗi loại phù hợp với những trường hợp cụ thể. Ngoài 5 kiểu cơ bản như String, Hash, List, Set và Zset, các phiên bản mới còn bổ sung thêm BitMap, HyperLogLog, GEO và Stream.
String – Chuỗi ký tự
Đây là kiểu dữ liệu đơn giản nhất dạng key-value, có thể lưu trữ văn bản, số nguyên hoặc số thực, dung lượng tối đa lên tới 512MB.
Cơ chế bên trong: Redis sử dụng SDS (Simple Dynamic String) thay vì chuỗi C truyền thống để đảm bảo:
- Hỗ trợ dữ liệu nhị phân nhờ thuộc tính
len. - Độ phức tạp O(1) khi lấy độ dài chuỗi.
- Tránh tràn bộ đệm do tự động mở rộng không gian khi nối chuỗi.
Encoding:
int: Khi giá trị là số nguyên có thể biểu diễn bằng kiểulong.embstr: Dành cho chuỗi ngắn (giới hạn khác nhau theo phiên bản: 32B ở v2.x, 39B ở v3-v4, 44B từ v5).raw: Với chuỗi dài hơn, dùng SDS riêng biệt.
Một số lệnh thường dùng:
SET user:name "Nguyen Van A"
GET user:name
INCR page:views
SETEX session:token 3600 "abc123"
SETNX lock:import true
Tình huống sử dụng:
- Lưu tạm đối tượng: Mã hóa JSON rồi lưu vào key.
- Bộ đếm: Đếm lượt xem bài viết, lượt thích bằng INCR/DECR.
- Khóa phân tán: Dùng SET + NX + EX để tạo khóa, giải phóng bằng script Lua kiểm tra token.
- Session chia sẻ: Lưu trạng thái đăng nhập trên Redis để dùng chung giữa các server backend.
List – Danh sách liên kết kép
Danh sách tuần tự, hỗ trợ chèn/xóa ở đầu/cuối danh sách. Mỗi list chứa tối đa hơn 4 tỷ phần tử.
Cơ chế bên trong:
- Phiên bản cũ: Dùng ziplist nếu số lượng phần tử < 512 và kích thước mỗi phần tử < 64 byte.
- Phiên bản mới (>= 3.2): Sử dụng quicklist – cấu trúc lai giữa ziplist và danh sách liên kết.
Lệnh phổ biến:
LPUSH queue:tasks "task1" "task2"
RPOP queue:tasks
BRPOP queue:tasks 30
LRANGE logs 0 9
Ứng dụng làm hàng đợi tin nhắn:
- Bảo toàn thứ tự: LPUSH để thêm, RPOP để đọc.
- Chặn khi rỗng: BRPOP giúp tiết kiệm CPU.
- Xử lý trùng lặp: Tự sinh ID duy nhất kèm nội dung tin nhắn.
- Đảm bảo tin nhắn: Dùng BRPOPLPUSH sao lưu sang list dự phòng nếu xử lý thất bại.
Hạn chế: Không hỗ trợ nhiều consumer cùng nhận một tin nhắn – vấn đề này được giải quyết bởi Stream.
Hash – Bảng băm
Cấu trúc lưu trữ theo cặp field-value, rất phù hợp với dữ liệu đối tượng.
Cơ chế triển khai:
- Dùng ziplist nếu ít hơn 512 field và mỗi giá trị nhỏ hơn 64 byte.
- Từ Redis 7.0, ziplist bị loại bỏ, thay bằng listpack.
Lệnh tiêu biểu:
HSET user:100 name "Binh" age 28 email "binh@example.com"
HGET user:100 name
HMGET user:100 name age
HINCRBY cart:uid:200 item:77 1
Ứng dụng:
- Lưu thông tin người dùng: Mỗi field đại diện cho một thuộc tính.
- Giỏ hàng: Key là user ID, field là mã sản phẩm, value là số lượng.
Set – Tập hợp không trùng
Tập hợp vô hướng, không cho phép phần tử trùng lặp, hỗ trợ phép toán tập hợp như giao, hợp, hiệu.
Cơ chế:
- Nếu tất cả phần tử là số nguyên và số lượng < 512: dùng intset.
- Ngược lại: dùng bảng băm.
Lệnh cơ bản:
SADD likes:post:101 u:1001 u:1002 u:1003
SREM likes:post:101 u:1001
SISMEMBER likes:post:101 u:1001
SINTER followers:u1 followers:u2
Tình huống:
- Thích bài viết: Đảm bảo mỗi người chỉ thích một lần.
- Theo dõi chung: Tìm người dùng theo dõi cùng một chủ đề bằng SINTER.
- Quay thưởng: Dùng SPOP để chọn ngẫu nhiên và loại khỏi danh sách, tránh trúng giải nhiều lần.
Zset – Tập hợp sắp xếp
Giống Set nhưng mỗi phần tử gắn kèm điểm số (score), cho phép sắp xếp tăng/giảm theo score.
Cơ chế:
- Phù hợp với ziplist nếu số phần tử < 128 và mỗi phần tử < 64 byte.
- Không thỏa điều kiện: dùng skip list (danh sách nhảy).
Lệnh quan trọng:
ZADD rankings 200 "article:1" 150 "article:2" 90 "article:3"
ZINCRBY rankings 10 "article:3"
ZREVRANGE rankings 0 2 WITHSCORES
ZRANGEBYSCORE rankings 100 200
Ứng dụng:
- Bảng xếp hạng: Bài viết theo lượt xem, sản phẩm theo doanh số.
- Sắp xếp theo tên/tên miền: Dùng ZRANGEBYLEX với score đồng nhất để duyệt theo thứ tự từ điển.
BitMap – Mảng bit
Không phải kiểu dữ liệu riêng biệt mà tận dụng từng bit của chuỗi byte trong String để biểu diễn trạng thái bật/tắt.
Lệnh chính:
SETBIT user:100:signin:202310 5 1
GETBIT user:100:signin:202310 5
BITCOUNT user:100:signin:202310
BITPOS user:100:signin:202310 1
Tình huống:
- Điểm danh: Mỗi bit đại diện một ngày trong tháng.
- Kiểm tra trạng thái: Theo dõi người dùng đang online/offline.
- Thống kê người dùng điểm danh liên tục: Kết hợp BITOP AND nhiều bitmap rồi dùng BITCOUNT.
HyperLogLog – Đếm số lượng phần tử không trùng
Dùng để ước lượng số lượng phần tử duy nhất (cardinality) với sai số khoảng 0.81%, nhưng cực kỳ tiết kiệm bộ nhớ (~12KB dù dữ liệu lên tới 2^64).
Lệnh:
PFADD page:home:uv ip:192.168.1.10 user:abc
PFCOUNT page:home:uv
PFMERGE total:uv day1:uv day2:uv day3:uv
Ứng dụng: Thống kê lượt truy cập độc lập (UV) cho trang web có hàng triệu lượt view.
GEO – Vị trí địa lý
Dựa trên Zset, dùng GeoHash mã hóa tọa độ thành điểm số để hỗ trợ tìm kiếm bán kính.
Lệnh:
GEOADD stores 105.85 21.02 "CuaHangA" 105.87 21.05 "CuaHangB"
GEODIST stores "CuaHangA" "CuaHangB" km
GEORADIUS stores 105.86 21.03 5 km WITHDIST
Ứng dụng: Tìm cửa hàng/gara gần nhất, chức năng "gọi xe" trong app vận tải.
Stream – Hàng đợi tin nhắn hiện đại
Được thiết kế đặc biệt cho hệ thống message queue từ Redis 5.0, khắc phục các điểm yếu của List.
Tính năng nổi bật:
- Tự sinh ID toàn cục duy nhất theo thời gian.
- Hỗ trợ nhóm tiêu thụ (consumer group) – mỗi tin nhắn được xử lý bởi một consumer trong nhóm.
- Cơ chế xác nhận (ACK) đảm bảo tin nhắn không bị mất.
- Lưu lịch sử đã đọc (PENDING list) để xử lý lại khi lỗi.
Ví dụ:
XADD logstream * level "error" msg "DB connection failed"
XGROUP CREATE logstream group:err 0-0
XREADGROUP GROUP group:err consumer:1 STREAMS logstream >
XACK logstream group:err 1654256265584-0
So sánh với hệ thống chuyên dụng:
- Ưu điểm: Đơn giản, tích hợp sẵn trong Redis, hiệu suất cao.
- Hạn chế: Dữ liệu nằm trong RAM nên dễ mất nếu tắt đột ngột; khó mở rộng khi tích tụ quá nhiều tin nhắn.
Do đó, Redis Stream phù hợp với hệ thống nhẹ, chấp nhận mất mát dữ liệu nhỏ. Với yêu cầu cao về độ tin cậy và khả năng mở rộng, nên dùng Kafka hoặc RabbitMQ.
Kết luận
Mỗi kiểu dữ liệu Redis đều được tối ưu cho một lớp bài toán riêng:
- String: Bộ nhớ đệm, đếm, khóa.
- List: FIFO/LIFO, hàng đợi đơn giản.
- Hash: Đối tượng, giỏ hàng.
- Set: Loại bỏ trùng lặp, toán tập hợp.
- Zset: Xếp hạng, sắp xếp.
- BitMap: Trạng thái nhị phân quy mô lớn.
- HyperLogLog: Đếm phần tử duy nhất cực lớn.
- GEO: Dịch vụ vị trí.
- Stream: Hàng đợi tin nhắn cần độ tin cậy cao.
Việc lựa chọn đúng kiểu dữ liệu giúp tận dụng tối đa hiệu suất và tiết kiệm tài nguyên hệ thống.