Luyện Phỏng Vấn IT — 2000+ Câu Hỏi Phỏng Vấn IT Có Đáp Án 2026
Redis
Redis (Remote Dictionary Server) là in-memory data structure store, thường được dùng làm cache, message broker, và session store.
- Redis nhanh vì toàn bộ data được lưu trong RAM — tốc độ đọc/ghi RAM nhanh hơn disk hàng nghìn lần.
- Ngoài ra, Redis dùng I/O multiplexing (epoll/kqueue) với event loop tương tự Node.js, tránh được overhead của context switching và lock contention.
- Command execution vẫn single-threaded để đảm bảo atomicity.
- Redis 7.x dùng multi-threaded I/O theo mặc định cho network (đọc/ghi socket), vẫn single-threaded cho command execution để tránh race condition.
- Benchmark thông thường: Redis đạt 100,000-1,000,000 ops/giây tùy loại operation và hardware.
Redis có 5 kiểu dữ liệu cơ bản (String, Hash, List, Set, Sorted Set), mỗi loại tối ưu cho một use case khác nhau.
Các kiểu dữ liệu Redis và khi nào dùng:
- String: kiểu cơ bản nhất, dùng cho cache HTML/JSON, counter (
INCR pageview), session token. Lệnh:SET key value EX 3600,GET key,INCR counter. - Hash: lưu object có nhiều field (như row trong database), hiệu quả hơn lưu từng field dưới dạng String riêng. Dùng cho: user profile, product info. Lệnh:
HSET user:1 name 'Alice' age 30,HGETALL user:1. - List: linked list có thể push/pop từ cả hai đầu — dùng cho message queue, activity feed, recent items. Lệnh:
LPUSH queue task1,RPOP queue. - Set: tập hợp unique member — dùng cho tags, unique visitors, friend list. Lệnh:
SADD tags:post:1 redis database,SISMEMBER,SUNION. - Sorted Set: như Set nhưng mỗi member có score — dùng cho leaderboard, rate limiting, priority queue. Lệnh:
ZADD leaderboard 1000 'Alice',ZRANGE leaderboard 0 9 REV WITHSCORES.
Memcached là pure cache với chỉ một kiểu dữ liệu (string), multi-threaded, đơn giản.
- Redis có nhiều kiểu dữ liệu (String, Hash, List, Set, Sorted Set, Stream, HyperLogLog, Geospatial), hỗ trợ persistence, pub/sub, transactions, Lua scripting, clustering native.
- Redis phù hợp cho hầu hết use case hiện đại.
- Nên dùng Memcached khi: chỉ cần simple key-value cache, muốn tận dụng multi-threading tốt hơn cho CPU-bound workload (hiếm gặp trong practice), team đã quen thuộc.
- Nên dùng Redis khi: cần data structure phong phú, persistence, pub/sub, session store, rate limiting, leaderboard, distributed lock — về cơ bản hầu hết mọi trường hợp.
- Trong thực tế, Redis đã thay thế Memcached ở phần lớn use case mới; Memcached chỉ còn được dùng trong legacy system.
Redis là multi-purpose in-memory store được dùng rộng rãi cho caching, session, rate limiting, leaderboard và distributed lock trong hầu hết production stack hiện đại.
Các use case phổ biến của Redis trong production:
- Caching: lưu kết quả database query, API response, rendered HTML để giảm latency và tải cho database — đây là use case phổ biến nhất.
- Session store: lưu session người dùng với TTL, dễ share giữa nhiều server instance.
- Rate limiting: dùng INCR + EXPIRE hoặc Sorted Set để giới hạn API request theo IP/user.
- Leaderboard: dùng Sorted Set để rank user theo điểm, cực kỳ efficient với
ZADD,ZRANGE,ZRANK. - Queue: dùng List với
LPUSH/BRPOPđể implement simple task queue (hoặc Redis Streams cho production). - Pub/Sub: real-time notification, live dashboard, chat.
- Distributed lock: implement lock bằng
SET key value NX EX timeoutđể tránh race condition trong distributed system. - Geospatial: dùng Geo commands (
GEOADD,GEODIST,GEOSEARCH FROMMEMBER/FROMLONLAT BYRADIUS) cho tính năng tìm store gần nhất —GEORADIUSđã deprecated từ Redis 6.2. - Full-text search: RedisSearch module (Redis Stack) cho phép search index trực tiếp trên Redis data.
HyperLogLog là probabilistic data structure để đếm unique elements (cardinality estimation) với sai số chuẩn ~0.81% nhưng dùng chỉ 12KB bộ nhớ cố định bất kể có 1 hay 1 tỷ unique element. Set chính xác 100% nhưng tốn memory tỷ lệ với số element. Lệnh: PFADD pageviews user123 user456, PFCOUNT pageviews (trả về ước lượng số unique users), PFMERGE daily_total hour1 hour2 (merge nhiều HLL). Khi dùng HyperLogLog: đếm unique visitors/day, unique queries/hour, unique IPs — khi cần approximate count với memory cực nhỏ và không cần biết chính xác element nào đã add. Khi dùng Set: khi cần biết element cụ thể có trong tập không (SISMEMBER), khi cần list members, khi số element < vài triệu và memory là vừa đủ.
Ví dụ: Google Analytics đếm unique page views dùng HyperLogLog — 1 tỷ visitors chỉ tốn 12KB thay vì hàng GB.
RDB (Redis Database Snapshot): tạo snapshot toàn bộ data ra file .rdb theo định kỳ (ví dụ: save 900 1 = snapshot nếu có ít nhất 1 key thay đổi trong 900 giây).
- RDB compact, restart nhanh vì chỉ load một file, nhưng có thể mất data của khoảng thời gian kể từ snapshot cuối.
- AOF (Append-Only File): log mọi write operation vào file
.aof; khi restart, replay toàn bộ command để rebuild state. - AOF bền hơn (cấu hình
appendfsync alwayskhông mất data,everysecmất tối đa 1 giây), nhưng file lớn hơn và restart chậm hơn. - Có thể dùng cả hai cùng lúc (khuyến nghị cho production): RDB cho backup định kỳ, AOF cho durability.
- Redis 7.0 giới thiệu RDB-AOF hybrid format — AOF rewrite dùng RDB snapshot + delta AOF, kết hợp ưu điểm của cả hai.
- Nếu Redis chỉ dùng thuần túy làm cache, có thể disable cả hai để tối đa performance.
Redis Pub/Sub cho phép publisher gửi message đến một channel và tất cả subscriber đang lắng nghe channel đó nhận được message ngay lập tức.
- Lệnh:
SUBSCRIBE news,PUBLISH news 'Hello World'. - Pub/Sub của Redis là fire-and-forget — message không được lưu trữ, nếu không có subscriber nào online khi publish thì message bị mất; subscriber mới không nhận được message cũ.
- Hạn chế so với Kafka: không có message persistence, không replay, không consumer group với offset management, không đảm bảo delivery.
- Pub/Sub phù hợp cho: real-time notification, chat message, live dashboard update, cache invalidation signal.
- Nếu cần reliable messaging với durability và replay, dùng Redis Streams (thêm vào Redis 5.0) hoặc Kafka.
- Redis Keyspace Notifications là biến thể của Pub/Sub để lắng nghe events từ Redis chính nó (key expired, key set, etc.).
Redis Transaction dùng MULTI/EXEC: các command giữa MULTI và EXEC được queue lại và execute atomically — không có command nào khác được xen vào giữa.
- Tuy nhiên, Redis transaction không có rollback — nếu một command fail (runtime error như sai kiểu data), các command khác vẫn execute.
- Lỗi cú pháp (syntax error) khi queue command sẽ khiến toàn bộ transaction bị discard; chỉ runtime error mới bị bỏ qua.
DISCARDhủy transaction.WATCH keycho phép optimistic locking: nếu key thay đổi trước khi EXEC, transaction bị abort (EXEC trả về nil) — dùng để implement compare-and-swap. - Pipeline khác với transaction: pipeline đơn giản là gom nhiều command gửi một lần để giảm network round-trip, không đảm bảo atomicity.
Ví dụ dùng WATCH: đọc balance, kiểm tra đủ tiền, WATCH balance, MULTI, deduct, EXEC — nếu có race condition thì EXEC fail và retry.
- Trong thực tế, Lua script thường được ưa hơn MULTI/EXEC vì atomic và flexible hơn.
Key expiration: dùng EXPIRE key seconds, PEXPIRE key milliseconds, hoặc SET key value EX seconds để tự động xóa key sau khoảng thời gian.
- Redis kiểm tra expiry theo hai cách: lazy expiration (kiểm tra khi access key) và active expiration (background job mỗi 100ms xóa random sample 20 key trong set expired keys).
- Eviction policy được áp dụng khi Redis đạt
maxmemory:noeviction(reject write khi full),allkeys-lru(xóa key ít dùng nhất gần đây trong toàn bộ keyspace),volatile-lru(chỉ xóa key có TTL ít dùng nhất),allkeys-lfu(xóa key ít dùng nhất theo frequency — Redis 4.0+),volatile-ttl(xóa key có TTL ngắn nhất),allkeys-random. - Cache dùng
allkeys-lruhoặcallkeys-lfu; session store dùngvolatile-lru; data không thể mất dùngnoeviction. - Nên set
maxmemory-policyphù hợp và monitorevicted_keysmetric.
Best practice là dùng dấu hai chấm : làm separator để tạo namespace hierarchy, ví dụ: user:1001:profile, user:1001:session, product:sku:ABC123:stock.
- Các rule quan trọng: dùng tên có ý nghĩa và mô tả rõ object type + identifier + attribute (ví dụ:
order:order_id:status); tránh key quá dài (mỗi key đều tốn memory cho metadata overhead); tránh key quá ngắn khó đọc; thống nhất format trong toàn team và document lại. - Dùng
SCANthay vìKEYS *khi cần tìm key theo pattern vì KEYS block Redis;SCANiteration không block. - Đối với hot key (một key được access cực nhiều), có thể dùng local cache hoặc key sharding (
user:1001:profile:shard:3). - Một pattern hay là thêm version vào key namespace để dễ cache invalidation hàng loạt:
v2:user:1001:profile— khi cần invalidate tất cả, chỉ cần tăng version prefix.
Redis Bitmap không phải data type riêng — đó là String với các bit operation. Mỗi String key có thể chứa đến 512MB = ~4 tỷ bits. Lệnh: SETBIT key offset 1, GETBIT key offset, BITCOUNT key (đếm số bit=1), BITOP AND/OR/XOR dest key1 key2. Use case điển hình: Daily Active Users (DAU) — dùng user_id làm offset, mỗi ngày một key active:2024-01-15; SETBIT active:2024-01-15 user_id 1 khi user login; BITCOUNT active:2024-01-15 = số user active hôm nay. Memory: 1 triệu users = 1 triệu bits = 125KB — cực kỳ efficient. Feature flags per user: SETBIT feature:dark-mode user_id 1 để enable tính năng cho user cụ thể. Streak tracking: check consecutive days login. BITPOS: tìm vị trí bit đầu tiên = 0 hoặc 1 — dùng để assign ID.
Lưu ý: nếu user_id lớn (> 100 triệu), bitmap vẫn chỉ tốn 12MB cho 100M users — vẫn hiệu quả.
Leaderboard:
ZADD leaderboard 9500 'alice' # alice: 9500 điểm
ZADD leaderboard 8200 'bob'
ZRANGE leaderboard 0 9 REV WITHSCORES # top 10 giảm dần
ZRANK leaderboard 'alice' # rank của alice
ZINCRBY leaderboard 100 'alice' # tăng 100 điểmO(log N) cho mỗi update. Sliding Window Rate Limiter dùng Sorted Set:
# Key: ratelimit:user123, score = timestamp, member = unique_id
# (pseudocode - wrap in Lua script for atomicity in production)
ZADD ratelimit:user123 now() uuid()
ZREMRANGEBYSCORE ratelimit:user123 0 (now() - window)
count = ZCARD ratelimit:user123
if count > limit: reject
EXPIRE ratelimit:user123 windowTất cả thao tác này cần wrap trong Lua script để atomic.
- So với Fixed Window: Sliding Window chính xác hơn, không có boundary spike.
- Nhược điểm: tốn memory hơn (lưu timestamp từng request) — dùng ZSET với member là timestamp+uuid.
String approach: serialize toàn bộ object thành JSON và lưu vào một key — đơn giản, một GET là lấy toàn bộ.
- Nhược điểm: muốn update một field phải GET toàn bộ object, deserialize, update, serialize lại, SET — tốn băng thông và CPU; race condition nếu concurrent write. Hash approach: mỗi field là một Hash field —
HSET user:1 name 'Alice' age 30 email 'a@b.com';HGET user:1 namelấy một field;HINCRBY user:1 age 1tăng atomic không cần GET. Memory trade-off: Redis tự dùnglistpackencoding (compact array, đổi tên từ ziplist trong Redis 7.0) khi Hash nhỏ (<hash-max-listpack-entries=128field vàhash-max-listpack-value=64bytes) — hash nhỏ tốn ít memory hơn nhiều String key riêng lẻ. - Khi vượt ngưỡng, Redis chuyển sang
hashtableencoding. Quyết định: nhiều field thay đổi độc lập → Hash; object luôn đọc/ghi toàn bộ → String (JSON); object rất lớn (> 1KB serialized) → String với compression.
Cache-Aside (Lazy Loading): application tự quản lý cache.
- Read: check cache → miss → query DB → set cache → return.
- Write: update DB, invalidate hoặc update cache.
- Ưu điểm: chỉ cache data thực sự được request (no unnecessary warming); linh hoạt.
- Nhược điểm: cache miss gây thêm latency, cold start problem, potential stampede.
- Phổ biến nhất cho read-heavy workloads. Write-Through: mỗi write cập nhật cả DB lẫn cache đồng thời (application hoặc cache layer).
- Ưu điểm: cache luôn fresh.
- Nhược điểm: write latency cao hơn; cache nhiều data ít được đọc (waste memory).
- Tốt cho write-then-read workloads. Write-Behind (Write-Back): write vào cache trước, batch flush xuống DB async sau.
- Ưu điểm: write latency cực thấp, reduce DB write load.
- Nhược điểm: risk data loss nếu cache fail trước khi flush; eventual consistency.
- Dùng cho: high-frequency write (game score, view counter, shopping cart), khi DB write là bottleneck.
- Session cần đặc tính: đọc/ghi nhanh (mỗi request authenticated cần verify session), TTL-based expiration (logout tự động sau idle time), và không cần persist lâu dài.
- Redis phù hợp hoàn hảo: O(1) GET/SET, native TTL support (
SETEX session:abc123 3600 '{userId:1,...}'), in-memory nên sub-millisecond latency. - So với database: query DB mỗi request tốn 1-10ms thêm; DB không có native TTL; cần background job cleanup expired sessions. Implementation pattern:
# Login: tạo session
SET session:{token} {json_data} EX 86400
# Verify: đọc session
GET session:{token}
# Logout: xóa session ngay
DEL session:{token}
# Extend: renew TTL khi user active
EXPIRE session:{token} 86400Với Sentinel/Cluster: session available ngay cả khi một node fail. Lưu ý: không lưu sensitive data trong session JSON — chỉ lưu user_id, roles; sensitive data lấy từ DB.
Express.js connect-redis, Next.js iron-session đều support Redis backend.
Redis Sentinel là hệ thống HA cho Redis single-master/replica setup — tự động monitor, notify và failover khi master fail.
- Sentinel chạy như separate process (thường 3+ Sentinel nodes để quorum). Chức năng: Monitoring — Sentinel liên tục ping master và replicas; Notification — alert qua pub/sub khi có vấn đề; Automatic failover — khi master down, Sentinel bầu (quorum majority) và promote replica thành master mới, cập nhật cấu hình các replica còn lại để follow master mới; Service discovery — client query Sentinel để lấy địa chỉ master hiện tại. Quorum: số Sentinel tối thiểu phải đồng ý master down để bắt đầu failover — tránh split-brain khi network partition.
- Setup tối thiểu: 3 Sentinel + 1 master + 1 replica. Khác với Cluster: Sentinel không sharding, không scale write horizontally — chỉ HA.
- Dùng Sentinel khi: cần HA nhưng data fit trong 1 server; dùng Cluster khi: cần scale beyond single server.
Redis có 8 eviction policy; allkeys-lru là lựa chọn tốt nhất cho pure cache, volatile-lru khi mix cache và persistent keys, noeviction khi data không được phép mất.
Khi Redis đạt maxmemory, eviction policy quyết định key nào bị xóa để nhường chỗ. Có 8 policy chính:
noeviction(default): trả lỗi khi full — dùng cho DB không chấp nhận mất dataallkeys-lru: evict key ít dùng gần đây nhất trong toàn bộ keyspace — tốt nhất cho cache thuần túyallkeys-lfu(Redis 4.0+): evict key ít được access nhất về tần suất — tốt hơn LRU khi access pattern có hot/cold keys rõ ràngallkeys-random: evict random — hiếm dùngvolatile-lru: evict key có TTL, ưu tiên LRU — dùng khi mix cache + persistent keysvolatile-lfu: evict key có TTL, ưu tiên LFUvolatile-random: evict random trong keys có TTLvolatile-ttl: evict key có TTL ngắn nhất — dùng khi muốn keys expire sớm bị evict trước
Thực tế: cache-only Redis → allkeys-lru; Redis vừa cache vừa persistent → volatile-lru (chỉ evict keys có TTL, không đụng đến persistent keys). Monitor evicted_keys metric để biết eviction đang xảy ra.
Lua script trong Redis chạy atomically — toàn bộ script không thể bị interrupt bởi command khác, đây là điểm mạnh so với MULTI/EXEC. Dùng lệnh EVAL script numkeys key [key ...] arg [arg ...] hoặc EVALSHA (dùng SHA hash của script đã load để tránh gửi lại script). Use case: rate limiting — đọc counter, kiểm tra limit, tăng counter, set TTL trong một atomic operation; distributed lock — check và set lock atomically; complex conditional update không thể implement bằng một Redis command. Ví dụ rate limiter: local current = redis.call('INCR', KEYS[1]); if current == 1 then redis.call('EXPIRE', KEYS[1], ARGV[1]) end; return current.
Lưu ý: Lua script block toàn bộ Redis trong khi chạy, không nên viết script chạy lâu; nên dùng SCRIPT LOAD để pre-load script và gọi bằng EVALSHA cho hiệu năng tốt hơn.
Redis Cluster chia keyspace thành 16384 hash slot; mỗi key được hash bằng CRC16 và map vào một slot cụ thể (slot = CRC16(key) % 16384).
- Mỗi master node chịu trách nhiệm một range slot (ví dụ: node A: 0-5460, node B: 5461-10922, node C: 10923-16383), mỗi master có ít nhất một replica.
- Client dùng CLUSTER-aware client library để route request trực tiếp đến node chứa slot tương ứng; nếu route sai, node trả về
MOVEDredirect. - Hash tags (
{user}.profile) cho phép force nhiều key vào cùng một slot để support multi-key operations và transaction. - Resharding (di chuyển slot giữa các node) được thực hiện online không cần downtime bằng
redis-cli --cluster reshard. - Minimum recommended: 3 master + 3 replica (total 6 nodes) để có HA và survive node failure.
- Limitation: không support cross-slot transaction và multi-key operation (trừ khi dùng hash tag).
Redis Streams (Redis 5.0) là log data structure lưu message theo thứ tự với persistent storage, consumer group, và message acknowledgment — lấp đầy khoảng cách giữa Pub/Sub (fire-and-forget) và Kafka (phức tạp, separate infrastructure).
- Mỗi message có auto-generated ID dạng
timestamp-sequence(ví dụ:1638000000000-0). - Consumer group trong Redis Streams tương tự Kafka:
XREADGROUP GROUP mygroup consumer1 COUNT 10 STREAMS mystream >đọc undelivered message;XACK mystream mygroup message-idacknowledge;XPENDINGkiểm tra message chưa ack (để xử lý lại nếu consumer crash).XADD stream * field1 value1thêm message;MAXLEN ~1000để cap stream size. - Dùng Redis Streams khi: cần reliable messaging nhưng không muốn setup Kafka, data volume vừa phải, team đã dùng Redis.
- Dùng Kafka khi: data volume cực lớn, cần long-term retention, multi-datacenter replication, hoặc cần Kafka ecosystem (Kafka Streams, Connect).
Dùng appropriate data type: Hash thay vì nhiều String key riêng lẻ cho object nhỏ (Redis tự động dùng listpack encoding khi Hash nhỏ hơn hash-max-listpack-entries entries và mỗi value nhỏ hơn hash-max-listpack-value bytes — memory efficient hơn hashtable).
- Lưu ý: Redis 7.0 đổi tên ziplist thành listpack; config hiện tại dùng
hash-max-listpack-entriesvàhash-max-listpack-value. - Compressed encoding: Redis tự optimize encoding dựa trên size (listpack, intset, skiplist).
- Dùng
OBJECT ENCODING keyđể xem encoding. - Giảm key size:
usr:1001:prfthay vìuser:1001:profiletiết kiệm memory nhân với hàng triệu key. - Set TTL cho tất cả cache key tránh memory leak.
- Dùng
MEMORY USAGE keyđể đo memory một key,MEMORY DOCTORđể chẩn đoán vấn đề memory. - Nén value ở application layer (gzip JSON trước khi lưu) giảm 70-80% với JSON lớn.
- Monitor
used_memory,mem_fragmentation_ratio(>1.5 là vấn đề fragmentation — có thể bậtactivedefrag yeshoặc dùngMEMORY PURGEđể defragment).
Cache stampede xảy ra khi một hot cache key expire, hàng trăm/nghìn request đồng thời không thấy key trong cache, tất cả cùng query database, gây overload.
- Cách phòng tránh: Probabilistic Early Expiration — thay vì expire chính xác, tính toán xác suất recompute tăng dần khi gần expire, một request 'may mắn' sẽ recompute trước khi thực sự expire (không cần lock).
- Mutex/Distributed lock — request đầu tiên acquire lock và load data, các request khác đợi hoặc trả về stale data.
- Stale-while-revalidate — trả về data cũ ngay lập tức trong khi background job refresh cache (không có downtime nhưng data có thể stale ngắn).
- Cache warming — preload cache trước khi deploy hoặc khi key sắp expire.
- Dùng TTL jitter (thêm random ±10-20% vào TTL) để tránh nhiều key expire cùng lúc (tránh stampede hàng loạt).
- Trong practice, kết hợp mutex lock + stale cache fallback là pattern phổ biến nhất.
Vận hành Redis production tốt đòi hỏi cấu hình security đúng (auth, TLS, ACL), chọn persistence phù hợp, tránh blocking operation, và monitor các metric quan trọng như evicted_keys và slowlog.
Best practices khi vận hành Redis trong production:
- Security: bật authentication (
requirepass), dùng ACL (ACL SETUSER ... nocommands +@write) thay vìrename-commandcho Redis 6.0+ để kiểm soát permission chi tiết hơn, bind Redis chỉ trên internal interface (không expose ra internet), dùng TLS cho Redis 6+. - Availability: dùng Sentinel hoặc Cluster, đặt
replica-priorityhợp lý, test failover định kỳ. - Performance: đặt
maxmemoryphù hợp với RAM (để lại 20-30% cho OS và fragmentation), chọnmaxmemory-policyđúng, monitorslowlog(slowlog-log-slower-than 10000microseconds). - Tránh blocking operation: không dùng
KEYS *trên production (dùngSCAN), không dùngSMEMBERStrên Set lớn (dùngSSCAN), cẩn thận vớiSORT,LRANGEtrên List dài. - Connection management: dùng connection pool (
max pool size = 10-50), settcp-keepalive 300. - Backup: cấu hình RDB snapshot, monitor
rdb_last_save_timevàrdb_changes_since_last_save. - Upgrade: Redis minor version backward compatible, major version cần test kỹ; dùng Redis managed service (ElastiCache, Redis Cloud) để giảm operational burden.
Basic distributed lock:
# SET key value NX EX timeout (atomic: set only if not exists)
SET lock:resource unique_id NX EX 30-- Release: Lua script để atomic check-and-delete
if redis.call('GET', KEYS[1]) == ARGV[1] then
return redis.call('DEL', KEYS[1])
endVấn đề với single Redis instance: nếu Redis crash sau khi client A acquire lock nhưng trước khi expire, lock mất vĩnh viễn; nếu Redis failover sang replica, replica có thể chưa nhận lock → client B cũng acquire được lock → 2 clients cùng hold lock. Redlock (Martin Kleppmann cảnh báo vẫn không hoàn hảo): acquire lock trên N (>=5) independent Redis instance — phải là các host độc lập thực sự, không dùng chung persistence; lock chỉ valid khi acquire thành công trên majority (N/2+1) trong thời gian < lock validity.
- Kleppmann argue rằng nếu process bị pause dài hơn TTL (GC, network), client vẫn tin rằng đang hold lock trong khi lock đã expire và được acquire bởi client khác.
- Đây là thuật toán phức tạp, nhiều tranh cãi.
- Thực tế production: dùng Redlock cho non-critical locks (rate limiting, idempotency key); dùng ZooKeeper hoặc etcd cho critical distributed coordination.
- Library:
ioredis+redlocknpm package; JavaRedisson.
Cache invalidation là bài toán khó nổi tiếng (Phil Karlton: 'there are only two hard things in Computer Science: cache invalidation and naming things').
- Các chiến lược: TTL-based expiration: đơn giản nhất — set TTL phù hợp, data stale tối đa TTL seconds.
- Phù hợp cho data có freshness requirement rõ ràng. Event-driven invalidation: khi data trong DB thay đổi, publish event → subscriber DEL cache key tương ứng ngay.
- Dùng Debezium CDC hoặc domain events.
- Chính xác hơn TTL nhưng phức tạp hơn. Write-through invalidation: khi write DB, đồng thời DELETE hoặc SET cache mới. Versioned keys: thêm version vào cache key (
product:123:v5), khi update thì tăng version — key cũ tự expire theo TTL; không cần explicit invalidation. Cache tags (advanced): nhóm nhiều cache key theo tag (tag:category:electronics), khi category thay đổi thì invalidate tất cả key có tag đó — Redis không native support, cần implement bằng Set lưu list key per tag. - Thực tế: kết hợp short TTL (30-60s) + event invalidation để balance freshness và complexity.
Redis Cluster chia key space thành 16384 hash slots (0-16383).
- Mỗi key được assign slot =
CRC16(key) % 16384. - Cluster có N master nodes, mỗi master sở hữu một range slot (ví dụ: 3 masters mỗi master có ~5461 slots). MOVED redirect: khi client gửi request đến node sai (key không thuộc slot của node đó), node trả về
MOVED <slot> <ip:port>— client phải resend đến đúng node. - Smart client cache slot map để tránh redirect (redis-py, Jedis, ioredis tự handle). ASK redirect: trong quá trình resharding (migrate slot giữa nodes), một số key đã migrate, số khác chưa.
- Node cũ trả
ASK <slot> <new_node>cho key đã migrate — client gửiASKING+ request đến node mới một lần (không cache như MOVED). Key tags:{user_id}.orders— chỉ hashuser_idphần trong{}, đảm bảo keys liên quan cùng slot để dùng multi-key commands. - Cluster yêu cầu ít nhất 6 nodes (3 master + 3 replica) để đảm bảo HA.
Mỗi Redis command thông thường = 1 round-trip: client gửi request, đợi response, rồi gửi request tiếp theo.
- Với latency 1ms/request, 1000 commands = 1 giây. Pipelining gom nhiều commands vào một network call, gửi cùng lúc, nhận responses về cùng lúc — giảm round-trips xuống còn 1 (hoặc vài lần batch).
Ví dụ:
pipe = redis.pipeline()
for i in range(1000):
pipe.set(f'key:{i}', i)
results = pipe.execute() # 1 round-trip thay vì 1000Pipelining không đảm bảo atomicity (khác với MULTI/EXEC).
- Commands trong pipeline có thể bị xen kẽ với commands từ client khác. Khi nào hiệu quả: bulk insert/update, batch GET/SET, warming cache — bất kỳ lúc nào có nhiều commands độc lập nhau. Khi không giúp: một command phụ thuộc kết quả của command trước — phải dùng Lua script hoặc MULTI/EXEC.
- Khi network latency cao (>1ms như cross-datacenter), pipelining có thể tăng throughput 10-100x.