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

Chi tiết về cấu trúc dữ liệu bên trong Redis (5) —— quicklist


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ứ năm trong loạt bài của chúng tôi. Trong nội dung hôm nayboi tu vi, chúng ta sẽ tìm hiểu về một cấu trúc dữ liệu nội bộ của Redis được gọi là Kiểu dữ liệu list mà Redis cung cấp cho người dùng bên ngoài thực chất dựa trên cấu trúc dữ liệu này để hoạt động hiệu quả hơn. Quicklist đóng vai trò quan trọng trong việc tối ưu hóa cách Redis quản lý và xử lý các danh sách lớn trong bộ nhớ.

Trong quá trình thảo luậnkết quả bóng đá việt nam hôm nay, chúng ta sẽ đề cập đến hai cấu hình Redis quan trọng (ở phần ADVANCED CONFIG trong tệp redis.conf):

								
									list-max-ziplist-size -2
list-compress-depth 0

								

Trong quá trình thảo luậnboi tu vi, chúng tôi sẽ giải thích chi tiết ý nghĩa của hai cấu hình này.

Lưu ý: Thực hiện này về quicklist dựa trên nhánh 3.2 của mã nguồn Redis.

Tổng quan về quicklist

Redis cung cấp kiểu dữ liệu danh sách (list) ở tầng giao diện người dùngkết quả bóng đá việt nam hôm nay, thường được sử dụng như một hàng đợi (queue). Một số thao tác mà nó hỗ trợ bao gồm: - **Thêm phần tử vào cuối danh sách**: Bạn có thể dễ dàng thêm một hoặc nhiều phần tử vào cuối danh sách bằng lệnh `RPUSH` hoặc `LPUSH`, tùy thuộc vào vị trí bạn muốn thêm. - **Lấy phần tử từ đầu danh sách**: Để lấy ra phần tử đầu tiên trong danh sách và đồng thời xóa nó khỏi danh sách, bạn có thể sử dụng lệnh `LPOP`. Nếu bạn muốn lấy nhưng không xóa phần tử đó, hãy sử dụng lệnh `LRANGE`. - **Xử lý theo nguyên tắc FIFO**: Redis giúp duy trì cấu trúc FIFO (First In First Out), giống như một hàng đợi thực tế. Điều này làm cho việc quản lý các tác vụ trở nên hiệu quả hơn trong các ứng dụng thời gian thực. - **Thực hiện thao tác với nhiều phần tử cùng lúc**: Với lệnh `BRPOP` hoặc `BLPOP`, bạn có thể chờ đợi đến khi có phần tử mới thêm vào danh sách trước khi thực hiện bất kỳ hoạt động nào, rất hữu ích trong các trường hợp cần xử lý đồng bộ. Những tính năng này đã khiến Redis trở thành một lựa chọn phổ biến cho các hệ thống yêu cầu hiệu suất cao và khả năng mở rộng.

  • lpush : Chèn dữ liệu vào bên trái (tức là phần đầu của danh sách).
  • rpop : Xóa dữ liệu từ bên phải (tức là phần cuối của danh sách).
  • rpush : Chèn dữ liệu vào bên phải (tức là phần cuối của danh sách).
  • lpop : Xóa dữ liệu từ bên trái (tức là phần đầu của danh sách).

Các hoạt động này đều có độ phức tạp thời gian O(1).

Tất nhiênkết quả bóng đá việt nam hôm nay, list cũng hỗ trợ thao tác truy cập tại bất kỳ vị trí nào giữa danh sách, chẳng hạn lindex Ghi giá trị linsert kết quả bóng đá việt nam hôm nay, nhưng chúng cần duyệt qua cả danh sách, do đó độ phức tạp thời gian cao hơn, ở mức O(N).

Tóm lạiboi tu vi, list có một số đặc điểm như sau: đây là một danh sách có thể duy trì thứ tự các phần tử (thứ tự của các phần tử được xác định bởi vị trí chèn), dễ dàng thêm và xóa dữ liệu ở hai đầu danh sách, nhưng việc truy xuất ở giữa danh sách sẽ có độ phức tạp thời gian O(n). Điều này không giống hệt với những gì mà một danh sách liên kết đôi sở hữu sao? Danh sách liên kết đôi cũng cho phép người dùng chèn và xóa dữ liệu tại hai đầu rất hiệu quả, trong khi vẫn cho phép truy cập vào bất kỳ phần tử nào trong danh sách theo thứ tự đã thiết lập. Tuy nhiên, nếu cần truy xuất các phần tử ở giữa, nó vẫn phải duyệt qua từng nút, dẫn đến thời gian thực hiện tương tự như list thông thường. Vì vậy, cả hai đều có ưu nhược điểm riêng, tùy thuộc vào nhu cầu sử dụng mà chúng ta có thể chọn giải pháp phù hợp nhất.

Thực hiện nội bộ của list chính là một danh sách liên kết hai chiều. Trong phần chú thích đầu tệ ckết quả bóng đá việt nam hôm nay, quicklist được mô tả như sau:

A doubly linked list of ziplists

Nó thực sự là một danh sách liên kết hai chiều và là danh sách liên kết hai chiều với ziplist.

Điều này có nghĩa là gì?

Chúng ta đều hiểu rằngboi tu vi, danh sách liên kết hai chiều được tạo thành từ nhiều nút (Node). Điều này có nghĩa là mỗi nút trong quicklist đều là một ziplist. Ziplist - một cấu trúc dữ liệu đặc biệt mà chúng ta đã tìm hiểu trước đây... Bài trước Đã được giới thiệu.

