Danh sách bài viết

Bài 5: Tổng Quan Redis Data Structures

Nhiều người mới dùng Redis chỉ biết tới SET/GET và coi nó là một cache key-value đơn giản. Thực ra Redis là một data structure server: value có thể là String, Hash, List, Set, Sorted Set, Bitmap, HyperLogLog, Streams hay GEO, mỗi loại có tập lệnh riêng được tối ưu cho một nhóm bài toán. Bài này là bản đồ tổng thể — không đi sâu từng structure (việc đó dành cho Module 2) mà giúp bạn biết mỗi structure giải quyết bài toán gì, vài lệnh chính, độ phức tạp Big-O đáng lưu ý, và quan trọng nhất là cách chọn đúng structure. Chọn sai structure không chỉ khiến code khó đọc mà còn biến một thao tác đáng lẽ O(1) thành O(N), hoặc tốn RAM gấp nhiều lần.

25/05/2026
15 phút đọc
0 lượt xem
1

Mục Tiêu Bài Học

Đây là bài tổng quan, mở đầu cho việc đi sâu từng data structure ở Module 2. Mục tiêu là cho bạn một bản đồ tư duy: nhìn vào một bài toán là biết ngay nên cân nhắc structure nào. Sau bài này bạn sẽ:

  • Phát biểu được vì sao Redis là một data structure server chứ không chỉ là key-value cache.
  • Kể tên và mô tả ngắn gọn bản chất của 9 data structure cốt lõi: String, Hash, List, Set, Sorted Set, Bitmap, HyperLogLog, Streams, GEO.
  • Gắn mỗi structure với một vài use case tiêu biểu và vài lệnh chính của nó.
  • Nhận biết các con số Big-O đáng lưu ý (ví dụ HGETALL là O(N), SISMEMBER là O(1)).
  • Dùng được bảng mapping bài toán → data structure để chọn nhanh cho tình huống thực tế.
  • Hiểu hệ quả khi chọn sai structure (biến O(1) thành O(N), tốn RAM) và tránh các anti-pattern như nhồi mọi thứ vào một JSON String hay tạo big key.

Bài này cố tình không đi sâu vào internal encoding hay từng tham số lệnh — đó là việc của các bài deep ở Module 2. Ở đây ta chỉ vẽ bức tranh lớn.

2

Redis Là Một Data Structure Server

Một Map<String, String> thông thường chỉ cho bạn lưu chuỗi và đọc lại chuỗi. Redis khác ở chỗ: key luôn là chuỗi, nhưng value có thể là nhiều kiểu cấu trúc khác nhau, mỗi kiểu mang theo một tập lệnh chuyên biệt được cài đặt sẵn ở phía server.

Ví dụ, nếu value là một Sorted Set, bạn không cần đọc cả tập về client rồi tự sắp xếp — Redis có sẵn lệnh lấy top-N theo score trong thời gian logarit. Nếu value là một Set, bạn hỏi "phần tử này có trong tập không?" và nhận câu trả lời trong O(1). Logic xử lý nằm cạnh dữ liệu, trong RAM, nên vừa nhanh vừa nguyên tử (atomic) vì Redis thực thi lệnh tuần tự.

Đó là lý do người ta gọi Redis là data structure server: nó cung cấp các cấu trúc dữ liệu kinh điển (như bạn học trong môn cấu trúc dữ liệu) nhưng ở dạng dịch vụ chạy qua mạng, chia sẻ được giữa nhiều process và server.

Trong bài này ta điểm qua 9 structure cốt lõi. Cuối bài có nhắc thêm một câu về Bitfield và các module của Redis Stack (RedisJSON, Bloom filter, RedisTimeSeries) — những thứ học sau. Một quy tắc xuyên suốt: chọn structure theo hình dạng tự nhiên của dữ liệu và theo thao tác bạn cần làm nhiều nhất, không phải theo thói quen.

3

String, Hash, List — Bộ Ba Thường Dùng Nhất

String

Bản chất: kiểu cơ bản nhất, một dãy byte binary-safe (chứa được text, số, hay blob nhị phân như ảnh), tối đa 512MB mỗi value. String còn được Redis hiểu như số nguyên/float khi bạn dùng lệnh đếm.

Use case: cache một giá trị (HTML đã render, JSON một object nhỏ), counter (lượt xem, lượt thích) với INCR, lưu token/session đơn giản kèm TTL.

