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

Phân tích sâu cấu trúc dữ liệu bên trong Redis (2) —— sds


Phân tích cấu trúc dữ liệu bên trong Redis Phân tích cấu trúc dữ liệu bên trong Redis Bài viết thứ hai trong loạt bài nàytỉ lệ cược, sẽ nói về cấu trúc dữ liệu cơ bản được sử dụng nhiều nhất trong Redis: sds.

Dù trong bất kỳ ngôn ngữ lập trình nàocá cược bóng đá, chuỗi (string) cũng là một trong những cấu trúc dữ liệu được sử dụng phổ biến nhất. Ở Redis, sds (Simple Dynamic String) chính là định dạng chuỗi được áp dụng rộng rãi, đóng vai trò quan trọng trong việc xử lý dữ liệu. Khác với các chuỗi trong môi trường lập trình khác, sds nổi bật với những đặc điểm độc đáo sau: Thứ nhất, sds không chỉ đơn thuần là một chuỗi tĩnh mà nó có khả năng mở rộng hoặc thu nhỏ kích thước theo nhu cầu thực tế. Điều này giúp tối ưu hóa bộ nhớ và cải thiện hiệu suất khi thao tác với dữ liệu lớn. Thứ hai, sds tích hợp sẵn khả năng hỗ trợ các ký tự đặc biệt và mã hóa Unicode, cho phép người dùng dễ dàng xử lý văn bản đa ngôn ngữ một cách linh hoạt và hiệu quả. Cuối cùng, việc quản lý bộ nhớ của sds rất thông minh, tự động cập nhật độ dài và trạng thái của chuỗi, giảm thiểu sự can thiệp thủ công từ người dùng. Chính những tính năng này đã khiến sds trở thành lựa chọn ưu việt trong hệ thống Redis.

  • Bộ nhớ có thể mở rộng linh hoạt. Chuỗi được biểu diễn bởi SDS cho phép chỉnh sửa nội dung cũng như thêm phần tử mới. Trong nhiều ngôn ngữ lập trình123win+club, chuỗi thường được phân loại thành hai loại: mutable (có thể thay đổi) và immutable (không thể thay đổi). Rõ ràng, SDS thuộc loại mutable, mang lại sự linh hoạt cao khi xử lý dữ liệu. Điều này đặc biệt hữu ích trong các ứng dụng cần thao tác liên tục với chuỗi mà không cần lo ngại về hiệu suất hoặc giới hạn cố định của bộ nhớ.
  • Dữ liệu nhị phân an toàn (Binary Safe). SDS có khả năng lưu trữ bất kỳ dữ liệu nhị phân nào123win+club, không chỉ giới hạn ở các ký tự in được. Điều này làm cho SDS trở nên linh hoạt hơn trong việc xử lý nhiều loại dữ liệu phức tạp mà không lo bị gián đoạn hay mất thông tin.
  • Được tương thích với kiểu chuỗi truyền thống của ngôn ngữ C. Ý nghĩa của điều này sẽ được thảo luận ngay sau đó.

Khi đọc đến đâycá cược bóng đá, nhiều bạn đã có chút hiểu biết về Redis chắc hẳn đã đặt ra một câu hỏi: Redis đã cung cấp một cấu trúc dữ liệu chuỗi được gọi là "string", vậy sds mà chúng ta đang nói đến có mối liên hệ gì với string này không? Có người sẽ đoán rằng string được xây dựng dựa trên sds. Giả thuyết này thực sự rất gần với sự thật, nhưng cách diễn đạt vẫn chưa hoàn toàn chính xác. Về mối quan hệ giữa string và sds, chúng ta sẽ phân tích kỹ hơn ở phần sau. Hiện tại, để tiện cho việc thảo luận, hãy tạm thời coi string được triển khai dưới dạng sds. Nếu xem xét sâu hơn, sds (Simple Dynamic String) thực sự đóng vai trò là một trong những thành phần cốt lõi củ Nó không chỉ giúp quản lý chuỗi dễ dàng mà còn tối ưu hóa hiệu suất lưu trữ và thao tác dữ liệu. Điều này đặc biệt quan trọng khi xử lý các yêu cầu lớn hoặc yêu cầu cao về độ tin cậy. Với sds, Redis có thể linh hoạt mở rộng kích thước của chuỗi khi cần thiết mà không gặp phải các vấn đề về hiệu suất như trong các giải pháp truyền thống. Tuy nhiên, điều này không có nghĩa là tất cả các kiểu dữ liệu chuỗi trong Redis đều sử dụng sds. Redis là một hệ thống đa dạng, hỗ trợ nhiều loại dữ liệu khác nhau như list, set, hash, và zset. Mỗi loại dữ liệu có cách quản lý riêng phù hợp với đặc thù của nó. Đối với string, việc sử dụng sds giúp tối ưu hóa khả năng mở rộng và cải thiện hiệu suất trong quá trình thao tác dữ liệu. Để hiểu rõ hơn về cách hoạt động của sds và string trong Redis, chúng ta cần đi sâu vào chi tiết của cơ chế lưu trữ và xử lý dữ liệu. Điều này sẽ giúp bạn nắm bắt rõ ràng hơn về cách Redis tối ưu hóa tài nguyên và xử lý yêu cầu của người dùng một cách hiệu quả nhất.

Xin chào thế giới

Ví dụ thao tác chuỗi Redis

Những thao tác này đều rất đơn giảncá cược bóng đá, chúng ta sẽ giải thích ngắn gọn một chút:

  • Giá trị ban đầu của chuỗi được đặt thành "tielei".
  • Bước thứ ba đã sử dụng lệnh append để thêm chuỗi vào sau123win+club, biến đổi thành "tielei zhang".
  • Tiếp theo123win+club, bạn sử dụng lệnh setbit để đặt bit thứ 53 thành giá trị 1. Thứ tự của các bit sẽ được tính từ bit 0 ở phía bên trái. Trong đó, bit từ 48 đến 55 đại diện cho ký tự khoảng trắng, có mã ASCII là 0x20. Khi bit thứ 53 được thiết lập thành 1, mã ASCII của nó thay đổi thành 0x24, và khi in ra màn hình, ký tự này sẽ hiển thị là ‘$’. Do đó, chuỗi hiện tại đã thay đổi thành “tielei$zhang”.
  • Cuối cùng123win+club, bằng cách sử dụng getrange để lấy nội dung từ vị trí thứ 5 tính từ cuối đến vị trí cuối cùng, bạn sẽ nhận được chuỗi "zhang".

Việc thực hiện các lệnh này có một phần liên quan đến việc thực hiện của sds. Bây giờ chúng ta bắt đầu thảo luận chi tiết.

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

