Chi tiết về cấu trúc dữ liệu bên trong Redis Chi tiết về cấu trúc dữ liệu bên trong Redis Bài thứ ba trong loạt bài nàyxóc đĩa, 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 cái gì là robj? Nó có tác dụng gì?
Từ góc độ người dùng Redisxóc đĩa, một nút Redis có thể chứa nhiều cơ sở dữ liệu (trong chế độ không phải cluster, mặc định là 16 cơ sở dữ liệu, trong khi ở chế độ cluster chỉ có thể là 1 cơ sở dữ liệu). Mỗi cơ sở dữ liệu đảm nhận việc duy trì mối liên hệ giữa không gian khóa (key space) và không gian đối tượng (object space). Trong đó, khóa (key) có kiểu chuỗi (string), còn giá trị (value) có thể thuộc nhiều loại khác nhau như chuỗi (string), danh sách (list), bản đồ (hash) hoặc thậm chí nhiều loại phức tạp hơn. Điều này cho thấy rằng, mặc dù kiểu của khóa luôn cố định là chuỗi, nhưng kiểu giá trị mà nó ánh xạ đến lại đa dạng và linh hoạt.
Từ góc độ thực hiện bên trong của Redisxóc đĩa, 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 loại cấu trúc dữ liệu, đó chính là chuỗi động sds (simple dynamic string). Còn đối với value thì phức tạp hơn, vì trong cùng một dict cần có khả năng lưu trữ các giá trị thuộc nhiều kiểu dữ liệu khác nhau. Để đạt được điều này, Redis sử dụng một cấu trúc dữ liệu chung gọi là robj (viết đầy đủ là redisObject). Ví dụ, nếu giá trị value là một danh sách (list), thì cấu trúc lưu trữ bên trong sẽ là quicklist (cách thức hoạt động cụ thể của quicklist sẽ được thảo luận ở phần sau). Nếu giá trị value là một chuỗi (string), thì thông thường nó sẽ được biểu diễn dưới dạng sds. Tuy nhiên, thực tế còn phức tạp hơn một chút. Chẳng hạn, nếu giá trị của một giá trị string là một số nguyên, Redis sẽ chuyển đổi nó thành kiểu long để lưu trữ, từ đó giảm thiểu việc sử dụng bộ nhớ. Một robj có thể đại diện cho một sds, một quicklist, hoặc thậm chí một giá trị long. Điều này làm cho robj trở thành một công cụ linh hoạt và mạnh mẽ trong việc quản lý các kiểu dữ liệu khác nhau mà Redis hỗ trợ.
hxóc đĩa, chúng ta có thể tìm thấy mã liên quan đến định nghĩa của robj, như sau (hãy lưu ý rằng các đoạn mã trong loạt bài viết này đều được lấy từ nhánh 3.2 của mã nguồn Redis):
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ần đặc biệt chú ý ở đây chính là trường "encoding". Đối với cùng một loại "type"keo banh, vẫn có thể tồn tại nhiều dạng "encoding" khác nhau. Điều này cho thấy rằng cùng một kiểu dữ liệu, nhưng có thể được biểu diễn nội bộ theo nhiều cách khác biệt. Và những phương thức biểu diễn nội bộ này sẽ ảnh hưởng đến việc sử dụng tài nguyên bộ nhớ cũng như hiệu suất tìm kiếm.
Ví dụxóc đĩa, khi type = OBJ_STRING, có nghĩa là robj này lưu trữ một chuỗi string, và encoding lúc 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 đặt thành OBJ_HASHkết quả bóng đá việt nam hôm nay, điều này có nghĩa là đối tượng robj đang lưu trữ một cấu trúc dữ liệu dạng 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 hashtable**: Trong trường hợp này, dữ liệu sẽ được lưu trữ dưới dạng bảng băm (hashtable), cho phép truy xuất nhanh các phần tử bằng cách sử dụng khóa (key). Điều này đặc biệt hữu ích khi bạn cần thao tác với nhiều cặp key-value và muốn tối ưu hóa hiệu suất. 2. **Encoding dạng ziplist hoặc linkedlist**: Nếu dữ liệu trong hash có kích thước nhỏ và số lượng phần tử ít, hệ thống có thể chọn cách lưu trữ nó dưới dạng danh sách liên kết (linkedlist) hoặc ziplist để tiết kiệm bộ nhớ. Tuy nhiên, việc này có thể ảnh hưởng đến hiệu suất khi kích thước dữ liệu tăng lên. Việc lựa chọn encoding phù hợp sẽ giúp tối ưu hóa cả về tốc độ và tài nguyên hệ thống.
Phần còn lại của bài viết này sẽ tập trung phân tích sâu về đối tượng robjkết quả bóng đá việt nam hôm nay, đại diện cho chuỗi string, với ba kiểu mã hóa encoding khác nhau. Trong các đoạn mã trước đó, chúng ta đã thấy mười kiểu encoding khác nhau. Ở đây, chúng tôi sẽ giải thích sơ lược về chúng, và hy vọng trong các bài viết tiếp theo của loạt bài này, chúng ta sẽ có dịp tìm hiểu thêm về những kiểu mã hóa này. Mỗi kiểu mã hóa đều có vai trò quan trọng và mang đến những lợi ích riêng biệt trong việc quản lý bộ nhớ cũng như cải thiện hiệu suất xử lý dữ liệu. Việc hiểu rõ cách hoạt động của từng loại encoding sẽ giúp người dùng tối ưu hóa cách sử dụng và vận hành hệ thống một cách hiệu quả hơn. Hãy cùng khám phá chi tiết hơn về từng loại encoding này để thấy được sự đa dạng và linh hoạt mà nó mang lại trong việc xử lý chuỗi string.
Chúng ta hãy tóm tắt lại vai trò của robj:
Khi thực hiện lệnh set của Redisxóc đĩa, Redis sẽ đầu tiên biểu diễn giá trị value (kiểu chuỗi) 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 quy trình mã hóa nhằm cố gắng biểu diễn giá trị này dưới dạng một phương thức mã hóa khác tiết kiệm bộ nhớ hơn. Phần lõi của quá trình này nằm trong hàm tryObjectEncoding của tệp object.c. Hơn nữa, Redis không chỉ đơn giản dừng lại ở việc kiểm tra xem liệu có thể tối ưu hóa bộ nhớ hay không mà còn cân nhắc nhiều yếu tố khác nhau như kích thước của dữ liệu, cấu trúc nội tại của giá trị, và yêu cầu truy xuất thường xuyên để đưa ra quyết định cuối cùng về cách mã hóa phù hợp nhất. Điều này giúp đảm bảo rằng Redis hoạt động hiệu quả và ổn định trong nhiều kịch bản sử dụng khác nhau.
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 objectkết quả bóng đá việt nam hôm nay, 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 encodedkeo banh, 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 encodedxóc đĩa,
* 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 tryxóc đĩa, 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 hoạt động khá phức tạpkeo banh, chúng ta cần xem kỹ từng bước hoạt động:
#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)
Đối với hàm createEmbeddedStringObject được sử dụng trong đókeo banh, chúng ta nên xem qua mã nguồn của nó: Hàm này có thể đã được tích hợp từ trước để xử lý các chuỗi nhúng một cách hiệu quả. Hãy cùng khám phá cách nó hoạt động bên trong: createEmbeddedStringObject dường như là một phần quan trọng trong hệ thống quản lý tài nguyên chuỗi. Việc hiểu rõ mã nguồn sẽ giúp chúng ta tối ưu hóa hơn nữa quy trình này. Nó có thể liên quan đến việc quản lý bộ nhớ hoặc tối ưu hóa tốc độ truy xuất dữ liệu chuỗi. Hãy cùng phân tích kỹ hơn về đoạn mã thực thi của nó.
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ụckeo banh, giúp tối ưu hóa việc lưu trữ chuỗi ngắn, từ đó 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: 1. **Phần dữ liệu robj**: Là cấu trúc quản lý thông tin cơ bản của đối tượng, bao gồm kiểu dữ liệu, trạng thái và tham chiếu. 2. **Phần dữ liệu sds**: Là vùng lưu trữ chuỗi, được tối ưu hóa để tăng hiệu suất khi thao tác với chuỗi. 3. **Khu vực đệm (padding)**: Một số không gian trống được thêm vào để đảm bảo tính đồng nhất và hiệu quả trong việc quản lý bộ nhớ. 4. **Vùng dữ liệu mở rộng (extra space)**: Được sử dụng khi cần mở rộng kích thước chuỗi mà không phải di chuyển bộ nhớ, cải thiện hiệu suất khi chỉnh sửa chuỗi. Bằng cách sắp xếp tất cả các thành phần này trong một khối bộ nhớ liên tục, Redis có thể đạt được hiệu quả cao hơn trong việc quản lý tài nguyên và tối ưu hóa hiệu suất tổng thể.
Tổng cộng không vượt quá 64 byte (16 + 3 + 44 + 1)kết quả bóng đá việt nam hôm nay, do đó chuỗi ngắn này có thể dễ dàng được phân bổ hoàn toàn trong một khối bộ nhớ có độ dài 64 byte. Điều này giúp tối ưu hóa việc sử dụng 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 banh, ví dụ 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ã. Giải mã giúp khôi phục lại thông tin gốc từ dữ liệu đã được mã hóa, đảm bảo rằng nội dung nhận được là chính xác và dễ hiểu. Đây là một bước quan trọng trong việc xử lý dữ liệu, đặc biệt là trong các giao tiếp qua mạng hoặc khi lưu trữ thông tin nhạy cảm.
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ọngxóc đĩa, là nơi thực hiện các thao tác cốt lõi để giải mã đối tượng, đảm bảo tính chính xác và hiệu quả 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"
);
}
}
Quy trình này tương đối đơn giảnkeo banh, 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ướckeo banh, chúng ta đã sơ lược về mối liên hệ giữa sds và string. Sau khi tìm hiểu về khái niệm robj trong bài viết này, hãy cùng nhìn 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 sử dụng rộng rãi trong Redis để lưu trữ chuỗi. Nó cung cấp các tính năng như dễ dàng mở rộng hoặc thu nhỏ kích thước khi cần thiết. Trong khi đó, string là kiểu dữ liệu cơ bản nhất mà bất kỳ người dùng nào cũng có thể dễ dàng nhận ra trong bất kỳ ngôn ngữ lập trình nào, bao gồm cả C, nơi Redis được viết bằng ngôn ngữ này. Khi nói đến mối quan hệ giữa sds và string, chúng ta không chỉ đơn thuần đang nói về hai công cụ riêng biệt mà còn là sự kết hợp hài hòa giữa tính linh hoạt của sds và sự đơn giản của string. Sds tận dụng những lợi thế của string trong việc tối ưu hóa hiệu suất và giảm thiểu chi phí bộ nhớ, đồng thời thêm vào khả năng điều chỉnh kích thước theo nhu cầu thực tế. Nhờ có robj, một đối tượng trong Redis, sds có thể được tích hợp sâu hơn vào hệ thống, cho phép nó đóng vai trò như một phần không thể thiếu trong việc quản lý và xử lý dữ liệu chuỗi. Điều này làm nổi bật vai trò của sds trong việc hỗ trợ các tác vụ phức tạp hơn, chẳng hạn như xử lý chuỗi lớn hoặc xây dựng các ứng dụng yêu cầu độ chính xác cao. Tóm lại, sds và string không chỉ là hai yếu tố tách biệt mà còn là sự kết hợp mạnh mẽ, giúp tạo nên nền tảng vững chắc cho việc phát triển và vận hành Redis. Qua việc giới thiệu robj, chúng ta càng thấy rõ tầm quan trọng của việc hiểu rõ mối quan hệ giữa các thành phần này.
Đáng chú ý là trong việc triển khai các lệnh append và setbitxóc đĩa, chúng đều cuối cùng sẽ gọi đến hàm dbUnshareStringValue trong tệp db.c. Hàm này chuyển đổi mã hóa nội bộ của đối tượng string thành OBJ_ENCODING_RAW (chỉ những đối tượng robj có mã hóa này mới cho phép phần sds bên trong được bổ sung thêm nội dung sau này một cách tự do), đồng thời giải phóng trạng thái chia sẻ đối tượng nếu cần thiết. 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
;
}
Việc tăng và giảm đếm 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 hàm giảm giá trị tham chiếukết quả bóng đá việt nam hôm nay, được gọi là Khi số lượng tham chiếu còn lại chỉ còn một (refcount đã giảm xuống 1), sau khi decrRefCount được thực hiện, toàn bộ đối tượng robj sẽ bị giải phóng hoàn toàn khỏi bộ nhớ. Hàm này đóng vai trò quan trọng trong việc quản lý tài nguyên, đảm bảo rằng không có tài sản nào bị lãng phí hoặc giữ lại vô ích. Khi refcount đạt đến ngưỡng cuối cùng và decrRefCount được kích hoạt, hệ thống sẽ tự động dọn dẹp mọi dữ liệu liên quan đến robj, giúp tối ưu hóa hiệu suất của ứng dụng. Đây là một phần không thể thiếu trong cơ chế quản lý bộ nhớ tự động mà các lập trình viên cần đặc biệt lưu ý khi phát triển phần mềm.
Khi sử dụng lệnh del trong Rediskeo banh, quá trình giải phóng giá trị value sẽ phụ thuộc vào hoạt động giảm tham chiế Điều này có nghĩa là mỗi khi lệnh del được thực hiện, Redis sẽ tự động giảm số lượng tham chiếu của giá trị liên quan và xóa nó khỏi bộ nhớ nếu không còn tham chiếu nào khác tồn tại. Đây là một cơ chế quản lý bộ nhớ quan trọng giúp Redis duy trì hiệu suất và tối ưu hóa việc sử dụng tài nguyên hệ thống.
Dựa trên nội dung bài viết mà chúng ta vừa thảo luậnkeo banh, có thể dễ dàng nhận thấy rằng robj chính là lớp dữ liệu đầu tiên mà Redis cung cấp ra bên ngoài, bao gồm các kiểu dữ liệu cơ bản như chuỗi (string), danh sách (list), bảng băm (hash), tập hợp (set) và tập hợp có thứ tự (sorted set). Mỗi loại 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, v.v. Điều gì phân biệt chúng với nhau chính là cách mã hóa (encoding). Có thể nói, robj đóng vai trò như một cây cầu nối giữa hai tầng cấu trúc dữ liệu này, giúp chúng hoạt động hài hòa và hiệu quả trong hệ thống Redis.
Bài viết này sẽ đi sâu vào giới thiệu chi tiết về việc triển khai của đối tượng chuỗi thuộc kiểu OBJ_STRINGkeo banh, quá trình mã hóa và giải mã của nó trong Redis rất quan trọng và được áp dụng rộng rãi. Chúng ta có thể còn gặp lại các khái niệm này trong các thảo luận tiếp theo. Hiện tại, với nền tảng kiến thức về robj, phần tiếp theo chúng ta sẽ về ziplist và mối liên hệ giữa nó với hash. Khi nói đến ziplist, đây là một cấu trúc dữ liệu đặc biệt mà Redis sử dụng để tối ưu hóa không gian lưu trữ cho các giá trị nhỏ. Ziplist thường được dùng khi bạn cần lưu trữ các tập hợp hoặc bản đồ nhỏ (như các trường hợp của hash), vì nó giúp giảm thiểu bộ nhớ tiêu thụ so với việc sử dụng các danh sách liên kết thông thường. Điều thú vị là, trong Redis, ziplist có thể đóng vai trò như một phần cơ bản của việc quản lý hash, giúp tăng hiệu suất và giảm đáng kể lượng bộ nhớ cần thiết. Tuy nhiên, cũng cần lưu ý rằng ziplist không phải lúc nào cũng là lựa chọn tốt nhất. Khi kích thước của dữ liệu vượt qua ngưỡng tối đa cho phép, Redis sẽ tự động chuyển sang sử dụng các cấu trúc dữ liệu khác, chẳng hạn như hashtable, để đảm bảo hiệu suất ổn định. Điều này cho thấy cách Redis linh hoạt trong việc điều chỉnh và tối ưu hóa cơ sở dữ liệu của mình dựa trên yêu cầu thực tế.
Xử lý bất đồng bộ trong Android và iOS (phần một) – Mở đầu mã hóa chuỗi thành kiểu long commit f648c5a 。