Lệnh chính: SET, GET, INCR, INCRBY, APPEND, SETEX. SET/GET/INCR là O(1).

Hash

Bản chất: một map field → value (giống một object/dictionary nhỏ) nằm gọn trong một key. Khi Hash nhỏ, Redis dùng encoding listpack rất tiết kiệm RAM so với việc lưu mỗi field thành một String riêng.

Use case: lưu một object có nhiều field — profile người dùng (name, email, age), cấu hình, giỏ hàng. Bạn đọc/ghi từng field mà không phải nạp lại toàn bộ object.

Lệnh chính: HSET, HGET, HMGET, HGETALL, HDEL, HINCRBY. HSET/HGET trên một field là O(1), nhưng HGETALL là O(N) theo số field — Hash quá lớn sẽ làm lệnh này chậm và chặn server.

List

Bản chất: một danh sách có thứ tự, hoạt động như linked list hai đầu — thêm/xoá ở hai đầu rất rẻ (O(1)).

Use case: hàng đợi (queue) đơn giản, stack, timeline/feed mới nhất, lưu N log gần nhất (kết hợp LTRIM để giới hạn độ dài).

Lệnh chính: LPUSH/RPUSH (đẩy trái/phải), LPOP/RPOP (lấy ra), LRANGE (lấy một dải), LLEN, BLPOP (lấy có chờ). Push/pop ở đầu/cuối là O(1); LRANGE và truy cập theo index ở giữa là O(N).

4

Set Và Sorted Set — Tập Hợp Và Xếp Hạng

Set

Bản chất: tập hợp các phần tử unique, không có thứ tự. Mỗi phần tử chỉ xuất hiện một lần; thêm trùng không gây lỗi mà bị bỏ qua.

Use case: kiểm tra membership ("user này đã like bài viết chưa?"), lưu tag, lọc trùng (unique visitor trong một phiên), và đặc biệt là các phép toán tập hợp giữa nhiều Set: giao (SINTER), hợp (SUNION), hiệu (SDIFF) — ví dụ "bạn chung của A và B".

Lệnh chính: SADD, SISMEMBER, SMEMBERS, SCARD, SREM, SINTER/SUNION/SDIFF. SADDSISMEMBERO(1) — đây chính là lý do dùng Set để kiểm tra thành viên thay vì quét một List.

Sorted Set (ZSet)

Bản chất: giống Set (phần tử unique) nhưng mỗi member kèm một score kiểu số. Các member luôn được sắp xếp theo score, nên bạn lấy theo thứ hạng hoặc theo khoảng score cực nhanh. Bên dưới Redis dùng skiplist + hash table.

Use case: bảng xếp hạng (leaderboard) theo điểm, ranking bài viết theo lượt tương tác, hàng đợi có ưu tiên, và delayed queue (dùng timestamp làm score rồi lấy các job đến hạn).

Lệnh chính: ZADD, ZRANGE (lấy theo thứ hạng), ZRANGEBYSCORE (lấy theo khoảng score), ZRANK, ZSCORE, ZINCRBY, ZPOPMIN. ZADD và truy vấn theo rank/score là O(log N) cộng số phần tử trả về — vẫn rất nhanh kể cả với hàng triệu member.

5

Bitmap Và HyperLogLog — Đếm Tiết Kiệm RAM

Bitmap

Bản chất: không phải một kiểu riêng mà là các thao tác bit trên String. Bạn coi một String như một dãy bit khổng lồ và bật/tắt từng bit theo offset. Một triệu bit chỉ tốn khoảng 125KB RAM.

Use case: theo dõi trạng thái nhị phân theo từng user-id: DAU (daily active users — bật bit tại offset = user id mỗi khi họ hoạt động trong ngày), feature flag bật/tắt theo user, chuỗi đăng nhập liên tiếp (login streak — mỗi bit là một ngày).

Lệnh chính: SETBIT (bật/tắt bit tại offset), GETBIT, BITCOUNT (đếm số bit bằng 1), BITOP (AND/OR/XOR giữa các bitmap). SETBIT/GETBIT là O(1); BITCOUNT là O(N) theo độ dài.

HyperLogLog

