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

Giải thích chi tiết về cấu trúc dữ liệu nội bộ của Redis (7) —— intset


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:

  1. Tập trung trình bày về cấu trúc dữ liệu intset.
  2. Thảo luận cách set được xây dựng trên intset và dict nền tảng đó.
  3. Trong phần thảo luận nàybóng đá wap, chúng ta sẽ đi sâu vào việc triển khai thuật toán cho các phép toán cơ bản của tập hợp (set) như hợp (union), giao (intersection) và hiệu (difference), đồng thời phân tích độ phức tạp thời gian (time complexity) của từng phương pháp. Đặc biệt, đối với phép tính hiệu tập hợp, Redis đã cung cấp hai thuật toán khác nhau để tối ưu hóa hiệu suất, mỗi thuật toán đều có ưu điểm riêng trong việc xử lý dữ liệu lớn hoặc yêu cầu cụ thể về tốc độ truy xuất. Trước hết, phép hợp của hai tập hợp là việc kết hợp tất cả các phần tử từ hai tập hợp mà không lặp lại bất kỳ phần tử nào. Thuật toán này thường sử dụng phương pháp quét và kiểm tra sự hiện diện của từng phần tử trong tập hợp thứ hai sau khi đã thêm tất cả các phần tử từ tập hợp đầu tiên. Độ phức tạp thời gian của phương pháp này thường ở mức O(n), trong đó n là số lượng phần tử cần kiểm tra. Tiếp theo, phép giao giữa hai tập hợp đòi hỏi tìm ra những phần tử chung giữa hai tập hợp. Điều này có thể thực hiện bằng cách duyệt qua từng phần tử của tập hợp đầu tiên và kiểm tra xem nó có tồn tại trong tập hợp thứ hai hay không. Với phương pháp này, độ phức tạp thời gian cũng là O(n), nhưng có thể được tối ưu hóa hơn nhờ việc sử dụng các cấu trúc dữ liệu như cây b-tree hoặc bảng băm (hash table). Cuối cùng, về phép tính hiệu tập hợp, đây là quá trình loại bỏ khỏi tập hợp thứ nhất tất cả các phần tử có mặt trong tập hợp thứ hai. Trong Redis, hai thuật toán phổ biến để tính hiệu tập hợp là phương pháp quét trực tiếp và sử dụng cơ chế bitset. Phương pháp quét trực tiếp có độ phức tạp O(n), còn bitset, nhờ khả năng lưu trữ thông tin một cách chặt chẽ dưới dạng mảng bit, có thể đạt được hiệu suất cao hơn nhiều trong trường hợp dữ liệu lớn. Bitset đặc biệt hữu ích khi tập hợp chứa nhiều phần tử và các phần tử này có giá trị số nguyên liên tục. Tóm lại, việc lựa chọn thuật toán phù hợp phụ thuộc vào đặc thù của dữ liệu và yêu cầu cụ thể của ứng dụng. Redis cung cấp nhiều công cụ mạnh mẽ để tối ưu hóa các thao tác trên tập hợp, giúp người dùng có thể linh hoạt chọn giải pháp phù hợp với nhu cầu của mình.

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.

Giới thiệu sơ lược về cấu trúc dữ liệu intset

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:

  • Ban đầuboi tu vi, khi mới được tạo ra, intset sẽ sử dụng định dạng mã hóa có kích thước bộ nhớ nhỏ nhất là INTSET_ENC_INT16 (có giá trị là 2 byte) để lưu trữ dữ liệu. Đây được coi là lựa chọn tối ưu cho các trường hợp cần tiết kiệm không gian mà vẫn đảm bảo hiệu suất cơ bản trong việc quản lý các phần tử số nguyên.
  • Mỗi lần thêm một phần tử mớitỷ số bóng đá hôm nay, thì dựa vào kích thước của phần tử để quyết định xem có cần nâng cấp mã hóa dữ liệu hay không.

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

Ví dụ thêm dữ liệu vào intset

