Part 04 · PostgreSQL & Redis · 4.1.07

Redis command model và data structures

Redis nhanh vì giữ working set trong memory và thực thi commands theo mô hình đơn giản, nhưng latency vẫn phụ thuộc command complexity, kích thước key, network round trips và bounded resource design.


Execution model

Redis dùng event loop để xử lý network events và phần lớn command execution theo thứ tự tuần tự. Vì vậy một command hoàn tất mà không bị command khác chen giữa, nhưng điều đó không có nghĩa server chỉ có đúng một thread: I/O threads và background threads/processes có thể phục vụ networking, persistence, lazy free và công việc phụ trợ.

Hệ quả quan trọng là command O(N), script/function dài, key rất lớn hoặc response khổng lồ có thể tăng latency cho mọi client dùng cùng server. Big-O phải được đọc cùng kích thước thực tế, worst-case input và tần suất gọi.

Big key là vấn đề latency lẫn memory: xóa, expire, replicate, migrate hoặc serialize một value lớn có thể tạo spike. Đặt giới hạn kích thước/cardinality từ domain, theo dõi slow log và sampling memory thay vì chờ server hết RAM.

Memory database và internal encodings

Dataset chính nằm trong RAM. Mỗi key/value có logical type, còn Redis có thể đổi internal encoding theo size và content để cân bằng memory với CPU. Dung lượng thực tế không chỉ là payload: còn key/object metadata, allocator overhead và fragmentation, expires dictionary, replication backlog, client output buffers, persistence copy-on-write và module data.

Tín hiệuDùng để trả lời
MEMORY USAGE keyMột key ước tính dùng bao nhiêu bytes, có thể điều chỉnh sampling cho nested values.
INFO memoryDataset, RSS, peak, fragmentation, allocator và memory thuộc replication/client.
MEMORY STATSBreakdown chi tiết để phân biệt dataset với overhead.
SLOWLOG / command latencyCommands nào chiếm server time; slow log không bao gồm toàn bộ network I/O tới client.

Chọn data type từ operations

TypeOperations phù hợpCaveat
StringBytes, cached document, counter, bitmap/bitfield.Whole-value update; tránh blob quá lớn.
HashObject fields, partial field read/write, field counters.Không nên biến một hash thành unbounded container.
ListOrdered sequence, push/pop ở hai đầu, bounded recent items.Không phải durable queue; random access/range lớn tốn chi phí.
SetMembership, union/intersection/difference.Operations qua nhiều set lớn có thể block đáng kể.
Sorted SetRanking, score/time ordering, range by score/rank.Score là double; uniqueness theo member, không theo score.
StreamAppend-only entries, consumer groups, pending-delivery tracking.Cần trim, reclaim và idempotent consumer; không tự tạo exactly-once side effects.
HyperLogLogApproximate distinct count với memory cố định nhỏ.Không liệt kê members và có sai số.
GeospatialStore coordinates và radius/box search.Dựa trên sorted set; hiểu precision và coordinate limits.

Bitmap và bitfield là operations trên String, thích hợp flags/counters compact khi offset có giới hạn rõ. Chọn structure dựa trên commands cần chạy, complexity và lifecycle; không serialize mọi thứ thành JSON rồi đánh mất atomic field operations.

Atomicity, transactions và scripts

Một command Redis là atomic so với command khác, nhưng chuỗi GET → compute → SET gồm nhiều round trips không atomic. Ưu tiên command atomic có sẵn như INCR, conditional SET hoặc data-structure operation trước khi xây transaction phức tạp.

Cơ chếGuarantee chínhKhông guarantee
MULTI/EXECQueue rồi thực thi commands liên tiếp, không bị client command khác xen giữa.Không rollback commands đã chạy khi runtime error; queued syntax errors có behavior khác.
WATCHOptimistic check-and-set; EXEC abort nếu watched key đổi.Không tự retry hoặc ngăn contention/starvation.
Lua scripts / FunctionsServer-side composition chạy atomic so với commands khác.Không nên chạy lâu, block, tạo unbounded work hoặc gọi commands bị cấm trong script context.

Retry optimistic transaction phải bounded và recompute từ fresh values. Script/function cần giới hạn input và thời gian; deploy/versioning phải tương thích cluster topology và failover behavior.

Pipelining và connection behavior

Pipelining gửi nhiều commands mà không chờ từng response, giảm round trips và tăng throughput. Nó không tạo transaction hay atomic batch; commands từ clients khác vẫn có thể xen giữa nếu không dùng cơ chế atomic riêng.

Batch quá lớn giữ request/response buffers, tăng tail latency và có thể chặn client processing. Dùng bounded batch theo bytes lẫn command count, flush định kỳ và áp backpressure. Connection pool cũng phải bounded; một số clients multiplex concurrent requests trên một connection, trong khi blocking commands, Pub/Sub hoặc transaction state thường cần connection riêng theo client-library contract.

Streams và consumer groups

producer --XADD--> stream
                       |
                  consumer group
                  /      |      \
               C1       C2       C3
                |        |
             pending entries list
                |
          XACK hoặc reclaim

XREADGROUP giao entry cho consumer và ghi nhận nó trong pending entries list; XACK xác nhận xử lý. Consumer chết để lại pending entries cần inspect và claim/recovery. Thiết kế phải có retry/backoff, poison-message policy, idempotency key và trim/retention; acknowledge sau business commit nếu muốn tránh mất work.

Redis Streams hỗ trợ delivery tracking nhưng không thể atomically commit side effect ở database khác chỉ bằng XACK. Exactly-once business outcome cần idempotent handler, inbox/deduplication hoặc transaction boundary phù hợp.

Key design và production-safe iteration

Key nên có namespace, version và tenant/domain rõ, ví dụ cart:v2:{tenantId}:userId. Đặt TTL theo ownership, tránh tên quá dài và unbounded key cardinality. Không đưa secrets hoặc raw PII vào key vì key xuất hiện trong diagnostics, replication và backups.

Mental model: Redis không chỉ là “map trong RAM”. Data type là API concurrency: chọn đúng type giúp một business transition trở thành một atomic command và giảm round trips.
Nguồn tham khảo