Bản chất: một cấu trúc xác suất để ước lượng cardinality — đếm số phần tử unique một cách xấp xỉ, sai số tiêu chuẩn khoảng 0,81%. Điểm mạnh nằm ở bộ nhớ: bất kể bạn thêm bao nhiêu phần tử (vài chục, hay vài tỉ), nó chỉ tốn tối đa khoảng 12KB.

Use case: đếm số visitor unique của một trang, số IP khác nhau, số search term duy nhất — những chỗ mà con số chính xác tuyệt đối không quan trọng nhưng quy mô lại rất lớn và dùng Set sẽ ngốn RAM khủng khiếp.

Lệnh chính: PFADD (thêm phần tử), PFCOUNT (ước lượng số unique), PFMERGE (gộp nhiều HLL). Thao tác là O(1) trên mỗi phần tử. Lưu ý: HLL không liệt kê được các phần tử, chỉ đếm.

6

Streams Và GEO — Log Sự Kiện Và Vị Trí

Streams

Bản chất: một append-only log — mỗi entry có một ID (mặc định theo timestamp) và một tập field-value, các entry được thêm vào cuối và không đổi. Khác với List, Streams hỗ trợ consumer group: nhiều consumer chia nhau xử lý, theo dõi entry nào đã được nhận và xác nhận (ACK), cho phép xử lý lại nếu consumer chết.

Use case: message queue tin cậy (đảm bảo mỗi message được xử lý và ACK), event sourcing, ghi log sự kiện cho nhiều consumer cùng đọc lại.

Lệnh chính: XADD (thêm entry), XREAD (đọc theo ID), XREADGROUP (đọc trong consumer group), XACK (xác nhận đã xử lý), XLEN, XRANGE. XADD là O(1), XREAD/XRANGE hiệu quả nhờ cấu trúc cây bên dưới.

GEO

Bản chất: không phải kiểu lưu trữ riêng — GEO được xây dựng trên Sorted Set. Redis mã hoá kinh độ/vĩ độ thành một số (geohash) dùng làm score, nhờ đó có thể truy vấn các điểm gần một vị trí.

Use case: tìm "địa điểm gần đây" — tài xế gần khách, cửa hàng quanh vị trí người dùng, bạn bè ở gần.

Lệnh chính: GEOADD (thêm điểm với lng/lat), GEOSEARCH (tìm điểm trong bán kính hoặc hình hộp quanh một vị trí), GEODIST (khoảng cách giữa hai điểm), GEOPOS. Vì là ZSet bên dưới nên các thao tác có chi phí cỡ O(log N).

Ngoài 9 cái trên, Redis còn có Bitfield (thao tác trên các trường số nguyên đóng gói trong String) và các module của Redis Stack: RedisJSON (lưu/truy vấn JSON theo path), Bloom filter (kiểm tra "có thể đã thấy" xác suất), RedisTimeSeries (dữ liệu chuỗi thời gian). Những thứ này sẽ học sau khi bạn đã vững các structure nền tảng.

7

Bảng Mapping: Bài Toán Sang Data Structure

Đây là phần quan trọng nhất của bài. Khi gặp một bài toán, hãy soi nó vào bảng này trước khi viết code. Cột "Vì sao" giúp bạn hiểu lý do thay vì học vẹt.

Bài toán Data structure Vì sao
Cache một giá trị, đếm lượt (view, like)StringGET/SET/INCR đều O(1), đơn giản và rẻ
Lưu object nhiều field (profile, config)HashĐọc/ghi từng field O(1), gọn RAM, không phải parse cả object
Hàng đợi đơn giản, timeline mới nhấtListPush/pop hai đầu O(1), giữ thứ tự chèn
Kiểm tra thành viên ("đã like chưa?"), lưu tagSetSISMEMBER O(1) thay vì quét O(N) như List
Giao/hợp/hiệu giữa các tập (bạn chung)SetCó sẵn SINTER/SUNION/SDIFF phía server
Bảng xếp hạng, ranking, top-N theo điểmSorted SetSắp theo score sẵn, lấy top-N và rank trong O(log N)
Delayed queue (job đến hạn theo thời gian)Sorted SetDùng timestamp làm score, ZRANGEBYSCORE lấy job đến hạn
Trạng thái on/off theo từng user (DAU, flag, streak)BitmapMỗi user là 1 bit, cực tiết kiệm RAM, BITCOUNT để tổng hợp
Đếm unique cực lớn ở mức xấp xỉHyperLogLogChỉ ~12KB cho mọi quy mô, sai số ~0,81%
Message queue tin cậy, event log nhiều consumerStreamsConsumer group + ACK đảm bảo xử lý đáng tin cậy
Tìm điểm "gần đây" theo toạ độGEO (trên ZSet)GEOSEARCH truy vấn theo bán kính cực nhanh

