Redis内部数据存储形式咨询:存储为SDS还是对象?
Great question—let’s break this down step by step to clarify how Redis handles data storage under the hood, since it’s easy to get confused between the wire protocol (RESP), internal string representation (SDS, Simple Dynamic String), and Redis’s core object system.
1. RESP is Parsed into RedisObjects (Not Stored Directly)
First, Redis does NOT store raw RESP data or SDS as top-level values. When a client sends a request via RESP, Redis’s network layer (in networking.c) first parses the RESP payload into RedisObject instances—this is the fundamental unit of data storage in Redis.
For example:
- If you send an integer RESP like
:123\r\n, Redis parses this into an integer-type RedisObject. For small integers (default 0-9999, configurable viashared-integers), it even reuses a pre-allocated shared integer array to save memory. - If you send a bulk string like
$5\r\nhello\r\n, it gets parsed into a string-type RedisObject, with its underlying storage being an SDS.
2. RedisObject: The Core Wrapper
Every piece of data in Redis’s memory is wrapped in a RedisObject struct (defined in server.h), simplified here:
typedef struct redisObject { unsigned type:4; // Object type: string, list, set, hash, etc. unsigned encoding:4; // Underlying storage format: int, SDS, quicklist, etc. void *ptr; // Pointer to the actual data // Refcount, LRU tracking, and other metadata fields } robj;
The encoding field is what enables Redis’s memory optimizations:
- For small integers,
encodingisREDIS_ENCODING_INT, andptrpoints directly to a shared integer (no SDS needed). - For strings,
encodingis eitherREDIS_ENCODING_EMBSTR(small strings embedded directly in the object) orREDIS_ENCODING_RAW(larger strings backed by a standalone SDS).
3. Why dict.c Uses void* for Keys/Values
The dictAdd(dict *d, void *key, void *val) function uses void* because it’s storing pointers to RedisObject instances, not raw data.
In Redis’s main database dictionary:
- All keys are string-type RedisObjects (since Redis requires keys to be strings).
- Values can be any RedisObject type (string, integer, list, set, hash, etc.).
Using void* keeps the dict implementation generic, so it can work with any RedisObject type without hardcoding type constraints.
4. How Lists, Sets, and Complex Structures Are Stored
Complex data types are also wrapped in RedisObjects, with their ptr pointing to specialized low-level structures:
- Lists: Default to
quicklist(a hybrid of compact ziplists and linked lists). Small elements are stored in space-efficient ziplists; larger elements are split into linked list nodes. The RedisObject’stypeisREDIS_LIST, andencodingisREDIS_ENCODING_QUICKLIST. - Sets: Use
intset(a compact array of integers) if all elements are integers and the set size is small. Once elements are non-integers or the set grows beyond a threshold, it switches to adict(hash table) where each key is a string RedisObject. The RedisObject’stypeisREDIS_SET, withencodingset to eitherREDIS_ENCODING_INTSETorREDIS_ENCODING_HT. - Hashes: Follow a similar pattern—start with ziplist for small key-value pairs, switch to a dict when entries get larger or more numerous.
5. Where to Find the "SDS to Object" Code
You won’t find a single dedicated "SDS to object" function because this conversion happens as part of RESP parsing and object creation. Here are the key spots to look:
- In
object.c:createStringObject(const char *ptr, size_t len)takes a raw C string, creates an SDS from it, and wraps it in a string RedisObject. - In
networking.c: The RESP parser decodes the incoming payload into raw data (strings, integers, etc.), then callscreateObjectvariants (likecreateStringObject,createIntObject) to turn that raw data into a RedisObject.
内容的提问来源于stack exchange,提问作者Alexander Mayr