Trong hình trên:

  • Intset mới chỉ có headerbóng đá wap, tổng cộng 8 byte. Trong đó encoding = 2, length = 0。
  • Sau khi thêm hai phần tử 13 và 5bóng đá wap, vì chúng là các số nguyên nhỏ, đều có thể được biểu diễn bằng 2 byte, do đó encoding Không thay đổiboi tu vi, giá trị vẫn là 2.
  • Khi thêm 32768 vàobóng đá wap, con số đó đã vượt quá giới hạn mà hai byte có thể biểu diễn được. Hai byte thông thường chỉ có thể chứa các giá trị trong phạm vi từ -32.768 đến 32.767. Khi vượt qua ngưỡng này, cần phải sử dụng nhiều byte hơn để lưu trữ giá trị mới, điều đó đồng nghĩa với việc bạn sẽ cần một cách thức khác để quản lý và xử lý dữ liệu lớn hơn. Đây là một trong những thách thức cơ bản khi làm việc với các hệ thống số học và lập trình, nơi mà việc quản lý bộ nhớ và phạm vi giá trị đóng vai trò rất quan trọng trong hiệu suất của chương trình. 15 ~2 15 -1, và 32768 bằng 2 15 bóng đá wap, vượt quá phạm vi), do đó 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ử.
  • Trong quá trình thêm mỗi phần tửtỷ số bóng đá hôm nay, intset luôn giữ thứ tự từ nhỏ đến lớn.
  • Có mối quan hệ chặt chẽ hơn với ziplist Tương tự như vậybóng đá wap, intset cũng được lưu trữ theo định dạng little endian (xem thêm tại bài viết của Wikipedia). Định dạng này giúp tối ưu hóa việc xử lý và truy xuất dữ liệu một cách nhanh chóng trong bộ nhớ, cho phép các thao tác số học thực hiện hiệu quả hơn. Endianness ). Ví dụbóng đá wap, trong hình trên, sau khi intset thêm tất cả dữ liệu, điều này biểu thị 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:

  • Ziplist có thể lưu trữ bất kỳ chuỗi nhị phân nàoboi tu vi, trong khi intset chỉ có thể lưu trữ số nguyên.
  • Ziplist là không theo thứ tựtỷ số bóng đá hôm nay, trong khi intset được sắp xếp từ nhỏ đến lớn. Do đó, khi tìm kiếm trong ziplist, bạn chỉ có thể duyệt từng phần tử một, còn với intset, bạn có thể áp dụng phương pháp tìm kiếm nhị phân, giúp cải thiện hiệu suất đáng kể. Điều này cho thấy intset mang lại lợi thế rõ rệt hơn trong các tác vụ yêu cầu tốc độ và hiệu quả cao.
  • Ziplist có thể mã hóa biến dài cho từng mục dữ liệu (mỗi mục dữ liệu trước tiên có trường độ dài dữ liệu 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 )。

Các hoạt động tìm kiếm và thêm phần tử của intset

Để 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.
  • Thực hiện thuật toán tìm kiếm nhị phân. 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.
    • Xử lý đặc biệt hai trường hợp biên: khi phần tử cần tìm
    • Thực hiện quá trình tìm kiếm nhị phân. Lưu ý: Nếu cuối cùng không tìm thấytỷ số bóng đá hôm nay, 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ế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.
    • Vị trí được chỉ định. min Mã nguồn xuất hiện
  • Sẽ thực hiện các chuyển đổi tương ứng. 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).
  • Thêm phần tử mới vào intset

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;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;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
  • Mã hóa dữ liệu yêu cầu lớn hơn mã hóa hiện tại của intsetboi tu vi, thì sẽ gọi 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
  • Nếu không tìm thấybóng đá wap, thì gọi intsetSearch (xem
  • ). Điều này có thể dẫn đến một bản sao dữ liệu. Đồng thờiboi tu vi, trong 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
  • Khi gặp những tình huống tương tự. 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
  • Ví dụ lệnh set intsetAdd Ý nghĩa của các lệnh trên:

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ấ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!

