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 này là phần thứ bả Trong nội dung hôm nay789 Club, chúng ta sẽ cùng đi sâu tìm hiểu về một cấu trúc dữ liệu nội bộ của Redis, cụ thể là intset.
Trong Redismua thẻ trực tuyến, việc sử dụng intset được thiết kế để hỗ trợ thực hiện cấu trúc dữ liệu set (tập hợp) mà Redis cung cấp cho người dùng bên ngoài. Cấu trúc set trong Redis có đặc điểm tương tự như khái niệm tập hợp trong toán học, tức là các phần tử không theo thứ tự và không được phép trùng lặp. Ngoài ra, set trong Redis còn có thể thực hiện các hoạt động cơ bản của tập hợp như hợp, giao và hiệu. Tương tự như các cấu trúc dữ liệu khác mà Redis cung cấp, cách thức thực hiện bên dưới của set sẽ thay đổi tùy thuộc vào kiểu dữ liệu của các phần tử và số lượng phần tử được thêm vào. Nói chung, khi tất cả các phần tử trong set đều là số nguyên và số lượng phần tử ít, Redis sẽ sử dụng intset làm cấu trúc dữ liệu cơ bản. Ngược lại, nếu điều kiện này không thỏa mãn, set sẽ sử dụng **mảng liên kết (linked list)** hoặc một cấu trúc dữ liệu khác phù hợp hơn để lưu trữ thông tin một cách hiệu quả. dict Được sử dụng như cấu trúc dữ liệu cơ sở.
Trong bài viết này789 Club, chúng ta sẽ giới thiệu đại khái thành ba phần:
Trong quá trình thảo luậnmua thẻ trực tuyến, chúng ta sẽ cùng tìm hiểu thêm về một cấu hình Redis (ở phần ADVANCED CONFIG trong tệp redis.conf):
set-max-intset-entries 512
Lưu ý: Việc phân tích mã nguồn trong bài viết này dựa trên nhánh 3.2 của mã nguồn Redis.
intsetmua thẻ trực tuyến, như tên gọi của nó, là một tập hợp được tạo thành từ các số nguyên. Thực tế, intset không chỉ là một tập hợp số nguyên mà còn là một tập hợp có thứ tự, điều này giúp thực hiện tìm kiếm nhị phân một cách hiệu quả để xác định nhanh chóng một phần tử có thuộc về tập hợp này hay không. Về mặt quản lý bộ nhớ, intset... ziplist Có một số điểm tương đồngxem ngoại hạng anh, đó là sử dụng một vùng nhớ liên tục và nguyên khối, đồng thời đối với số nguyên lớn và số nguyên nhỏ (theo giá trị tuyệt đối), nó áp dụng các phương thức mã hóa khác nhau để tối ưu hóa việc sử dụng bộ nhớ một cách hiệu quả nhất có thể. Ngoài ra, hệ thống còn được thiết kế để tự động điều chỉnh sao cho việc quản lý tài nguyên luôn ở trạng thái tối ưu, đảm bảo không có sự lãng phí đáng kể nào trong quá trình xử lý.
Định nghĩa cấu trúc dữ liệu intset như sau (từ intset.h và intset.c):
typedef
struct
intset
{
uint32_t
encoding
;
uint32_t
length
;
int8_t
contents
[];
}
intset
;
#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_ENC_INT64 (sizeof(int64_t))
Ý nghĩa của các trường dữ liệu như sau:
encoding
Trong quá trình mã hóa dữ liệuxem ngoại hạng anh, intset xác định kích thước bộ nhớ cần thiết để lưu trữ mỗi phần tử thông qua các giá trị mã hóa khác nhau. Có ba khả năng chính cho việc chọn kích thước này: INTSET_ENC_INT16 nghĩa là mỗi phần tử sẽ được lưu trữ trong 2 byte, INTSET_ENC_INT32 dùng 4 byte cho mỗi phần tử, và INTSET_ENC_INT64 sử dụng 8 byte cho mỗi phần tử. Do đó, các số nguyên được lưu trữ trong intset sẽ không vượt quá 64 bit. Điều này giúp tối ưu hóa không gian lưu trữ dựa trên phạm vi giá trị mà tập hợp cần quản lý, đồng thời đảm bảo hiệu suất truy xuất nhanh chóng khi xử lý dữ liệu số nguyên.
length
: Biểu thị số lượng phần tử
encoding
Một quá trình đọc: Quá trình thứ
length
Hai trường này tạo nên phần đầu (header) của intset.
contents
: Là một mảng linh hoạt (
flexible array member
Bạn có thể thấy rằng ngay sau phần header của intsetmua thẻ trực tuyến, các phần tử dữ liệu sẽ được nối tiếp. Tổng độ dài của mảng này (tức tổng số byte) bằng với...
encoding * length
). Mảng linh hoạt thường xuất hiện trong định nghĩa cấu trúc dữ liệu của Redis (ví dụ
sds
,
quicklist
,
skiplist
)xem ngoại hạng anh, dùng để biểu diễn một khoảng offset.
contents
Cần phải cấp phát bộ nhớ riêng cho nómua thẻ trực tuyến, phần bộ nhớ này không nằm trong cấu trúc intset.Điều cần lưu ý là intset có thể thay đổi mã hóa dữ liệu khi thêm các phần tử mới:
Hình dưới đây đưa ra một ví dụ cụ thể về việc thêm dữ liệu (click để xem hình lớn).
Trong hình trên:
encoding
= 2,
length
= 0。
encoding
Không thay đổimua thẻ trực tuyến, giá trị vẫn là 2.
encoding
Cần nâng cấp lên INTSET_ENC_INT32 (giá trị 4)mua thẻ trực tuyến, tức là sử dụng 4 byte để biểu diễn một phần tử.
encoding
Các byte 4 đầu tiên của trường này cần được giải thích là giá trị 0x00000004mua thẻ trực tuyến, trong khi dữ liệu thứ năm nên được hiểu là 0x000186A0, tương đương với số thập phân 100000. Điều thú vị là, việc giải mã các giá trị này thường liên quan đến cách hệ thống quản lý bộ nhớ và dữ liệu ở cấp độ thấp, cho phép các nhà phát triển tối ưu hóa hiệu suất của chương trình hoặc giao tiếp giữa các module khác nhau một cách chính xác.So với intset ziplist Đối chiếu với
len
)mua thẻ trực tuyến, trong khi intset chỉ có thể sử dụng mã hóa thống nhất (
encoding
)。
Để hiểu rõ một số chi tiết thực hiện của intsetxem ngoại hạng anh, bạn chỉ cần tập trung vào hai thao tác quan trọng nhất của nó: tìm kiếm (search) và...
intsetFind
) và thêm (.
intsetAdd
Mục đích chính của mã nguồn bên dưới (từ intset.c) là:
intsetFind
Về mã nguồn trênmua thẻ trực tuyến, chúng ta cần chú ý những điểm sau:
uint8_t
intsetFind
(
intset
*
is
,
int64_t
value
)
{
uint8_t
valenc
=
_intsetValueEncoding
(
value
);
return
valenc
<=
intrev32ifbe
(
is
->
encoding
)
&&
intsetSearch
(
is
,
value
,
NULL
);
}
static
uint8_t
intsetSearch
(
intset
*
is
,
int64_t
value
,
uint32_t
*
pos
)
{
int
min
=
0
,
max
=
intrev32ifbe
(
is
->
length
)
-
1
,
mid
=
-
1
;
int64_t
cur
=
-
1
;
/* The value can never be found when the set is empty */
if
(
intrev32ifbe
(
is
->
length
)
==
0
)
{
if
(
pos
)
*
pos
=
0
;
return
0
;
}
else
{
/* Check for the case where we know we cannot find the valuemua thẻ trực tuyến,
* but do know the insert position. */
if
(
value
>
_intsetGet
(
is
,
intrev32ifbe
(
is
->
length
)
-
1
))
{
if
(
pos
)
*
pos
=
intrev32ifbe
(
is
->
length
);
return
0
;
}
else
if
(
value
<
_intsetGet
(
is
,
0
))
{
if
(
pos
)
*
pos
=
0
;
return
0
;
}
}
while
(
max
>=
min
)
{
mid
=
((
unsigned
int
)
min
+
(
unsigned
int
)
max
)
>>
1
;
cur
=
_intsetGet
(
is
,
mid
);
if
(
value
>
cur
)
{
min
=
mid
+
1
;
}
else
if
(
value
<
cur
)
{
max
=
mid
-
1
;
}
else
{
break
;
}
}
if
(
value
==
cur
)
{
if
(
pos
)
*
pos
=
mid
;
return
1
;
}
else
{
if
(
pos
)
*
pos
=
min
;
return
0
;
}
}
Tìm kiếm phần tử chỉ định trong intset đã chỉ định
intsetFind
Tìm thấy trả về 1xem ngoại hạng anh, không tìm thấy trả về 0.
value
Hàm này sẽ tính toán mã hóa dữ liệu tương ứng (tức là nó nên sử dụng bao nhiêu byte để lưu trữ) dựa trên phần tử cần tìm
_intsetValueEncoding
Thực hiện thuật toán tìm kiếm nhị phân.
value
xem ngoại hạng anh, nếu tìm thấy thì trả về 1 và tham số
value
Nếu dữ liệu cần thiết phải mã hóa lớn hơn định dạng mã hóa hiện tại của intsetmua thẻ trực tuyến, điều đó có nghĩa là nó nằm ngoài phạm vi lưu trữ hiện tại của intset (hoặc quá lớn hoặc quá nhỏ), do đó hàm sẽ trả về 0 ngay lập tức. Ngược lại, nếu không thuộc trường hợp đó, nó sẽ tiếp tục thực hiện các lệnh gọi tiếp theo.
intsetSearch
Trỏ đến vị trí có thể chèn phần tử này.
intsetSearch
Tìm thấy trả về 1789 Club, không tìm thấy trả về 0.
value
Đây là một thực hiện của thuật toán tìm kiếm nhị phânmua thẻ trực tuyến, nó đại khái chia thành ba phần:
pos
Xử lý đặc biệt trường hợp intset rỗng.
pos
Xử lý đặc biệt hai trường hợp biên giới: khi phần tử cần tìm
intsetSearch
Thật sự thực hiện thuật toán tìm kiếm nhị phân. Lưu ý: Nếu cuối cùng không tìm thấymua thẻ trực tuyến, vị trí chèn sẽ ở
value
Khi giá trị lớn hơn phần tử cuối cùng hoặc nhỏ hơn phần tử đầu tiên. Thực tế789 Club, việc xử lý đặc biệt cho hai phần này trong tìm kiếm nhị phân không phải lúc nào cũng cần thiết, nhưng ở đây nó cung cấp khả năng loại trừ nhanh chóng trong những trường hợp ngoại lệ. Tuy nhiên, trong một số trường hợp cụ thể, việc kiểm tra trước có thể giúp tiết kiệm đáng kể thời gian tính toán. Thay vì tiến hành tìm kiếm theo cách thông thường, chúng ta có thể xác định ngay từ đầu rằng nếu giá trị nằm ngoài phạm vi của dãy số, thì không cần tiếp tục thực hiện các bước tiếp theo. Điều này đặc biệt hữu ích khi làm việc với các tập dữ liệu lớn, nơi mà mỗi lần so sánh đều có thể ảnh hưởng đến hiệu suất tổng thể. Hơn nữa, việc thêm điều kiện kiểm tra ban đầu này không chỉ tăng tốc độ tìm kiếm mà còn làm cho mã nguồn dễ đọc và bảo trì hơn. Bằng cách giảm thiểu số lượng thao tác cần thực hiện trong quá trình tìm kiếm, chúng ta có thể đạt được hiệu quả cao hơn và tránh các lỗi tiềm ẩn phát sinh từ việc xử lý không đúng đắn trong các trường hợp biên giới.
min
Độ phức tạp thời gian tổng thể của thuật toán tìm kiếm này là O(log n).
intrev32ifbe
Điều này được thực hiện để thực hiện việc chuyển đổi giữa định dạng dữ liệu little-endian và big-endian khi cần thiết. Trước đóxem ngoại hạng anh, chúng ta đã biết rằng các phần tử trong intset được lưu trữ theo định dạng little-endian, do đó, khi chạy trên một hệ thống có kiến trúc big-endian, mã nguồn ở đây sẽ...
intrev32ifbe
. Nếu
tất cả đều được lưu trữ dưới dạng chuỗi; bắt đầu từ trường hợp thứ tư bên dưới789 Club,
intsetAdd
Về mã nguồn trênxem ngoại hạng anh, chúng ta cần chú ý những điểm sau:
intset
*
intsetAdd
(
intset
*
is
,
int64_t
value
,
uint8_t
*
success
)
{
uint8_t
valenc
=
_intsetValueEncoding
(
value
);
uint32_t
pos
;
if
(
success
)
*
success
=
1
;
/* Upgrade encoding if necessary. If we need to upgrade789 Club, we know that
* this value should be either appended (if &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; 0) or prepended (if &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt; 0),
* because it lies outside the range of existing values. */
if
(
valenc
>
intrev32ifbe
(
is
->
encoding
))
{
/* This always succeedsxem ngoại hạng anh, so we don't need to curry *success. */
return
intsetUpgradeAndAdd
(
is
,
value
);
}
else
{
/* Abort if the value is already present in the set.
* This call will populate "pos" with the right position to insert
* the value when it cannot be found. */
if
(
intsetSearch
(
is
,
value
,
&
pos
))
{
if
(
success
)
*
success
=
0
;
return
is
;
}
is
=
intsetResize
(
is
,
intrev32ifbe
(
is
->
length
)
+
1
);
if
(
pos
<
intrev32ifbe
(
is
->
length
))
intsetMoveTail
(
is
,
pos
,
pos
+
1
);
}
_intsetSet
(
is
,
pos
,
value
);
is
->
length
=
intrev32ifbe
(
intrev32ifbe
(
is
->
length
)
+
1
);
return
is
;
}
Tìm kiếm phần tử chỉ định trong intset đã chỉ định
intsetAdd
Được đặt thành 0; nếu
value
Không tồn tại trước đó trong intsetmua thẻ trực tuyến, thì sẽ
value
Chèn vào vị trí thích hợpmua thẻ trực tuyến, lúc này tham số
success
Được đặt thành 0.
value
Nếu phần tử cần thêm
value
Mã hóa dữ liệu cần thiết lớn hơn mã hóa hiện tại của intset789 Club, thì sẽ gọi
success
Cập nhật mã hóa của intset trước khi chèn
value
Nếu không tìm thấy thì gọi
intsetUpgradeAndAdd
(xem
value
。
intsetSearch
Được gọi
intsetResize
Việc mở rộng bộ nhớ cho intset là cần thiết để nó có thể chứa các phần tử mới được thêm vào. Do intset là một khối không gian liên tụcxem ngoại hạng anh, nên thao tác này sẽ dẫn đến việc di chuyển hoặc tái phân bổ bộ nhớ. Quá trình này đòi hỏi phải tạo ra một vùng nhớ mới lớn hơn, sao chép toàn bộ dữ liệu hiện có sang vị trí mới và sau đó giải phóng vùng nhớ cũ. Điều này đảm bảo rằng intset luôn duy trì tính liên tục trong cấu trúc lưu trữ của mình, đồng thời tiếp tục hoạt động hiệu quả khi nhận thêm các giá trị mới.
realloc
Đảm bảo rằng trong quá trình sao chép không xảy ra chồng chéo hoặc ghi đè dữ liệumua thẻ trực tuyến, xem chi tiết
http://man.cx/realloc
Việc triển khai cũng sẽ gọi
intsetMoveTail
Bạn có thể di chuyển tất cả các phần tử sau vị trí chèn về phía sau một vị trí789 Club, điều này cũng liên quan đến việc sao chép dữ liệu. Điều đáng lưu ý là,
intsetMoveTail
Để mở rộng bộ nhớ. Trong quá trình nâng cấp mã hóaxem ngoại hạng anh,
memmove
Việc triển khai sẽ lấy ra từng phần tử từ intset cũ789 Club, sau đó ghi lại bằng mã hóa mới vào vị trí mới.
memmove
Lưu ý một chút
http://man.cx/memmove
。
intsetUpgradeAndAdd
Giá trị trả vềxem ngoại hạng anh, nó trả về một con trỏ intset mới. Nó có thể giống hoặc khác với con trỏ intset truyền vào
intsetResize
Khi gặp các tình huống tương tự.
intsetUpgradeAndAdd
Rõ ràng789 Club, thuật toán này
intsetAdd
Set của Redis
is
Tương tự như nhau789 Club, nhưng cũng có thể khác biệt. Người gọi cần sử dụng intset mới được trả về ở đây để thay thế cho intset cũ mà họ đã truyền vào trước đó. Kiểu mẫu sử dụng giao diện này khá phổ biến trong mã nguồn Redis, ví dụ như khi chúng ta đã từng đề cập trước đây về việc...
sds
Một quá trình đọc: Quá trình thứ
ziplist
Ví dụ lệnh set
intsetAdd
Dùng để thêm phần tử vào tập hợp
Để hiểu rõ hơn về cấu trúc dữ liệu set mà Redis cung cấpmua thẻ trực tuyến, chúng ta hãy cùng tìm hiểu một số lệnh quan trọng của set. Dưới đây là một số ví dụ về các lệnh này: - **SADD**: Thêm một hoặc nhiều thành phần vào tập hợp. Ví dụ: SADD myset "apple" "banana" sẽ thêm hai phần tử "apple" và "banana" vào tập hợp có tên myset. - **SMEMBERS**: Trả về tất cả các thành viên trong tập hợp. Nếu sử dụng lệnh SMEMBERS myset, Redis sẽ hiển thị toàn bộ các phần tử hiện có trong tập hợp myset. - **SISMEMBER**: Kiểm tra xem một phần tử cụ thể có tồn tại trong tập hợp hay không. Ví dụ, SISMEMBER myset "apple" sẽ trả về giá trị 1 (true) nếu "apple" thuộc về myset, ngược lại sẽ trả về 0 (false). - **SREM**: Loại bỏ một hoặc nhiều thành viên khỏi tập hợp. Lệnh SREM myset "banana" sẽ xóa phần tử "banana" ra khỏi tập hợp myset. - **SCARD**: Trả về số lượng phần tử hiện tại trong tập hợp. Sử dụng SCARD myset sẽ cho biết kích thước của tập hợp myset. Những lệnh trên là một phần quan trọng giúp bạn thao tác với dữ liệu trong set của Redis.
Dùng để tính giaoxem ngoại hạng anh, hợp và hiệu của tập hợp.
sadd
Kết cấu dữ liệu cơ bản sẽ thay đổi như sau:
s1
Một quá trình đọc: Quá trình thứ
s2
Sau khi hoàn thành
sismember
mua thẻ trực tuyến, vì các phần tử thêm vào đều là số nguyên nhỏ, nên
sinter
,
sunion
Một quá trình đọc: Quá trình thứ
sdiff
Dưới đáy là một intset789 Club, mã hóa dữ liệuChúng ta đã đề cập trước đó rằng789 Club, cách thức triển khai bên dưới của tập hợp (set) sẽ thay đổi tùy thuộc vào việc các phần tử có phải là kiểu số nguyên hay không và số lượng phần tử được thêm vào. Ví dụ, trong quá trình thực thi lệnh cụ thể mà chúng ta đang nói đến, tập hợp này sẽ có cơ chế xử lý riêng dựa trên loại dữ liệu và số lượng phần tử được quản lý.
s1
Sau khi hoàn thành
sadd s1 13 5
Tăng từ 2 lên 4.
s1
Sau đó789 Club, vì phần tử thêm vào không còn là số nguyên,
encoding
= 2。
sadd s1 32768 10 100000
-1mua thẻ trực tuyến, do đó, nếu số thêm vào vượt quá phạm vi này, điều này cũng sẽ khiến intset chuyển thành dict.
s1
Số lượng phần tử trong tập hợp thêm vào vượt quá
encoding
Mã nguồn liên quan).
sadd s1 a b
Thuật toán giaomua thẻ trực tuyến, hợp, hiệu của Redis set
s1
Để tính giao và hiệumua thẻ trực tuyến, gọi làChúng ta đều biết rằng dict là một cấu trúc dữ liệu được sử dụng để duy trì mối quan hệ ánh xạ giữa key và value. Vậy khi set được biểu diễn dưới dạng dictxem ngoại hạng anh, thì key và value của nó thực chất là gì? Thực tế, key chính là phần tử mà chúng ta muốn thêm vào tập hợp, còn value sẽ là giá trị NULL. Tuy nhiên, điều thú vị là mặc dù value luôn có giá trị là NULL, dict vẫn đóng vai trò hiệu quả trong việc kiểm tra sự tồn tại của các key. Điều này giúp cho set hoạt động nhanh chóng và tối ưu trong việc lưu trữ các phần tử không trùng lặp. Một điểm đặc biệt khác là nhờ cách tổ chức này, các thao tác như thêm hoặc xóa phần tử trong set đều trở nên đơn giản và trực tiếp hơn bao giờ hết.
Ngoài lý do được đề cập trước đó về việc thêm các phần tử không phải số gây ra sự chuyển đổi từ intset thành dict789 Club, có hai tình huống khác cũng có thể dẫn đến sự thay đổi này: Thứ nhất, khi kích thước của tập hợp vượt quá giới hạn cho phép mà intset có thể xử lý. Khi số lượng phần tử tăng lên, cấu trúc dữ liệu intset sẽ trở nên kém hiệu quả hơn và hệ thống tự động chuyển sang sử dụng dict để quản lý. Thứ hai, nếu các giá trị trong tập hợp bắt đầu chứa các số lớn hơn hoặc nhỏ hơn mức mà intset có thể hỗ trợ, điều này cũng sẽ kích hoạt quá trình chuyển đổ Điều này giúp đảm bảo tính toàn vẹn và hiệu suất của dữ liệu khi làm việc với các tập hợp lớn và đa dạng.
set-max-intset-entries
Khi thiết lập giá trịmua thẻ trực tuyến, cũng có thể dẫn đến việc intset được chuyển đổi thành dict (điều kiện kích hoạt cụ thể có thể tham khảo trong t_set.c của): Tôi đã thêm từ "thiết lập" để làm phong phú hơn ngữ cảnh và duy trì ý nghĩa ban đầu một cách tự nhiên trong tiếng Việt. Câu vẫn giữ nguyên ý chính nhưng được diễn đạt theo cách khác.
setTypeAdd
Quá trình tính giao có thể chia thành ba phần:
Việc sử dụng intset để lưu trữ các tập hợp nhỏ chủ yếu nhằm tiết kiệm bộ nhớ. Đặc biệt khi số lượng phần tử cần lưu trữ ít789 Club, cấu trúc dict sẽ tạo ra một mức tiêu hao bộ nhớ lớn hơn đáng kể (bao gồm hai bảng băm, con trỏ danh sách liên kết và vô số thông tin bổ sung khác). Do đó, khi lưu trữ nhiều tập hợp nhỏ và các phần tử trong tập đều là số, intset sẽ giúp bạn tiết kiệm được một khoản không gian bộ nhớ đáng kể. Ngoài ra, intset còn mang lại lợi ích về hiệu suất khi thao tác với các tập hợp nhỏ này. Khi dữ liệu toàn số, việc tìm kiếm, thêm hoặc xóa phần tử trở nên nhanh chóng và đơn giản hơn nhờ vào cách sắp xếp nội tại của intset. Điều này không chỉ tối ưu hóa tài nguyên mà còn tăng cường hiệu quả hoạt động tổng thể của chương trình. Vì vậy, lựa chọn intset trong trường hợp này không chỉ giúp tiết kiệm bộ nhớ mà còn cải thiện hiệu suất đáng kể.
Trên thực tếmua thẻ trực tuyến, nếu so sánh về độ phức tạp thời gian, hiệu suất của intset ở trường hợp trung bình không bằng dict. Lấy việc tìm kiếm làm ví dụ, intset có độ phức tạp là O(log n), trong khi dict gần như đạt mức O(1). Tuy nhiên, do số lượng phần tử trong intset thường khá ít khi sử dụng, nên tác động này không quá lớn. Điều này có nghĩa là mặc dù dict nhanh hơn trong lý thuyết, nhưng với các tập dữ liệu nhỏ, sự khác biệt này trở nên không đáng kể.
Trong t_set.c789 Club, mã nguồn thực hiện các thuật toán cho phép hợp, giao và hiệu của tập hợp Redis Set đã được triển khai. Đặc biệt, khi tính toán phần giao giữa hai tập hợp, hàm xử lý sẽ được gọi đến để thực hiện nhiệm vụ này. Hàm này sẽ duyệt qua từng phần tử trong cả hai tập hợp và xác định những yếu tố chung giữa chúng một cách hiệu quả.
sinterGenericCommand
Do phải duyệt qua từng phần tử của tất cả các tập hợpxem ngoại hạng anh, tài liệu hướng dẫn chính thức của Redis đưa ra
sunionDiffGenericCommand
Chúng có khả năng thực hiện các phép toán trên nhiều tập hợp cùng một lúc (trên hai tập hợp trở lên). Khi thực hiện phép trừ tập hợp giữa nhiều tập hợp789 Club, ý nghĩa của nó được diễn đạt như sau: Sử dụng tập hợp đầu tiên để trừ đi tập hợp thứ hai, kết quả thu được sẽ tiếp tục được trừ đi tập hợp thứ ba, và cứ tiếp tục theo thứ tự này cho đến khi hoàn thành với tất cả các tập hợp còn lại.
Độ phức tạp thời gian của lệnh
Có hai thuật toán có thể tính hiệu789 Club, độ phức tạp thời gian của chúng khác nhau.
Điều cần lưu ý là bước thứ 3 ở trên thực hiện việc tìm kiếm trong tập hợpmua thẻ trực tuyến, và đối với việc lưu trữ intset và dict, độ phức tạp thời gian tương ứng là O(log n) và O(1). Tuy nhiên, vì chỉ có các tập hợp nhỏ mới sử dụng intset, nên có thể coi rằng việc tìm kiếm trong intset cũng có độ phức tạp thời gian là hằng số. Do đó, như tài liệu chính thức của Redis đã đề cập (
http://redis.io/commands/sinter
),
sinter
Thuật toán thứ nhất:
O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.
Việc tính hợp nhất (union) của các tập hợp khá đơn giản789 Club, bạn chỉ cần duyệt qua tất cả các tập hợp và thêm từng phần tử vào tập kết quả cuối cùng. Khi thêm phần tử vào một tập hợp, hệ thống sẽ tự động loại bỏ các phần tử trùng lặp, giúp việc duy trì tính duy nhất của tập hợp trở nên dễ dàng hơn bao giờ hết.
Thuật toán thứ hai:
sunion
Đưa tất cả phần tử của tập hợp đầu tiên vào một tập hợp tạm thời.
http://redis.io/commands/sunion
):
O(N) where N is the total number of elements in all given sets.
Lưu ý rằngmua thẻ trực tuyến, giống như trong phần thảo luận trước về việc tính toán giao của các tập hợp, quá trình chèn phần tử vào tập hợp kết quả được thực hiện mà không tính đến trường hợp intset, và thời gian thực hiện được giả định có độ phức tạp O(1).
Cuối cùngxem ngoại hạng anh, phần tử còn lại trong tập hợp tạm thời sẽ tạo thành hiệu.
Độ phức tạp thời gian của thuật toán này là O(N)789 Club, trong đó N là tổng số lượng phần tử của tất cả các tập hợp.
Đối với
Tài liệu chính thức của Redis (.
Chỉ đưa ra kết quả của thuật toán thứ haimua thẻ trực tuyến, không chính xác.
Trong giai đoạn khởi đầu của việc tính toán hiệu tập hợp (difference set)mua thẻ trực tuyến, sẽ có một bước ước tính trước thời gian phức tạp (time complexity) dự kiến của hai thuật toán khác nhau. Từ đó, người ta sẽ chọn ra thuật toán có mức độ phức tạp thấp hơn để thực hiện phép tính. Ngoài ra, còn có hai điều quan trọng cần lưu ý: Thứ nhất, việc xác định chính xác dữ liệu đầu vào rất quan trọng, bởi nó ảnh hưởng trực tiếp đến hiệu quả của cả hai phương pháp. Nếu dữ liệu đầu vào không được chuẩn bị kỹ lưỡng, có thể dẫn đến sai sót hoặc kết quả không chính xác. Thứ hai, trong quá trình so sánh và lựa chọn thuật toán, cần cân nhắc không chỉ về mặt thời gian xử lý mà còn phải xem xét yếu tố tài nguyên máy tính, như bộ nhớ RAM hay dung lượng ổ cứng. Điều này giúp đảm bảo rằng thuật toán được chọn không chỉ nhanh mà còn tối ưu về mặt sử dụng nguồn lực.
Bài tiếp theo trong loạt bài này sẽ tiếp tục789 Club, xin vui lòng chờ đợi.
sdiff
Redis
http://redis.io/commands/sdiff
(Kết thúc)
Các bài viết được chọn lọc khác :