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 viết thứ hai trong loạt bài nàyVSBET, 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àoVSBET, cấu trúc dữ liệu chuỗi cũng luôn là một trong những đối tượng được sử dụng phổ biến nhất. Trong Redis, sds (Simple Dynamic String) chính là định dạng chuỗi được áp dụng rộng rãi. Tên đầy đủ của nó là Simple Dynamic String, và so với các loại chuỗi khác trong các môi trường lập trình khác nhau, sds nổi bật với những đặc điểm sau: Trước hết, sds là một cấu trúc động, có khả năng tự điều chỉnh kích thước khi cần thiết mà không yêu cầu người dùng phải thực hiện nhiều thao tác phức tạp. Điều này giúp tối ưu hóa hiệu suất và giảm thiểu sự cố do lỗi quản lý bộ nhớ. Thứ hai, sds cung cấp khả năng truy cập nhanh và dễ dàng vào các phần tử bên trong chuỗi, giúp tăng tốc độ xử lý dữ liệu. Đồng thời, nó hỗ trợ các thao tác cơ bản như thêm, xóa hoặc sửa đổi nội dung một cách linh hoạt và hiệu quả. Cuối cùng, sds còn mang lại tính linh hoạt cao khi tích hợp với các chức năng nâng cao của Redis như xử lý dữ liệu nhị phân và mã hóa, nhờ đó trở thành một công cụ mạnh mẽ cho việc lưu trữ và quản lý chuỗi trong hệ thống.
Khi đọc đến đâyVSBET, 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 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 có thể đ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 chi tiết sau. Còn bây giờ, để thuận tiện cho việc thảo luận, hãy tạm thời coi string được triển khai ở tầng dưới cùng là sds. Để làm rõ hơn, sds (Simple Dynamic String) là một thư viện nội bộ của Redis dùng để quản lý chuỗi động, trong khi string là một phần của giao diện công khai mà Redis cung cấp cho người dùng. Điều thú vị là, mặc dù string được sử dụng rộng rãi trong các tác vụ hàng ngày, nhưng thực tế nó thường được chuyển tiếp trực tiếp từ các hàm xử lý bên trong của sds. Điều này tạo nên một sự đồng bộ chặt chẽ giữa hai đối tượng, giúp tối ưu hóa hiệu suất và giảm thiểu các yêu cầu bộ nhớ không cần thiết. Tuy nhiên, đừng nhầm lẫn rằng string và sds là hoàn toàn giống nhau. Mặc dù sds là nền tảng cho string, nhưng string còn bao gồm các tính năng bổ sung như mã hóa, định dạng và thậm chí cả khả năng tương thích với các giao thức mạng khác nhau. Điều này khiến string trở thành một lớp trừu tượng cao hơn so với sds, phù hợp hơn với nhu cầu đa dạng của người dùng. Vì vậy, nếu bạn thấy rằng việc sử dụng string trong Redis dường như đơn giản và mượt mà, đó là nhờ vào sự hỗ trợ mạnh mẽ từ sds ở phía sau. Tuy nhiên, hãy nhớ rằng, không phải lúc nào string cũng chỉ là sds – nó còn có thể mang theo nhiều yếu tố khác tùy thuộc vào ngữ cảnh cụ thể!
Trước khi đi sâu vào việc triển khai cụ thể của SDStỷ số bóng đá hôm nay, hãy thử đứng từ góc độ người sử dụng Redis để quan sát các hoạt động chính mà kiểu dữ liệu string hỗ trợ. Dưới đây là một ví dụ về hoạt động cụ thể: Giả sử bạn muốn lưu trữ và thao tác với một chuỗi giá trị Đầu tiên, bạn có thể dễ dàng thêm hoặc cập nhật giá trị bằng lệnh `SET`. Ví dụ, nếu bạn thực hiện lệnh `SET mykey "Hello Redis"`, Redis sẽ lưu trữ chuỗi "Hello Redis" dưới khóa "mykey". Tiếp theo, nếu bạn muốn truy xuất giá trị này, chỉ cần sử dụng lệnh `GET` như sau: `GET mykey`. Kết quả trả về sẽ là chuỗi "Hello Redis". Không chỉ dừng lại ở đó, Redis còn cung cấp nhiều tính năng mạnh mẽ khác cho kiểu dữ liệu string, chẳng hạn như tăng/giảm giá trị số (`INCR`, `DECR`) hoặc thay thế một phần của chuỗi (dựa trên vị trí ký tự). Tất cả những thao tác này đều được tối ưu hóa để đảm bảo hiệu suất cao nhất trong quá trình xử lý dữ liệu. Với những khả năng đa dạng này, Redis không chỉ đơn thuần là một công cụ lưu trữ dữ liệu mà còn là nền tảng lý tưởng cho các ứng dụng cần tốc độ và độ tin cậy cao.
Tất cả các thao tác trên đều khá đơn giảnVSBET, chúng ta sẽ giải thích sơ qua một chút:
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.
Chúng ta đều biết rằng trong ngôn ngữ lập trình CVSBET, 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ì nó được biểu diễn dưới dạng con trỏ ký tự (char *). Tuy nhiên, vì không cho phép xuất hiện byte 0 ở giữa chuỗi, nên nó 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 có nghĩa là nếu bạn muốn lưu trữ các tập tin nhị phân hoặc dữ liệu phức tạp khác, bạn cần tìm đến những phương pháp thay thế phù hợp 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 hẳn đã có người thắc mắcVSBET, liệu sds có thực sự giống với char * hay không? Trước đây chúng ta đã đề cập rằng sds và chuỗi C truyền thống có tính tương thích kiểu dữ liệu, vì vậy định nghĩa kiểu của chúng dường như 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 một chuỗi C, thì cũng có thể sử dụng sds. Tuy nhiên, sds và char * không phải là hoàn toàn giống nhau. SDS được thiết kế để xử lý dữ liệu nhị phân an toà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, không giống như chuỗi C, vốn kết thúc bằng ký tự '\0'. Do đó, sds chắc chắn cần một trường độ dài (length field). Nhưng trường này nằm ở đâu? Thực tế, bên cạnh dữ liệu, sds còn chứa một cấu trúc header ẩn đi, bao gồm các thông tin quan trọng như độ dài và các thuộc tính khác của chuỗi.
struct
__attribute__
((
__packed__
))
sdshdr5
{
unsigned
char
flags
;
/* 3 lsb of typetỷ số bóng đá hôm nay, 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 typeVSBET, 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ỷ số bóng đá hôm nay, 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ỷ số bóng đá hôm nay, 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ỷ số bóng đá hôm nay, 5 unused bits */
char
buf
[];
};
Sds (Simple Dynamic String) có tất cả 5 loại header khác nhau. Nguyên nhân có đến 5 loại header là để các chuỗi có độ dài khác nhau có thể sử dụng các kích thước header phù hợp. Điều này cho phép chuỗi ngắn sử dụng header nhỏ hơnlive casino, từ đó giúp tối ưu hóa việc quản lý bộ nhớ và giảm thiểu lượng tài nguyên tiêu tốn.
Một chuỗi sds hoàn chỉnh được cấu trúc bởi hai phần liền kề nhau trong địa chỉ bộ nhớ:
Ngoài sdshdr5 ratỷ số bóng đá hôm nay, các header khác đều chứa 3 trườ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
Chúng ta cần phân tích cẩn thận cấu trúc dữ liệu sds.
Hình ảnh phía trên là một ví dụ về cấu trúc nội bộ của SDS. Hình minh họa cho thấy cấu trúc bộ nhớ của hai chuỗi SDStỷ số bóng đá hôm nay, cụ thể là s1 và s2. Trong đó, một chuỗi sử dụng header kiểu sdshdr8, còn chuỗi kia sử dụng header kiểu sdshdr16. Mặc dù chúng sử dụng các loại header khác nhau, nhưng cả hai đều biểu diễn cùng một giá trị chuỗi có độ dài 6 ký tự: "tielei". Tiếp theo, chúng ta sẽ đi sâu vào mã nguồn để giải thích từng phần trong cấu trúc này. --- Trước tiên, hãy xem xét cách mà sdshdr8 và sdshdr16 được định nghĩa. Header sdshdr8 được sử dụng khi kích thước dữ liệu nhỏ hơn hoặc bằng 255 byte (kích thước tối đa mà một byte có thể biểu diễn), trong khi sdshdr16 phù hợp với trường hợp dữ liệu lớn hơn 255 byte. Điều này giúp tối ưu hóa việc quản lý bộ nhớ dựa trên kích thước thực tế của chuỗi. Ví dụ, đối với chuỗi "tielei" có độ dài 6, header sdshdr8 sẽ đủ khả năng lưu trữ thông tin về chiều dài và vùng dữ liệu của chuỗi. Tuy nhiên, nếu chuỗi dài hơn (ví dụ như độ dài 300), thì sdshdr16 sẽ được sử dụng để đảm bảo đủ không gian cho việc quản lý bộ nhớ. Tiếp theo, chúng ta sẽ phân tích chi tiết cách mỗi phần tử trong cấu trúc hoạt động. Đầu tiên là phần quản lý độ dài (length field), đây là nơi lưu trữ số lượng ký tự thực tế của chuỗi. Sau đó là phần không gian trống (free field), dùng để xác định số lượng byte chưa được sử dụng trong vùng bộ nhớ. Cuối cùng là phần dữ liệu thực tế (buf), nơi lưu trữ chính các ký tự của chuỗi. Nhờ có sự linh hoạt trong việc chọn loại header tùy thuộc vào kích thước chuỗi, SDS có thể tối ưu hóa hiệu suất và giảm thiểu lãng phí bộ nhớ. Điều này đặc biệt quan trọng khi ứng dụng cần xử lý nhiều chuỗi với kích thước khác nhau.
Trong trường hợp của sds (Simple Dynamic String)live casino, các con trỏ ký tự như s1 và s2 thực chất là những tham chiếu đến vị trí bắt đầu của dữ liệu thực tế (mảng ký tự). Trong khi đó, phần header lại nằm ở hướng địa chỉ bộ nhớ thấp hơn. Trong tệp sds.h, có một số macro được định nghĩa để hỗ trợ việc phân tích header này. Những macro này đóng vai trò quan trọng trong việc giải mã thông tin liên quan đến cấu trúc sds, giúp người dùng dễ dàng tương tác với dữ liệu bên trong mà không cần phải lo lắng về chi tiết kỹ thuật bên dưới.
#define SDS_TYPE_MASK 7
#define SDS_TYPE_BITS 3
#define SDS_HDR_VAR(Ttỷ số bóng đá hôm nay,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ỷ số bóng đá hôm nay, SDS_HDR được sử dụng để lấy vị 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ẽ là con trỏ header của s2. Điều này cho phép người dùng dễ dàng thao tác và truy cập thông tin header một cách hiệu quả trong các thao tác xử lý chuỗi.
Trước khi sử dụng SDS_HDRVSBET, chúng ta cần hiểu rõ đó là loại header nào để xác định tham số đầu tiên mà SDS_HDR sẽ nhận. Cách để lấy kiểu header từ con trỏ chuỗi sds là dịch chuyển về hướng địa chỉ thấp hơn 1 byte để lấy giá trị của trường flags. Ví dụ, s1[-1] và s2[-1] sẽ lần lượt trả về giá trị flags của s1 và s2. Sau đó, chúng ta lấy 3 bit thấp nhất của flags để xác định loại header. Điều này giúp đảm bảo rằng các tham số được truyền đúng cách và quá trình xử lý tiếp theo có thể diễn ra suôn sẻ.
Với con trỏ headertỷ số bóng đá hôm nay, chúng ta có thể nhanh chóng xác định các trường len và alloc:
Trong các định nghĩa kiểu header kháctỷ số bóng đá hôm nay, còn có một số điểm cần chú ý:
Cho đến thời điểm nàylive casino, chúng ta đã có thể rõ ràng nhận thấy rằng: phần header của chuỗi sds thực tế ẩn mình ở phía trước dữ liệu chuỗi thực sự (hướng về vù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, nó giúp tối ưu hóa việc truy xuất thông tin cơ bản của chuỗi mà không cần duyệt qua toàn bộ nội dung. Điều này đồng nghĩa với việc tiết kiệm được rất nhiều thời gian và tài nguyên hệ thống. Thứ hai, cách sắp xếp này cho phép dễ dàng điều chỉnh độ dài của chuỗi mà không cần phải di chuyển toàn bộ dữ liệu liên quan, nhờ đó tăng cường hiệu quả quản lý bộ nhớ. Cuối cùng, việc đặt header ở vị trí dễ tiếp cận giúp các nhà phát triển dễ dàng kiểm soát và thao tác trên các thuộc tính của chuỗi như độ dài hoặc dung lượng mà không cần viết thêm quá nhiều mã lệnh phức tạp.
Khi đã hiểu rõ cấu trúc dữ liệu sdslive casino, các hàm hoạt động cụ thể của nó sẽ dễ hiểu hơn.
Ở đâ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 đólive casino, 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 với SDS_TYPE_MASK để xác định kiểu header; sau đó, dựa trên các loại header khác nhau, nó gọi hàm SDS_HDR để nhận giá trị con trỏ bắt đầu của header, từ đó có thể lấy được trường len. Quy trình này không chỉ cho thấy cách thức tối ưu hóa trong việc quản lý bộ nhớ mà còn thể hiện sự tinh vi trong việc xử lý cấu trúc dữ liệu của ngôn ngữ lập trình, giúp tăng hiệu suất và giảm thiểu lỗi trong quá trình vận hành hệ thống.
Qua mã nguồn của sdsReqTypeVSBET, dễ dàng nhận thấy:
Về phần mã thực hiện của sdsReqTypeVSBET, từ phiên bản 3.2.0 trở về trước, nó luôn gặp phải vấn đề liên quan đến giá trị giới hạn độ dài. Mãi cho đến phiên bản nhánh 3.2 gần đây, vấn đề này mới được giải quyết. Trong suốt thời gian qua, các lập trình viên đã nỗ lực không ngừng để khắc phục lỗi và đảm bảo tính ổn định cho tham số này trong mọi trường hợp sử dụng. commit 6032340 Đã sửa chữa.
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
]));
}
sdsnewlen sẽ tạo ra một chuỗi sds có độ dài là initlen và sử dụng mảng ký tự được chỉ định bởi init (dữ liệu nhị phân bất kỳ) để khởi tạo dữ liệu. Nếu init là NULLlive casino, thì dữ liệu sẽ được khởi tạo bằng toàn bộ các giá trị 0. Khi thực hiện điều này, chúng ta cần đặc biệt chú ý đến: - Việc đảm bảo rằng kích thước của dữ liệu được truyền vào phù hợp với mục đích sử dụng. - Kiểm tra kỹ lưỡng tham số đầu vào để tránh lỗi khi init là NULL. - Hiệu suất khi xử lý khối dữ liệu lớn và cách tối ưu hóa bộ nhớ. - Xác định rõ ràng việc quản lý bộ nhớ cho chuỗi sds sau khi nó được tạo ra. Tất cả những yếu tố này đều đóng vai trò quan trọng trong việc duy trì tính ổn định và hiệu quả của mã nguồn.
Khi làm việc với sdsfreetỷ số bóng đá hôm nay, điều quan trọng cần lưu ý là bạn phải giải phóng toàn bộ bộ nhớ, vì vậy trước tiên cần tính toán chính xác vị trí con trỏ bắt đầu của header và truyền nó vào hàm s_free. Chính con trỏ này cũng chính là địa chỉ được trả về từ hàm s_malloc khi gọ Điều này rất quan trọng để đảm bảo rằng toàn bộ vùng bộ nhớ đã được cấp phát sẽ được giải phóng một cách hiệu quả và an toàn, tránh tình trạng rò rỉ bộ nhớ hoặc các vấn đề không mong muốn khác.
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 spaceVSBET, 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 changestỷ số bóng đá hôm nay, 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ẽ nối một chuỗi dữ liệu nhị phân có độ dài len tại vị trí được chỉ bởi biến t vào cuối chuỗi sds của biến s. Điều thú vị là lệnh append được trình bày ở phần đầu bài viết về string cũng thực hiện chức năng tương tự bằng cách gọi hàm sdscatlen bên trong để hoàn thành nhiệm vụ này. Ngoài ralive casino, việc sử dụng sdscatlen không chỉ giới hạn trong các thao tác đơn giản như vậy. Nó còn có thể kết hợp với nhiều hàm khác nhau để xử lý các chuỗi phức tạp hơn theo cách linh hoạt và hiệu quả. Từ đó, giúp người lập trình có thêm công cụ mạnh mẽ để quản lý dữ liệu nhị phân một cách tối ưu trong ứng dụng của mình.
Trong quá trình triển khai của sdscatlenlive casino, 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. Tùy thuộc vào tình huống cụ thể, việc gọi sdsMakeRoomFor có thể dẫn đến việc cấp phát bộ nhớ mới hoặc cũng có thể không cần thực hiện điều đó nếu đã có đủ không gian sẵn có trong bộ nhớ hiện tại. Hàm sdsMakeRoomFor luôn tính toán cẩn thận trạng thái hiện tại của chuỗi và đưa ra quyết định tối ưu nhất về việc có nên mở rộng vùng nhớ hay không. Điều này giúp tối đa hóa hiệu suất khi làm việc với các chuỗi lớn và giảm thiểu nguy cơ lãng phí tài nguyên hệ thống.
Hàm MakeRoomFor đóng vai trò rất quan trọng trong việc triển khai của sds (simple dynamic string). Khi xem xét mã nguồn thực hiện của nólive casino, có một số điểm đáng chú ý mà chúng ta cần đặc biệt quan tâm: Đầu 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 thực hiện việc cấp phát bộ nhớ lại quá thường xuyên. Điều này giúp tối ưu hóa hiệu suất, đặc biệt khi các thao tác chỉnh sửa chuỗi xảy ra liên tục. Thứ hai, phần cốt lõi của hàm này là kiểm tra xem còn bao nhiêu byte rỗng trong vùng bộ nhớ hiện tại. Nếu không gian không đủ, hàm sẽ thực hiện việc tăng kích thước của chuỗi bằng cách tạo ra một bản sao lớn hơn và di chuyển dữ liệu từ bản gốc sang bản sao mới. Thêm vào đó, MakeRoomFor cũng xử lý các trường hợp đặc biệt như khi chuỗi chưa được khởi tạo hoặc khi yêu cầu thêm không gian vượt quá giới hạn tối đa cho phép. Việc hiểu rõ cách hoạt động của hàm này sẽ giúp lập trình viên tận dụng tốt các tính năng của sds trong ứng dụng của mình.
Từ giao diện của hàm sdscatlenlive casino, chúng ta có thể nhận ra một mô hình sử dụng đặc trưng: khi gọi hàm này, bạn 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ỉ, nên sau khi hoàn thành việc gọi hàm, biến cũ trước đó sẽ trở nên không còn hiệu lực nữa, và tất cả các tham chiếu liên quan đều nên được thay thế bằng biến mới được trả về. Không chỉ riêng hàm sdscatlen, mà những hàm khác trong sds như sdscpy, sdstrim, sdsjoin, hay thậm chí các cấu trúc dữ liệu trong Redis có khả năng mở rộng bộ nhớ tự động như ziplist, cũng đều tuân theo mô hình sử dụng tương tự. Điều này cho phép tối ưu hóa tài nguyên và đảm bảo tính nhất quán trong quá trình làm việc với chuỗi dữ liệu trong hệ thống.
Bây giờ chúng ta hãy quay lại xem ví dụ về thao tác chuỗi đã cho ở đầu bài viết.
Tuy nhiênlive casino, bên cạnh việc hỗ trợ các thao tác cơ bản, khi giá trị được lưu trữ trong chuỗi (string) là một số, nó còn có thể thực hiện các hoạt động như incr (tăng giá trị), decr (giảm giá trị). Vậy, khi chuỗi lưu trữ giá trị dạng số, cấu trúc lưu trữ nội bộ của nó có phải vẫn là sds không? Thực tế thì không. Hơn nữa, trong trường hợp này, cách thức hoạt động của setbit và getrange cũng sẽ có sự khác biệt. Những chi tiết thú vị này, chúng ta sẽ cùng tìm hiểu sâu hơn khi thảo luận về robj ở bài viết tiếp theo.