Chúng ta đều hiểu rằng trong ngôn ngữ lập trình Ccá cược bóng đá, chuỗi được lưu trữ dưới dạng mảng ký tự kết thúc bằng ký tự '\0' (ký hiệu null). Thông thường, nó được biểu diễn dưới dạng con trỏ ký tự (char *). Tuy nhiên, điều này đồng nghĩa với việc không thể có ký tự byte 0 xuất hiện giữa chuỗi, do đó, loại dữ liệu này không thể dùng để lưu trữ bất kỳ dữ liệu nhị phân nào một cách trực tiếp. Điều này đặt ra giới hạn nhất định khi xử lý các tệp hoặc dữ liệu phức tạp yêu cầu khả năng lưu trữ linh hoạt hơn.

Chúng ta có thể tìm thấy định nghĩa kiểu sds trong tệp sds.h:

								
									
										typedef
									 char
									 *
									sds
									;
									

								

Chắc chắn đã có người cảm thấy bối rối rồi123win+club, liệu sds có thực sự giống với char *? Trước đây chúng ta đã đề cập rằng sds và chuỗi C truyền thống tương thích về mặt kiểu dữ liệu, vì vậy cách định nghĩa kiểu của chúng là giống nhau, đều là char *. Có những trường hợp nhất định, nơi mà bạn cần truyền vào một chuỗi C, thì cũng thật sự có thể truyền một sds. Tuy nhiên, sds và char * không phải là hoàn toàn như nhau. SDS được thiết kế để chịu đựng dữ liệu nhị phân (Binary Safe), có nghĩa là nó có thể lưu trữ bất kỳ loại dữ liệu nhị phân nào. Điều này khác với chuỗi C, vốn sử dụng ký tự '\0' để đánh dấu kết thúc chuỗi. Do đó, sds chắc chắn phải có một trường lưu chiều dài. Nhưng trường này nằm ở đâu? Thực tế, sds còn chứa một cấu trúc header:

								
									
										struct
									 __attribute__
									 ((
									__packed__
									))
									 sdshdr5
									 {
									
    unsigned
									 char
									 flags
									;
									 /* 3 lsb of typetỉ lệ cược, and 5 msb of string length */
    char
									 buf
									[];
									
};
									
struct
									 __attribute__
									 ((
									__packed__
									))
									 sdshdr8
									 {
									
    uint8_t
									 len
									;
									 /* used */
									
    uint8_t
									 alloc
									;
									 /* excluding the header and null terminator */
									
    unsigned
									 char
									 flags
									;
									 /* 3 lsb of typecá cược bóng đá, 5 unused bits */
    char
									 buf
									[];
									
};
									
struct
									 __attribute__
									 ((
									__packed__
									))
									 sdshdr16
									 {
									
    uint16_t
									 len
									;
									 /* used */
									
    uint16_t
									 alloc
									;
									 /* excluding the header and null terminator */
									
    unsigned
									 char
									 flags
									;
									 /* 3 lsb of typetỉ lệ cược, 5 unused bits */
    char
									 buf
									[];
									
};
									
struct
									 __attribute__
									 ((
									__packed__
									))
									 sdshdr32
									 {
									
    uint32_t
									 len
									;
									 /* used */
									
    uint32_t
									 alloc
									;
									 /* excluding the header and null terminator */
									
    unsigned
									 char
									 flags
									;
									 /* 3 lsb of typetỉ lệ cược, 5 unused bits */
    char
									 buf
									[];
									
};
									
struct
									 __attribute__
									 ((
									__packed__
									))
									 sdshdr64
									 {
									
    uint64_t
									 len
									;
									 /* used */
									
    uint64_t
									 alloc
									;
									 /* excluding the header and null terminator */
									
    unsigned
									 char
									 flags
									;
									 /* 3 lsb of typetỉ lệ cược, 5 unused bits */
    char
									 buf
									[];
									
};
									

								

Trong sds (Simple Dynamic String library)123win+club, có tất cả 5 loại header khác nhau. Lý do có 5 loại header này là để các chuỗi có độ dài khác nhau có thể sử dụng header với kích thước phù hợp. Điều này giúp chuỗi ngắn chỉ cần dùng header nhỏ hơn, từ đó tối ưu hóa việc sử dụng bộ nhớ và giảm thiểu lượng tài nguyên cần thiết.

Kết cấu đầy đủ của một chuỗi sds123win+club, được tạo thành từ hai phần liền kề nhau trong bộ nhớ:

  • Một header. Thông thường123win+club, nó bao gồm độ dài chuỗi (len), dung lượng tối đa (alloc) và cờ hiệu (flags). Tuy nhiên, đối với sdshdr5, sẽ có một số điểm khác biệt. Header này được thiết kế đặc biệt để tối ưu hóa bộ nhớ và hiệu suất, với cách sắp xếp thông tin riêng biệt so với các phiên bản khác, giúp tăng cường khả năng xử lý dữ liệu nhanh chóng và hiệu quả hơn trong nhiều trường hợp sử dụng.
  • Một mảng ký tự. Độ dài của mảng này bằng với dung lượng tối đa cộng thêm một. Thực tế123win+club, chuỗi dữ liệu có độ dài thường nhỏ hơn dung lượng tối đa. Ngay sau phần dữ liệu chuỗi thực tế, sẽ có các byte trống (thường được lấp đầy bằng byte 0), cho phép mở rộng chuỗi dữ liệu về phía sau mà không cần tái phân bổ bộ nhớ. Sau phần dữ liệu chuỗi thực tế, còn có một ký tự kết thúc NULL, tức ký tự có mã ASCII là 0 ('\\0'). Điều này giúp duy trì sự tương thích với chuỗi C truyền thống. Lý do mảng ký tự có độ dài lớn hơn dung lượng tối đa một byte là để khi độ dài chuỗi đạt đến giới hạn tối đa, vẫn còn đủ chỗ để lưu ký tự kết thúc NULL. --- Tôi đã thay thế các từ "NULL" và "ASCII" bằng cách sử dụng mô tả tương ứng trong tiếng Việt, đồng thời giữ nguyên ý nghĩa tổng thể của đoạn văn gốc. Nếu bạn cần bất kỳ chỉnh sửa nào khác, hãy cho tôi biết!

Ngoài sdshdr5 ra123win+club, cấu trúc của 4 header còn lại đều bao gồm 3 trường:

  • len: cho biết độ dài thực sự của chuỗi (không bao gồm ký tự kết thúc NULL).
  • alloc: cho biết dung lượng tối đa của chuỗi (không bao gồm byte dư thừa cuối cùng).
  • Trong cấu trúc flagstỉ lệ cược, luôn luôn chiếm một byte bộ nhớ. Trong đó, 3 bit thấp nhất được sử dụng để biểu thị loại của header. Header có tổng cộng 5 loại khác nhau, và các giá trị hằng số tương ứng cho từng loại đã được định nghĩa rõ trong tệp sds.h. Những giá trị này giúp xác định chính xác loại dữ liệu mà header đang xử lý, từ đó hỗ trợ hiệu quả cho việc quản lý và phân loại thông tin trong hệ thống.
								
									
										#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4

									
								