Ziplist về bản chất là một danh sách có khả năng duy trì thứ tự của các mục dữ liệu theo thứ tự chúng được thêm vàoboi tu vi, đồng thời nó cũng là một danh sách tối ưu về bộ nhớ, trong đó các mục dữ liệu sẽ liên tiếp kề nhau trong bộ nhớ. Ví dụ, nếu bạn có một quicklist gồm 3 nút và mỗi nút ziplist chứa 4 mục dữ liệu, thì nhìn chung, danh sách này sẽ hiển thị tổng cộng 12 mục dữ liệu. Điều này cho thấy sự hiệu quả khi sử dụng ziplist trong việc quản lý và sắp xếp dữ liệu, giúp tối ưu hóa không gian lưu trữ mà vẫn đảm bảo tính liền mạch giữa các phần tử.

Tại sao quicklist lại được thiết kế theo cách này? Nếu tóm tắt lạikết quả bóng đá việt nam hôm nay, đó thực chất là một sự đánh đổi giữa không gian và thời gian: Trong thế giới lập trình, khi xây dựng một cấu trúc dữ liệu, chúng ta thường phải đối mặt với lựa chọn giữa việc tối ưu hóa tốc độ truy xuất dữ liệu (thời gian) và việc tiết kiệm bộ nhớ (không gian). Quicklist dường như đã chọn con đường giữa hai cực đoan này. Nó không chỉ đảm bảo rằng các thao tác thêm/xóa dữ liệu diễn ra nhanh chóng mà còn duy trì mức tiêu thụ bộ nhớ ở mức hợp lý. Một lý do khác cho sự thiết kế này có thể là do nó giúp tối ưu hóa hiệu suất trong các hệ thống lớn, nơi mà cả tốc độ và dung lượng lưu trữ đều đóng vai trò quan trọng. Quicklist có thể được điều chỉnh để phù hợp với nhiều loại ứng dụng khác nhau, từ những hệ thống thời gian thực đòi hỏi độ chính xác cao đến các nền tảng xử lý hàng loạt dữ liệu lớn.

  • Danh sách liên kết hai chiều rất thuận tiện cho việc thực hiện các thao tác push và pop ở cả hai đầu của danh sáchboi tu vi, nhưng nó đòi hỏi một lượng bộ nhớ khá lớn. Trước hết, ngoài việc lưu trữ dữ liệu, mỗi nút trong danh sách này còn cần phải lưu thêm hai con trỏ để theo dõi mối quan hệ với các nút kế cận. Tiếp đến, các nút trong danh sách liên kết hai chiều là những khối bộ nhớ riêng lẻ, không được sắp xếp liên tục trong bộ nhớ. Khi số lượng nút tăng lên, khả năng cao sẽ tạo ra các mảnh vỡ bộ nhớ (memory fragmentation), làm ảnh hưởng đến hiệu suất của chương trình.
  • Vì ziplist là một khối bộ nhớ liên tụckết quả bóng đá việt nam hôm nay, nó có hiệu quả lưu trữ rất cao. Tuy nhiên, điều này cũng đồng nghĩa với việc các thao tác sửa đổi gặp nhiều bất lợi. Mỗi khi có sự thay đổi dữ liệu, bộ nhớ cần được realloc lại một lần. Đặc biệt, khi kích thước của ziplist ngày càng dài, việc realloc có thể dẫn đến việc sao chép hàng loạt dữ liệu, làm giảm hiệu suất đáng kể. Một khi khối lượng dữ liệu tăng lên, khả năng quản lý bộ nhớ trở nên phức tạp hơn. Khi dữ liệu lớn dần, việc di chuyển toàn bộ dữ liệu sang vùng nhớ mới không chỉ làm gián đoạn hoạt động mà còn gia tăng chi phí tài nguyên. Điều này không chỉ ảnh hưởng đến hiệu quả xử lý mà còn tạo ra áp lực đáng kể cho hệ thống. Vì vậy, việc sử dụng ziplist trong trường hợp cần nhiều thao tác chỉnh sửa thường không phải là lựa chọn tối ưu.

Vì vậykết quả bóng đá việt nam hôm nay, kết hợp các ưu điểm của danh sách liên kết hai chiều và ziplist, quicklist đã ra đời.

Tuy nhiênkết quả bóng đá ngoại hạng anh, điều này cũng đặt ra một vấn đề mới: một nút quicklist nên chứa bao nhiêu phần tử trong ziplist là hợp lý? Ví dụ, cùng là để lưu trữ 12 mục dữ liệu, chúng ta có thể thiết lập một quicklist gồm 3 nút, mỗi nút ziplist chứa 4 mục, hoặc cũng có thể tạo một quicklist với 6 nút, mỗi nút ziplist chỉ chứa 2 mục. Sự lựa chọn này phụ thuộc vào nhiều yếu tố như hiệu suất truy xuất và mức độ tối ưu hóa bộ nhớ. Một quicklist với ít nút hơn nhưng mỗi nút chứa nhiều phần tử trong ziplist có thể cải thiện tốc độ duyệt qua danh sách, nhưng lại tiềm ẩn nguy cơ tốn bộ nhớ hơn khi các mục dữ liệu không được phân bổ đồng đều. Ngược lại, một quicklist với nhiều nút nhỏ hơn sẽ làm giảm áp lực bộ nhớ, nhưng sẽ cần thêm thời gian để xử lý khi thực hiện các thao tác trên danh sách. Vì vậy, việc xác định cấu trúc phù hợp đòi hỏi phải cân nhắc kỹ lưỡng giữa tốc độ truy xuất và việc quản lý tài nguyên hiệu quả. Điều này không chỉ liên quan đến cách sắp xếp dữ liệu mà còn ảnh hưởng trực tiếp đến hiệu năng tổng thể của hệ thống.