Lưu ý: nhiều bài toán có thể giải bằng nhiều cách; bảng này nêu lựa chọn tự nhiên và hiệu quả nhất trong đa số trường hợp. Khi đi sâu ở Module 2 bạn sẽ thấy các đánh đổi tinh tế hơn.

8

Code Minh Hoạ Ngắn (ioredis & redis-py)

Dựng Redis cục bộ để chạy thử:

docker run --name redis-demo -p 6379:6379 -d redis:7-alpine
docker exec -it redis-demo redis-cli PING   # PONG

Các đoạn dưới chỉ là chạm tay cho quen — mỗi structure sẽ được đào sâu ở Module 2. Trước tiên là String, Hash, List, Set bằng TypeScript với ioredis (cài npm i ioredis):

import Redis from "ioredis";

const redis = new Redis({ host: "127.0.0.1", port: 6379 });

async function main(): Promise<void> {
  // String: cache + counter
  await redis.set("post:1:title", "Tong quan Redis");
  await redis.incr("post:1:views"); // 1, 2, 3...

  // Hash: lưu object profile theo field
  await redis.hset("user:1", { name: "Canh", email: "[email protected]", age: "30" });
  const name = await redis.hget("user:1", "name"); // "Canh"

  // List: hàng đợi đơn giản (đẩy phải, lấy trái = FIFO)
  await redis.rpush("queue:emails", "job-1", "job-2");
  const job = await redis.lpop("queue:emails"); // "job-1"

  // Set: kiểm tra membership trong O(1)
  await redis.sadd("post:1:likers", "user:1", "user:2");
  const liked = await redis.sismember("post:1:likers", "user:1"); // 1

  console.log({ name, job, liked });
  await redis.quit();
}

main();

Tiếp theo là Sorted Set, Bitmap, HyperLogLog và GEO — vẫn bằng ioredis:

// Sorted Set: leaderboard, lấy top 3 điểm cao nhất
await redis.zadd("leaderboard", 120, "alice", 95, "bob", 200, "carol");
const top3 = await redis.zrange("leaderboard", 0, 2, "REV", "WITHSCORES");
// ["carol","200","alice","120","bob","95"]

// Bitmap: đánh dấu user 42 hoạt động hôm nay, rồi đếm DAU
await redis.setbit("dau:2026-05-25", 42, 1);
const dau = await redis.bitcount("dau:2026-05-25"); // số user active

// HyperLogLog: đếm visitor unique xấp xỉ
await redis.pfadd("visitors:2026-05-25", "ip-1", "ip-2", "ip-1");
const uniques = await redis.pfcount("visitors:2026-05-25"); // ~2

// GEO: thêm điểm rồi tìm trong bán kính 5km
await redis.geoadd("shops", 106.700, 10.776, "shop:1");
const near = await redis.geosearch(
  "shops", "FROMLONLAT", 106.701, 10.777, "BYRADIUS", 5, "km", "ASC"
);

Tương đương bằng Python với redis-py (cài pip install redis):

import redis

r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)

# String
r.set("post:1:title", "Tong quan Redis")
r.incr("post:1:views")

# Hash
r.hset("user:1", mapping={"name": "Canh", "email": "[email protected]", "age": "30"})
name = r.hget("user:1", "name")  # "Canh"

# List (FIFO)
r.rpush("queue:emails", "job-1", "job-2")
job = r.lpop("queue:emails")  # "job-1"

# Set: membership O(1)
r.sadd("post:1:likers", "user:1", "user:2")
liked = r.sismember("post:1:likers", "user:1")  # True

# Sorted Set: top 3
r.zadd("leaderboard", {"alice": 120, "bob": 95, "carol": 200})
top3 = r.zrange("leaderboard", 0, 2, desc=True, withscores=True)

# Bitmap: DAU
r.setbit("dau:2026-05-25", 42, 1)
dau = r.bitcount("dau:2026-05-25")

# HyperLogLog: unique xấp xỉ
r.pfadd("visitors:2026-05-25", "ip-1", "ip-2", "ip-1")
uniques = r.pfcount("visitors:2026-05-25")  # ~2