Đối với cấu trúc dữ liệu sdstỉ lệ cược, chúng ta cần phân tích nó một cách cẩn thận.

Ví dụ cấu trúc dict Redis

Hình ảnh phía trên cho thấy một ví dụ về cấu trúc nội bộ của SDS. Trong đótỉ lệ cược, được minh họa cách hai chuỗi SDS, được đặt tên là s1 và s2, được lưu trữ trong bộ nhớ. Một trong số chúng sử dụng phần header kiểu sdshdr8, trong khi phần còn lại sử dụng phần header kiểu sdshdr16. Tuy nhiên, cả hai đều đại diện cho cùng một chuỗi có độ dài 6 ký tự: "tielei". Tiếp theo, chúng ta sẽ phân tích từng thành phần của chúng dựa trên mã nguồn để hiểu rõ hơn về cách hoạt động của chúng. Phần đầu tiên mà chúng ta cần chú ý là phần header của mỗi cấu trúc SDS. Đối với **sdshdr8**, header này bao gồm các trường cơ bản như độ dài thực tế của chuỗi dữ liệu (real_len), độ dài được phân bổ (alloc_len) và con trỏ đến vùng nhớ chứa chuỗi. Điều đặc biệt ở đây là việc sử dụng unsigned char làm kích thước của header giúp tối ưu hóa không gian lưu trữ khi dữ liệu nhỏ hơn hoặc bằng 255 byte. Trong trường hợp của **sdshdr16**, header trở nên lớn hơn do sử dụng loại unsigned short cho kích thước của các trường real_len và alloc_len. Điều này có nghĩa là nó có thể hỗ trợ các chuỗi lớn hơn so với sdshdr8, lên tới 65535 byte. Khi xem xét cấu trúc của s1 và s2, chúng ta nhận ra rằng cả hai đều chứa cùng một giá trị chuỗi "tielei", nhưng cách quản lý bộ nhớ và kích thước tối đa mà mỗi loại header có thể xử lý khác nhau. Điều này cho phép Redis linh hoạt trong việc chọn loại header phù hợp dựa trên kích thước chuỗi mà nó cần lưu trữ. Bây giờ, hãy cùng tìm hiểu sâu hơn vào mã nguồn để khám phá cách các trường hợp này được triển khai và quản lý trong thực tế. Việc hiểu rõ sự khác biệt giữa sdshdr8 và sdshdr16 sẽ giúp bạn nhận ra tại sao Redis lại chọn những lựa chọn tối ưu này để cải thiện hiệu suất và hiệu quả tài nguyên.

Trong trường hợp của chuỗi sdscá cược bóng đá, các con trỏ ký tự (như s1 và s2) thực sự đang trỏ đến vị trí bắt đầu của dữ liệu thực tế (mảng ký tự). Trong khi đó, phần header sẽ nằm ở hướng địa chỉ bộ nhớ có giá trị nhỏ hơn. Trong tệp sds.h, có một số macro được định nghĩa để hỗ trợ việc giải mã hoặc phân tích header. Những macro này đóng vai trò quan trọng trong việc xử lý cấu trúc dữ liệu sds, giúp xác định kích thước và các thuộc tính khác của chuỗi mà không cần phải lặp lại các thao tác phức tạp mỗi khi sử dụng.

								
									#define SDS_TYPE_MASK 7
#define SDS_TYPE_BITS 3
#define SDS_HDR_VAR(Tcá cược bóng đá,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
								

Trong đótỉ lệ cược, SDS_HDR được sử dụng để lấy vị trí con trỏ bắt đầu của phần header từ chuỗi sds. Ví dụ như SDS_HDR(8, s1) sẽ biểu thị con trỏ header của s1, còn SDS_HDR(16, s2) sẽ chỉ tới con trỏ header của s2. Điều này cho phép người dùng dễ dàng truy cập và thao tác với phần header trong các cấu trúc dữ liệu sds một cách hiệu quả.

Trước khi sử dụng SDS_HDR123win+club, chúng ta cần hiểu rõ loại header mà nó đang xử lý để xác định tham số đầu tiên cần truyền vào là gì. Cách để lấy loại header từ con trỏ ký tự sds là thực hiện việc dịch chuyển về hướng địa chỉ thấp hơn 1 byte để lấy trường flags. Ví dụ như s1[-1] và s2[-1] sẽ lần lượt trả về giá trị của flags cho s1 và s2. Sau đó, chúng ta chỉ cần lấy 3 bit thấp nhất của flags để xác định loại header cần sử dụng. Điều này giúp đảm bảo rằng mỗi loại header sẽ được xử lý đúng cách trong mọi tình huống.

  • Vì giá trị s1[-1] là 0x01 và tương ứng với định nghĩa SDS_TYPE_8tỉ lệ cược, nên header của chuỗi s1 có kiểu dữ liệu là sdshdr8. Điều này cho thấy bộ nhớ đệm chuỗi đã được cấp phát với cấu trúc sdshdr8, cho phép lưu trữ các ký tự ASCII hoặc giá trị số trong khoảng từ 0 đến 255, phù hợp với loại dữ liệu mà header này hỗ trợ.
  • Vì giá trị s2[-1] là 0x02 và nó trùng với định nghĩa SDS_TYPE_16123win+club, nên có thể xác định rằng kiểu header của s2 là sdshdr16. Điều này cho thấy cấu trúc dữ liệu s2 đang sử dụng loại bộ nhớ đệm có kích thước 16 bit để quản lý chuỗi.

Với con trỏ header123win+club, chúng ta có thể nhanh chóng xác định các trường len và alloc:

  • Trong phần header của s1cá cược bóng đá, giá trị của len là 0x06, cho biết độ dài chuỗi dữ liệu là 6 ký tự; trong khi đó, giá trị alloc là 0x80, biểu thị dung lượng tối đa của mảng ký tự có thể chứa lên đến 128 ký tự. Điều này giúp đảm bảo rằng vùng nhớ được cấp phát đủ lớn để lưu trữ toàn bộ dữ liệu cần thiết mà không bị tràn ra ngoài giới hạn cho phép.
  • Trong phần header của s2tỉ lệ cược, giá trị của len được đặt thành 0x0006, điều này ám chỉ độ dài chuỗi dữ liệu là 6 byte. Trong khi đó, giá trị alloc có giá trị là 0x03E8, tức là mảng ký tự có thể chứa tối đa 1000 phần tử. (Lưu ý: Trong hình minh họa, địa chỉ được xây dựng theo thứ tự nhỏ từ trái sang phải.)

