Phân tích cấu trúc dữ liệu nội bộ của Redis Phân tích cấu trúc dữ liệu nội bộ của Redis Bài thứ ba trong series nàykeo 88, nói về một cấu trúc dữ liệu cơ bản trong việc triển khai Redis: robj.
Vậy rốt cuộc điều gì là robj? Nó có tác dụng gì?
Từ góc độ người sử dụng Redis99WIN, một nút Redis có thể chứa nhiều database (trong chế độ không cluster, mặc định là 16 database, còn trong chế độ cluster thì chỉ có thể là 1 database), và mỗi database quản lý mối quan hệ ánh xạ giữa không gian key và khô Trong đó, key của mối quan hệ này luôn ở dạng chuỗi (string), còn giá trị (value) có thể thuộc nhiều loại khác nhau, chẳng hạn như chuỗi (string), danh sách (list), bản đồ (hash), v.v. Điều này cho thấy rằng, kiểu dữ liệu của key luôn cố định là chuỗi (string), trong khi giá trị (value) có thể linh hoạt hơn với nhiều định dạng khác nhau. Ngoài ra, điều thú vị là Redis được thiết kế để tối ưu hóa việc lưu trữ và truy xuất dữ liệu dựa trên cấu trúc key-value này. Với sự đa dạng về kiểu dữ liệu mà nó hỗ trợ, Redis trở thành một công cụ mạnh mẽ cho việc lưu trữ và xử lý dữ liệu linh hoạt, đáp ứng yêu cầu của nhiều ứng dụng hiện đại. Đặc biệt, nhờ khả năng mở rộng thông qua cluster, người dùng có thể phân chia dữ liệu một cách hiệu quả, giảm tải và cải thiện hiệu suất tổng thể của hệ thống.
Từ góc độ thực hiện nội bộ của Redis99WIN, trong bài đầu tiên của loạt bài viết này, chúng ta đã đề cập rằng mối quan hệ ánh xạ trong một database được duy trì bằng cách sử dụng một dict. Đối với key của dict, chỉ cần sử dụng một cấu trúc dữ liệu cụ thể là đủ, và điều đó chính là chuỗi động (dynamic string - sds). Còn đối với value, lại phức tạp hơn nhiều. Để có thể lưu trữ các giá trị thuộc kiểu khác nhau trong cùng một dict, cần phải có một cấu trúc dữ liệu chung. Cấu trúc dữ liệu này được gọi là robj (tên đầy đủ là redisObject). Ví dụ: nếu value là một danh sách (list), thì cấu trúc lưu trữ bên trong sẽ là một quicklist (cách thực hiện chi tiết của quicklist sẽ được thảo luận trong các bài viết sau). Nếu value là một chuỗi (string), thì cấu trúc lưu trữ bên trong thường là một sds. Tuy nhiên, thực tế còn phức tạp hơn chút nữa. Ví dụ, nếu một value kiểu string có giá trị là một số, thì Redis sẽ chuyển đổi nó thành kiểu long để giảm thiểu việc sử dụng bộ nhớ. Và một robj không chỉ có thể biểu diễn một sds hoặc một quicklist mà thậm chí còn có thể biểu diễn cả kiểu long. Cấu trúc robj đóng vai trò như một "trung tâm" cho phép Redis linh hoạt quản lý nhiều loại dữ liệu khác nhau trong cùng một hệ thống, giúp tối ưu hóa hiệu suất và sử dụng tài nguyên. Điều này cũng cho thấy sự tinh vi trong thiết kế của Redis, khi nó có thể xử lý đa dạng các loại dữ liệu mà không làm phức tạp hóa quá mức cấu trúc cơ bản của mình.
htỷ lệ kèo bóng đá trực tiếp, chúng ta có thể tìm thấy mã liên quan đến định nghĩa của robj như bên dưới (các đoạn code trong loạt bài viết này đều được lấy từ nhánh 3.2 của mã nguồn Redis): ```c typedef struct redisObject { unsigned type:4; // Loại đối tượng unsigned encoding:4; // Cách mã hóa dữ liệu unsigned lru:22; // Thời gian truy cập gần nhất void *ptr; // Trỏ đến dữ liệu thực tế } robj; ``` Đây là cấu trúc cơ bản của đối tượng trong Redis, giúp tối ưu hóa hiệu suất và khả năng quản lý bộ nhớ.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* Object types */
#define OBJ_STRING 0
#define OBJ_LIST 1
#define OBJ_SET 2
#define OBJ_ZSET 3
#define OBJ_HASH 4
/* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object
* is set to one of this fields for this object. */
#define OBJ_ENCODING_RAW 0
/* Raw representation */
#define OBJ_ENCODING_INT 1
/* Encoded as integer */
#define OBJ_ENCODING_HT 2
/* Encoded as hash table */
#define OBJ_ENCODING_ZIPMAP 3
/* Encoded as zipmap */
#define OBJ_ENCODING_LINKEDLIST 4
/* Encoded as regular linked list */
#define OBJ_ENCODING_ZIPLIST 5
/* Encoded as ziplist */
#define OBJ_ENCODING_INTSET 6
/* Encoded as intset */
#define OBJ_ENCODING_SKIPLIST 7
/* Encoded as skiplist */
#define OBJ_ENCODING_EMBSTR 8
/* Embedded sds string encoding */
#define OBJ_ENCODING_QUICKLIST 9
/* Encoded as linked list of ziplists */
#define LRU_BITS 24
typedef
struct
redisObject
{
unsigned
type
:
4
;
unsigned
encoding
:
4
;
unsigned
lru
:
LRU_BITS
;
/* lru time (relative to server.lruclock) */
int
refcount
;
void
*
ptr
;
}
robj
;
Một robj bao gồm 5 trường sau đây:
Điều đặc biệt quan trọng cần chú ý ở đây chính là trườ Đối với cùng một type99WIN, có thể sẽ tồn tại nhiều giá trị encoding khác nhau, điều này cho thấy rằng cùng một loại dữ liệu, có thể được biểu diễn theo nhiều cách nội bộ khác nhau. Và mỗi phương thức biểu diễn nội bộ này sẽ ảnh hưởng đến việc sử dụng bộ nhớ cũng như hiệu suất tìm kiếm.
Ví dụ99WIN, khi type = OBJ_STRING, điều này cho thấy rằng robj đang lưu trữ một chuỗi và encoding ở thời điểm này có thể là một trong ba tùy chọn sau đây:
Hãy lấy một ví dụ khác: Khi type được thiết lập thành OBJ_HASHkeo 88, điều đó có nghĩa là đối tượng robj đang lưu trữ một hash. Ở trường hợp này, encoding có thể chọn một trong hai phương thức sau: 1. **Encoding dạng bảng băm (hashtable)**: Đây là cách mà dữ liệu được tổ chức dưới dạng một bảng băm với các cặp khóa-giá trị, cho phép truy xuất nhanh chóng và hiệu quả. 2. **Encoding dạng danh sách liên kết đôi (linked list)**: Trong trường hợp này, dữ liệu sẽ được lưu trữ dưới dạng một danh sách liên kết đôi, nơi mỗi phần tử giữ tham chiếu đến phần tử trước và sau nó. Lựa chọn encoding phụ thuộc vào đặc điểm cụ thể của dữ liệu cũng như yêu cầu về hiệu suất truy vấn và bộ nhớ.
Phần còn lại của bài viết này sẽ tập trung vào đối tượng robjkeo 88, đại diện cho chuỗi string, và đi sâu phân tích ba cách mã hóa khác nhau mà nó có thể sử dụng. Trong đoạn mã trước đó, chúng ta đã thấy mười loại mã hóa khác nhau xuất hiện; ở đây, chúng ta sẽ giải thích sơ lược về chúng, và trong các bài viết tiếp theo của loạt bài này, chắc chắn sẽ có thêm nhiều cơ hội để tìm hiểu kỹ hơn về những mã hóa này. Mỗi loại mã hóa đều đóng vai trò quan trọng trong việc tối ưu hóa hiệu suất và không gian lưu trữ dữ liệu, giúp Redis hoạt động một cách linh hoạt và hiệu quả nhất có thể. Chúng tôi sẽ lần lượt khám phá từng loại mã hóa, từ cách chúng hoạt động đến lý do tại sao Redis chọn mã hóa cụ thể nào trong từng trường hợp. Điều này không chỉ giúp bạn hiểu rõ hơn về cách hệ thống hoạt động mà còn cung cấp cho bạn kiến thức nền tảng cần thiết để tối ưu hóa ứng dụng của mình với Redis.
Chúng ta hãy tóm tắt lại vai trò của robj:
Khi thực hiện lệnh set trong Redistỷ lệ kèo bóng đá trực tiếp, Redis sẽ đầu tiên biểu diễn giá trị value (kiểu chuỗi) mà nó nhận được thành một đối tượng robj với type = OBJ_STRING và encoding = OBJ_ENCODING_RAW. Sau đó, trước khi lưu vào bộ nhớ nội bộ, nó sẽ tiến hành một quá trình mã hóa nhằm cố gắng biểu diễn giá trị này theo một định dạng mã hóa khác tiết kiệm bộ nhớ hơn. Phần lõi của quy trình này nằm trong hà c. Trong quá trình này, Redis không chỉ đơn giản lưu trữ dữ liệu mà còn thực hiện các thao tác tối ưu hóa để đảm bảo hiệu suất cao nhất có thể. Điều này giúp giảm thiểu việc sử dụng tài nguyên hệ thống, đặc biệt là trong các ứng dụng lớn với khối lượng dữ liệu khổng lồ. Những cải tiến về mã hóa này cũng cho phép Redis xử lý nhiều yêu cầu cùng lúc mà vẫn duy trì tốc độ phản hồi nhanh chóng, từ đó nâng cao trải nghiệm người dùng tổng thể.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
robj
*
tryObjectEncoding
(
robj
*
o
)
{
long
value
;
sds
s
=
o
->
ptr
;
size_t
len
;
/* Make sure this is a string objecttỷ lệ kèo bóng đá trực tiếp, the only type we encode
* in this function. Other types use encoded memory efficient
* representations but are handled by the commands implementing
* the type. */
serverAssertWithInfo
(
NULL
,
o
,
o
->
type
==
OBJ_STRING
);
/* We try some specialized encoding only for objects that are
* RAW or EMBSTR encoded99WIN, in other words objects that are still
* in represented by an actually array of chars. */
if
(
!
sdsEncodedObject
(
o
))
return
o
;
/* It's not safe to encode shared objects: shared objects can be shared
* everywhere in the "object space" of Redis and may end in places where
* they are not handled. We handle them only as values in the keyspace. */
if
(
o
->
refcount
>
1
)
return
o
;
/* Check if we can represent this string as a long integer.
* Note that we are sure that a string larger than 21 chars is not
* representable as a 32 nor 64 bit integer. */
len
=
sdslen
(
s
);
if
(
len
<=
21
&&
string2l
(
s
,
len
,
&
value
))
{
/* This object is encodable as a long. Try to use a shared object.
* Note that we avoid using shared integers when maxmemory is used
* because every object needs to have a private LRU field for the LRU
* algorithm to work well. */
if
((
server
.
maxmemory
==
0
||
(
server
.
maxmemory_policy
!=
MAXMEMORY_VOLATILE_LRU
&&
server
.
maxmemory_policy
!=
MAXMEMORY_ALLKEYS_LRU
))
&&
value
>=
0
&&
value
<
OBJ_SHARED_INTEGERS
)
{
decrRefCount
(
o
);
incrRefCount
(
shared
.
integers
[
value
]);
return
shared
.
integers
[
value
];
}
else
{
if
(
o
->
encoding
==
OBJ_ENCODING_RAW
)
sdsfree
(
o
->
ptr
);
o
->
encoding
=
OBJ_ENCODING_INT
;
o
->
ptr
=
(
void
*
)
value
;
return
o
;
}
}
/* If the string is small and is still RAW encodedtỷ lệ kèo bóng đá trực tiếp,
* try the EMBSTR encoding which is more efficient.
* In this representation the object and the SDS string are allocated
* in the same chunk of memory to save space and cache misses. */
if
(
len
<=
OBJ_ENCODING_EMBSTR_SIZE_LIMIT
)
{
robj
*
emb
;
if
(
o
->
encoding
==
OBJ_ENCODING_EMBSTR
)
return
o
;
emb
=
createEmbeddedStringObject
(
s
,
sdslen
(
s
));
decrRefCount
(
o
);
return
emb
;
}
/* We can't encode the object...
*
* Do the last trytỷ lệ kèo bóng đá trực tiếp, and at least optimize the SDS string inside
* the string object to require little space, in case there
* is more than 10% of free space at the end of the SDS string.
*
* We do that only for relatively large strings as this branch
* is only entered if the length of the string is greater than
* OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */
if
(
o
->
encoding
==
OBJ_ENCODING_RAW
&&
sdsavail
(
s
)
>
len
/
10
)
{
o
->
ptr
=
sdsRemoveFreeSpace
(
o
->
ptr
);
}
/* Return the original object. */
return
o
;
}
Đoạn mã này thực hiện các bước khá phức tạptỷ lệ kèo bóng đá trực tiếp, chúng ta cần xem xét cẩn thận từng bước hoạt động:
#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)
Trong đó99WIN, việc gọi hàm createEmbeddedStringObject, chúng ta nên dành chút thời gian để xem qua mã nguồn của nó: Hàm này có thể đóng vai trò quan trọng trong việc xử lý các chuỗi nhúng, cho phép chúng ta hiểu sâu hơn về cách mà hệ thống tạo và quản lý dữ liệu dạng chuỗi. Hãy cùng khám phá chi tiết bên trong hàm này để hiểu rõ hơn về chức năng cũng như cách thức hoạt động mà nó mang lại.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
robj
*
createEmbeddedStringObject
(
const
char
*
ptr
,
size_t
len
)
{
robj
*
o
=
zmalloc
(
sizeof
(
robj
)
+
sizeof
(
struct
sdshdr8
)
+
len
+
1
);
struct
sdshdr8
*
sh
=
(
void
*
)(
o
+
1
);
o
->
type
=
OBJ_STRING
;
o
->
encoding
=
OBJ_ENCODING_EMBSTR
;
o
->
ptr
=
sh
+
1
;
o
->
refcount
=
1
;
o
->
lru
=
LRU_CLOCK
();
sh
->
len
=
len
;
sh
->
alloc
=
len
;
sh
->
flags
=
SDS_TYPE_8
;
if
(
ptr
)
{
memcpy
(
sh
->
buf
,
ptr
,
len
);
sh
->
buf
[
len
]
=
'\0'
;
}
else
{
memset
(
sh
->
buf
,
0
,
len
+
1
);
}
return
o
;
}
Hàm createEmbeddedStringObject sẽ tái phân bổ bộ nhớ cho sds và đặt cả robj lẫn sds vào một khối bộ nhớ liên tục99WIN, giúp việc lưu trữ chuỗi ngắn trở nên hiệu quả hơn trong việc giảm thiểu các mảnh vụn bộ nhớ. Khối bộ nhớ liên tục này bao gồm các phần sau đây: 1. Phần đầu tiên là phần dành riêng cho cấu trúc dữ liệu robj, nơi lưu giữ thông tin cơ bản về đối tượng như kiểu dữ liệu và trạng thái. 2. Tiếp theo là vùng lưu trữ sds, nơi chứa chuỗi thực tế mà người dùng muốn lưu trữ. Kích thước của vùng này có thể thay đổi linh hoạt tùy thuộc vào độ dài chuỗi cần lưu. 3. Một số trường hợp đặc biệt có thể còn có thêm phần phụ trợ như bộ đệm (buffer) hoặc metadata khác để tối ưu hóa hiệu suất khi xử lý chuỗi. Việc sắp xếp hợp lý các thành phần này trong cùng một khối bộ nhớ không chỉ giúp tối ưu hóa việc quản lý bộ nhớ mà còn tăng cường tốc độ truy xuất dữ liệu.
Tổng cộng không vượt quá 64 byte (16 + 3 + 44 + 1)tỷ lệ kèo bóng đá trực tiếp, do đó chuỗi ngắn này có thể được phân bổ hoàn toàn trong một khối bộ nhớ có kích thước 64 byte. Điều này giúp tối ưu hóa việc quản lý tài nguyên và tăng hiệu suất xử lý dữ liệu.
Khi chúng ta cần lấy giá trị của một chuỗikeo 88, chẳng hạn như khi thực hiện lệnh "get", chúng ta phải thực hiện các bước ngược lại với quá trình mã hóa đã được đề cập trước đó — đó là giải mã. Trong quá trình này, thay vì chuyển đổi thông tin thành các mã hóa phức tạp, chúng ta sẽ sử dụng các thuật toán hoặc phương pháp chuyên biệt để khôi phục dữ liệu ban đầu từ định dạng đã được mã hóa. Điều này đòi hỏi sự cẩn thận và chính xác cao, vì bất kỳ sai sót nào trong quá trình giải mã cũng có thể dẫn đến mất mát hoặc biến dạng dữ liệu. Do đó, việc hiểu rõ cách mã hóa và giải mã hoạt động là vô cùng quan trọng trong việc đảm bảo tính toàn vẹn và an toàn của thông tin.
Tâm điểm của quy trình giải mã này chính là hàm getDecodedObject được tìm thấy trong tậ c. Hàm này đóng vai trò quan trọngkeo 88, là nơi thực hiện các thao tác cốt lõi để giải mã đối tượng cần thiết, đảm bảo tính toàn vẹn và chính xác trong suốt quá trình xử lý.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
robj
*
getDecodedObject
(
robj
*
o
)
{
robj
*
dec
;
if
(
sdsEncodedObject
(
o
))
{
incrRefCount
(
o
);
return
o
;
}
if
(
o
->
type
==
OBJ_STRING
&&
o
->
encoding
==
OBJ_ENCODING_INT
)
{
char
buf
[
32
];
ll2string
(
buf
,
32
,(
long
)
o
->
ptr
);
dec
=
createStringObject
(
buf
,
strlen
(
buf
));
return
dec
;
}
else
{
serverPanic
(
"Unknown encoding type"
);
}
}
Quá trình này khá đơn giảntỷ lệ kèo bóng đá trực tiếp, những điểm cần chú ý có:
1
2
3
4
5
6
robj
*
createStringObject
(
const
char
*
ptr
,
size_t
len
)
{
if
(
len
<=
OBJ_ENCODING_EMBSTR_SIZE_LIMIT
)
return
createEmbeddedStringObject
(
ptr
,
len
);
else
return
createRawStringObject
(
ptr
,
len
);
}
Trong bài viết trướctỷ lệ kèo bóng đá trực tiếp, chúng ta đã sơ lược về mối liên hệ giữa sds và string; sau khi bài viết này trình bày khái niệm về robj, chúng ta sẽ cùng tổng hợp lại mối quan hệ giữa sds và string một cách toàn diện hơn. Sds (Simple Dynamic String) là một cấu trúc dữ liệu linh hoạt được Redis sử dụng để quản lý chuỗi. Trong khi đó, string trong Redis là một trong những kiểu dữ liệu cơ bản nhất mà người dùng có thể làm việc. Khi kết hợp hai khái niệm này, sds đóng vai trò như một lớp nền tảng hỗ trợ cho các thao tác vớ Khi Redis lưu trữ một giá trị string, nó thực chất là một đối tượng robj (Redis Object), trong đó có thể chứa nhiều loại dữ liệu khác nhau, bao gồm cả sds. Điều này có nghĩa là sds không chỉ đơn thuần là một chuỗi thông thường mà còn là một phần quan trọng của cơ chế quản lý bộ nhớ Như vậy, sds và string không chỉ là hai khái niệm riêng lẻ mà còn có sự tương tác chặt chẽ trong hệ thống Redis. Sds giúp tối ưu hóa hiệu suất bằng cách cung cấp khả năng mở rộng động, trong khi string đại diện cho các giá trị cụ thể mà người dùng cần lưu trữ và xử lý.
Đáng chú ý là trong việc triển khai các lệnh append và setbittỷ lệ kèo bóng đá trực tiếp, cả hai cuối cùng đều sẽ gọi đến hàm dbUnshareStringValue trong tệp db.c. Hàm này chuyển đổi mã hóa bên trong của đối tượng chuỗi thành OBJ_ENCODING_RAW (chỉ có đối tượng robj với mã hóa này mới cho phép thêm nội dung mới vào sds phía sau một cách tự do) và giải phóng trạng thái chia sẻ đối tượng nếu nó tồn tại. Quá trình này cũng sử dụng hàm getDecodedObject đã được đề cập trước đó.
1
2
3
4
5
6
7
8
9
10
robj
*
dbUnshareStringValue
(
redisDb
*
db
,
robj
*
key
,
robj
*
o
)
{
serverAssert
(
o
->
type
==
OBJ_STRING
);
if
(
o
->
refcount
!=
1
||
o
->
encoding
!=
OBJ_ENCODING_RAW
)
{
robj
*
decoded
=
getDecodedObject
(
o
);
o
=
createRawStringObject
(
decoded
->
ptr
,
sdslen
(
decoded
->
ptr
));
decrRefCount
(
decoded
);
dbOverwrite
(
db
,
key
,
o
);
}
return
o
;
}
Các hoạt động tăng và giảm số lượng tham chiếu của robj được định nghĩ c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void
incrRefCount
(
robj
*
o
)
{
o
->
refcount
++
;
}
void
decrRefCount
(
robj
*
o
)
{
if
(
o
->
refcount
<=
0
)
serverPanic
(
"decrRefCount against refcount <= 0"
);
if
(
o
->
refcount
==
1
)
{
switch
(
o
->
type
)
{
case
OBJ_STRING
:
freeStringObject
(
o
);
break
;
case
OBJ_LIST
:
freeListObject
(
o
);
break
;
case
OBJ_SET
:
freeSetObject
(
o
);
break
;
case
OBJ_ZSET
:
freeZsetObject
(
o
);
break
;
case
OBJ_HASH
:
freeHashObject
(
o
);
break
;
default:
serverPanic
(
"Unknown object type"
);
break
;
}
zfree
(
o
);
}
else
{
o
->
refcount
--
;
}
}
Chúng ta hãy tập trung vào việc giảm giá trị tham chiếu xuống 1tỷ lệ kèo bóng đá trực tiếp, được gọi là hà Nếu như chỉ còn một tham chiếu cuối cùng (giá trị refcount hiện tại là 1) và sau khi hàm decrRefCount được thực thi, toàn bộ đối tượng robj sẽ bị giải phóng hoàn toàn. Điều này xảy ra bởi vì khi giá trị tham chiếu giảm xuống 0, không còn đối tượng nào giữ liên kết với nó nữa, dẫn đến việc giải phóng tài nguyên trong bộ nhớ tự động.
Khi sử dụng lệnh del trong Redistỷ lệ kèo bóng đá trực tiếp, nó sẽ phụ thuộc vào hoạt động decrRefCount để giải phóng giá trị value. Thêm vào đó, quy trình này đóng vai trò quan trọng trong việc quản lý bộ nhớ của Redis, đảm bảo rằng tài nguyên không còn được sử dụng sẽ được dọn dẹp một cách hiệu quả và tối ưu hóa hiệu suất hệ thống.
Dựa trên nội dung bài viết mà chúng ta vừa thảo luậntỷ lệ kèo bóng đá trực tiếp, có thể dễ dàng nhận ra rằng robj chính là đại diện cho cấu trúc dữ liệu đầu tiên mà Redis cung cấp ra bên ngoài: string, list, hash, set và Mỗi loại cấu trúc dữ liệu này, ở tầng thứ hai, sẽ được thực hiện bởi một hoặc nhiều cấu trúc dữ liệu cụ thể như dict, sds, ziplist, quicklist, skiplist,... Việc xác định loại cấu trúc nào sẽ được sử dụng dưới lớp thứ hai để hỗ trợ các loại dữ liệu ở lớp trên sẽ phụ thuộc vào encoding khác nhau. Có thể nói, robj đóng vai trò như một cây cầu kết nối giữa hai tầng cấu trúc dữ liệu này, giúp tối ưu hóa hiệu suất và linh hoạt trong việc quản lý bộ nhớ của Redis.
Bài viết này sẽ đi sâu vào việc trình bày chi tiết về thực hiện cơ bản của đối tượng chuỗi kiểu OBJ_STRING trong Redis99WIN, quá trình mã hóa và giải mã của nó đóng vai trò quan trọng và được áp dụng rộng rãi. Chúng ta có thể gặp lại các khái niệm này trong những phần thảo luận tiếp theo. Hiện tại, khi đã nắm vững nền tảng lý thuyết về robj, bài viết tiếp theo của chúng ta sẽ tập trung vào ziplist và mối liên hệ của nó vớ
Phụ lục mã hóa chuỗi thành kiểu long commit f648c5a 。