Trang chủ > Công nghệ máy chủ > Nội dung chính

Chi tiết về cấu trúc dữ liệu bên trong Redis (3) —— robj


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ợ.

Định nghĩa cấu trúc dữ liệu của robj

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:

  • Loại dữ liệu của đối tượng: Đây là loại thông tin xác định kiểu dữ liệu mà đối tượng đang lưu trữ. Nó chiếm 4 bit trong bộ nhớ. Có thể có 5 giá trị khác nhau cho trường này: OBJ_STRINGkết quả bóng đá việt nam hôm nay, OBJ_LIST, OBJ_SET, OBJ_ZSET và OBJ_HASH, mỗi giá trị tương ứng với một trong năm cấu trúc dữ liệu mà Redis cung cấp cho người dùng bên ngoài (cụ thể là năm loại cấu trúc dữ liệu đầu tiên mà chúng ta đã đề cập đến trong bài viết đầu tiên). Mỗi loại dữ liệu này đều đóng vai trò quan trọng trong việc quản lý và xử lý dữ liệu theo cách hiệu quả và linh hoạt mà Redis mang lại.
  • Phương thức mã hóa: Cách biểu diễn nội bộ của đối tượng (cũng có thể được gọi là mã hóa). Mỗi giá trị chiếm 4 bit. Có tổng cộng 10 giá trị có thể xảy rakeo banh, tương ứng với 10 hằng số OBJ_ENCODING_XXX trong mã nguồn trước đó. Những hằng số này giúp xác định cách dữ liệu được lưu trữ và xử lý bên trong hệ thống một cách tối ưu.
  • LRU: Dùng cho thuật toán thay thế LRUkeo banh, chiếm 24 bit. Điều này không phải là trọng tâm trong phần thảo luận hiện tại, vì vậy chúng ta có thể tạm thời bỏ qua nó. Ngoài ra, việc hiểu sâu về cách hoạt động của LRU trong hệ thống lưu trữ chỉ thực sự cần thiết khi bạn đang tìm hiểu sâu về cơ chế quản lý bộ nhớ hoặc cache. Trong nhiều trường hợp cụ thể, việc phân bổ các bit cho LRU chỉ là một phần nhỏ trong cấu trúc tổng thể và không ảnh hưởng trực tiếp đến hoạt động chung của hệ thống. Vì vậy, nếu mục tiêu chính không phải là tối ưu hóa hiệu suất cache, bạn hoàn toàn có thể tập trung vào các khía cạnh khác quan trọng hơn.
  • refcount: Đếm tham chiếu. Nó cho phép các đối tượng robj được chia sẻ trong một số trường hợp.
  • Con trỏ ptr: Đây là con trỏ dữ liệuxóc đĩa, dẫn đến nơi thực sự lưu trữ dữ liệu. Ví dụ, đối với một robj đại diện cho chuỗi (string), con trỏ ptr của nó có thể hướng tới một cấu trúc sds; còn đối với một robj đại diện cho danh sách (list), con trỏ ptr của nó có thể dẫn đến mộ Mỗi loại dữ liệu sẽ được lưu trữ theo cách riêng, và ptr đóng vai trò quan trọng trong việc tìm kiếm và thao tác dữ liệu đúng nơi cần thiết.