Đây lại là một vấn đề khó cần tìm điểm cân bằng. Chúng ta chỉ phân tích từ góc độ hiệu quả lưu trữ:

  • Khi mỗi nút quicklist có ziplist càng ngắnboi tu vi, mức độ mảnh nhớ phân mảnh sẽ càng tăng. Khi số lượng mảnh nhớ phân mảnh gia tăng, nó có thể tạo ra nhiều khoảng trống nhỏ không thể được sử dụng trong bộ nhớ, từ đó làm giảm hiệu quả lưu trữ. Tình huống cực đoan của điều này là khi mỗi nút quicklist chỉ chứa một mục dữ liệu trong ziplist, điều này về cơ bản đã biến nó thành một danh sách liên kết hai chiều thông thường.
  • Mỗi nút quicklist với ziplist dài hơn sẽ khiến việc cấp phát một vùng nhớ liên tục lớn cho ziplist trở nên khó khăn hơn. Có thể xảy ra tình trạng trong bộ nhớ có nhiều mảnh trống nhỏkết quả bóng đá ngoại hạng anh, tổng cộng chúng có dung lượng đáng kể, nhưng không tìm được một mảnh trống đủ lớn để gá Điều này cũng sẽ làm giảm hiệu quả lưu trữ. Trường hợp cực đoan của vấn đề này là quicklist chỉ có duy nhất một nút, và tất cả các mục dữ liệu đều được phân bổ vào ziplist của chính nút này. Thực chất, điều đó đã biến quicklist thành một ziplist đơn thuần.

Rõ ràngboi tu vi, việc duy trì độ dài hợp lý cho ziplist trên một nút quicklist là rất quan trọng. Tuy nhiên, "hợp lý" ở đây cụ thể là bao nhiêu phần tử thì còn tùy thuộc vào từng ngữ cảnh sử dụng riêng biệt. Trong thực tế, Redis cung cấp một tham số cấu hình để giúp người dùng tùy chỉnh hành vi này theo nhu cầu của mình. Tham số này cho phép bạn điều chỉnh giới hạn tối đa hoặc tối thiểu của các phần tử trong ziplist, từ đó cân bằng giữa hiệu suất và tài nguyên hệ thống. Việc hiểu rõ về cách hoạt động của tham số này có thể giúp bạn tối ưu hóa hiệu suất của Redis trong môi trường thực tế. list-max-ziplist-size boi tu vi, để người dùng có thể điều chỉnh theo tình huống riêng của mình.

								
									list-max-ziplist-size -2

								

Chúng ta hãy giải thích chi tiết ý nghĩa của tham số này. Nó có thể lấy giá trị dương hoặc âm.

Khi giá trị được đặt thành dươngkết quả bóng đá ngoại hạng anh, điều này có nghĩa là giới hạn độ dài của ziplist trên mỗi nút quicklist sẽ được xác định bởi số lượng mục dữ liệu. Ví dụ, khi tham số này được cấu hình thành 5, nó có nghĩa là mỗi nút quicklist chỉ có thể chứa tối đa 5 mục dữ liệu trong ziplist của nó. Ngoài ra, việc điều chỉnh giá trị này không chỉ giúp quản lý bộ nhớ hiệu quả mà còn ảnh hưởng đến cách dữ liệu được truy xuất và xử lý trong hệ thống. Một giá trị nhỏ hơn có thể làm tăng tốc độ truy xuất nhưng cũng đồng nghĩa với việc cần nhiều nút quicklist hơn để lưu trữ toàn bộ dữ liệu, từ đó có thể dẫn đến sự gia tăng về chi phí tài nguyên. Ngược lại, một giá trị lớn hơn sẽ giảm số lượng nút cần thiết nhưng có thể làm chậm quá trình truy cập vào dữ liệu nếu khối lượng thông tin quá lớn.

Khi sử dụng giá trị âmkết quả bóng đá ngoại hạng anh, điều này cho thấy rằng kích thước của mỗi nút quicklist sẽ được giới hạn bởi số lượng byte chiếm dụng, cụ thể là độ dài của ziplist. Ở chế độ này, giá trị chỉ có thể nằm trong khoảng từ -1 đến -5, và ý nghĩa của từng giá trị như sau:

  • Mỗi nút quicklist trên bảng chú thích ziplist không được vượt quá kích thước 64 KB (chú ý: 1 KB tương đương với 1024 byte). Điều này giúp tối ưu hóa bộ nhớ và đảm bảo hiệu suất hoạt động ổn định cho hệ thống.
  • -4: Kích thước ziplist trên mỗi nút quicklist không được vượt quá 32 Kb.
  • -3: Kích thước ziplist trên mỗi nút quicklist không được vượt quá 16 Kb.
  • Mỗi nút quicklist trong Redis không được phép vượt quá 8 KB về kích thước của ziplist. Đây là một giới hạn mặc định mà dự án đã thiết lập (-2 tượng trưng cho giá trị mặc định này). Điều này giúp duy trì hiệu suất và quản lý bộ nhớ một cách tối ưu khi xử lý dữ liệu.
  • -1: Kích thước ziplist trên mỗi nút quicklist không được vượt quá 4 Kb.

Ngoài rakết quả bóng đá ngoại hạng anh, mục tiêu thiết kế của list là để có thể lưu trữ danh sách dữ liệu dài một cách hiệu quả. Ví dụ, trong hướng dẫn này được cung cấp trên trang web chính thức của Redis: Writing a simple Twitter clone with PHP and Redis kết quả bóng đá ngoại hạng anh, nghĩa là sử dụng list để lưu trữ dữ liệu timeline giống như Twitter.

