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 này là phần thứ bả Trong nội dung hôm naybóng đá wap, chúng ta sẽ cùng tìm hiểu về một cấu trúc dữ liệu nội bộ của Redis mang tên intset. Đây là một phần quan trọng giúp Redis tối ưu hóa việc lưu trữ và xử lý các tập hợp dữ liệu số nguyên, và nó đóng vai trò đặc biệt trong hiệu suất hoạt động của hệ thống. chủ đề thú vị!
Trong Redistỷ số bóng đá hôm nay, 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. Set trong Redis tương tự như khái niệm tập hợp trong toán học, nơi các phần tử không có thứ tự và không thể lặp lại. Ngoài ra, cấu trúc set của Redis còn hỗ trợ các thao tác cơ bản như hợp, giao và hiệu giữa các tập hợp. Tương tự như các cấu trúc dữ liệu khác mà Redis cung cấp, cách thức triển khai dưới lớp của set sẽ thay đổi tùy thuộc vào loại dữ liệu của các phần tử cũng như số lượng phần tử được thêm vào. Tóm lại, khi tất cả các phần tử trong set đều là số nguyên và số lượng phần tử này không quá lớn, Redis sẽ sử dụng intset làm cấu trúc lưu trữ bên dưới. Ngược lại, nếu các phần tử không phải toàn bộ là số nguyên hoặc số lượng phần tử vượt quá giới hạn nhất định, Redis sẽ chuyển sang sử dụng một cấu trúc dữ liệu khác phù hợp hơn để đảm bảo hiệu suất tối ưu. dict Được sử dụng như cấu trúc dữ liệu cơ sở.
Trong bài viết nàyboi tu vi, chúng ta sẽ giới thiệu đại khái thành ba phần:
Trong quá trình thảo luậnboi tu vi, chúng ta sẽ còn đề cập đến 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 thảo luận trong bài viết này dựa trên nhánh mã nguồn 3.2 của Redis.
Dựa trên tên gọi của nótỷ số bóng đá hôm nay, intset rõ ràng là một tập hợp gồm 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 được sắp xếp theo thứ tự nhất định, giúp việc tìm kiếm nhị phân trở nên dễ dàng và nhanh chóng hơn khi xác định xem 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 đồng ở chỗ nó là một vùng nhớ liên tục và nguyên khốitỷ số bóng đá hôm nay, đồng thời sử dụng các phương thức mã hóa khác nhau cho số nguyên lớn và số nguyên nhỏ (dựa trên giá trị tuyệt đối), từ đó tối ưu hóa việc sử dụng bộ nhớ một cách hiệu quả.
Định nghĩa cấu trúc dữ liệu của intset như sau (được lấy 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 từng trường như sau:
encoding
Trong việc mã hóa dữ liệutỷ số bóng đá hôm nay, intset sẽ xác định kích thước bộ nhớ cần thiết để lưu trữ từng phần tử trong số các phần tử của nó. Có ba khả năng chính về cách thức này: INTSET_ENC_INT16 nghĩa là mỗi phần tử sẽ chiếm 2 byte, INTSET_ENC_INT32 có nghĩa là mỗi phần tử sẽ chiếm 4 byte, còn INTSET_ENC_INT64 cho phép mỗi phần tử sử dụng tới 8 byte. Do đó, các giá trị nguyên được lưu trữ trong intset sẽ không vượt quá giới hạn 64 bit. Điều này giúp tối ưu hóa không gian lưu trữ và hiệu suất hoạt động của cấu trúc dữ liệu này tùy thuộc vào loại giá trị mà nó cần quản lý.
length
: Thể hiện số lượng phần tử
encoding
Ghi giá trị
length
Hai trường này cấu thành phần đầu (header) của intset.
contents
: Là một mảng linh hoạt (flexible array)
flexible array member
Bạn có thể thấy rằng sau phần header của intset sẽ ngay lập tức tiếp nối các phần tử dữ liệu. Toàn bộ chiều dài của mảng này (tức tổng số byte) được xác định bởi một giá trị cụ thể nào đótỷ số bóng đá hôm nay, tùy thuộc vào cách mà các phần tử đã được sắp xếp và loại dữ liệu được lưu trữ bên trong. Điều này cho phép intset tối ưu hóa việc quản lý không gian lưu trữ cũng như tăng tốc độ truy xuất thông qua việc sắp xếp hợp lý các phần tử bên trong nó.
encoding * length
. Mảng linh hoạt này xuất hiện trong nhiều định nghĩa cấu trúc dữ liệu của Redis (ví dụ
sds
,
quicklist
,
skiplist
)tỷ số bóng đá hôm nay, được sử dụng để biểu diễn một khoảng cách.
contents
Cần phải cấp phát bộ nhớ riêng cho nóboi tu vi, phần bộ nhớ này không bao gồ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 (nhấn để xem lớn hơn).
Trong hình trên:
encoding
= 2,
length
= 0。
encoding
Không thay đổiboi tu vi, giá trị vẫn là 2.
encoding
Cần nâng cấp lên INTSET_ENC_INT32 (giá trị là 4)tỷ số bóng đá hôm nay, tức là 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 nên được giải thích là giá trị 0x00000004tỷ số bóng đá hôm nay, trong khi đó, dữ liệu thứ 5 sẽ mang ý nghĩa tương đương với 0x000186A0, tức là số 100000. Đây là cách mà các byte trong chuỗi dữ liệu được mã hóa theo chuẩn cụ thể để đảm bảo tính nhất quán và chính xác trong việc xử lý thông tin.So với intset ziplist so sánh:
len
)tỷ số bóng đá hôm nay, trong khi intset chỉ có thể sử dụng mã hóa thống nhất (uniform encoding).
encoding
)。
Để hiểu rõ hơn về một số chi tiết thực hiện của intsetbóng đá wap, bạn chỉ cần tập trung vào hai hoạt động chính yếu của nó: tìm kiếm (search) và...
intsetFind
) và thêm (insert
intsetAdd
) phần tử.
intsetFind
Mã nguồn chính liên quan như sau (lấy từ intset.c):
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 valuebóng đá wap,
* 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
;
}
}
Về mã nguồn nàytỷ số bóng đá hôm nay, những điểm cần chú ý bao gồm:
intsetFind
Tìm kiếm phần tử cụ thể trong intset đã chỉ định
value
bóng đá wap, nếu tìm thấy trả về 1, nếu không tìm thấy trả về 0.
_intsetValueEncoding
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
value
nằm trong phạm vi nào.
value
Nếu dữ liệu cần thiết mã hóa lớn hơn định dạng mã hóa hiện tại của intsetbóng đá wap, đ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. Nếu không, nó sẽ tiếp tục gọi phương thức tương ứng.
intsetSearch
boi tu vi, nếu tìm thấy, thì trả về 1 và tham số
intsetSearch
Tìm kiếm phần tử cụ thể trong intset đã chỉ định
value
Trỏ đến vị trí của phần tử được tìm thấy; nếu không tìm thấyboi tu vi, thì trả về 0 và tham số
pos
Trỏ đến vị trí có thể chèn phần tử này.
pos
Đây là một thực hiện của thuật toán tìm kiếm nhị phânboi tu vi, nó chia thành ba phần chính:
intsetSearch
Xử lý đặc biệt trường hợp intset rỗng.
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ếtỷ số bóng đá hôm nay, 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 thất bại nhanh chóng trong một số tình huống đặc biệt. Trong trường hợp giá trị nằm ngoài phạm vi của dãy số, thay vì tiếp tục thực hiện các bước tìm kiếm phức tạp, chúng ta có thể lập tức kết luận rằng giá trị không tồn tại trong mảng. Điều này giúp tiết kiệm thời gian và tối ưu hóa hiệu suất của thuật toán, đặc biệt khi làm việc với các tập dữ liệu lớn. Đây là một chiến lược thông minh để tăng tốc độ xử lý trong một số trường hợp nhất định.
min
Mã nguồn xuất hiện
intrev32ifbe
đây là để thực hiện việc chuyển đổi giữa định dạng little-endian và big-endian khi cần thiết. Như chúng ta đã đề cập trước đóbóng đá wap, các dữ liệu trong intset được lưu trữ theo thứ tự little-endian, do đó khi chạy trên một hệ thống có kiến trúc big-endian, phần này của mã sẽ ...
intrev32ifbe
Độ 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).
Bắt đầu chuyển sang lưu trữ dưới dạng số nguyên.
intsetAdd
Mã nguồn chính liên quan như sau (lấy từ intset.c):
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 upgradebóng đá wap, 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 succeedsbóng đá wap, 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
;
}
Về mã nguồn nàyboi tu vi, những điểm cần chú ý bao gồm:
intsetAdd
. Nếu
value
Đã tồn tại trước khi thêmboi tu vi, thì sẽ không thêm trùng lặp, tham số
value
Sẽ được đặt thành 0; nếu
success
Không tồn tại trong intset ban đầuboi tu vi, thì
value
Sẽ được chèn vào vị trí thích hợptỷ số bóng đá hôm nay, tham số
value
Sẽ được đặt thành 0.
success
Nếu phần tử cần thêm
value
Để nâng cấp mã hóa của intset trước khi chèn
intsetUpgradeAndAdd
tỷ số bóng đá hôm nay, nếu tìm thấy, thì sẽ không thêm trùng lặp.
value
。
intsetSearch
(xem
intsetResize
Việc mở rộng bộ nhớ cho intset để có thể chứa các phần tử mới là cần thiết. Do intset là một khối không gian liên tụctỷ số bóng đá hôm nay, 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 tất cả dữ liệu hiện tại từ vùng nhớ cũ sang vùng nhớ mới, và sau đó thêm phần tử mới vào trong đó. Điều này không chỉ giúp intset duy trì tính toàn vẹn của cấu trúc dữ liệu mà còn đảm bảo hiệu suất tối ưu khi làm việc với các thao tác tiếp theo.
realloc
Có gọi
http://man.cx/realloc
Để hoàn thành bản sao dữ liệu này.
intsetMoveTail
Bạn có thể di chuyển toàn bộ các phần tử sau vị trí chèn về phía sau một vị tríboi tu vi, thao tác này cũng yêu cầu một lần sao chép dữ liệu. Điều đáng lưu ý là,
intsetMoveTail
Đảm bảo rằng trong quá trình sao chép không gây ra sự chồng chéo hoặc ghi đè dữ liệubóng đá wap, xem cụ thể
memmove
Việc thực hiện cũng sẽ gọi
memmove
Để mở rộng bộ nhớ. Khi nâng cấp mã hóabóng đá wap,
http://man.cx/memmove
。
intsetUpgradeAndAdd
Việc thực hiện sẽ lấy từng phần tử từ intset cũ rabóng đá wap, rồi viết lại bằng mã hóa mới vào vị trí mới.
intsetResize
Chú ý một chút
intsetUpgradeAndAdd
Giá trị trả vềbóng đá wap, nó trả về một con trỏ intset mới. Nó có thể khác với con trỏ intset truyền vào
intsetAdd
Rõ ràngboi tu vi, độ phức tạp thời gian tổng thể của thuật toán này là O(n).
is
Điều này có thể giống hoặc khác nhau. Người gọi phải 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 như vậy rất phổ biến trong mã nguồn Redistỷ số bóng đá hôm nay, ví dụ như khi chúng ta đã từng đề cập trước đây về...
sds
Ghi giá trị
ziplist
Set của Redis
intsetAdd
Ý nghĩa của các lệnh trên:
Để hiểu rõ hơn về cấu trúc dữ liệu set mà Redis cung cấp cho người dùngbóng đá wap, 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 thường được sử dụng: 1. **SADD**: Thêm một hoặc nhiều phần tử vào tập hợp. Nếu phần tử đã tồn tại, nó sẽ không được thêm lại. Ví dụ: `SADD myset "apple" "banana"` 2. **SMEMBERS**: Trả về tất cả các phần tử trong tập hợp. Ví dụ: `SMEMBERS myset` 3. **SISMEMBER**: Kiểm tra xem một phần tử có tồn tại trong tập hợp hay không. Ví dụ: `SISMEMBER myset "orange"` 4. **SCARD**: Trả về số lượng phần tử trong tập hợp. Ví dụ: `SCARD myset` 5. **SREM**: Xóa một hoặc nhiều phần tử khỏi tập hợp. Ví dụ: `SREM myset "banana"` 6. **SPOP**: Loại bỏ và trả về một phần tử ngẫu nhiên từ tập hợp. Ví dụ: `SPOP myset` 7. **SRANDMEMBER**: Trả về một phần tử ngẫu nhiên từ tập hợp nhưng không xóa nó. Ví dụ: `SRANDMEMBER myset` Những lệnh này giúp bạn dễ dàng thao tác với tập hợp trong Redis, cho phép bạn quản lý dữ liệu một cách hiệu quả và linh hoạt. Hãy thử áp dụng chúng trong các dự án để thấy được sức mạnh của Redis!
Dùng để kiểm tra xem phần tử cụ thể có tồn tại trong tập hợp hay không.
sadd
Dùng để tính giaotỷ số bóng đá hôm nay, hợp và hiệu của tập hợp.
s1
Ghi giá trị
s2
Kết cấu dữ liệu phía dưới của set sẽ thay đổi như sau:
sismember
Sau khi thực hiện xong
sinter
,
sunion
Ghi giá trị
sdiff
boi tu vi, vì tất cả phần tử thêm vào đều là số nguyên nhỏ, do đóChúng ta đã đề cập trước đó rằng việc triển khai cơ bản của tập hợp (set) sẽ thay đổi tùy thuộc vào việc phần tử có phải là kiểu số nguyên hay khôngbóng đá wap, cũng như số lượng phần tử được thêm vào. Ví dụ cụ thể hơn, trong quá trình thực thi lệnh mà chúng ta vừa thảo luận, tập hợp sẽ hoạt động khác nhau dựa trên các yếu tố này. Nếu các phần tử là số nguyên và số lượng không quá lớn, hệ thống có thể sử dụng phương pháp hashing đơn giản để tối ưu hóa tốc độ truy xuất. Ngược lại, nếu phần tử phức tạp hoặc số lượng tăng lên đáng kể, cơ chế quản lý bộ nhớ và cách tổ chức dữ liệu sẽ được điều chỉnh linh hoạt để đảm bảo hiệu suất ổn định.
s1
Dưới đáy là một intsetbóng đá wap, mã hóa dữ liệu
sadd s1 13 5
bóng đá wap, dưới đáy vẫn là một intset, nhưng mã hóa dữ liệu
s1
Từ 2 tăng lên 4.
encoding
= 2。
sadd s1 32768 10 100000
Hiện thực bên dưới sẽ chuyển thành một dict.
s1
-1tỷ số bóng đá hôm nay, 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.
encoding
Số lượng phần tử trong tập hợp thêm vào vượt quá
sadd s1 a b
Mã nguồn liên quan).
s1
Thuật toán giaoboi tu vi, hợp, và hiệu của Redis setChú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 dictbóng đá wap, 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 lại là null. Điều này giúp dict đóng vai trò như một công cụ hiệu quả để quản lý các phần tử duy nhất trong set, bởi vì mỗi key trong dict phải là duy nhất, điều này phù hợp với bản chất của set.
Ngoài lý do đã đề cập trước đó về việc chuyển đổi từ intset thành dict do thêm các phần tử không phải số nguyênboi tu vi, còn có hai tình huống khác 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 tối đa mà intset có thể hỗ trợ. Khi số lượng phần tử tăng lên, intset có xu hướng trở nên kém hiệu quả và hệ thống tự động chuyển sang sử dụng dict để quản lý dữ liệu. Thứ hai, nếu có sự xuất hiện của số nguyên lớn hơn hoặc nhỏ hơn phạm vi cho phép của intset, nó cũng sẽ kích hoạt quá trình chuyển đổi. Điều này xảy ra khi giá trị của phần tử nằm ngoài khoảng mà intset có thể lưu trữ, buộc hệ thống phải sử dụng một cấu trúc dữ liệu linh hoạt hơn như dict.
set-max-intset-entries
Khi thiết lập giá trịbóng đá wap, 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): Trong quá trình thực hiện thao tác cấu hình, một số trường hợp đặc biệt sẽ khiến cấu trúc dữ liệu intset (một mảng các phần tử nguyên) bị chuyển đổi thành dạng dict (bảng băm). Điều này thường xảy ra khi có sự thay đổi về kiểu dữ liệu hoặc khi cần xử lý thêm các yếu tố phức tạp hơn. Để hiểu rõ hơn về những điều kiện cụ thể nào có thể gây ra sự chuyển đổi này, bạn có thể tìm hiểu kỹ hơn trong tập tin mã nguồn t_set.c, nơi mà mọi thuật toán và quy tắc liên quan đều được cẩn thận ghi lại.
setTypeAdd
Giao
Khi làm việc với các tập hợp nhỏboi tu vi, sử dụng intset để lưu trữ là một lựa chọn tối ưu chủ yếu vì nó giúp tiết kiệm bộ nhớ. Đặc biệt, khi số lượng phần tử trong tập hợp ít, cấu trúc dữ liệu dict sẽ gây ra sự tiêu tốn đáng kể về mặt bộ nhớ (bao gồm hai bảng băm, con trỏ danh sách liên kết và rất nhiều thông tin bổ sung khác). Do đó, khi phải quản lý hàng loạt các tập hợp nhỏ mà toàn bộ phần tử đều là số, intset có thể giúp bạn tiết kiệm được một lượng đáng kể dung lượng bộ nhớ. Ngoài ra, intset còn mang lại lợi ích khác như cải thiện tốc độ xử lý do giảm thiểu số lần thao tác trên bộ nhớ, đồng thời giúp giảm thiểu xung đột khi thực hiện các phép toán cơ bản trên tập hợp, chẳng hạn như thêm, xóa hoặc kiểm tra sự tồn tại của phần tử. Điều này đặc biệt hữu ích trong các ứng dụng cần tối ưu hóa hiệu suất và giảm thiểu tài nguyên máy chủ.
Trên thực tếbóng đá wap, khi so sánh về độ phức tạp thời gian, hiệu suất của intset thường không bằng dict trong trường hợp trung bình. Hãy 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 được coi là O(1). Tuy nhiên, do số lượng phần tử trong tập hợp khi sử dụng intset thường khá ít, nên tác động này không đáng kể. Thêm vào đó, intset có lợi thế đặc biệt khi kích thước của tập dữ liệu nhỏ và các phần tử đều là kiểu nguyên. Điều này giúp giảm thiểu không gian lưu trữ và tăng tốc độ xử lý trong một số trường hợp cụ thể. Ngược lại, dict mặc dù nhanh hơn trong việc tìm kiếm nhưng tiêu tốn nhiều bộ nhớ hơn, điều này có thể trở thành bất lợi nếu dữ liệu chỉ ở mức vừa phải hoặc nhỏ.
Trong t_set.ctỷ số bóng đá hôm nay, bạn sẽ tìm thấy 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ợ Đặc biệt, khi tính toán phần giao giữa các tập hợp, mã này sẽ gọi đến một hàm cụ thể được thiết kế riêng để xử lý thao tác này. Hàm này sử dụng logic chuyên sâu để đảm bảo tính chính xác và hiệu quả trong việc xác định các phần tử chung giữa hai hoặc nhiều tập hợp.
sinterGenericCommand
Độ phức tạp thời gian của lệnh
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 (bao gồm hơn 2 tập hợp). Khi thực hiện phép trừ tập hợp giữa nhiều tập hợptỷ số bóng đá hôm nay, ý nghĩa của nó được hiểu 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ừ với tập hợp thứ ba, và cứ tiếp tục theo thứ tự đó cho đến khi hoàn tất quá trình.
Do phải duyệt qua từng phần tử của tất cả các tập hợptỷ số bóng đá hôm nay, tài liệu chính thức của Redis cho biết
Hiệu
Điều quan trọng cần lưu ý là bước thứ 3 trong quá trình trên thực hiện việc tìm kiếm trong tập hợpboi tu vi, 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, do chỉ có các tập hợp nhỏ mới sử dụng intset, nên chúng ta có thể coi việc tìm kiếm trong intset cũng có độ phức tạp thời gian gần như là hằng số. Do đó, giống như tài liệu chính thức của Redis đã đề cập (
http://redis.io/commands/sinter
),
sinter
Có hai thuật toán có thể tính hiệuboi tu vi, và độ phức tạp thời gian của chúng có sự khác biệ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) là điều dễ dàng nhấtboi tu vi, 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 một phần tử vào tập hợp, hệ thống sẽ tự động loại bỏ những phần tử trùng lặp, đảm bảo rằng tập hợp luôn duy trì tính chất không trùng lặp của mình.
Độ phức tạp thời gian của thuật toán này là O(N*M)bóng đá wap, trong đó N là số lượng phần tử của tập hợp đầu tiên, M là số lượng tập hợp.
sunion
Thuật toán thứ hai:
http://redis.io/commands/sunion
):
O(N) where N is the total number of elements in all given sets.
Lưu ý rằng ở đâybóng đá wap, 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ả sẽ được thực hiện mà không xét đến trường hợp intset, và thời gian thực hiện được giả định có độ phức tạp là O(1).
Duyệt qua tất cả các tập hợp tiếp theobóng đá wap, đối với mỗi phần tử gặp phải, xóa nó khỏi tập hợp tạm thời.
Cuối cùngboi tu vi, các phần tử còn lại trong tập hợp tạm thời tạo thành hiệu.
Độ phức tạp thời gian của thuật toán này là O(N)tỷ số bóng đá hôm nay, 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 (
Ở phần khởi đầu của việc tính toán hiệu tập hợp (difference set)tỷ số bóng đá hôm nay, sẽ thực hiện ước lượng trước thời gian dự kiến cho hai thuật toán khác nhau để so sánh độ phức tạp của chúng. Sau đó, chọn thuật toán có độ phức tạp thấp hơn để tiến hành xử lý. Ngoài ra, còn có hai điểm quan trọng cần lưu ý: Thứ nhất, cần đảm bảo rằng dữ liệu đầu vào đã được chuẩn bị đầy đủ và không có lỗi, vì bất kỳ sai sót nào trong dữ liệu ban đầu cũng có thể dẫn đến kết quả không chính xác. Thứ hai, phải xem xét đến tài nguyên hệ thống mà thuật toán sẽ sử dụng, như bộ nhớ và CPU, để tránh tình trạng quá tải trong quá trình chạy thuật toán, đặc biệt khi làm việc với các tập dữ liệu lớn.
) chỉ đưa ra kết quả của thuật toán thứ haiboi tu vi, là không chính xác.
sdiff
Phần tiếp theo của loạt bài sẽ được tiếp tụcbóng đá wap, hãy đón chờ.
http://redis.io/commands/sdiff
(Kết thúc)
Các bài viết được chọn lọc khác :