Đ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:

  • Với OBJ_ENCODING_RAWxóc đĩa, chuỗi sẽ được biểu diễn ở định dạng gốc, cụ thể là sử dụng sds (simple dynamic string) để lưu trữ và quản lý dữ liệu. Đây là cách trực tiếp nhất để lưu trữ chuỗi mà không qua bất kỳ hình thức nén hoặc mã hóa nào khác.
  • Khi sử dụng OBJ_ENCODING_INTkết quả bóng đá việt nam hôm nay, chuỗi sẽ được biểu diễn dưới dạng số nguyên, cụ thể là một kiểu dữ liệu long. Điều này có nghĩa là giá trị của chuỗi sẽ được lưu trữ và xử lý như một số nguyên dài trong bộ nhớ.
  • Với OBJ_ENCODING_EMBSTRxóc đĩa, chuỗi sẽ được biểu diễn bằng một loại sds (simple dynamic string) đặc biệt có cấu trúc tích hợp. Đây là một phương pháp tối ưu hóa giúp lưu trữ và xử lý chuỗi hiệu quả hơn. Chúng ta sẽ đi sâu vào tìm hiểu kỹ hơn về cơ chế này trong phần tiếp theo.

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.

  • OBJ_ENCODING_HT: Hash sử dụng một dict để biểu diễn.
  • Với OBJ_ENCODING_ZIPLISTkeo banh, kiểu dữ liệu hash sẽ được biểu diễn bằng một ziplist (cách thực hiện cụ thể của ziplist sẽ được thảo luận chi tiết hơn trong các bài viết sau). Đây là một cách lưu trữ hiệu quả về bộ nhớ khi số lượng phần tử trong hash không quá lớn.

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.

  • Với OBJ_ENCODING_RAWxóc đĩa, đây là cách biểu diễn thuần nhất có thể. Thực chất, chỉ có kiểu dữ liệu string mới sử dụng giá trị mã hóa này (được biểu thị dưới dạng sds). Điều này cho phép lưu trữ và thao tác dữ liệu một cách trực tiếp mà không qua bất kỳ sự chuyển đổi nào.
  • OBJ_ENCODING_INT: Biểu diễn dưới dạng số. Thực tế sử dụng long để biểu thị.
  • OBJ_ENCODING_HT: Biểu diễn dưới dạng dict.
  • OBJ_ENCODING_ZIPMAP: Đây là một định dạng mã hóa cũ và đã ngừng sử dụng. Bạn chỉ có thể tìm thấy nó trong các phiên bản Redis nhỏ hơn 2.6. Định dạng này từng được sử dụng để lưu trữ các trường và giá trị trong một đối tượngxóc đĩa, nhưng với sự phát triển của Redis, nó đã bị thay thế bởi các phương thức hiệu quả hơn.
  • OBJ_ENCODING_LINKEDLIST: Cũng là một cách biểu diễn cũkeo banh, hiện không còn dùng nữa.
  • OBJ_ENCODING_ZIPLIST: Biểu diễn dưới dạ
  • OBJ_ENCODING_INTSET: đại diện cho kiểu intset. Được sử dụng trong cấu trúc dữ liệu set để lưu trữ các phần tử dạng số nguyên một cách hiệu quả.
  • OBJ_ENCODING_SKIPLIST: Đại diện cho định dạ Được sử dụng trong cấu trúc dữ liệu sorted setkeo banh, giúp tối ưu hóa việc tìm kiếm và sắp xếp các phần tử một cách hiệu quả.
  • OBJ_ENCODING_EMBSTR: Biểu diễn dưới dạng một sds đặc biệt được nhúng vào.
  • OBJ_ENCODING_QUICKLIST: đại diệ Được sử dụng cho cấu trúc dữ liệu list. Quicklist là một sự kết hợp thông minh giữa các nút list và các khối compressed ziplistxóc đĩa, giúp tối ưu hóa bộ nhớ và tăng tốc độ truy xuất cho danh sách liên kết. Đây là lựa chọn hiệu quả khi xử lý các danh sách có kích thước lớn trong hệ thống.

Chúng ta hãy tóm tắt lại vai trò của robj:

  • Cung cấp một cách biểu diễn thống nhất cho nhiều loại dữ liệu.
  • Cho phép cùng một loại dữ liệu sử dụng các cách biểu diễn nội bộ khác nhaukết quả bóng đá việt nam hôm nay, từ đó tiết kiệm bộ nhớ trong một số trường hợp.
  • Hỗ trợ chia sẻ đối tượng và đếm tham chiếu. Khi đối tượng được chia sẻkết quả bóng đá việt nam hôm nay, nó chỉ chiếm một bản sao bộ nhớ, tiếp tục tiết kiệm bộ nhớ.