Khi danh sách trở nên dàikết quả bóng đá ngoại hạng anh, dữ liệu ở hai đầu danh sách thường có xu hướng được truy cập nhiều hơn so với những phần giữa. Việc truy cập vào các phần giữa không chỉ ít tần suất hơn mà còn đòi hỏi hiệu suất thấp hơn. Nếu ứng dụng của bạn phù hợp với đặc điểm này, thì danh sách (list) cũng cung cấp một tùy chọn để nén các nút giữa, giúp tối ưu hóa việc sử dụng bộ nhớ. Trong Redis, tham số cấu hình có thể được điều chỉnh để tối ưu hóa cách xử lý danh sách dựa trên nhu cầu cụ thể của bạn. Tham số này cho phép người dùng kiểm soát mức độ nén, từ đó cân bằng giữa hiệu suất truy xuất và việc tiết kiệm tài nguyên. Điều này đặc biệt hữu ích khi bạn cần quản lý một lượng lớn dữ liệu trong bộ nhớ, nhưng vẫn muốn duy trì hiệu quả cao nhất trong các hoạt động liên quan đến danh sách. Với cách tiếp cận linh hoạt này, Redis mang lại khả năng tùy chỉnh mạnh mẽ cho các nhà phát triển để tối ưu hóa hệ thống theo yêu cầu riêng biệt. list-compress-depth Là để hoàn thành việc thiết lập này.

								
									list-compress-depth 0

								

Tham số này thể hiện số lượng nút ở hai đầu của quicklist mà không bị nén. Lưu ý: Số lượng nút ở đây đề cập đến số nút trong danh sách liên kết đôi của quicklistkết quả bóng đá việt nam hôm nay, chứ không phải số mục dữ liệu bê Thực tế, khi một nút quicklist được nén, toàn bộ ziplist trên đó sẽ bị nén như một khối duy nhất.

Tham số list-compress-depth Ý nghĩa của giá trị tham số như sau:

Do 0 là một giá trị đặc biệtkết quả bóng đá ngoại hạng anh, có thể dễ dàng nhận thấy rằng nút đầu tiên và nút cuối cùng của danh sách quicklist luôn không bị nén lại. Điều này giúp việc truy xuất dữ liệu ở hai đầu danh sách trở nên nhanh chóng và hiệu quả hơn.

Thuật toán nén nội bộ của quicklist trong Redis sử dụng LZF —— một thuật toán nén không mất dữ liệu.

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

Định nghĩa cấu trúc dữ liệu của quicklist có thể tìm thấ h:

								
									
										typedef
									 struct
									 quicklistNode
									 {
									
    struct
									 quicklistNode
									 *
									prev
									;
									
    struct
									 quicklistNode
									 *
									next
									;
									
    unsigned
									 char
									 *
									zl
									;
									
    unsigned
									 int
									 sz
									;
									             /* ziplist size in bytes */
									
    unsigned
									 int
									 count
									 :
									 16
									;
									     /* count of items in ziplist */
									
    unsigned
									 int
									 encoding
									 :
									 2
									;
									   /* RAW==1 or LZF==2 */
									
    unsigned
									 int
									 container
									 :
									 2
									;
									  /* NONE==1 or ZIPLIST==2 */
									
    unsigned
									 int
									 recompress
									 :
									 1
									;
									 /* was this node previous compressed? */
									
    unsigned
									 int
									 attempted_compress
									 :
									 1
									;
									 /* node can't compress; too small */
									
    unsigned
									 int
									 extra
									 :
									 10
									;
									 /* more bits to steal for future usage */
									
}
									 quicklistNode
									;
									

typedef
									 struct
									 quicklistLZF
									 {
									
    unsigned
									 int
									 sz
									;
									 /* LZF size in bytes*/
									
    char
									 compressed
									[];
									
}
									 quicklistLZF
									;
									

typedef
									 struct
									 quicklist
									 {
									
    quicklistNode
									 *
									head
									;
									
    quicklistNode
									 *
									tail
									;
									
    unsigned
									 long
									 count
									;
									        /* total count of all entries in all ziplists */
									
    unsigned
									 int
									 len
									;
									           /* number of quicklistNodes */
									
    int
									 fill
									 :
									 16
									;
									              /* fill factor for individual nodes */
									
    unsigned
									 int
									 compress
									 :
									 16
									;
									 /* depth of end nodes not to compress;0=off */
									
}
									 quicklist
									;
									

								