# GEO: tìm trong bán kính 5km
r.geoadd("shops", (106.700, 10.776, "shop:1"))
near = r.geosearch(
    "shops", longitude=106.701, latitude=10.777,
    radius=5, unit="km", sort="ASC",
)

Đừng lo nếu chưa nắm hết tham số — ở đây mục tiêu chỉ là thấy mỗi structure có bộ lệnh riêng phù hợp với bài toán của nó.

9

Chọn Sai Structure: Chậm Và Tốn RAM

Chọn đúng structure không chỉ là chuyện code đẹp — nó quyết định độ phức tạp và mức tiêu thụ RAM. Vài ví dụ kinh điển:

Dùng List để kiểm tra membership

Giả sử bạn lưu danh sách user đã like một bài viết vào một List và muốn biết "user X đã like chưa". Bạn buộc phải quét cả List để tìm — đó là O(N). Với một bài viral có hàng trăm nghìn like, mỗi lần kiểm tra là một vòng quét tốn kém, và vì Redis thực thi lệnh tuần tự, một lệnh chậm có thể chặn cả server.

Đổi sang Set: SISMEMBER trả lời trong O(1), bất kể tập lớn cỡ nào. Cùng một bài toán, một bên O(N) một bên O(1).

Đọc cả object chỉ để lấy một field

Nếu bạn lưu profile dưới dạng một String JSON, mỗi lần chỉ cần đọc email bạn vẫn phải GET toàn bộ chuỗi rồi parse ở client. Với Hash, HGET user:1 email chỉ lấy đúng field cần, O(1), không tốn băng thông và CPU parse.

Đếm unique lớn bằng Set

Đếm số visitor unique trong ngày bằng Set đúng về mặt logic, nhưng nếu có hàng chục triệu visitor thì Set sẽ lưu từng phần tử và ngốn hàng trăm MB RAM. HyperLogLog cho cùng câu trả lời (xấp xỉ, sai số ~0,81%) chỉ với ~12KB. Nếu bạn không cần liệt kê phần tử, HLL là lựa chọn đúng.

Thông điệp: trước khi code, hãy hỏi "thao tác mình làm nhiều nhất là gì, và nó có O(1) với structure này không?".

10

Pitfalls Và Anti-patterns

Các lỗi tư duy phổ biến khi chọn và dùng data structure:

  • Nhồi mọi thứ vào một JSON String: lưu cả object lớn dưới dạng một chuỗi JSON rồi lúc nào cũng GET về và parse ở client. Hệ quả: mỗi lần đọc/ghi một field cũng phải nạp và serialize toàn bộ, tốn băng thông và CPU, lại dễ race condition khi nhiều process cùng cập nhật. Object có nhiều field nên dùng Hash.
  • Chọn structure theo thói quen: mặc định dùng String cho mọi thứ vì "quen tay". Hãy xuất phát từ thao tác bạn cần làm nhiều nhất (membership? ranking? đếm unique?) rồi mới chọn structure khớp với thao tác đó.
  • Big key — tạo một collection khổng lồ trong một key: một Hash/Set/List/ZSet chứa hàng triệu phần tử trong cùng một key gây ra nhiều vấn đề: các lệnh O(N) như HGETALL, SMEMBERS, LRANGE 0 -1 chặn server lâu; key này khó phân tán đều khi chạy cluster (cả key nằm trên một node); xoá nó (DEL) cũng tốn kém. Hãy chia nhỏ (sharding theo prefix, hoặc tách theo thời gian như dau:2026-05-25).
  • Lạm dụng các lệnh O(N) trên dữ liệu lớn: HGETALL, SMEMBERS, KEYS * trên tập lớn làm tăng latency cho tất cả client khác (vì thực thi tuần tự). Ưu tiên các lệnh có giới hạn (HMGET field cần thiết, SSCAN/HSCAN để duyệt dần, LRANGE theo khoảng nhỏ).
  • Dùng HyperLogLog khi cần con số chính xác hoặc cần liệt kê: HLL chỉ ước lượng và không trả lại các phần tử. Nếu nghiệp vụ yêu cầu đếm chính xác hoặc lấy ra danh sách, hãy dùng Set (chấp nhận tốn RAM hơn).