Quá trình mã hóa robj kiểu string

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:

  • Bước 1 kiểm trakeo banh, kiể Đảm bảo chỉ thực hiện các thao tác trên các đối tượng kiểu string.
  • Bước kiểm tra thứ hai là xác minh việc mã hóa (encoding). Macro sdsEncodedObject được định nghĩa trong tệp server.hkeo banh, và mục tiêu của nó là đảm bảo rằng các thao tác chỉ được thực hiện trên các đối tượng chuỗi có mã hóa là OBJ_ENCODING_RAW hoặc OBJ_ENCODING_EMBSTR. Cả hai loại mã hóa này đều sử dụng cấu trúc sds để lưu trữ dữ liệu chuỗi, vì vậy bạn có thể cân nhắc thực hiện thêm các bước mã hóa bổ sung nếu cần thiết. Điều này giúp tối ưu hóa hiệu suất và đảm bảo tính nhất quán cho dữ liệu trong hệ thống.
							
								
									#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)
								
							
  • Bước kiểm tra thứ ba là kiểm tra refcount (đếm tham chiếu). Đối với các đối tượng được chia sẻ có số lượng tham chiếu lớn hơn 1xóc đĩa, chúng sẽ được sử dụng ở nhiều nơi khác nhau. Khi quá trình mã hóa kết thúc, con trỏ đối tượng của robj có thể thay đổi (chúng ta đã đề cập đến kiểu giao diện tương tự khi giải thích về hàm sdscatlen trong bài trước). Do đó, việc cập nhật tất cả các vị trí tham chiếu đối với các đối tượng có số lượng tham chiếu lớn hơn 1 trở nên rất phức tạp. Vì vậy, đối với các đối tượng có đếm tham chiếu lớn hơn 1, chúng tôi quyết định không tiến hành quá trình mã hóa. Điều này giúp tránh những rủi ro và khó khăn phát sinh từ việc quản lý tham chiếu không đồng bộ.
  • Bạn có thể thử chuyển đổi chuỗi thành kiểu số nguyên dài 64 bit (long). Phạm vi giá trị mà kiểu long 64 bit có thể biểu diễn là từ -2^63 đến 2^63 - 1xóc đĩa, khi được viết dưới dạng thập phân sẽ có độ dài tối đa là 20 chữ số (bao gồm dấu trừ nếu là số âm). Điều kiện kiểm tra nhỏ hơn hoặc bằng 21 ở đây dường như là thừa thãi, thực tế chỉ cần kiểm tra nhỏ hơn hoặc bằng 20 là đủ (nếu tôi tính nhầm, hãy cho tôi biết ngay nhé). Hàm string2l nếu thành công trong việc chuyển đổi chuỗi thành long, nó sẽ trả về giá trị 1 và lưu giá trị long đã được chuyển đổi vào biến value.
  • Khi chuyển thành long thành côngkeo banh, sẽ chia ra làm hai trường hợp.
    • Trường hợp đầu tiên: Nếu cấu hình của Redis không yêu cầu sử dụng thuật toán thay thế LRU và giá trị số được chuyển thành kiểu long lại khá nhỏ (dưới OBJ_SHARED_INTEGERSkết quả bóng đá việt nam hôm nay, hiện tại giá trị này là 10.000), thì sẽ sử dụng đối tượng số chia sẻ để biểu diễn. Lý do tại sao điều kiện ở đây liên quan đến LRU là vì thuật toán LRU cần mỗi đối tượng robj có giá trị lru khác nhau, do đó nếu áp dụng LRU thì không thể chia sẻ robj. Mảng shared.integers có chiều dài 10.000, trong đó đã lưu trữ trước 10.000 đối tượng số nhỏ. Những đối tượng số này đều thuộc kiểu string robj với mã hóa encoding = OBJ_ENCODING_INT. Để hiểu rõ hơn, khi Redis khởi động, nó sẽ tạo ra một loạt các đối tượng số từ 0 đến 9.999 và lưu chúng vào mảng shared.integers. Điều này giúp tối ưu hóa bộ nhớ, bởi vì nếu có nhiều yêu cầu truy cập cùng một số nhỏ, thay vì tạo ra một robj mới cho mỗi yêu cầu, Redis có thể chỉ đơn giản trả về con trỏ đến đối tượng đã tồn tại trong mảng shared.integers. Điều này không chỉ tiết kiệm tài nguyên mà còn cải thiện hiệu suất của hệ thống. Tuy nhiên, nếu yêu cầu cần một số có giá trị lớn hơn hoặc cần một hành vi đặc biệt, Redis sẽ tạo ra một robj hoàn toàn mới, không thuộc chia sẻ. Thêm nữa, việc sử dụng mảng shared.integers cũng như các đối tượng số này không chỉ giới hạn ở trường hợp LRU, mà còn có thể mở rộng sang các trường hợp khác nơi mà việc chia sẻ dữ liệu sẽ mang lại lợi ích tối đa trong việc tối ưu hóa tài nguyên và tăng cường hiệu quả hoạt động của Redis.
    • Trường hợp thứ hai: Nếu bước trước đó không thể sử dụng đối tượng nhỏ chung để biểu diễnkeo banh, thì lúc này obj gốc sẽ được mã hóa thành encoding = OBJ_ENCODING_INT. Ở đây, trường hợp ptr sẽ trực tiếp lưu trữ giá trị kiểu long. Lưu ý rằng ptr ban đầu là một con trỏ void * (tức là nó lưu trữ địa chỉ bộ nhớ), vì vậy trên máy 64-bit, nó có kích thước 64 bit, đủ khả năng để lưu trữ một giá trị long 64 bit. Điều này có nghĩa là ngoài obj gốc ra, nó sẽ không cần thêm vùng nhớ nào khác để lưu trữ giá trị chuỗi. Thêm vào đó, việc sử dụng cách này giúp tối ưu hóa bộ nhớ, đặc biệt khi các giá trị số nguyên lớn thường xuất hiện trong ứng dụng. Điều này làm giảm đáng kể nhu cầu về tài nguyên bộ nhớ và cải thiện hiệu suất tổng thể của hệ thống, nhất là khi có sự gia tăng số lượng obj trong cơ sở dữ liệu.
  • Tiếp theo là xử lý các chuỗi không thể chuyển thành long 64-bit. Cuối cùng thực hiện thêm hai bước xử lý:
    • Nếu độ dài chuỗi đủ nhỏ (nhỏ hơn hoặc bằng OBJ_ENCODING_EMBSTR_SIZE_LIMITxóc đĩa, được định nghĩa là 44), thì khi đó việc gọi hàm createEmbeddedStringObject sẽ mã hóa nó thành encoding = OBJ_ENCODING_EMBSTR. Trong ngữ cảnh này, việc sử dụng OBJ_ENCODING_EMBSTR cho phép tối ưu hóa bộ nhớ vì chuỗi ngắn sẽ được lưu trữ trong cùng một khối với đối tượng, thay vì tách biệt như các trường hợp chuỗi dài hơn. Điều này giúp cải thiện hiệu suất truy xuất và giảm thiểu sự tiêu hao tài nguyên, đặc biệt là khi có nhiều chuỗi nhỏ cần quản lý trong hệ thống. Đây là một chiến lược quan trọng để tối ưu hóa hiệu quả của các tác vụ liên quan đến xử lý chuỗi trong môi trường lập trình.
    • Nếu tất cả các lần thử mã hóa trước đó đều thất bại (vẫn còn ở trạng thái OBJ_ENCODING_RAW) và trong sds có quá nhiều byte dư thừakết quả bóng đá việt nam hôm nay, thì sẽ có một nỗ lực cuối cùng để giải phóng những byte không cần thiết bằng cách gọi đến giao diện sdsRemoveFreeSpace của sds. Điều này giúp tối ưu hóa bộ nhớ và cải thiện hiệu suất khi lưu trữ dữ liệu.