quicklistNode là cấu trúc đại diện cho một nút trong quicklistkết quả bóng đá ngoại hạng anh, với ý nghĩa của từng trường như sau:

  • prev: Con trỏ trỏ đến nút trước trong danh sách liên kết.
  • next: Con trỏ trỏ đến nút sau trong danh sách liên kết.
  • Trỏ dữ liệu: Nếu dữ liệu của nút hiện tại không được nénboi tu vi, trỏ này sẽ hướng đến một cấu trúc ziplist; ngược lại, nó sẽ hướng đến một cấu trú Cấu trúc ziplist lưu trữ dữ liệu ở định dạng không nén, trong khi quicklistLZF lưu trữ phiên bản nén của dữ liệu, giúp tối ưu hóa bộ nhớ và cải thiện hiệu suất đọc ghi.
  • sz: Biểu thị kích thước tổng của ziplist mà con trỏ zl đang trỏ đến (bao gồm zlbytes , zltail , zllen , zlend Bạn có thể tương tác với các mục dữ liệu khác nhau. Tuy nhiênkết quả bóng đá việt nam hôm nay, cần lưu ý rằng: nếu ziplist đã được nén, giá trị của "sz" vẫn sẽ là kích thước của ziplist trước khi bị nén. Điều này có nghĩa là dù dữ liệu đã được nén lại, thông số "sz" vẫn giữ nguyên giá trị ban đầu, phản ánh kích thước gốc của ziplist trước khi quá trình nén diễn ra. Đây là một điểm đặc biệt mà bạn nên ghi nhớ khi làm việc với định dạng dữ liệu này.
  • biến count: biểu thị số lượng mục dữ liệ Trường này chỉ có 16 bit. Sau đâyboi tu vi, chúng ta sẽ cùng nhau tìm hiểu xem liệu 16 bit này có đủ để lưu trữ tất cả các thông tin cần thiết hay không. Nhìn chung, việc sử dụng một trường có kích thước nhỏ như vậy cho phép tối ưu hóa bộ nhớ, nhưng cũng đặt ra thách thức khi cần quản lý nhiều phần tử hơn mức dự kiến ban đầu.
  • Mã hóa: cho biết liệu ziplist có bị nén (và sử dụng thuật toán nén nào) hay không. Hiện tại chỉ có hai giá trị có thể: 2 nghĩa là đã được nén và sử dụng thuật toán nén cụ thể như zlibkết quả bóng đá ngoại hạng anh, một trong những phương pháp phổ biến để tối ưu hóa không gian lưu trữ. LZF thuật toán nén)boi tu vi, 1 biểu thị không nén.
  • Container: Đây là một trường dự trữ được thiết kế ban đầu để xác định xem một nút quicklist bên dưới sẽ lưu trữ dữ liệu trực tiếp hay sử dụng ziplist để lưu trữboi tu vi, hoặc có thể sử dụng cấu trúc khác để lưu trữ dữ liệu (vì nó đóng vai trò như một thùng chứa dữ liệu, nên được đặt tên là container). Tuy nhiên, trong triển khai hiện tại, giá trị của trường này luôn cố định ở giá trị 2, biểu thị việc sử dụng ziplist làm phương thức lưu trữ dữ liệu. Trong quá trình phát triển, ý tưởng ban đầu về sự linh hoạt trong cách chọn phương thức lưu trữ dường như đã không được thực hiện đầy đủ, và hiện tại container chỉ duy trì một chức năng đơn giản với giá trị cố định, nhằm tối ưu hóa hiệu suất bằng cách sử dụng ziplist như một giải pháp phổ biến cho việc lưu trữ dữ liệ
  • Khi chúng ta sử dụng các lệnh như lindex để xem một phần dữ liệu đã được nénkết quả bóng đá việt nam hôm nay, hệ thống cần tạm thời giải nén dữ liệu. Khi đó, tùy chọn recompress=1 sẽ được đặt làm dấu hiệu để thông báo rằng dữ liệu cần được nén lại khi có cơ hội phù hợp. Điều này giúp đảm bảo rằng dữ liệu vẫn duy trì trạng thái tối ưu về không gian lưu trữ sau khi thao tác xem hoặc chỉnh sửa hoàn tất.
  • Giá trị này chỉ có ý nghĩa đối với chương trình kiểm thử tự động của Redisboi tu vi, vì vậy chúng ta không cần phải quan tâm đến nó.
  • extra: Trường mở rộng khác. Hiện tại trong triển khai của Redis không sử dụng trường này.

Cấu trúc quicklistLZF đại diện cho một ziplist đã được nén. Trong đó:

Cấu trúc thực sự đại diện cho quicklist là cấu trúc cùng tên quicklist này:

  • head: Con trỏ trỏ đến nút đầu tiên (nút đầu tiên bên trái) của danh sách.
  • tail: Con trỏ trỏ đến nút cuối cùng (nút đầu tiên bên phải) của danh sách.
  • count: Tổng số lượng mục dữ liệu của tất cả các ziplist.
  • len: Số lượng nú
  • fill: 16 bitkết quả bóng đá ngoại hạng anh, cài đặt kích thước ziplist, lưu trữ list-max-ziplist-size giá trị của tham số.
  • compress: 16 bitkết quả bóng đá việt nam hôm nay, cài đặt độ sâu nén nút, lưu trữ list-compress-depth giá trị của tham số.

Hình ảnh cấu trúc quicklist của Redis

Hình ảnh phía trên là một ví dụ về cấu trúc củ Trong trường hợp nàykết quả bóng đá việt nam hôm nay, kích thước ziplist và mức độ nén của các nút được thiết lập như sau:

								
									list-max-ziplist-size 3
list-compress-depth 2

								

Những điểm cần chú ý trong ví dụ này là:

  • Ở mỗi đầu có hai nút màu cam vàngkết quả bóng đá việt nam hôm nay, những nút này chưa bị nén. Pointer dữ liệu của chúng, zl, trỏ đến ziplist thực sự. Những nút còn lại ở giữa đã bị nén, pointer dữ liệu zl của chúng trỏ đến cấu trúc ziplist đã được nén, tức là một cấu trú
  • Ở nút đầu tiên bên tráiboi tu vi, danh sách liên kết đơn ziplist chứa 2 phần tử dữ liệu, trong khi ở nút cuối cùng bên phải, ziplist có 1 phần tử. Các nút còn lại ở giữa đều có ziplist chứa đúng 3 phần tử (bao gồm cả các phần tử nằm trong nút đã được nén). Điều này cho thấy rằng các thao tác chỉnh sửa, như thêm hoặc xóa phần tử, đã diễn ra nhiều lần tại hai đầu của bảng. Có thể nói, sự khác biệt này là dấu hiệu rõ ràng về cách mà dữ liệu đã được xử lý và quản lý qua thời gian, đặc biệt ở các khu vực đầu và cuối của cấu trúc dữ liệu này. push Ghi giá trị pop Một trạng thái sau khi thực hiện thao tác.