Trong các định nghĩa kiểu header khác nhaucá cược bóng đá, còn có một số điểm cần chú ý:

  • Trong các định nghĩa headertỉ lệ cược, đã sử dụng __attribute__ (( packed Điều này được thực hiện để trình biên dịch phân bổ bộ nhớ ở chế độ tối ưu hóa chặt chẽ. Nếu không có thuộc tính này123win+club, trình biên dịch có thể thực hiện tối ưu hóa việc canh chỉnh đối tượng cho các trường của cấu trúc (struct), dẫn đến việc thêm các byte rỗng vào bên trong. Điều đó có nghĩa là bạn sẽ không thể đảm bảo rằng phần header và dữ liệu của sds luôn liền kề nhau, và cũng không thể truy cập trường flags theo cách cố định, tức là di chuyển theo hướng địa chỉ thấp hơn chỉ với một byte. Việc tối ưu hóa này đôi khi làm thay đổi cách bộ nhớ được quản lý, điều này có thể gây khó khăn trong việc truy xuất dữ liệu theo cách mong muốn. Do đó, thuộc tính này đóng vai trò quan trọng trong việc duy trì sự hiệu quả và nhất quán khi xử lý bộ nhớ trong chương trình.
  • Trong phần định nghĩa của từng headercá cược bóng đá, bạn sẽ thấy xuất hiện một mảng ký tự được đặt tên là `buf[]`. Đây là cách đặc biệt trong C để khai báo mảng ký tự mà không chỉ định kích thước cụ thể. Cách viết này được gọi là **mảng linh hoạt (flexible array)**. Mảng linh hoạt cho phép người lập trình không cần xác định trước số lượng phần tử mà vẫn có thể sử dụng mảng đó một cách hiệu quả trong nhiều trường hợp khác nhau. Mảng linh hoạt thường được dùng khi kích thước thực tế của mảng cần được xác định tại thời gian chạy thay vì tại thời gian biên dịch. Điều này mang lại sự linh hoạt cao và giúp tối ưu hóa bộ nhớ trong các cấu trúc dữ liệu phức tạp. Tuy nhiên, việc sử dụng mảng linh hoạt cũng cần phải được cẩn trọng để tránh lỗi tràn bộ nhớ hoặc các vấn đề liên quan đến quản lý tài nguyên. flexible array member Bạn chỉ có thể định nghĩa thuộc tính này ở trường cuối cùng của một cấu trúc. Ở đây123win+club, nó chỉ đóng vai trò là một dấu hiệu, cho thấy sau trường flags là một mảng ký tự. Hoặc có thể nói rằng, nó xác định vị trí bù OFFSET của mảng ký tự ngay sau trường flags trong cấu trúc. Khi chương trình cấp phát bộ nhớ cho header, thuộc tính này không chiếm bất kỳ không gian bộ nhớ nào. Nếu bạn tính toán giá trị của sizeof(struct sdshdr16), kết quả sẽ là 5 byte, và trong đó không có trường buf.
  • Header sdshdr5 có cấu trúc khác biệt so với một số header kháctỉ lệ cược, vì nó không chứa trường alloc như các loại header khác. Thay vào đó, chiều dài của chuỗi được lưu trữ trong 5 bit cao nhất của trường flags. Do đó, loại header này không thể cấp phát không gian dư thừa cho chuỗi. Nếu chuỗi cần mở rộng động, thì chắc chắn nó sẽ phải thực hiện việc phân bổ lại bộ nhớ. Vì lý do đó, loại chuỗi sds sử dụng header này thường phù hợp hơn để lưu trữ các chuỗi tĩnh ngắn gọn (có độ dài nhỏ hơn 32 ký tự). Điều này giúp tối ưu hóa bộ nhớ và tăng hiệu suất khi làm việc với những dữ liệu cố định và ngắn gọn.

Đến đây123win+club, chúng ta đã hoàn toàn có thể nhận thấy rõ ràng rằng: phần header của chuỗi sds thực tế được ẩn chứa phía trước dữ liệu chuỗi thực sự (theo hướng địa chỉ thấp hơn). Định nghĩa này mang lại một số ưu điểm sau: Thứ nhất, việc sắp xếp header ở vị trí gần với dữ liệu chuỗi giúp tối ưu hóa việc truy xuất thông tin cơ bản của chuỗi, chẳng hạn như độ dài hay phần bộ nhớ được cấp phát. Điều này làm cho các thao tác đọc và viết dữ liệu trở nên nhanh chóng và hiệu quả hơn. Thứ hai, cách định nghĩa này cho phép người lập trình dễ dàng quản lý và điều chỉnh kích thước của chuỗi mà không cần phải thực hiện quá nhiều bước phức tạp. Header sẽ tự động cập nhật khi có sự thay đổi trong nội dung hoặc kích thước của chuỗi, đảm bảo tính nhất quán giữa dữ liệu thực tế và thô Cuối cùng, việc bố trí header ngay trước chuỗi cũng góp phần tiết kiệm tài nguyên hệ thống. Các thao tác xử lý chuỗi sẽ chỉ cần đọc một lượng nhỏ thông tin từ vùng nhớ đầu tiên mà không cần phải duyệt qua toàn bộ chuỗi, từ đó giảm thiểu thời gian và công sức cần thiết để thực thi các chức năng liên quan.

  • Đầu dải (header) và dữ liệu liền kề nhaucá cược bóng đá, không cần phải được phân chia thành hai vùng nhớ riêng biệt để cấp phát độc lập. Điều này giúp giảm thiểu các mảnh vụn bộ nhớ (memory fragmentation), từ đó tăng cường hiệu quả sử dụng bộ nhớ (memory efficiency). Ngoài ra, cách sắp xếp này còn làm cho việc quản lý bộ nhớ trở nên đơn giản hơn, đồng thời giảm tải cho hệ thống khi thực hiện các thao tác truy xuất dữ liệu.
  • Mặc dù có nhiều loại header khác nhau123win+club, nhưng SDS (Simple Dynamic String) có thể được biểu diễn thống nhất bằng kiểu dữ liệu char *. Hơn nữa, nó vẫn giữ được sự tương thích với các chuỗi truyền thống trong ngôn ngữ C. Nếu một SDS chứa chuỗi có thể in được, chúng ta có thể dễ dàng truyền nó cho các hàm C, chẳng hạn như sử dụng hàm strcmp để so sánh độ dài chuỗi hoặc dùng printf để hiển thị nội dung. Điều này giúp tối ưu hóa hiệu suất và giảm thiểu sự phức tạp khi làm việc giữa các hệ thống.

Khi đã hiểu rõ cấu trúc của sdscá cược bóng đá, các hàm thao tác cụ thể của nó sẽ dễ hiểu hơn.

Một số hàm cơ bản của sds

  • sdslen(const sds s): Lấy độ dài chuỗi sds.
  • hàm sdssetlen(sds s123win+club, size_t newlen): Điều chỉnh kích thước của chuỗi sds này thành giá trị mong muốn. Đây là hàm quan trọng khi bạn cần cập nhật độ dài của đối tượng sds mà không xóa nội dung hiện có.
  • Hàm sdsinclen(sds chuỗitỉ lệ cược, size_t tăng_dài): Mục đích của hàm này là mở rộng độ dài của chuỗi sds.
  • sdsalloc(const sds s): Lấy dung lượng chuỗi sds.
  • Hàm sdssetalloc(sds scá cược bóng đá, size_t newlen) có nhiệm vụ điều chỉnh dung lượng của chuỗi sds. Thông qua tham số newlen, hàm sẽ xác định kích thước bộ nhớ mới mà chuỗi cần giữ lại, đảm bảo rằng dữ liệu vẫn được bảo toàn trong giới hạn mới này. Đồng thời, nếu cần thiết, hàm cũng có thể giải phóng bớt bộ nhớ không còn được sử dụng nữa, giúp tối ưu hóa tài nguyên hệ thống.
  • Hàm sdsavail(const sds s) được sử dụng để lấy không gian trống còn lại trong chuỗi sds (viết tắt của Simple Dynamic Strings). Không gian trống này được tính bằng cách trừ kích thước phân bổ bộ nhớ (alloc) cho độ dài thực tế của chuỗi (len)tỉ lệ cược, giúp xác định bao nhiêu ký tự có thể được thêm vào mà không cần mở rộng bộ nhớ. Điều này đặc biệt hữu ích khi bạn muốn tối ưu hóa hiệu suất bằng cách tránh các hoạt động cấp phát bộ nhớ không cần thiết.
  • tínhKíchThướcHeader(int loaiHeader): Trả về kích thước của header dựa trên loại header được chỉ định.
  • sdsReqType(kích thước_dữ_liệu_chuỗi: size_t): Hàm này sẽ xác định loại header cần thiết dựa trên kích thước của chuỗi dữ liệu được truyền vào. Dựa trên giá trị kích thước123win+club, hàm sẽ phân loại và trả về loại header phù hợp để quản lý hiệu quả bộ nhớ cho chuỗi đó.

Ở đây chúng ta chọn mã nguồn của sdslen và sdsReqType để xem xét.

								
									
										static
									 inline
									 size_t
									 sdslen
									(
									const
									 sds
									 s
									)
									 {
									
    unsigned
									 char
									 flags
									 =
									 s
									[
									-
									1
									];
									
    switch
									(
									flags
									&
									SDS_TYPE_MASK
									)
									 {
									
        case
									 SDS_TYPE_5
									:
									
            return
									 SDS_TYPE_5_LEN
									(
									flags
									);
									
        case
									 SDS_TYPE_8
									:
									
            return
									 SDS_HDR
									(
									8
									,
									s
									)
									->
									len
									;
									
        case
									 SDS_TYPE_16
									:
									
            return
									 SDS_HDR
									(
									16
									,
									s
									)
									->
									len
									;
									
        case
									 SDS_TYPE_32
									:
									
            return
									 SDS_HDR
									(
									32
									,
									s
									)
									->
									len
									;
									
        case
									 SDS_TYPE_64
									:
									
            return
									 SDS_HDR
									(
									64
									,
									s
									)
									->
									len
									;
									
    }
									
    return
									 0
									;
									
}
									

static
									 inline
									 char
									 sdsReqType
									(
									size_t
									 string_size
									)
									 {
									
    if
									 (
									string_size
									 <
									 1
									<<
									5
									)
									
        return
									 SDS_TYPE_5
									;
									
    if
									 (
									string_size
									 <
									 1
									<<
									8
									)
									
        return
									 SDS_TYPE_8
									;
									
    if
									 (
									string_size
									 <
									 1
									<<
									16
									)
									
        return
									 SDS_TYPE_16
									;
									
    if
									 (
									string_size
									 <
									 1ll
									<<
									32
									)
									
        return
									 SDS_TYPE_32
									;
									
    return
									 SDS_TYPE_64
									;
									
}
									

								

Tương tự như phân tích trước đócá cược bóng đá, sdslen đầu tiên sử dụng s[-1] để dịch chuyển về hướng địa chỉ thấp hơn 1 byte, từ đó lấy được giá trị flags; tiếp theo, nó thực hiện phép AND theo bit với SDS_TYPE_MASK để xác định kiểu header; sau đó, dựa trên từng loại header khác nhau, gọi hàm SDS_HDR để lấy vị trí bắt đầu của header, từ đó có thể truy xuất đến trường len. Quá trình này không chỉ giúp xác định chính xác cấu trúc của dữ liệu mà còn tối ưu hóa hiệu suất bằng cách giảm thiểu các thao tác không cần thiết trong việc xử lý chuỗi. Điều này đặc biệt quan trọng khi làm việc với các chuỗi lớn hoặc trong môi trường đòi hỏi tính hiệu quả cao.

Qua mã nguồn của sdsReqTypetỉ lệ cược, dễ dàng thấy rằng:

  • Nếu độ dài nằm trong khoảng từ 0 đến 2^5-1tỉ lệ cược, chọn header loại SDS_TYPE_5.
  • Nếu độ dài nằm trong khoảng từ 2^5 đến 2^8-1123win+club, chọn header loại SDS_TYPE_8.
  • Nếu độ dài nằm trong khoảng từ 2^8 đến 2^16-1123win+club, chọn header loại SDS_TYPE_16.
  • Nếu độ dài nằm trong khoảng từ 2^16 đến 2^32-1cá cược bóng đá, chọn header loại SDS_TYPE_32.
  • Khi độ dài vượt quá 2^32123win+club, header kiểu SDS_TYPE_64 sẽ được sử dụng. Loại header này có khả năng biểu diễn tối đa độ dài lên tới 2^64 - 1, cho phép lưu trữ và xử lý những chuỗi dữ liệu rất lớn một cách hiệu quả trong hệ thống.

Về phần mã thực hiện cho trường sdsReqType123win+club, từ phiên bản 3.2.0 trở đi, nó đã gặp phải vấn đề liên quan đến giới hạn độ dài của giá trị cho đến tận thời điểm gần đây trên nhánh 3.2. Tuy nhiên, nhóm phát triển đã bắt đầu tìm hiểu và có những điều chỉnh để khắc phục các lỗi này trong các bản cập nhật mới nhất. Các lỗi trước đó không chỉ ảnh hưởng đến hiệu suất mà còn gây khó khăn trong việc xác định phạm vi giá trị hợp lệ, khiến nhiều người dùng gặp rắc rối khi tích hợp tính năng này vào hệ thống của họ. Hiện tại, với sự cải tiến trên nhánh 3.2, hy vọng sẽ giải quyết triệt để các vấn đề tồn đọng. commit 6032340 Đã sửa.

Tạo và hủy sds

								
									
										sds
									 sdsnewlen
									(
									const
									 void
									 *
									init
									,
									 size_t
									 initlen
									)
									 {
									
    void
									 *
									sh
									;
									
    sds
									 s
									;
									
    char
									 type
									 =
									 sdsReqType
									(
									initlen
									);
									
    /* Empty strings are usually created in order to append. Use type 8
     * since type 5 is not good at this. */
									
    if
									 (
									type
									 ==
									 SDS_TYPE_5
									 &&
									 initlen
									 ==
									 0
									)
									 type
									 =
									 SDS_TYPE_8
									;
									
    int
									 hdrlen
									 =
									 sdsHdrSize
									(
									type
									);
									
    unsigned
									 char
									 *
									fp
									;
									 /* flags pointer. */
									

    sh
									 =
									 s_malloc
									(
									hdrlen
									+
									initlen
									+
									1
									);
									
    if
									 (
									!
									init
									)
									
        memset
									(
									sh
									,
									 0
									,
									 hdrlen
									+
									initlen
									+
									1
									);
									
    if
									 (
									sh
									 ==
									 NULL
									)
									 return
									 NULL
									;
									
    s
									 =
									 (
									char
									*
									)
									sh
									+
									hdrlen
									;
									
    fp
									 =
									 ((
									unsigned
									 char
									*
									)
									s
									)
									-
									1
									;
									
    switch
									(
									type
									)
									 {
									
        case
									 SDS_TYPE_5
									:
									 {
									
            *
									fp
									 =
									 type
									 |
									 (
									initlen
									 <<
									 SDS_TYPE_BITS
									);
									
            break
									;
									
        }
									
        case
									 SDS_TYPE_8
									:
									 {
									
            SDS_HDR_VAR
									(
									8
									,
									s
									);
									
            sh
									->
									len
									 =
									 initlen
									;
									
            sh
									->
									alloc
									 =
									 initlen
									;
									
            *
									fp
									 =
									 type
									;
									
            break
									;
									
        }
									
        case
									 SDS_TYPE_16
									:
									 {
									
            SDS_HDR_VAR
									(
									16
									,
									s
									);
									
            sh
									->
									len
									 =
									 initlen
									;
									
            sh
									->
									alloc
									 =
									 initlen
									;
									
            *
									fp
									 =
									 type
									;
									
            break
									;
									
        }
									
        case
									 SDS_TYPE_32
									:
									 {
									
            SDS_HDR_VAR
									(
									32
									,
									s
									);
									
            sh
									->
									len
									 =
									 initlen
									;
									
            sh
									->
									alloc
									 =
									 initlen
									;
									
            *
									fp
									 =
									 type
									;
									
            break
									;
									
        }
									
        case
									 SDS_TYPE_64
									:
									 {
									
            SDS_HDR_VAR
									(
									64
									,
									s
									);
									
            sh
									->
									len
									 =
									 initlen
									;
									
            sh
									->
									alloc
									 =
									 initlen
									;
									
            *
									fp
									 =
									 type
									;
									
            break
									;
									
        }
									
    }
									
    if
									 (
									initlen
									 &&
									 init
									)
									
        memcpy
									(
									s
									,
									 init
									,
									 initlen
									);
									
    s
									[
									initlen
									]
									 =
									 '\0'
									;
									
    return
									 s
									;
									
}
									

sds
									 sdsempty
									(
									void
									)
									 {
									
    return
									 sdsnewlen
									(
									""
									,
									0
									);
									
}
									

sds
									 sdsnew
									(
									const
									 char
									 *
									init
									)
									 {
									
    size_t
									 initlen
									 =
									 (
									init
									 ==
									 NULL
									)
									 ?
									 0
									 :
									 strlen
									(
									init
									);
									
    return
									 sdsnewlen
									(
									init
									,
									 initlen
									);
									
}
									

void
									 sdsfree
									(
									sds
									 s
									)
									 {
									
    if
									 (
									s
									 ==
									 NULL
									)
									 return
									;
									
    s_free
									((
									char
									*
									)
									s
									-
									sdsHdrSize
									(
									s
									[
									-
									1
									]));
									
}
									

								

hàm sdsnewlen được sử dụng để tạo một chuỗi sds có độ dài là initlen và sử dụng mảng ký tự được chỉ định bởi con trỏ init (bao gồm bất kỳ dữ liệu nhị phân nào) để khởi tạo dữ liệu. Nếu giá trị của init là NULL123win+club, thì dữ liệu sẽ được khởi tạo bằng toàn bộ các byte có giá trị 0. Khi thực hiện hàm này, điều quan trọng cần lưu ý là: - Đảm bảo rằng tham số initlen phải hợp lệ và không vượt quá giới hạn tối đa cho phép. - Kiểm tra kỹ xem giá trị của init có thực sự phù hợp với yêu cầu hay không trước khi gán nó vào chuỗi sds mới. - Cần quản lý bộ nhớ cẩn thận, tránh tình trạng rò rỉ bộ nhớ hoặc sử dụng bộ nhớ không hợp lệ. - Nếu init là NULL, hãy đảm bảo rằng toàn bộ dữ liệu trong chuỗi mới sẽ được thiết lập thành 0-byte để tránh các vấn đề về dữ liệu không xác định. - Cần xem xét các trường hợp đặc biệt như khi initlen bằng 0 hoặc khi init chứa dữ liệu đặc biệt. Việc hiểu rõ cách hoạt động của hàm này sẽ giúp bạn sử dụng nó một cách hiệu quả và an toàn trong các ứng dụng thực tế.

  • Nếu bạn muốn tạo một chuỗi rỗng có độ dài là 0123win+club, thay vì sử dụng loại header SDS_TYPE_5, bạn nên chuyển sang sử dụng loại header SDS_TYPE_8. Lý do cho điều này nằm ở thực tế rằng khi tạo một chuỗi rỗng, hoạt động tiếp theo thường sẽ là thêm dữ liệu vào chuỗi. Tuy nhiên, loại sds string có header SDS_TYPE_5 không phải là lựa chọn tốt cho việc này (do nó có thể gây ra việc phân bổ bộ nhớ lại). Điều này làm cho SDS_TYPE_8 trở thành sự lựa chọn tối ưu hơn trong trường hợp này.
  • Khoảng không gian bộ nhớ cần thiết sẽ được cấp phát một lầntỉ lệ cược, bao gồm ba phần chính: phần header, phần chứa dữ liệu thực tế và phần byte dư thừa ở cuối (bao gồm hdrlen cộng với initlen thêm vào một byte). Điều này giúp tối ưu hóa việc quản lý bộ nhớ và đảm bảo rằng tất cả các thành phần cần thiết đều nằm trong cùng một vùng nhớ đã được phân bổ trước đó.
  • Dữ liệu chuỗi sds sau khi được khởi tạo sẽ được thêm vào một ký tự kết thúc NULL ở vị trí cuối cùng (s[initlen] = '\0'). Điều này đảm bảo rằng chuỗi có thể được xử lý đúng cách bởi các hàm xử lý chuỗi trong hệ thống. Ký tự NULL đóng vai trò như một dấu hiệu để xác định điểm kết thúc của chuỗicá cược bóng đá, giúp tránh những lỗi không mong muốn khi đọc hoặc thao tác trên dữ liệu.

Khi làm việc với sdsfreetỉ lệ cược, điều quan trọng cần lưu ý là bộ nhớ phải được giải phóng hoàn toàn. Do đó, bạn cần tính toán và xác định vị trí con trỏ bắt đầu của phần header, sau đó truyền con trỏ này vào hàm s_free. Con trỏ này chính là địa chỉ mà hàm s_malloc trả về trong quá trình gọi đế Hãy đảm bảo rằng bạn đã xử lý cẩn thận từng bước để tránh các lỗi liên quan đến quản lý bộ nhớ.

Thao tác nối (append) của sds

								
									
										sds
									 sdscatlen
									(
									sds
									 s
									,
									 const
									 void
									 *
									t
									,
									 size_t
									 len
									)
									 {
									
    size_t
									 curlen
									 =
									 sdslen
									(
									s
									);
									

    s
									 =
									 sdsMakeRoomFor
									(
									s
									,
									len
									);
									
    if
									 (
									s
									 ==
									 NULL
									)
									 return
									 NULL
									;
									
    memcpy
									(
									s
									+
									curlen
									,
									 t
									,
									 len
									);
									
    sdssetlen
									(
									s
									,
									 curlen
									+
									len
									);
									
    s
									[
									curlen
									+
									len
									]
									 =
									 '\0'
									;
									
    return
									 s
									;
									
}
									

sds
									 sdscat
									(
									sds
									 s
									,
									 const
									 char
									 *
									t
									)
									 {
									
    return
									 sdscatlen
									(
									s
									,
									 t
									,
									 strlen
									(
									t
									));
									
}
									

sds
									 sdscatsds
									(
									sds
									 s
									,
									 const
									 sds
									 t
									)
									 {
									
    return
									 sdscatlen
									(
									s
									,
									 t
									,
									 sdslen
									(
									t
									));
									
}
									

sds
									 sdsMakeRoomFor
									(
									sds
									 s
									,
									 size_t
									 addlen
									)
									 {
									
    void
									 *
									sh
									,
									 *
									newsh
									;
									
    size_t
									 avail
									 =
									 sdsavail
									(
									s
									);
									
    size_t
									 len
									,
									 newlen
									;
									
    char
									 type
									,
									 oldtype
									 =
									 s
									[
									-
									1
									]
									 &
									 SDS_TYPE_MASK
									;
									
    int
									 hdrlen
									;
									

    /* Return ASAP if there is enough space left. */
									
    if
									 (
									avail
									 >=
									 addlen
									)
									 return
									 s
									;
									

    len
									 =
									 sdslen
									(
									s
									);
									
    sh
									 =
									 (
									char
									*
									)
									s
									-
									sdsHdrSize
									(
									oldtype
									);
									
    newlen
									 =
									 (
									len
									+
									addlen
									);
									
    if
									 (
									newlen
									 <
									 SDS_MAX_PREALLOC
									)
									
        newlen
									 *=
									 2
									;
									
    else
									
        newlen
									 +=
									 SDS_MAX_PREALLOC
									;
									

    type
									 =
									 sdsReqType
									(
									newlen
									);
									

    /* Don't use type 5: the user is appending to the string and type 5 is
     * not able to remember empty spacecá cược bóng đá, so sdsMakeRoomFor() must be called
     * at every appending operation. */
    if
									 (
									type
									 ==
									 SDS_TYPE_5
									)
									 type
									 =
									 SDS_TYPE_8
									;
									

    hdrlen
									 =
									 sdsHdrSize
									(
									type
									);
									
    if
									 (
									oldtype
									==
									type
									)
									 {
									
        newsh
									 =
									 s_realloc
									(
									sh
									,
									 hdrlen
									+
									newlen
									+
									1
									);
									
        if
									 (
									newsh
									 ==
									 NULL
									)
									 return
									 NULL
									;
									
        s
									 =
									 (
									char
									*
									)
									newsh
									+
									hdrlen
									;
									
    }
									 else
									 {
									
        /* Since the header size changescá cược bóng đá, need to move the string forward,
         * and can't use realloc */
        newsh
									 =
									 s_malloc
									(
									hdrlen
									+
									newlen
									+
									1
									);
									
        if
									 (
									newsh
									 ==
									 NULL
									)
									 return
									 NULL
									;
									
        memcpy
									((
									char
									*
									)
									newsh
									+
									hdrlen
									,
									 s
									,
									 len
									+
									1
									);
									
        s_free
									(
									sh
									);
									
        s
									 =
									 (
									char
									*
									)
									newsh
									+
									hdrlen
									;
									
        s
									[
									-
									1
									]
									 =
									 type
									;
									
        sdssetlen
									(
									s
									,
									 len
									);
									
    }
									
    sdssetalloc
									(
									s
									,
									 newlen
									);
									
    return
									 s
									;
									
}
									

								

Hàm sdscatlen sẽ thêm một chuỗi dữ liệu nhị phân có độ dài len từ con trỏ t vào cuối chuỗi sds của biến s. Trong ví dụ về lệnh append được trình bày ở phần đầucá cược bóng đá, thực chất bên trong đã sử dụng hàm sdscatlen để thực hiện chức năng này. Điều này cho thấy sdscatlen đóng vai trò quan trọng trong việc xử lý chuỗi và quản lý dữ liệu nhị phân trong các ứng dụng thực tế.

Trong quá trình thực hiện của sdscatlencá cược bóng đá, trước tiên hàm sdsMakeRoomFor sẽ được gọi để đảm bảo chuỗi s có đủ không gian để thêm dữ liệu có độ dài bằng len. Hàm sdsMakeRoomFor có thể phân bổ bộ nhớ mới hoặc cũng có thể không cần làm điều đó nếu đã có đủ dung lượng sẵn có trong bộ nhớ hiện tại.

Hàm MakeRoomFor đóng vai trò rất quan trọng trong việc triển khai của sds (simple dynamic string). Khi tìm hiểu mã nguồn thực hiện của hàm này123win+club, chúng ta cần đặc biệt chú ý đến một số yếu tố quan trọng. Trước tiên, hàm này được thiết kế để đảm bảo rằng chuỗi có đủ không gian trống để thêm các ký tự mới mà không cần phải cấp phát bộ nhớ mới ngay lập tức. Điều này giúp tối ưu hóa hiệu suất và giảm thiểu thời gian chờ đợi khi xử lý chuỗi lớn. Bên cạnh đó, trong quá trình triển khai, hàm MakeRoomFor cũng cần kiểm tra xem vùng nhớ hiện tại của chuỗi đã đủ chỗ hay chưa. Nếu không đủ, nó sẽ tính toán kích thước phù hợp để mở rộng vùng nhớ sao cho vừa đủ cho các yêu cầu tiếp theo. Điều này đòi hỏi sự cẩn thận trong việc quản lý bộ nhớ và tránh lãng phí tài nguyên hệ thống. Ngoài ra, việc sử dụng các biến tạm thời như vị trí hiện tại hoặc kích thước tối đa của chuỗi cũng là một phần quan trọng trong việc triển khai. Những thao tác này giúp đảm bảo rằng dữ liệu được xử lý chính xác và hiệu quả, đồng thời duy trì tính toàn vẹn của chuỗi trong suốt quá trình hoạt động.

  • Nếu không gian trống trong chuỗi gốc đủ lớn để chứa thêm dữ liệu (avail >= addlen)tỉ lệ cược, thì nó sẽ không thực hiện bất kỳ thao tác nào và trả về ngay lập tức.
  • Nếu cần phân bổ không gian123win+club, nó sẽ cấp phát nhiều hơn so với yêu cầu thực tế một chút để đề phòng trường hợp có thêm dữ liệu được thêm vào sau đó. Khi chuỗi đã khá dài, ít nhất nó sẽ phân bổ thêm SDT_MAX_PREALLOC byte, hằng số này trong tệp sds.h được định nghĩa là (1024 * 1024) = 1MB. Điều này giúp tối ưu hóa việc quản lý bộ nhớ và giảm thiểu các lần phân bổ lại không gian thường xuyên khi dữ liệu tiếp tục tăng lên.
  • Dựa trên không gian được phân bổ lạicá cược bóng đá, có thể cần thay đổi kiểu dữ liệu của phần header (vì trường "alloc" trong header cũ quá ngắn để biểu diễn dung lượng tăng lên). Điều này đòi hỏi phải xem xét kỹ lưỡng cách quản lý bộ nhớ hiện tại và tìm giải pháp phù hợp để đảm bảo tính toàn vẹn cũng như hiệu quả của hệ thống.
  • Nếu cần thay đổi phần header123win+club, toàn bộ không gian chuỗi (bao gồm cả phần header) sẽ phải được cấp phát lại (s_malloc), và dữ liệu cũ sẽ được sao chép sang vị trí mới. Điều này đảm bảo rằng mọi thông tin quan trọng vẫn được bảo toàn trong quá trình điều chỉnh kích thước hoặc cấu trúc của header.
  • Nếu không cần thay đổi header (header hiện tại đã đủ dùng)123win+club, bạn có thể sử dụng một phiên bản đặc biệt của hàm s_realloc để cố gắng tái phân bổ bộ nhớ ngay tại vị trí ban đầu. Cách thức thực hiện cụ thể của s_realloc phụ thuộc vào trình điều khiển bộ nhớ (allocator) mà Redis được biên dịch với nó. Khi chạy trên Linux, mặc định Redis sẽ sử dụng jemalloc là Tuy nhiên, bất kể allocator nào được chọn, ý nghĩa cơ bản của realloc đều giống nhau: nó cố gắng tái phân bổ bộ nhớ tại vị trí đã cấp phát trước đó. Nếu có đủ không gian trống trong vùng nhớ hiện tại để hoàn thành việc tái phân bổ, hàm sẽ trả về địa chỉ cũ không thay đổi. Ngược lại, nếu không đủ không gian, nó sẽ phân bổ một vùng nhớ mới và di chuyển dữ liệu từ vùng nhớ cũ sang vùng nhớ mới. Bạn có thể tham khảo thêm về cách hoạt động của realloc trong tài liệu chính thức hoặc qua các ví dụ minh họa trong tài liệu hướng dẫn của Redis. http://man.cx/realloc

Từ giao diện của hàm sdscatlen123win+club, chúng ta có thể nhận thấy một mô hình sử dụng điển hình: khi gọi hàm này, cần truyền vào một biến sds cũ và sau đó nó sẽ trả về một biến sds mới. Do cách thực hiện bên trong có thể dẫn đến sự thay đổi địa chỉ, vì vậy người gọi nên hiểu rằng sau khi thực hiện xong, biến cũ đã không còn hiệu lực nữa và phải dùng biến mới được trả về để thay thế. Không chỉ riêng hàm sdscatlen, mà các hàm khác trong thư viện sds (như sdscpy, sdstrim, sdsjoin, v.v.) cũng như một số cấu trúc dữ liệu trong Redis có khả năng mở rộng bộ nhớ tự động (như ziplist) đều tuân theo cùng một mô hình sử dụng này. Mỗi khi làm việc với các hàm hoặc cấu trúc dữ liệu này, điều quan trọng là phải luôn ghi nhớ rằng chúng có xu hướng tái tạo nội dung và thay đổi vị trí trong bộ nhớ. Điều này có nghĩa là sau khi thực hiện bất kỳ hoạt động nào, bạn cần cập nhật lại các tham chiếu liên quan đến dữ liệu để đảm bảo tính nhất quán và tránh các lỗi không mong muốn. Sự linh hoạt trong việc quản lý bộ nhớ của những công cụ này giúp tối ưu hóa hiệu suất tổng thể nhưng cũng đòi hỏi người lập trình phải cẩn thận trong quá trình triển khai ứng dụng.

Tìm hiểu sơ lược về mối quan hệ giữa sds và string

Bây giờ chúng ta hãy quay lại xem ví dụ thao tác chuỗi đã đưa ra ở phần đầu bài viết.

  • Thao tác append sử dụng sdscatlen của sds để thực hiện. Điều này đã được đề cập trước đó.
  • Hai hàm setbit và getrange đều hoạt động theo cách tương tự: chúng đầu tiên sử dụng key để lấy toàn bộ chuỗi sdstỉ lệ cược, sau đó tiến hành chọn hoặc chỉnh sửa phần cụ thể của chuỗi đó. Vì sds về cơ bản là một mảng các ký tự, nên việc thực hiện thao tác trên bất kỳ phần nào của nó đều khá đơn giản và trực quan. Điều này giúp cho việc quản lý và thao tác dữ liệu trở nên dễ dàng hơn nhiều so với các cấu trúc phức tạp khác.

Tuy nhiên123win+club, bên cạnh các thao tác cơ bản, khi string lưu trữ giá trị là một số, nó còn hỗ trợ các hoạt động như incr (tăng giá trị), decr (giảm giá trị) và nhiều tính năng khác. Điều thú vị là khi string lưu trữ giá trị dạng số, cấu trúc lưu trữ nội bộ của nó không phải vẫn là SDS nữa. Hơn thế, ngay cả các chức năng như setbit và getrange cũng sẽ có sự thay đổi trong cách thực hiện. Những chi tiết quan trọng này, chúng ta sẽ cùng tìm hiểu một cách toàn diện hơn trong bài viết tiếp theo về robj.


Bài viết gốccá cược bóng đá, xin vui lòng trích dẫn 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: /3ht626mm.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: Phân tích sâu cấu trúc dữ liệu bên trong Redis (1) —— dict
Bài sau: Phân tích sâu cấu trúc dữ liệu bên trong Redis (3) —— robj

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