Đố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ể.

  • Cấu trúc robj 16 byte.
  • Header sdshdr8 3 byte.
  • Mảng sds với tối đa 44 byte.
  • 1 ký tự NULL kết thúc.

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.

Quá trình giải mã robj kiểu string

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ó:

  • Các đối tượng robj có mã hóa là OBJ_ENCODING_RAW và OBJ_ENCODING_EMBSTR sẽ được trả về mà không thay đổi gìkeo banh, giữ nguyên như ban đầu. Đối với người dùng, hai định dạng này không có sự khác biệt rõ rệt, vì cả hai đều sử dụng sds (simple dynamic string) được đóng gói bên trong. Điều này cho phép dữ liệu được lưu trữ một cách hiệu quả mà không làm thay đổi bản chất của chuỗi gốc, đồng thời mang lại sự thuận tiện trong việc xử lý và truy xuất thông tin.
  • Một chuỗi ký tự số được mã hóa thành đối tượng robjkeo banh, sau đó bạn cần chuyển đổi long trở lại thành chuỗi thập phân. Tiếp theo, sử dụng hàm createStringObject để chuyển đổi thành định dạng sds. Hãy lưu ý rằng: khi chuyển đổi từ long sang sds, độ dài của chuỗi sẽ không vượt quá 20 ký tự. Theo cách thực hiện của createStringObject, các chuỗi này chắc chắn sẽ được mã hóa dưới dạng OBJ_ENCODING_EMBSTR. Dưới đây là mã nguồn của hàm createStringObject: ```c robj *createStringObject(char *ptr, size_t len) { robj *o = zmalloc(sizeof(robj) + sizeof(sds)); o->type = OBJ_STRING; o->encoding = OBJ_ENCODING_EMBSTR; o->ptr = ptr; o->len = len; return o; } ```
							
								
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
													);
													
}
													