Bây giờ chúng ta hãy tính toán sơ bộ xem việc sử dụng 16 bit cho trường count trong cấu trúc quicklistNode có đủ để lưu trữ thông tin hay không. Trong QuicklistNodekết quả bóng đá ngoại hạng anh, count thường được dùng để đếm số lượng phần tử hoặc kích thước của dữ liệu. Với 16 bit, chúng ta có thể biểu diễn tối đa 2^16 = 65,536 giá trị khác nhau. Điều này có nghĩa là nó có thể xử lý các đối tượng hoặc phần tử có kích thước lên đến 65,535 đơn vị. Tuy nhiên, điều quan trọng là phải xem xét kỹ lưỡng các yêu cầu thực tế của ứng dụng. Ví dụ như nếu mỗi phần tử cần lưu trữ một lượng lớn thông tin, thì khả năng này có thể nhanh chóng bị vượt qua. Do đó, cần đánh giá kỹ lưỡng về mặt hiệu suất và yêu cầu bộ nhớ trước khi quyết định có nên thay đổi kích thước của count hay không. Nếu ứng dụng của bạn dự kiến sẽ xử lý một số lượng lớn các phần tử hoặc đối tượng lớn hơn, thì có lẽ cần xem xét tăng kích thước của count lên 32 bit để đảm bảo đủ dung lượng. Tuy nhiên, điều này cũng đồng nghĩa với việc tiêu tốn thêm tài nguyên bộ nhớ. Vì vậy, việc xác định xem liệu 16 bit có đủ hay không phụ thuộc rất nhiều vào đặc thù của từng ứng dụng cụ thể.

Chúng ta đã biết rằng kích thước ziplist bị hạn chế bởi list-max-ziplist-size tham số. Có hai trường hợp dựa trên giá trị dương và âm:

  • Khi tham số này có giá trị dươngkết quả bóng đá ngoại hạng anh, nó chính xác biểu thị số lượng tối đa các mục dữ liệu được lưu trữ trong ziplist mà zl đang trỏ đến trong cấu trú list-max-ziplist-size Các tham số được lưu trữ trong trường fill của cấu trú Vì trường fill có kích thước 16 bitkết quả bóng đá việt nam hôm nay, nên các giá trị mà nó có thể biểu diễn đều có thể được biểu thị bằng 16 bit. Điều này cho phép tối ưu hóa không gian lưu trữ và đảm bảo hiệu suất cao trong việc quản lý dữ liệu.
  • Khi tham số này nhận giá trị âmkết quả bóng đá việt nam hôm nay, độ dài tối đa mà ziplist có thể biểu diễn là 64 KB. Trong ziplist, mỗi phần tử dữ liệu cần ít nhất 2 byte để lưu trữ: 1 byte cho việc... prevrawlen boi tu vi, trường hợp 1 byte của data len trường và data hợp nhất; xem thêm Bài trước Vì vậykết quả bóng đá ngoại hạng anh, số lượng mục dữ liệu trong ziplist sẽ không vượt quá 32K, và việc sử dụng 16 bit để biểu diễn là đủ. Điều này giúp tối ưu hóa không gian lưu trữ một cách hiệu quả, đồng thời đảm bảo rằng các thao tác truy xuất dữ liệu vẫn diễn ra nhanh chóng mà không gặp bất kỳ trở ngại nào.

Trên thực tếkết quả bóng đá ngoại hạng anh, trong cách triển khai hiện tại của quicklist, kích thước của ziplist còn bị giới hạn bởi một số yếu tố khác và sẽ không bao giờ đạt đến giá trị tối đa được phân tích ở đây. Những giới hạn này xuất phát từ các cấu hình hệ thống và cách dữ liệu được lưu trữ bên trong, khiến khả năng đạt đến mức tối đa lý thuyết trở nên rất khó xảy ra trong thực tế.

Bây giờ chuyển sang giai đoạn phân tích mã nguồn.

Tạo quicklist

Khi chúng ta sử dụng lpush hoặc rpush lệnh để chèn dữ liệu lần đầu tiên vào một danh sách chưa tồn tạikết quả bóng đá việt nam hôm nay, Redis sẽ gọi quicklistCreate giao diện để tạo một quicklist rỗng.

								
									
										quicklist
									 *
									quicklistCreate
									(
									void
									)
									 {
									
    struct
									 quicklist
									 *
									quicklist
									;
									

    quicklist
									 =
									 zmalloc
									(
									sizeof
									(
									*
									quicklist
									));
									
    quicklist
									->
									head
									 =
									 quicklist
									->
									tail
									 =
									 NULL
									;
									
    quicklist
									->
									len
									 =
									 0
									;
									
    quicklist
									->
									count
									 =
									 0
									;
									
    quicklist
									->
									compress
									 =
									 0
									;
									
    quicklist
									->
									fill
									 =
									 -
									2
									;
									
    return
									 quicklist
									;
									
}
									

								

Trong nhiều cuốn sách hướng dẫn về cấu trúc dữ liệukết quả bóng đá ngoại hạng anh, khi triển khai danh sách liên kết hai chiều, người ta thường thêm vào một nút đầu trống để tạo điều kiện thuận lợi cho các thao tác chèn và xóa. Điều này giúp việc quản lý danh sách trở nên dễ dàng hơn bởi vì nó cung cấp một điểm tham chiếu cố định mà không cần phải kiểm tra xem danh sách có rỗng hay không mỗi khi thực hiện các hoạt động trên phần tử đầu tiên hoặc cuối cùng. Điều đó đồng nghĩa với việc coder sẽ không còn phải đối mặt với các trường hợp ngoại lệ phức tạp liên quan đến việc kiểm tra trạng thái của danh sách trong quá trình xử lý. quicklistCreate Mã nguồn có thể thấy rằng quicklist là một danh sách liên kết hai chiều không có nút đầu thừa (các head Ghi giá trị tail đều được khởi tạo thành NULL).

Thao tác push của quicklist