Mỗi pitfall ở đây sẽ được mổ xẻ kỹ cùng giải pháp trong các bài deep ở Module 2.

11

Tổng Kết, Quiz Và Bài Tiếp Theo

Tổng kết

  • Redis là một data structure server: key luôn là chuỗi nhưng value có nhiều kiểu, mỗi kiểu có tập lệnh riêng được tối ưu, chạy cạnh dữ liệu trong RAM và nguyên tử.
  • 9 structure cốt lõi: String (cache/counter), Hash (object nhiều field), List (queue/timeline), Set (membership/tag/set ops), Sorted Set (leaderboard/delayed queue), Bitmap (trạng thái on/off theo user), HyperLogLog (đếm unique xấp xỉ ~12KB), Streams (queue tin cậy/event log), GEO (tìm gần đây, xây trên ZSet).
  • Big-O đáng nhớ: SISMEMBER/SADD O(1) còn quét List là O(N); HGETALL/SMEMBERS là O(N); ZSet truy vấn theo rank/score là O(log N).
  • Dùng bảng mapping để chọn nhanh; xuất phát từ thao tác làm nhiều nhất.
  • Tránh anti-pattern: nhồi JSON vào String thay vì Hash, chọn theo thói quen, tạo big key, lạm dụng lệnh O(N).

Quiz 5 câu

  1. Vì sao gọi Redis là "data structure server" thay vì chỉ là key-value cache?
  2. Bạn cần kiểm tra "user X đã like bài viết này chưa" với hiệu năng tốt nhất. Chọn structure nào và độ phức tạp là bao nhiêu? So với việc dùng List thì khác gì?
  3. Cần đếm số visitor unique của một website lớn (hàng chục triệu lượt) ở mức xấp xỉ với ít RAM nhất. Dùng structure nào, tốn khoảng bao nhiêu RAM, và hạn chế của nó là gì?
  4. Vì sao HGETALL trên một Hash rất lớn là một anti-pattern, xét trên mô hình thực thi của Redis?
  5. "Big key" là gì và kể hai vấn đề nó gây ra?

Đáp án gợi ý

  1. Vì value của Redis không chỉ là chuỗi mà là nhiều cấu trúc dữ liệu (Hash, List, Set, Sorted Set, Stream...), mỗi loại đi kèm tập lệnh chuyên biệt được cài sẵn ở server, chạy cạnh dữ liệu trong RAM. Ta đẩy được logic (xếp hạng, kiểm tra thành viên, giao/hợp tập) xuống server thay vì kéo dữ liệu về client xử lý.
  2. Dùng Set với SISMEMBER, độ phức tạp O(1). Nếu dùng List thì phải quét tuyến tính để tìm phần tử, là O(N) — với tập lớn vừa chậm vừa có thể chặn server do Redis thực thi lệnh tuần tự.
  3. Dùng HyperLogLog (PFADD/PFCOUNT), tốn tối đa khoảng 12KB bất kể quy mô. Hạn chế: chỉ cho con số ước lượng (sai số tiêu chuẩn ~0,81%) và không liệt kê được các phần tử; nếu cần chính xác hoặc cần danh sách thì phải dùng Set.
  4. HGETALL là O(N) theo số field và Redis thực thi lệnh tuần tự trên một luồng; một lệnh O(N) chạy lâu sẽ chặn mọi lệnh khác, làm tăng latency cho tất cả client. Nên dùng HMGET lấy đúng field cần hoặc HSCAN duyệt dần.
  5. Big key là một collection (Hash/Set/List/ZSet) chứa quá nhiều phần tử trong một key. Hai vấn đề: (1) các lệnh O(N) trên nó chặn server lâu; (2) trong cluster nó không phân tán đều vì cả key nằm trên một node (ngoài ra DEL nó cũng tốn kém). Giải pháp: chia nhỏ theo prefix/thời gian.

Bài tiếp theo

Bài này cho bạn bản đồ về cái gì Redis lưu được. Bài 6 chuyển sang câu hỏi: dữ liệu trong RAM làm sao không mất khi restart? Ta sẽ tìm hiểu hai cơ chế persistence của Redis — RDB (snapshot định kỳ) và AOF (append-only file ghi lại từng lệnh ghi) — cùng chế độ hybrid kết hợp cả hai, và đánh đổi giữa độ bền dữ liệu, hiệu năng và thời gian khởi động.

Tham khảo