Nói lại về mối quan hệ giữa sds và string

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.

  • Chính xác mà nóikết quả bóng đá việt nam hôm nay, trong Redis, string được biểu diễn bằng một robj.
  • Biến robj được sử dụng để biểu diễn chuỗi có thể mã hóa thành ba dạng biểu diễn nội bộ khác nhau: OBJ_ENCODING_RAWkết quả bóng đá việt nam hôm nay, OBJ_ENCODING_EMBSTR và OBJ_ENCODING_INT. Trong đó, hai loại mã hóa đầu tiên sử dụng sds (simple dynamic string) để lưu trữ dữ liệu, còn loại mã hóa cuối cùng, OBJ_ENCODING_INT, sẽ trực tiếp lưu chuỗi dưới dạng kiểu long (số nguyên dài). Điều này giúp tối ưu hóa bộ nhớ và hiệu suất tùy thuộc vào kích thước và bản chất của chuỗi cần lưu trữ.
  • Khi thực hiện các thao tác tăng (incr) hoặc giảm (decr) trên chuỗi string trong Redisxóc đĩa, nếu chuỗi được mã hóa bên trong dưới dạng OBJ_ENCODING_INT, Redis có thể thực hiện phép toán cộng trừ trực tiếp mà không cần bước chuyển đổi. Tuy nhiên, nếu chuỗi được mã hóa dưới dạng OBJ_ENCODING_RAW hoặc OBJ_ENCODING_EMBSTR, Redis sẽ cố gắng chuyển đổi chuỗi lưu trữ trong sds thành kiểu long trước khi tiến hành các hoạt động tính toán. Nếu việc chuyển đổi này thành công, thì phép toán mới được thực hiện; ngược lại, nó sẽ không thể xử lý đúng cách và có thể dẫn đến lỗi hoặc kết quả không mong muốn.
  • Khi thực hiện các lệnh như appendkết quả bóng đá việt nam hôm nay, setbit và getrange trên một chuỗi đã được biểu diễn nội bộ dưới dạng kiểu long, thì những thao tác này vẫn nhắm đến giá trị chuỗi (tức là chuỗi ở dạng thập phân) chứ không phải trực tiếp thao tác với biểu diễn long bên trong. Ví dụ, chuỗi "32", nếu được xem như một mảng ký tự, sẽ chứa hai ký tự có mã ASCII lần lượt là 0x33 và 0x32. Khi chúng ta thực hiện lệnh setbit key 7 0, điều đó tương đương với việc thay đổi ký tự 0x33 thành 0x32, khiến chuỗi sau khi thực hiện trở thành "22". Tuy nhiên, nếu chúng ta giải thích chuỗi "32" dưới dạng long 64-bit, nó sẽ được biểu diễn là 0x0000000000000020. Khi thực hiện các thao tác bit trên đại diện long này, kết quả sẽ không giống như mong đợi. Do đó, trong quá trình triển khai các lệnh này, hệ thống sẽ chuyển đổi long thành chuỗi trước khi thực hiện bất kỳ hoạt động nào. Vì lý do giới hạn về mặt độ dài, đoạn mã chi tiết để thực hiện ba lệnh này sẽ không được trình bày ở đây. Những ai quan tâm có thể tham khảo mã nguồn Redis để hiểu thêm.
    • Hàm appendCommand trong t_string.c;
    • Hàm setbitCommand trong biops.c;
    • Hàm getrangeCommand trong t_string.c.

Đá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
													;
													
}
													

Thao tác đếm tham chiếu của robj

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


Bài viết gốckết quả bóng đá việt nam hôm nay, vui lòng ghi rõ nguồn và bao gồm mã QR bên dưới! Nếu không, từ chối tái bản!
Liên kết bài viết này: /w8pksnr4.html
Hãy theo dõi tài khoản Weibo cá nhân của tôi: Tìm kiếm tên tôi "Trương Thiết Lệ" trên Weibo.
Tài khoản WeChat của tôi: tielei-blog (Trương Thiết Lệ)
Bài trước: Chi tiết về cấu trúc dữ liệu bên trong Redis (2) —— sds
Bài sau: Con gái và những cuốn sách tranh của cô ấy

Bài viết mới nhất