Thao tác push của quicklist được thực hiện bằng cách gọi quicklistPush để thực hiện.

								
									
										void
									 quicklistPush
									(
									quicklist
									 *
									quicklist
									,
									 void
									 *
									value
									,
									 const
									 size_t
									 sz
									,
									
                   int
									 where
									)
									 {
									
    if
									 (
									where
									 ==
									 QUICKLIST_HEAD
									)
									 {
									
        quicklistPushHead
									(
									quicklist
									,
									 value
									,
									 sz
									);
									
    }
									 else
									 if
									 (
									where
									 ==
									 QUICKLIST_TAIL
									)
									 {
									
        quicklistPushTail
									(
									quicklist
									,
									 value
									,
									 sz
									);
									
    }
									
}
									

/* Add new entry to head node of quicklist.
 *
 * Returns 0 if used existing head.
 * Returns 1 if new head created. */
									
int
									 quicklistPushHead
									(
									quicklist
									 *
									quicklist
									,
									 void
									 *
									value
									,
									 size_t
									 sz
									)
									 {
									
    quicklistNode
									 *
									orig_head
									 =
									 quicklist
									->
									head
									;
									
    if
									 (
									likely
									(
									
            _quicklistNodeAllowInsert
									(
									quicklist
									->
									head
									,
									 quicklist
									->
									fill
									,
									 sz
									)))
									 {
									
        quicklist
									->
									head
									->
									zl
									 =
									
            ziplistPush
									(
									quicklist
									->
									head
									->
									zl
									,
									 value
									,
									 sz
									,
									 ZIPLIST_HEAD
									);
									
        quicklistNodeUpdateSz
									(
									quicklist
									->
									head
									);
									
    }
									 else
									 {
									
        quicklistNode
									 *
									node
									 =
									 quicklistCreateNode
									();
									
        node
									->
									zl
									 =
									 ziplistPush
									(
									ziplistNew
									(),
									 value
									,
									 sz
									,
									 ZIPLIST_HEAD
									);
									

        quicklistNodeUpdateSz
									(
									node
									);
									
        _quicklistInsertNodeBefore
									(
									quicklist
									,
									 quicklist
									->
									head
									,
									 node
									);
									
    }
									
    quicklist
									->
									count
									++
									;
									
    quicklist
									->
									head
									->
									count
									++
									;
									
    return
									 (
									orig_head
									 !=
									 quicklist
									->
									head
									);
									
}
									

/* Add new entry to tail node of quicklist.
 *
 * Returns 0 if used existing tail.
 * Returns 1 if new tail created. */
									
int
									 quicklistPushTail
									(
									quicklist
									 *
									quicklist
									,
									 void
									 *
									value
									,
									 size_t
									 sz
									)
									 {
									
    quicklistNode
									 *
									orig_tail
									 =
									 quicklist
									->
									tail
									;
									
    if
									 (
									likely
									(
									
            _quicklistNodeAllowInsert
									(
									quicklist
									->
									tail
									,
									 quicklist
									->
									fill
									,
									 sz
									)))
									 {
									
        quicklist
									->
									tail
									->
									zl
									 =
									
            ziplistPush
									(
									quicklist
									->
									tail
									->
									zl
									,
									 value
									,
									 sz
									,
									 ZIPLIST_TAIL
									);
									
        quicklistNodeUpdateSz
									(
									quicklist
									->
									tail
									);
									
    }
									 else
									 {
									
        quicklistNode
									 *
									node
									 =
									 quicklistCreateNode
									();
									
        node
									->
									zl
									 =
									 ziplistPush
									(
									ziplistNew
									(),
									 value
									,
									 sz
									,
									 ZIPLIST_TAIL
									);
									

        quicklistNodeUpdateSz
									(
									node
									);
									
        _quicklistInsertNodeAfter
									(
									quicklist
									,
									 quicklist
									->
									tail
									,
									 node
									);
									
    }
									
    quicklist
									->
									count
									++
									;
									
    quicklist
									->
									tail
									->
									count
									++
									;
									
    return
									 (
									orig_tail
									 !=
									 quicklist
									->
									tail
									);
									
}
									

								

Dù chèn dữ liệu vào đầu hoặc đuôikết quả bóng đá ngoại hạng anh, đều bao gồm hai trường hợp:

  • Nếu kích thước ziplist trên nút đầu tiên (hoặc nút cuối cùng) không vượt quá giới hạn (tức là _quicklistNodeAllowInsert trả về 1)boi tu vi, thì dữ liệu mới sẽ được chèn trực tiếp vào ziplist (gọi ziplistPush )。
  • Nếu danh sách liên kết ziplist ở nút đầu (hoặc nút cuối) quá lớnkết quả bóng đá việt nam hôm nay, thì sẽ tạo ra một nút quicklistNode mới (cùng lúc cũng tạo ra một ziplist mới). Sau đó, nút mới này sẽ được chèn vào danh sách liên kết hai chiều quicklist (thực hiện thông qua việc gọi hàm). _quicklistInsertNodeAfter )。

Điều nàyboi tu vi, giá trị đọc được _quicklistInsertNodeAfter thực hiệnkết quả bóng đá việt nam hôm nay, còn sẽ nén các nút bên trong theo list-compress-depth cấu hình). Việc thực hiện nó khá phức tạpkết quả bóng đá ngoại hạng anh, chúng ta ở đây không đi sâu vào chi tiết.

Các thao tác khác của quicklist