Các phần tử thêm vào vừa có số, vừa có ký tự (

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

  • Sau khi thực hiện xong 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。
  • Sau đóbóng đá wap, vì phần tử thêm vào không còn là số, 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á
  • Sau đótỷ số bóng đá hôm nay, vì phần tử thêm vào không còn là số, 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 set

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

  • Bạn đã thêm một sốbóng đá wap, nhưng giá trị này vượt quá phạm vi có thể lưu trữ của kiểu số nguyên có dấu 64 bit. Với `intset`, dải giá trị nguyên mà nó có thể biểu diễn nằm trong khoảng từ **-9,223,372,036,854,775,808** (tức là **-2^63**) đến **9,223,372,036,854,775,807** (tức là **2^63 - 1**). Khi giá trị mới vượt khỏi giới hạn này, `intset` không còn khả năng lưu trữ nó một cách chính xác. 64 ~2 64 bóng đá wap, tính hợp và hiệu gọi là
  • Chúng tôi sẽ giới thiệu ngắn gọn ba thuật toán này. 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ỏ.

Quá trình tính giao có thể chia thành ba phần:

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

Độ phức tạp thời gian của lệnh

Hiệu

  1. Bạn có thể kiểm tra từng tập hợp và coi những tập hợp không tồn tại như là tập hợp rỗng. Ngay khi phát hiện ra một tập hợp rỗngtỷ số bóng đá hôm nay, bạn không cần tiếp tục thực hiện các phép tính khác nữa, vì kết quả giao của tất cả các tập hợp sẽ là tập hợp rỗng.
  2. Bạn có thể sắp xếp các tập hợp theo số lượng phần tử từ ít đến nhiều. Việc sắp xếp này sẽ giúp ích trong quá trình tính toán sau nàybóng đá wap, khi bạn có thể bắt đầu với tập hợp có số lượng phần tử nhỏ nhất, từ đó giảm thiểu số lượng phần tử cần xử lý. Điều này không chỉ tiết kiệm thời gian mà còn làm cho quy trình trở nên logic và dễ kiểm soát hơn.
  3. Bạn có thể duyệt qua tập hợp đầu tiên sau khi đã sắp xếp (tức là tập hợp nhỏ nhất) và đối với mỗi phần tử trong tập hợp nàyboi tu vi, hãy kiểm tra lần lượt trong tất cả các tập hợp còn lại phía sau. Chỉ những phần tử mà bạn tìm thấy xuất hiện trong tất cả các tập hợp mới được thêm vào tập hợp kết quả cuối cùng.

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

Thuật toán thứ nhất:

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

Đư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.

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.

  • Bạn có thể duyệt qua tập hợp đầu tiên và đối với mỗi phần tử của nóbóng đá wap, tiến hành kiểm tra lần lượt trong tất cả các tập hợp còn lại. Chỉ những phần tử không xuất hiện trong bất kỳ tập hợp nào khác mới được thêm vào tập hợp kết quả cuối cùng. Đây là một cách tiếp cận để đảm bảo rằng chỉ có những phần tử thực sự duy nhất mới được giữ lại, sau khi đã so sánh toàn diện giữa các tập hợp.

Độ 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.

  • Đến một mức độ nhất địnhbóng đá wap, bạn nên ưu tiên sử dụng thuật toán thứ nhất vì nó yêu cầu ít thao tác hơn, chỉ cần thêm vào. Trong khi đó, thuật toán thứ hai lại cần thực hiện cả bước thêm và xóa. Điều này có thể làm tăng thời gian và phức tạp trong quá trình xử lý, do đó thuật toán đầu tiên thường được ưa chuộng hơn trong các tình huống cần tối ưu hóa hiệu suất.
  • Nếu bạn chọn thuật toán đầu tiênbóng đá wap, trước khi thực hiện thuật toán này, trong phần triển khai của Redis, tất cả các tập hợp từ tập hợp thứ hai trở đi sẽ được sắp xếp theo số lượng phần tử, từ nhiều đến ít. Việc sắp xếp này giúp tăng xác suất tìm thấy phần tử cần tìm một cách nhanh chóng, từ đó rút ngắn thời gian thực hiện quá trình tra cứu. Đồng thời, việc sắp xếp này cũng góp phần tối ưu hóa hiệu suất tổng thể của hệ thống, đặc biệt là khi phải xử lý với một lượng lớn dữ liệu phức tạp.

) 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


Bài viết gốcbóng đá wap, vui lòng ghi rõ nguồn và bao gồm mã QR bên dưới! Nếu không, từ chối tái bản!
Liên kết bài viết này: /wsp64jyo.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: Câu chuyện xảy ra trong Ngày Độc thân: Bí mật sinh nhật
Bài sau: Đường cong tăng trưởng về kỹ thuật