Các hoạt động của quicklist khá đa dạng và chi tiết thực hiện cũng tương đối phức tạp. Thay vì đi sâu phân tích từng dòng mã nguồnboi tu vi, chúng ta sẽ briefly giới thiệu qua một số thao tác quan trọng nhất. Trong hệ thống quicklist, việc thêm hoặc xóa các mục là những chức năng cốt lõi mà bất kỳ người dùng nào cũng cần hiểu rõ. Đầu tiên, khi thêm một mục mới vào quicklist, hệ thống phải kiểm tra vị trí thích hợp để đảm bảo thứ tự logic và không gây xung đột với các mục khác. Tiếp theo, việc xóa một mục yêu cầu xác nhận trạng thái của các mục liên kết, tránh tình trạng mất dữ liệu hoặc lỗi trong quá trình vận hành. Bên cạnh đó, việc sắp xếp lại các mục trong quicklist cũng là một thao tác quan trọng. Hệ thống thường sử dụng thuật toán dựa trên tần suất truy cập hoặc thời gian sử dụng gần nhất để sắp xếp lại danh sách cho phù hợp với thói quen người dùng. Điều này giúp cải thiện trải nghiệm tổng thể và tối ưu hóa hiệu suất. Như vậy, mặc dù quicklist có nhiều thao tác phức tạp, nhưng nếu nắm được những tính năng chính như trên, bạn đã có thể hiểu cơ bản về cách hoạt động của nó.

Thao tác pop của quicklist được thực hiện bằng cách gọi quicklistPopCustom để thực hiện. quicklistPopCustom Quá trình này cơ bản ngược lại so vớ Đầu tiênboi tu vi, bạn cần xóa phần tử dữ liệu tương ứng từ ziplist của nút đầu hoặc cuối danh sách. Nếu sau khi xóa mà ziplist trở nên trống rỗng, thì nút đầu hoặc cuối cũng cần được xóa đi. Sau khi xóa, có thể sẽ xuất hiện tình huống cần giải nén các nút bên trong để duy trì tính toàn vẹn của cấu trúc.

quicklist không chỉ thực hiện chèn từ đầu hoặc đuôikết quả bóng đá ngoại hạng anh, mà còn thực hiện chèn từ bất kỳ vị trí cụ thể nào. quicklistInsertAfter Ghi giá trị quicklistInsertBefore Bạn có thể chèn thêm các mục dữ liệu ngay sau hoặc ngay trước vị trí được chỉ định. Hoạt động chèn dữ liệu tại bất kỳ vị trí nào tùy ý này khá phức tạpkết quả bóng đá việt nam hôm nay, với nhiều nhánh logic khác nhau cần được xử lý. Một số trường hợp đặc biệt có thể yêu cầu kiểm tra điều kiện trước khi thực hiện thao tác, chẳng hạn như xác nhận xem vị trí đó đã tồn tại hay chưa, hoặc đảm bảo rằng không có xung đột dữ liệu nào xảy ra trong quá trình chèn.

  • Khi kích thước của ziplist tại vị trí chèn không vượt quá giới hạnboi tu vi, bạn chỉ cần chèn trực tiếp vào bên trong ziplist là được.
  • Khi kích thước của ziplist tại vị trí chèn vượt quá giới hạnkết quả bóng đá ngoại hạng anh, nhưng vị trí chèn nằm ở hai đầu của ziplist và kích thước ziplist của nút quicklist liền kề không vượt quá giới hạn, thì thay vì tạo thêm một ziplist mới, dữ liệu sẽ được chèn vào ziplist của nút quicklist liền kề đó. Điều này giúp tối ưu hóa việc sử dụng bộ nhớ và duy trì hiệu suất hệ thống ở mức tốt nhất.
  • Khi kích thước của ziplist tại vị trí chèn vượt quá giới hạnkết quả bóng đá việt nam hôm nay, nhưng vị trí chèn nằm ở hai đầu của ziplist và cả hai ziplist liền kề trong các nút quicklist cũng vượt quá giới hạn, lúc này cần phải tạo một nút quicklist mới để chèn vào. Điều này đảm bảo rằng cấu trúc dữ liệu vẫn hoạt động hiệu quả và tuân theo các nguyên tắc tối ưu hóa bộ nhớ, đặc biệt khi việc xử lý dữ liệu trở nên phức tạp hơn do kích thước lớn của các phần tử
  • Trong trường hợp ziplist có kích thước vượt quá giới hạn mà việc chèn dữ liệu xảy ra ở giữa ziplist (trường hợp phổ biến là chèn vào giữa)kết quả bóng đá ngoại hạng anh, cần phải chia ziplist hiện tại thành hai phần tử riêng biệt. Sau đó, thực hiện thao tác chèn dữ liệu vào một trong hai phần tử này. Điều này giúp duy trì hiệu quả và tối ưu hóa bộ nhớ khi xử lý các cấu trúc dữ liệu lớn hơn mức cho phép.

quicklistSetOptions Được sử dụng để thiết lập tham số cấu hình kích thước ziplist ( list-max-ziplist-size ) và tham số cấu hình độ sâu nén nút ( list-compress-depth Mã code khá đơn giảnboi tu vi, chỉ cần gán các giá trị tương ứng lần lượt cho trường fill và compress trong cấu trú Ngoài ra, đoạn mã này cũng đảm bảo rằng tất cả các tham số đầu vào đã được kiểm tra đầy đủ trước khi thực hiện việc gán giá trị, điều này giúp tăng cường độ ổn định và hiệu suất của chương trình.


Trong bài viết tiếp theoboi tu vi, chúng ta sẽ cùng tìm hiểu về cấu trúc dữ liệu skiplist và cách nó hỗ trợ cho kiểu dữ liệ Hãy cùng đón chờ những chia sẻ thú vị sắp tới nhé!


Bài viết gốcboi tu vi, 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: /qfwbpqpb.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: Blog Programmer Choice
Bài sau: [Truyện ngắn não bộ của lập trình viên] Mô tả ký hiệu ở tận cùng vũ trụ