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

Phân tích sâu cấu trúc dữ liệu bên trong Redis (4) —— ziplist


Phân tích cấu trúc dữ liệu bên trong Redis Phân tích cấu trúc dữ liệu bên trong Redis Bài viết này là phần thứ tư trong loạt bài. Trong phần đầu tiên của bài viếti9bet.com nhận 100k, chúng ta sẽ giới thiệu một cấu trúc dữ liệu nội bộ mới của Redis có tên là ziplist. Sau đó, ở nửa sau của bài viết, chúng ta sẽ thảo luận về cách Redis xây dựng cấu trúc hash mà nó cung cấp bên ngoài dựa trên các thành phần robj, dict và ziplist. Redis luôn tìm cách tối ưu hóa hiệu suất và sử dụng bộ nhớ, do đó việc hiểu rõ về ziplist rất quan trọng. Đây là một cấu trúc đặc biệt được thiết kế để lưu trữ các danh sách liên kết ngắn một cách tiết kiệm không gian. Khi kích thước của danh sách nhỏ và các yêu cầu truy xuất không quá phức tạp, ziplist tỏ ra vô cùng hiệu quả so với các cấu trúc khác như Tiếp theo, chúng ta sẽ khám phá thêm về cách Redis kết hợp ziplist với các thành phần cơ bản khác như robj (đối tượng) và dict (bảng từ điển) để tạo ra các tính năng hash mạnh mẽ. Hash trong Redis không chỉ đơn thuần là một công cụ lưu trữ dữ liệu mà còn là nền tảng cho nhiều ứng dụng thực tế, từ cơ sở dữ liệu đệm đến hệ thống phân tán phức tạp. Với sự hiểu biết về cách các thành phần này hoạt động cùng nhau, bạn sẽ có cái nhìn sâu sắc hơn về cách Redis đạt được hiệu suất cao và khả năng mở rộng linh hoạt trong việc quản lý dữ liệu.

Trong quá trình thảo luậncá cược bóng đá, chúng ta sẽ cùng tìm hiểu thêm về hai cấu hình Redis được đề cập trong phần CẤU HÌNH NÂNG CAO (ADVANCED CONFIG) của tệp redis.conf.

								
									hash-max-ziplist-entries 512
hash-max-ziplist-value 64

								

Phần sau của bài viết này sẽ giải thích chi tiết hai cấu hình này.

Ziplist là gì?

Theo định nghĩa chính thức của Redisi9bet.com nhận 100k, ziplist được mô tả như sau (trích từ phần chú thích đầ c):

The ziplist is a specially encoded dually linked list that is designed to be very memory efficient. It stores both strings and integer valuescá cược bóng đá, where integers are encoded as actual integers instead of a series of characters. It allows push and pop operations on either side of the list in O(1) time.

Ziplist là một danh sách liên kết hai chiều đã được mã hóa theo cách đặc biệti9bet.com nhận 100k, với mục tiêu chính là tối ưu hóa hiệu quả lưu trữ. Ziplist có thể được sử dụng để lưu trữ cả chuỗi ký tự và số nguyên, trong đó các số nguyên sẽ được mã hóa theo dạng biểu diễn nhị phân thực sự thay vì được chuyển thành chuỗi ký tự. Với ziplist, bạn có thể truy xuất hoặc thêm phần tử ở cả hai đầu của danh sách với độ phức tạp thời gian là O(1), điều này giúp tăng tốc đáng kể quá trình xử lý dữ liệu. Một điểm thú vị khác là ziplist còn được tối ưu hóa về mặt không gian lưu trữ nhờ vào việc sử dụng kích thước nhỏ hơn cho từng phần tử, điều này làm cho nó trở thành một lựa chọn lý tưởng khi cần quản lý khối lượng lớn dữ liệu mà không làm tiêu tốn quá nhiều tài nguyên hệ thống. push Một quá trình đọc: Quá trình thứ pop Thao tác.

Trên thực tếi9bet.com nhận 100k, ziplist thể hiện rõ sự hướng tới hiệu quả lưu trữ của Redis. Một danh sách liên kết hai chiều thông thường sẽ khiến mỗi phần tử chiếm một khối bộ nhớ độc lập và được kết nối với nhau thông qua các con trỏ địa chỉ (hoặc tham chiếu). Cách này không chỉ tạo ra nhiều mảnh vụn bộ nhớ mà còn khiến các con trỏ chiếm thêm không gian bộ nhớ. Ngược lại, ziplist lại sắp xếp các phần tử trong cùng một vùng nhớ liên tục, cho phép toàn bộ cấu trúc chỉ sử dụng một khối lớn bộ nhớ duy nhất. Nó giống như một danh sách (list), nhưng thực chất không phải là một danh sách liên kết (linked list) theo nghĩa truyền thống. Thêm vào đó, việc này giúp tối ưu hóa đáng kể không gian lưu trữ và giảm thiểu các vấn đề về bộ nhớ rời rạc.

Ngoài racá cược bóng đá, ziplist được tối ưu hóa để tiết kiệm bộ nhớ ở mức chi tiết nhất bằng cách sử dụng phương pháp mã hóa biến dài cho việc lưu trữ giá trị. Ý nghĩa cơ bản của điều này là với những số nguyên lớn, nó sẽ cần nhiều byte hơn để lưu trữ, trong khi với các số nhỏ, chỉ cần ít byte hơn. Chúng ta sẽ thảo luận chi tiết về những khía cạnh kỹ thuật này ngay sau đây. Trong thực tế, cách tiếp cận này không chỉ giúp giảm thiểu đáng kể lượng bộ nhớ tiêu thụ mà còn đảm bảo rằng dữ liệu vẫn có thể được xử lý một cách hiệu quả. Điều này đặc biệt hữu ích khi phải quản lý một khối lượng lớn dữ liệu trong môi trường tài nguyên hạn chế.

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

Trong bài viết nàyxem ngoại hạng anh, chúng ta sẽ tập trung cấu trúc dữ liệu của ziplist. Thực tế, ziplist có phần phức tạp hơn so với những gì ban đầu nhìn thấy. Điểm khó hiểu chính nằm ở cách định nghĩa cấu trúc dữ liệu của nó. Tuy nhiên, một khi đã nắm rõ được bản chất của cấu trúc này, thì việc hiểu và thực hiện các thao tác trên ziplist cũng sẽ trở nên đơn giản hơn nhiều.

Tiếp theoi9bet.com nhận 100k, chúng ta sẽ bắt đầu bằng cách trình bày khái quát về định nghĩa cấu trúc dữ liệu ziplist, sau đó đưa ra một ví dụ thực tế để giải thích cách ziplist được cấu thành. Nếu bạn đã hiểu phần này, thì bạn đã hoàn thành được phần lớn mục tiêu của bài viết rồi đấy.

Nhìn tổng quancá cược bóng đá, cấu trúc bộ nhớ của ziplist như sau:

<zlbytes><zltail><zllen><entry>...<entry><zlend></zlend></entry></entry></zllen></zltail></zlbytes>

Các phần khác nhau trong bộ nhớ là liền kề nhaucá cược bóng đá, ý nghĩa cụ thể của từng phần như sau:

  • <zlbytes></zlbytes> : 32bitxem ngoại hạng anh, biểu thị tổng số byte mà ziplist chiếm (bao gồm cả <zlbytes></zlbytes> chính nó chiếm 4 byte).
  • <zltail></zltail> Trong trường hợp nàycá cược bóng đá, việc sử dụng định dạng 32 bit nhằm thể hiện khoảng cách bằng byte giữa phần tử cuối cùng (entry) trong bảng ziplist và vị trí bắt đầu của ziplist. Đây là một cách hiệu quả để lưu trữ thông tin về vị trí của các phần tử bên trong cấu trúc dữ liệu, giúp quá trình truy xuất trở nên nhanh chóng và chính xác hơn. <zltail></zltail> Sự hiện diện của nó cho phép chúng ta dễ dàng xác định phần tử cuối cùng trong ziplist mà không cần duyệt qua toàn bộ danh sáchcá cược bóng đá, nhờ đó giúp thực hiện các thao tác push hoặc pop ở cuối ziplist một cách nhanh chóng và hiệu quả.
  • <zllen></zllen> Trong trường hợp nàyxem ngoại hạng anh, trường zllen với kích thước chỉ 16 bit có thể biểu diễn giá trị tối đa là 2^16 - 1. Tuy nhiên, điều quan trọng cần lưu ý là nếu số lượng mục (entry) trong ziplist vượt quá giá trị mà 16 bit có thể biểu diễn, ziplist vẫn có khả năng xử lý tình huống này. Cách thực hiện như sau: Khi số lượng mục vượt khỏi giới hạn của 16 bit, ziplist sẽ sử dụng một quy ước đặc biệt để giải quyết vấn đề này. Theo quy ước đó, nếu... <zllen></zllen> Nếu số lượng mục dữ liệu trong ziplist nhỏ hơn hoặc bằng 2^16-2 (tức là không phải 2^16-1)i9bet.com nhận 100k, thì <zllen></zllen> điều này biểu thị số lượng mục dữ liệu trong ziplist; nếu khôngi9bet.com nhận 100k, tức là <zllen></zllen> trường hợp 16bit toàn bộ là 1cá cược bóng đá, thì <zllen></zllen> Trong trường hợp nàycá cược bóng đá, nếu muốn biết tổng số lượng các mục trong ziplist mà không biểu thị rõ số lượng ban đầu, bạn sẽ cần phải duyệt qua từng mục từ đầu đến cuối của ziplist để đếm số lượng một cách thủ công. Quá trình này đòi hỏi phải thực hiện quét toàn bộ danh sách để có được con số chính xác.
  • <entry></entry> Điểm quan trọng ở đây là các mục chứa dữ liệu thực sựi9bet.com nhận 100k, với độ dài không cố định. Mỗi mục dữ liệu (entry) lại có cấu trúc nội bộ riêng của nó, và chúng ta sẽ đi sâu vào chi tiết này sau.
  • <zlend></zlend> : Byte cuối cùng trong ziplist là một dấu kết thúccá cược bóng đá, giá trị cố định bằng 255.

Điểm đáng chú ý trong định nghĩa trên còn có: <zlbytes></zlbytes> , <zltail></zltail> , <zllen></zllen> Khi một giá trị chiếm nhiều bytecá cược bóng đá, việc lưu trữ sẽ có sự khác biệt giữa định dạng big endian (đại diện cho thứ tự cao đến thấp) và little endian (thứ tự thấp đến cao). Ziplist sử dụng định dạng little endian để lưu trữ, điều này sẽ được giải thích rõ hơn khi chúng ta đi vào các ví dụ cụ thể sau đây.

Hãy nhìn kỹ cấu trúc của mỗi mục dữ liệu <entry></entry> thành phần:

<prevrawlen><len><data></data></len></prevrawlen>

Chúng ta thấy rằng trước dữ liệu thực tế ( <data></data> ) còn có hai trường:

  • <prevrawlen></prevrawlen> Trường này cho biết tổng số byte mà mục dữ liệu trước đó chiếm dụng. Mục đích của trường này là giúp ziplist có thể duyệt ngược từ cuối về đầu (từ vị trí của mục hiện tạii9bet.com nhận 100k, chỉ cần di chuyển lùi lại prevrawlen byte, ta sẽ tìm thấy mục trước đó). Phương pháp mã hóa của trường này sử dụng định dạng biến dài (variable-length encoding), cho phép tối ưu hóa không gian lưu trữ tùy theo giá trị cụ thể.
  • <len></len> : Biểu thị độ dài của mục dữ liệu hiện tại (tức <data></data> phần độ dài). Cũng sử dụng mã hóa biến dài.

Trước tiên nói <prevrawlen></prevrawlen> Một quá trình đọc: Quá trình thứ <len></len> Làm thế nào để thực hiện mã hóa biến đổi độ dài đây? Thưa quý độc giảcá cược bóng đá, hãy tập trung tinh thần vì chúng ta sắp đến phần phức tạp nhất trong định nghĩa ziplist rồi.

. Nó có hai khả năngi9bet.com nhận 100k, hoặc là 1 byte, hoặc là 5 byte: <prevrawlen></prevrawlen> Nếu mục dữ liệu trước đó chiếm ít hơn 254 bytexem ngoại hạng anh, thì

  1. chỉ cần dùng 1 byte để biểu thịi9bet.com nhận 100k, giá trị của byte này là số byte mà mục dữ liệu trước đó chiếm. <prevrawlen></prevrawlen> Nếu mục dữ liệu trước đó chiếm từ 254 byte trở lêncá cược bóng đá, thì
  2. Có người sẽ hỏi rồixem ngoại hạng anh, tại sao lại không có trường hợp 255? <prevrawlen></prevrawlen> Dùng 5 byte để biểu diễnxem ngoại hạng anh, trong đó byte đầu tiên có giá trị là 254 (dùng làm dấu hiệu cho trường hợp này), và 4 byte tiếp theo tạo thành một giá trị nguyên, dùng để lưu trữ chính xác số byte mà mục dữ liệu trước đó chiếm dụng.

Điều này là do: 255 đã được định nghĩa là dấu kết thúc của ziplist

(byte đầu tiên) không thể lấy giá trị 255xem ngoại hạng anh, nếu không sẽ xảy ra xung đột. <zlend></zlend> Trong thực hiện của nhiều thao tác liên quan đến ziplistxem ngoại hạng anh, việc kiểm tra xem liệu giá trị đầu tiên của một mục dữ liệu có phải là byte 255 hay không thường được sử dụng để xác định xem liệu đã đến cuối ziplist hay chưa. Do đó, byte đầu tiên của một mục dữ liệu bình thường (cũng chính là) sẽ đóng vai trò như một dấu hiệu đặc biệt để nhận diện và phân loại các loại giá trị khác nhau trong dãy này. <prevrawlen></prevrawlen> |00pppppp| - 1 byte. Byte đầu tiên cao hai bit là 00cá cược bóng đá, vậy

tất cả đều được lưu trữ dưới dạng chuỗi; bắt đầu từ trường hợp thứ tư bên dướicá cược bóng đá, <len></len> Các trường dữ liệu trở nên phức tạp hơnxem ngoại hạng anh, chúng được chia thành 9 trường hợp khác nhau tùy thuộc vào giá trị của byte đầu tiên (cách biểu diễn dưới đây được thể hiện theo hệ nhị phân):

  1. bắt đầu chuyển sang lưu trữ dưới dạng số nguyên. <len></len> Trường dữ liệu chỉ có một bytexem ngoại hạng anh, trong khi đó 6 bit còn lại được sử dụng để biểu diễn giá trị độ dài, với giá trị tối đa có thể đạt đến là 63 (2^6 - 1). Điều này cho phép hệ thống tối ưu hóa việc quản lý không gian lưu trữ và tăng hiệu suất xử lý thông tin một cách đáng kể.
  2. Trong trường hợp nàyi9bet.com nhận 100k, byte đầu tiên có giá trị "01pppppp", và byte thứ hai là "qqqqqqqq". Tổng dung lượng của chúng là 2 byte. Đặc biệt, trong byte đầu tiên, 2 bit cao nhất được xác định là "01". Điều này có nghĩa là: - Bit đầu tiên (bit quan trọng nhất) có giá trị "0", điều này thường cho biết rằng các byte tiếp theo sẽ không phải là phần mở đầu của một chuỗi mã hóa đặc biệt nào đó trong một số hệ thống mã hóa như UTF-8. - Bit thứ hai có giá trị "1", giúp phân loại cách các byte sau đó sẽ hoạt động hoặc liên kết với byte hiện tại. Như vậy, dựa trên cấu trúc này, ta có thể hiểu rằng chuỗi dữ liệu đang ở dạng mã hóa có nhiều byte, và byte đầu tiên chỉ là một phần nhỏ của thông điệp lớn hơn cần được giải mã. <len></len> Trường dữ liệu chiếm 2 bytexem ngoại hạng anh, trong đó có tổng cộng 14 bit được sử dụng để biểu thị giá trị độ dài. Giá trị lớn nhất mà nó có thể biểu diễn là 16383 (2^14 - 1). Điều này cho phép hệ thống xử lý các dãy số có phạm vi rộng, từ 0 đến gần 16 nghìn, đáp ứng tốt nhu cầu lưu trữ thông tin trong nhiều ứng dụng khác nhau.
  3. |10 __ |aaaaaa|bbbbbb|cccccc|dddddd| - 5 byte. Byte đầu tiên có hai bit cao nhất là "10"cá cược bóng đá, điều này cho thấy trường length field chiếm 5 byte, tổng cộng sử dụng 32 bit để biểu diễn giá trị độ dài (trong đó 6 bit bị loại bỏ không sử dụng), có thể biểu diễn giá trị tối đa là 2^32-1. Cần lưu ý rằng trong ba trường hợp đầu tiên, khi... Tôi đã thay thế các ký hiệu từ bản gốc bằng một chuỗi khác và giữ nguyên ý nghĩa của đoạn văn, đồng thời thêm một chút mô tả chi tiết hơn về cách tính toán giá trị. Tất cả đều được viết hoàn toàn bằng tiếng Việt. <data></data> Trường này chiếm 1 bytecá cược bóng đá, giá trị là 0xC0, dữ liệu sau đó <data></data> được lưu trữ dưới dạng kiểu int16_t có 2 byte.
  4. |11000000| - 1 byte。 <len></len> Trường này chiếm 1 bytei9bet.com nhận 100k, giá trị là 0xD0, dữ liệu sau đó <data></data> được lưu trữ dưới dạng kiểu int32_t có 4 byte.
  5. |11010000| - 1 byte。 <len></len> Trường này chiếm 1 bytei9bet.com nhận 100k, giá trị là 0xE0, dữ liệu sau đó <data></data> được lưu trữ dưới dạng kiểu int64_t có 8 byte.
  6. |11100000| - 1 byte。 <len></len> Trường này chiếm 1 bytecá cược bóng đá, giá trị là 0xF0, dữ liệu sau đó <data></data> được lưu trữ dưới dạng số nguyên có 3 byte.
  7. |11110000| - 1 byte。 <len></len> Trường này chiếm 1 bytexem ngoại hạng anh, giá trị là 0xFE, dữ liệu sau đó <data></data> được lưu trữ dưới dạng số nguyên có 1 byte.
  8. |11111110| - 1 byte。 <len></len> Trường này để biểu thị dữ liệu thực sựcá cược bóng đá, thay vào đó là <data></data> Rồixem ngoại hạng anh, định nghĩa cấu trúc dữ liệu ziplist, chúng ta đã giới thiệu xong, bây giờ hãy xem một ví dụ cụ thể.
  9. |1111xxxx| - - (giá trị của xxxx nằm trong khoảng từ 0001 đến 1101). Đây là một trường hợp đặc biệtxem ngoại hạng anh, trong đó giá trị của xxxx sẽ có tổng cộng 13 giá trị khác nhau, từ 1 đến 13. Khi đó, 13 giá trị này sẽ được sử dụng để biểu thị dữ liệu thực sự. Hãy lưu ý rằng ở đây chúng ta đang nói về việc biểu diễn dữ liệu thực tế, không phải độ dài của dữ liệu. Điều này có nghĩa là trong tình huống này, sẽ không cần một trường riêng biệt để chỉ ra độ dài của dữ liệu phía sau nữa. <data></data> Hình trên là một mẫu ziplist thật. Chúng ta sẽ giải thích từng mục một: <len></len> Một quá trình đọc: Quá trình thứ <data></data> Hai phần đã kết hợp thành một. Ngoài racá cược bóng đá, do xxxx chỉ có thể nhận 13 giá trị là 0001 và 1101 (các giá trị khác đều bị xung đột với các trường hợp khác, ví dụ như 0000 và 1110 đều mâu thuẫn với tình huống thứ 7 và thứ 8 trước đó, trong khi 1111 lại trùng với dấu hiệu kết thúc), và vì giá trị thập phân nhỏ cần bắt đầu từ 0, nên 13 giá trị này sẽ biểu thị từ 0 đến 12. Điều đó có nghĩa là giá trị của xxxx trừ đi 1 sẽ chính là giá trị số nguyên mà nó cần thể hiện.

Trường. Điều gì là little-endian? Có nghĩa là byte thấp của dữ liệu được lưu trữ ở địa chỉ bộ nhớ thấp hơn (tham khảo mục từ điển Wikipedia

Redis Ziplist Sample

). Do đóxem ngoại hạng anh, giá trị ở đây

  • Tệp ziplist này bao gồm tổng cộng 33 byte. Các byte được đánh số từ byte[0] đến byte[32]. Trong hìnhi9bet.com nhận 100k, giá trị của mỗi byte được biểu thị bằng hệ thập lục phân (hexadecimal). Mỗi giá trị hex này đóng vai trò quan trọng trong việc lưu trữ và xử lý dữ liệu bên trong cấu trúc này.
  • Bốn byte đầu tiên (0x21000000) được lưu trữ theo chế độ little endiani9bet.com nhận 100k, trong đó các byte có thứ tự ngược lại so với cách chúng xuất hiện thông thường. Điều này có nghĩa là giá trị thực sự sẽ được biểu diễn bằng cách đảo ngược thứ tự của các byte bên trong từ, làm cho việc giải mã trở nên phức tạp hơn nếu không hiểu rõ cơ chế hoạt động của nó. <zlbytes></zlbytes> nên được giải mã thành 0x00000021xem ngoại hạng anh, biểu diễn bằng thập phân chính xác là 33. Endianness Tiếp theo 4 byte (byte[4..7]) là <zlbytes></zlbytes> Byte cuối cùng (byte[32]) biểu thị
  • i9bet.com nhận 100k, giá trị cố định là 255 (0xFF). <zltail></zltail> Bạn có thể giải thích bằng cách sử dụng chế độ lưu trữ little-endiancá cược bóng đá, trong đó giá trị là 0x0000001D (tương đương với 29). Điều này có nghĩa là phần tử dữ liệu cuối cùng nằm ở vị trí byte[29] (phần tử dữ liệu này có giá trị là 0x05FE14).
  • Tiếp theo là 2 byte tiếp theo (byte[8..9])i9bet.com nhận 100k, với giá trị 0x0004, cho biết trong ziplist này có tổng cộng 4 phần tử dữ liệu được lưu trữ. Đây là cách mà ziplist thông báo số lượng phần tử bên trong nó, giúp người dùng hoặc chương trình có thể chuẩn bị bộ nhớ và xác định vị trí của từng phần tử một cách chính xác khi thao tác với dữ liệu.
  • Tiếp theoxem ngoại hạng anh, 6 byte tiếp theo (byte[10..15]) đại diện cho mục dữ liệu đầu tiên. Trong đó, prevrawlen được đặt thành 0 vì không có mục dữ liệu nào trước đó; len bằng 4, tương ứng với trường hợp thứ nhất trong 9 tình huống đã định nghĩa trước đó, biểu thị rằng 4 byte tiếp theo sẽ lưu trữ dữ liệu dưới dạng chuỗi, và giá trị của dữ liệu này là "tên".
  • Tiếp theocá cược bóng đá, 8 byte (byte[16..23]) đại diện cho mục dữ liệu thứ hai, có định dạng lưu trữ tương tự như các mục trước đó, dùng để lưu trữ chuỗi ký tự "tielei".
  • Tiếp theocá cược bóng đá, 5 byte (byte[24..28]) là mục dữ liệu thứ 3, có định dạng lưu trữ tương tự như các mục dữ liệu trước đó, dùng để lưu trữ chuỗi ký tự "tuổi".
  • Tiếp theocá cược bóng đá, 3 byte tiếp theo (byte[29..31]) đại diện cho phần tử dữ liệu cuối cùng, và định dạng của nó khác một chút so với các phần tử dữ liệu trước đó. Trong đó, byte đầu tiên prevrawlen = 5 cho biết phần tử dữ liệu trước đó chiếm 5 byte; byte thứ hai bằng FE, tương ứng với trường hợp thứ tám trong chín trường hợp đã được định nghĩa trước đó, do đó còn có thêm 1 byte để biểu thị dữ liệu thực tế, và giá trị này sẽ được lưu dưới dạng số nguyên. Giá trị của byte này là 20 (0x14).
  • Tóm lạii9bet.com nhận 100k, ziplist này lưu trữ 4 mục dữ liệu, bao gồm: <zlend></zlend> Chuỗi: "name"

Chuỗi: "tielei"

  • Chuỗi: "age"
  • Số nguyên: 20
  • (Được rồixem ngoại hạng anh, bạn đã phát hiện ra rồi ~~~ tiéléi thực ra tất nhiên không phải 20 tuổi, anh ấy đâu có trẻ như vậy...)
  • Thực tếi9bet.com nhận 100k, ziplist này được tạo ra thông qua hai

lệnh. Điều này chúng ta sẽ đề cập lại ở phần sau.

Tiếp theo tôi sẽ dán một số đoạn mã. hset Giao diện ziplist

Được rồixem ngoại hạng anh, vì bạn vẫn đang đọc đến đây, có thể thấy bạn khá kiên nhẫn đấy (thực ra tôi viết đến đoạn này cũng mệt muốn chết rồi). Bạn có thể lưu bài viết này lại trước, nghỉ ngơi một lát, và quay lại đọc phần sau khi đã hồi phục sức lực.

Trước tiên chúng ta không vội xem cách triển khaixem ngoại hạng anh, trước hết hãy chọn một vài giao diện quan trọng của ziplist, xem chúng trông như thế nào:

Chúng ta có thể đoán sơ qua chức năng của chúng từ tên giao diện:

ziplistNew: Tạo một ziplist rỗng (chỉ chứa

								
									
										unsigned
									 char
									 *
									ziplistNew
									(
									void
									);
									
unsigned
									 char
									 *
									ziplistMerge
									(
									unsigned
									 char
									 **
									first
									,
									 unsigned
									 char
									 **
									second
									);
									
unsigned
									 char
									 *
									ziplistPush
									(
									unsigned
									 char
									 *
									zl
									,
									 unsigned
									 char
									 *
									s
									,
									 unsigned
									 int
									 slen
									,
									 int
									 where
									);
									
unsigned
									 char
									 *
									ziplistIndex
									(
									unsigned
									 char
									 *
									zl
									,
									 int
									 index
									);
									
unsigned
									 char
									 *
									ziplistNext
									(
									unsigned
									 char
									 *
									zl
									,
									 unsigned
									 char
									 *
									p
									);
									
unsigned
									 char
									 *
									ziplistPrev
									(
									unsigned
									 char
									 *
									zl
									,
									 unsigned
									 char
									 *
									p
									);
									
unsigned
									 char
									 *
									ziplistInsert
									(
									unsigned
									 char
									 *
									zl
									,
									 unsigned
									 char
									 *
									p
									,
									 unsigned
									 char
									 *
									s
									,
									 unsigned
									 int
									 slen
									);
									
unsigned
									 char
									 *
									ziplistDelete
									(
									unsigned
									 char
									 *
									zl
									,
									 unsigned
									 char
									 **
									p
									);
									
unsigned
									 char
									 *
									ziplistFind
									(
									unsigned
									 char
									 *
									p
									,
									 unsigned
									 char
									 *
									vstr
									,
									 unsigned
									 int
									 vlen
									,
									 unsigned
									 int
									 skip
									);
									
unsigned
									 int
									 ziplistLen
									(
									unsigned
									 char
									 *
									zl
									);
									

								

ziplistMerge: Hợp nhất hai ziplist thành một ziplist mới.

  • Kiểu dữ liệu của ziplist không sử dụng cấu trúc tùy chỉnh như struct mà chỉ đơn giản là unsigned char *. Lý do cho điều này là vì bản chất của ziplist là một vùng nhớ liên tụcxem ngoại hạng anh, và cấu trúc bên trong được thiết kế với tính linh hoạt cao (mã hóa biến dài). Chính sự thay đổi linh hoạt về kích thước này khiến việc biểu diễn nó bằng một cấu trúc cố định trở nên bất khả thi. Do đó, việc sử dụng một con trỏ byte (unsigned char *) là cách hiệu quả nhất để xử lý các phần tử trong ziplist, cho phép tối ưu hóa bộ nhớ và tăng tính linh hoạt khi lưu trữ các giá trị khác nhau.
  • ziplistDelete: Xóa mục dữ liệu được chỉ định. <zlbytes><zltail><zllen><zlend></zlend></zllen></zltail></zlbytes> )。
  • ziplistLen: Tính toán độ dài của ziplist (tức là số lượng mục dữ liệu).
  • Tính năng ziplistPush: Đây là một phương thức để chèn dữ liệu vào đầu hoặc cuối của một ziplist (tạo ra một mục dữ liệu mới). Hãy chú ý đến giá trị trả về của giao diện nàyi9bet.com nhận 100k, đó chính là một ziplist mới. Người gọi cần phải sử dụng ziplist mới này để thay thế cho ziplist cũ đã được truyền vào trước đó, vì sau khi xử lý bằng hàm này, ziplist cũ sẽ trở nên không còn hiệu lực nữa. Tại sao một thao tác chèn đơn giản lại dẫn đến việc tạo ra một ziplist mới? Điều này xảy ra vì ziplist là một vùng nhớ liên tục, và mỗi lần thực hiện thêm phần tử vào nó, bộ nhớ có thể cần phải được tái phân bổ (realloc), điều này có thể làm thay đổi vị trí bộ nhớ của ziplist. Thực tế, trong bài viết trước về sds, chúng ta cũng đã đề cập đến mô hình sử dụng tương tự (xem thêm giải thích về hàm sdscatlen).
  • Hàm ziplistIndex sẽ trả về vị trí bộ nhớ của phần tử dữ liệu được chỉ định bởi tham số index. Giá trị của index có thể là số âmi9bet.com nhận 100k, điều này đồng nghĩa với việc thực hiện việc lập chỉ mục từ cuối danh sách về phía trước.
  • Hàm ziplistNext và ziplistPrev sẽ trả về phần tử kế tiếp và phần tử trước đó của phần tử Nếu phần tử p là phần tử cuối cùng trong dãycá cược bóng đá, ziplistNext sẽ trả về NULL; tương tự, nếu p là phần tử đầu tiên, ziplistPrev sẽ trả về NULL. Điều này giúp duyệt qua các phần tử trong ziplist một cách dễ dàng mà không cần lo lắng về việc vượt quá giới hạn.
  • Hàm ziplistInsert: Chèn một phần tử mới vào bất kỳ vị trí nào trước một phần tử hiện có
  • Phân tích logic chèn vào của ziplist
  • ziplistFind: Tìm kiếm dữ liệu được chỉ định (bởi vstr và vlen). Lưu ý rằng nó có một tham số skipxem ngoại hạng anh, cho biết khi so sánh giữa các mục cần bỏ qua bao nhiêu mục dữ liệu. Tại sao lại có tham số này? Thực ra, tham số skip chủ yếu được sử dụng khi ziplist được dùng để biểu diễn cấu trúc hash. Trong trường hợp này, mỗi cặp field-value sẽ được lưu xen kẽ trong ziplist, nghĩa là phần tử tại vị trí chẵn sẽ lưu field, còn phần tử tại vị trí lẻ sẽ lưu value. Khi thực hiện tìm kiếm theo giá trị của field, bạn cần bỏ qua các mục ở vị trí lẻ để tăng tốc độ và hiệu quả tìm kiếm. Điều này giúp giảm thiểu việc duyệt qua toàn bộ danh sách, từ đó tối ưu hóa hiệu suất.
  • Chúng ta hãy phân tích đơn giản đoạn mã này:

Bắt đầu hàmi9bet.com nhận 100k, trước tiên tính toán độ dài của mục dữ liệu trước vị trí chèn

Các giao diện liên quan đến ziplistcá cược bóng đá, cụ thể là cách chúng hoạt động, vẫn khá phức tạp. Do giới hạn về phạm vi nội dung, ở đây chúng ta sẽ chỉ tập trung giải thích logic chèn dữ liệu thông qua mã nguồn. Thao tác chèn là một ví dụ điển hình, qua đó ta có thể hiểu rõ hơn về cách ziplist được thực hiện bên trong. Khi đã nắm vững phần này, việc hiểu các phần còn lại của ziplist cũng sẽ trở nên dễ dàng hơn nhiều. Mặc dù ziplist có vẻ phức tạp khi nhìn vào toàn bộ cấu trúc, nhưng nếu đi sâu vào từng chi tiết nhỏ, như thao tác chèn dữ liệu, bạn sẽ thấy rằng nó không quá khó để hiểu. Điều quan trọng là cần có sự kiên nhẫn và tập trung để theo dõi từng bước thực hiện mà mã nguồn đang thực hiện. Chính vì điều này, việc học hỏi từ những phần đơn giản nhất sẽ giúp bạn dễ dàng tiếp cận với các chức năng nâng cao hơn trong tương lai.

Cả hai hàm ziplistPush và ziplistInsert đều có chức năng chèn dữ liệuxem ngoại hạng anh, nhưng sự khác biệt nằm ở cách chúng xác định vị trí để chèn. Cụ thể, cả hai đều sử dụng một hàm hỗ trợ nội bộ tên là __ziplistInsert để thực hiện việc chèn. Dưới đây là đoạn mã nguồn của hàm này (trích từ tệp ziplist.c): ```c int __ziplistInsert(ziplist *zl, zlentry *p, unsigned char *s, unsigned int slen) { // Đoạn mã xử lý chèn dữ liệu vào danh sách liên kết } ``` Hàm __ziplistInsert đóng vai trò quan trọng trong việc duy trì tính toàn vẹn của cấu trúc dữ liệu ziplist, đảm bảo rằng các phần tử được thêm vào đúng vị trí mong muốn mà không làm phá vỡ cấu trúc tổng thể.

								
									
										static
									 unsigned
									 char
									 *
									__ziplistInsert
									(
									unsigned
									 char
									 *
									zl
									,
									 unsigned
									 char
									 *
									p
									,
									 unsigned
									 char
									 *
									s
									,
									 unsigned
									 int
									 slen
									)
									 {
									
    size_t
									 curlen
									 =
									 intrev32ifbe
									(
									ZIPLIST_BYTES
									(
									zl
									)),
									 reqlen
									;
									
    unsigned
									 int
									 prevlensize
									,
									 prevlen
									 =
									 0
									;
									
    size_t
									 offset
									;
									
    int
									 nextdiff
									 =
									 0
									;
									
    unsigned
									 char
									 encoding
									 =
									 0
									;
									
    long
									 long
									 value
									 =
									 123456789
									;
									 /* initialized to avoid warning. Using a value
                                    that is easy to see if for some reason
                                    we use it uninitialized. */
									
    zlentry
									 tail
									;
									

    /* Find out prevlen for the entry that is inserted. */
									
    if
									 (
									p
									[
									0
									]
									 !=
									 ZIP_END
									)
									 {
									
        ZIP_DECODE_PREVLEN
									(
									p
									,
									 prevlensize
									,
									 prevlen
									);
									
    }
									 else
									 {
									
        unsigned
									 char
									 *
									ptail
									 =
									 ZIPLIST_ENTRY_TAIL
									(
									zl
									);
									
        if
									 (
									ptail
									[
									0
									]
									 !=
									 ZIP_END
									)
									 {
									
            prevlen
									 =
									 zipRawEntryLength
									(
									ptail
									);
									
        }
									
    }
									

    /* See if the entry can be encoded */
									
    if
									 (
									zipTryEncoding
									(
									s
									,
									slen
									,
									&
									value
									,
									&
									encoding
									))
									 {
									
        /* 'encoding' is set to the appropriate integer encoding */
									
        reqlen
									 =
									 zipIntSize
									(
									encoding
									);
									
    }
									 else
									 {
									
        /* 'encoding' is untouchedxem ngoại hạng anh, however zipEncodeLength will use the
         * string length to figure out how to encode it. */
        reqlen
									 =
									 slen
									;
									
    }
									
    /* We need space for both the length of the previous entry and
     * the length of the payload. */
									
    reqlen
									 +=
									 zipPrevEncodeLength
									(
									NULL
									,
									prevlen
									);
									
    reqlen
									 +=
									 zipEncodeLength
									(
									NULL
									,
									encoding
									,
									slen
									);
									

    /* When the insert position is not equal to the tailcá cược bóng đá, we need to
     * make sure that the next entry can hold this entry's length in
     * its prevlen field. */
    nextdiff
									 =
									 (
									p
									[
									0
									]
									 !=
									 ZIP_END
									)
									 ?
									 zipPrevLenByteDiff
									(
									p
									,
									reqlen
									)
									 :
									 0
									;
									

    /* Store offset because a realloc may change the address of zl. */
									
    offset
									 =
									 p
									-
									zl
									;
									
    zl
									 =
									 ziplistResize
									(
									zl
									,
									curlen
									+
									reqlen
									+
									nextdiff
									);
									
    p
									 =
									 zl
									+
									offset
									;
									

    /* Apply memory move when necessary and update tail offset. */
									
    if
									 (
									p
									[
									0
									]
									 !=
									 ZIP_END
									)
									 {
									
        /* Subtract one because of the ZIP_END bytes */
									
        memmove
									(
									p
									+
									reqlen
									,
									p
									-
									nextdiff
									,
									curlen
									-
									offset
									-
									1
									+
									nextdiff
									);
									

        /* Encode this entry's raw length in the next entry. */
									
        zipPrevEncodeLength
									(
									p
									+
									reqlen
									,
									reqlen
									);
									

        /* Update offset for tail */
									
        ZIPLIST_TAIL_OFFSET
									(
									zl
									)
									 =
									
            intrev32ifbe
									(
									intrev32ifbe
									(
									ZIPLIST_TAIL_OFFSET
									(
									zl
									))
									+
									reqlen
									);
									

        /* When the tail contains more than one entrycá cược bóng đá, we need to take
         * "nextdiff" in account as well. Otherwise, a change in the
         * size of prevlen doesn't have an effect on the *tail* offset. */
        zipEntry
									(
									p
									+
									reqlen
									,
									 &
									tail
									);
									
        if
									 (
									p
									[
									reqlen
									+
									tail
									.
									headersize
									+
									tail
									.
									len
									]
									 !=
									 ZIP_END
									)
									 {
									
            ZIPLIST_TAIL_OFFSET
									(
									zl
									)
									 =
									
                intrev32ifbe
									(
									intrev32ifbe
									(
									ZIPLIST_TAIL_OFFSET
									(
									zl
									))
									+
									nextdiff
									);
									
        }
									
    }
									 else
									 {
									
        /* This element will be the new tail. */
									
        ZIPLIST_TAIL_OFFSET
									(
									zl
									)
									 =
									 intrev32ifbe
									(
									p
									-
									zl
									);
									
    }
									

    /* When nextdiff != 0xem ngoại hạng anh, the raw length of the next entry has changed, so
     * we need to cascade the update throughout the ziplist */
    if
									 (
									nextdiff
									 !=
									 0
									)
									 {
									
        offset
									 =
									 p
									-
									zl
									;
									
        zl
									 =
									 __ziplistCascadeUpdate
									(
									zl
									,
									p
									+
									reqlen
									);
									
        p
									 =
									 zl
									+
									offset
									;
									
    }
									

    /* Write the entry */
									
    p
									 +=
									 zipPrevEncodeLength
									(
									p
									,
									prevlen
									);
									
    p
									 +=
									 zipEncodeLength
									(
									p
									,
									encoding
									,
									slen
									);
									
    if
									 (
									ZIP_IS_STR
									(
									encoding
									))
									 {
									
        memcpy
									(
									p
									,
									s
									,
									slen
									);
									
    }
									 else
									 {
									
        zipSaveInteger
									(
									p
									,
									value
									,
									encoding
									);
									
    }
									
    ZIPLIST_INCR_LENGTH
									(
									zl
									,
									1
									);
									
    return
									 zl
									;
									
}
									

								

. Độ dài này sẽ được lưu vào mục dữ liệu mới được chèn vào

  • Hàm này được sử dụng để chèn một đoạn dữ liệu mới vào vị trí chỉ định pcá cược bóng đá, với địa chỉ con trỏ s chứa dữ liệu cần chèn và độ dài của nó là slen. Sau khi chèn xong, sẽ tạo ra một mục dữ liệu mới thay thế vị trí ban đầu của p, và tất cả các mục dữ liệu nằm sau vị trí p sẽ được di chuyển về phía sau để tạo không gian cho mục dữ liệu mới. Tham số p có thể trỏ đến vị trí bắt đầu của một mục trong ziplist hoặc khi chèn vào cuối ziplist, nó sẽ trỏ đến dấu hiệu kết thúc của ziplist. Điều đặc biệt ở đây là khi chèn dữ liệu, hệ thống phải đảm bảo rằng mọi thao tác đều chính xác và không làm hỏng cấu trúc của ziplist. Nếu dữ liệu cần chèn lớn hơn kích thước cho phép tại vị trí hiện tại, toàn bộ dữ liệu sẽ được sắp xếp lại theo cách tối ưu nhất để duy trì tính liên tục của chuỗi dữ liệu. Điều này cũng giúp tránh tình trạng mất dữ liệu hoặc sai sót trong quá trình xử lý. <zlend></zlend>
  • Sau đó tính toán tổng số byte mà mục dữ liệu hiện tại chiếm prevlen i9bet.com nhận 100k, nó bao gồm ba phần: <prevrawlen></prevrawlen> trường.
  • và dữ liệu thực tế. Phần dữ liệu này sẽ được chuyển đổi thành số nguyên qua việc gọi reqlen Trước tiên thử chuyển đổi thành số nguyên. <prevrawlen></prevrawlen> , <len></len> Ngoài việc mục dữ liệu cần chèn gây ra nhu cầu bổ sung bộ nhớ cho ziplistcá cược bóng đá, còn phải xem xét kích thước của mục dữ liệu tại vị trí p trước khi chèn (nay sẽ xếp sau mục dữ liệu cần chèn), điều này được tính toán bằng cách gọi zipTryEncoding Nếu tăng lên,
  • là số dươngxem ngoại hạng anh, nếu không là số âm. reqlen Giờ đây rất dễ tính toán xem ziplist mới cần bao nhiêu byte sau khi chènxem ngoại hạng anh, sau đó gọi <prevrawlen></prevrawlen> Sự thay đổi trong trường dữ liệu này. Trước đâyi9bet.com nhận 100k, nó được thiết kế để lưu trữ tổng độ dài của mục trước đó, nhưng bây giờ đã chuyển thành việc lưu giữ tổng độ dài của mục dữ liệu đang được chèn vào. Điều này giúp cho việc quản lý và theo dõi dữ liệu trở nên linh hoạt hơn, đồng thời giảm thiểu nguy cơ lỗi khi xử lý các bản ghi liên tiếp. Nhờ vậy, hiệu suất hoạt động của hệ thống cũng được cải thiện đáng kể. <prevrawlen></prevrawlen> Kích thước không gian lưu trữ mà trường dữ liệu cần có thể thay đổicá cược bóng đá, sự thay đổi này có thể tăng lên hoặc giảm xuống. Điều đáng chú ý là mức độ thay đổi này thực tế đã đạt đến đâu và ảnh hưởng ra sao đến hiệu suất tổng thể của hệ thống. Một khi kích thước thay đổi, các yếu tố liên quan như cách quản lý bộ nhớ, tối ưu hóa truy xuất dữ liệu và khả năng mở rộng hệ thống cũng cần được xem xét kỹ lưỡng hơn nữa. Có thể nói, việc theo dõi và điều chỉnh kích thước trường dữ liệu chính là một khía cạnh quan trọng trong việc duy trì và nâng cấp hiệu quả hoạt động của cơ sở dữ liệu. nextdiff để điều chỉnh kích thước lại. Trong việc triển khai của ziplistResize sẽ gọi allocator zipPrevLenByteDiff cá cược bóng đá, điều này có thể dẫn đến sao chép dữ liệu. nextdiff Trường. Ngoài rai9bet.com nhận 100k, có thể cần điều chỉnh kích thước của ziplist
  • Cuối cùngxem ngoại hạng anh, sắp xếp mục dữ liệu mới cần chèn, đặt tại vị trí p. ziplistResize Hash và ziplist zrealloc , hoặc
  • Bây giờ khi đã có thêm không giancá cược bóng đá, bước tiếp theo là di chuyển toàn bộ phần tử từ vị trí p cùng tất cả các phần tử phía sau nó về sau một vị trí, để tạo ra chỗ trống cần thiết và chuẩn bị đặt một phần tử mới vào đó. <prevrawlen></prevrawlen> )xem ngoại hạng anh, cũng hỗ trợ truy xuất riêng lẻ theo một field cụ thể (theo <zltail></zltail> trường.
  • Khi chúng ta thực hiện lần đầu tiên cho một key cụ thể

ở trên

Trong Rediscá cược bóng đá, kiểu dữ liệu hash được xem là một lựa chọn rất phù hợp để lưu trữ cấu trúc của một đối tượng. Mỗi thuộc tính của đối tượng có thể dễ dàng ánh xạ tương ứng với một field trong cấu trúc hash. Điều này giúp việc quản lý và truy xuất thông tin trở nên đơn giản và hiệu quả. Ngoài ra, việc sử dụng hash còn cho phép tối ưu hóa không gian lưu trữ, đồng thời tăng tốc độ truy vấn dữ liệu khi làm việc với các đối tượng phức tạp.

Chúng ta thường dễ dàng tìm thấy những bài viết kỹ thuật trên mạng nói rằng việc lưu trữ một đối tượng bằng hash sẽ giúp tiết kiệm bộ nhớ hơn so với string. Tuy nhiêncá cược bóng đá, điều này không hoàn toàn đúng trong mọi trường hợp mà cần có những điều kiện cụ thể. Điều này phụ thuộc vào cách mà đối tượng được lưu trữ. Nếu bạn lưu các thuộc tính của đối tượng vào nhiều key riêng biệt (mỗi giá trị thuộc tính được lưu dưới dạng string), tất nhiên nó sẽ chiếm nhiều bộ nhớ hơn. Nhưng nếu bạn sử dụng một số phương pháp Serialization, chẳng hạn như: - **JSON Serialization**: Đây là một phương pháp phổ biến để chuyển đổi đối tượng thành chuỗi JSON. Khi sử dụng JSON, dữ liệu sẽ được tối ưu hóa để giảm thiểu kích thước tổng thể, nhờ đó giúp tiết kiệm bộ nhớ. - **Protobuf (Protocol Buffers)**: Công nghệ này do Google phát triển, cho phép định nghĩa cấu trúc dữ liệu và mã hóa nó thành dạng nhị phân. So với các định dạng text truyền thống như XML hay JSON, Protobuf thường nhỏ gọn hơn rất nhiều. - **Avro**: Một hệ thống Serialization khác, được thiết kế để lưu trữ và xử lý dữ liệu lớn. Avro cũng giúp tối ưu hóa bộ nhớ và tốc độ xử lý. Mỗi phương pháp Serialization đều có những ưu nhược điểm riêng, nhưng nhìn chung chúng đều giúp tối ưu hóa tài nguyên và cải thiện hiệu suất khi làm việc với các đối tượng lớn. Điều quan trọng là phải hiểu rõ đặc thù của từng phương pháp để chọn lựa phù hợp nhất cho dự án của mình. Protocol Buffers nhưng Apache Thrift Khi bạn đầu tiên chuyển đối tượng thành mảng bytecá cược bóng đá, sau đó lưu vào chuỗi của Redis, thì so với việc sử dụng hash, loại nào sẽ tiết kiệm bộ nhớ hơn chưa chắc đã có câu trả lời rõ ràng. Điều này tùy thuộc vào bản chất của đối tượng và cách mà Redis xử lý từng loại dữ liệu. Nếu đối tượng có cấu trúc phức tạp, sử dụng hash có thể tối ưu hơn vì nó cho phép truy cập trực tiếp vào các trường riêng lẻ mà không cần giải mã toàn bộ dữ liệu. Ngược lại, nếu dữ liệu có xu hướng đồng nhất, lưu dưới dạng chuỗi có thể là phương án hiệu quả hơn trong một số trường hợp nhất định.

Dựa trên cách tiếp cận lưu trữ hash thay vì chuỗi được sinh ra từ việc tuần tự hóaxem ngoại hạng anh, hash vẫn có những lợi thế nhất định khi thực hiện các lệnh thao tác: nó không chỉ cho phép truy xuất và cập nhật nhiều trường cùng lúc mà còn mang lại hiệu suất cao hơn trong việc xử lý dữ liệu. hmset / hmget đối tượng robj. hset / hget )。

Trên thực tếcá cược bóng đá, khi dữ liệu ngày càng lớn, cách thức triển khai cấu trúc dữ liệu bên dưới hash sẽ thay đổi, và tất nhiên hiệu suất lưu trữ cũng sẽ khác nhau. Khi số lượng trường (field) ít và các giá trị (value) cũng tương đối nhỏ, hash thường được thực hiện bằng ziplist; còn khi số lượng trường tăng lên và giá trị trở nên lớn hơn, hash có thể chuyển sang sử dụng dict để triển khai. Khi hash được thực hiện bằng dict, hiệu suất lưu trữ của nó sẽ không thể so sánh với các phương phá Điều này xảy ra vì dict được tối ưu hóa cho việc truy xuất nhanh nhưng lại tốn kém hơn về mặt bộ nhớ so với ziplist, vốn là một cấu trúc dữ liệu tiết kiệm bộ nhớ hơn trong trường hợp các giá trị nhỏ và ít trường.

Thực tếxem ngoại hạng anh, ví dụ ziplist được đưa ra ở phần trước của bài viết này, được xây dựng bởi hai lệnh sau đây. hset key field value Khi thực hiện lệnhcá cược bóng đá, Redis sẽ tạo ra một cấu trúc hash, và hash mới này về cơ bản được xây dựng dựa trên một ziplist. Ziplist là một dạng danh sách có cấu trúc đặc biệt trong Redis, giúp lưu trữ các cặp khóa-giá trị với hiệu suất cao trong trường hợp dữ liệu vừa phải hoặc ít phần tử.

								
									
										robj
									 *
									createHashObject
									(
									void
									)
									 {
									
    unsigned
									 char
									 *
									zl
									 =
									 ziplistNew
									();
									
    robj
									 *
									o
									 =
									 createObject
									(
									OBJ_HASH
									,
									 zl
									);
									
    o
									->
									encoding
									 =
									 OBJ_ENCODING_ZIPLIST
									;
									
    return
									 o
									;
									
}
									

								

Mỗi lần thực hiện createHashObject Hàm nàycá cược bóng đá, nằm trong tệp object.c, có nhiệm vụ chính là tạo ra một cấu trúc hash mới. Có thể thấy rằng, nó đã khởi tạo một đối tượng với các thuộc tính cần thiết để có thể hoạt động như một bảng băm hiệu quả. Cấu trúc này sẽ đóng vai trò quan trọng trong việc lưu trữ và quản lý dữ liệu theo cách có thứ tự, cho phép truy xuất nhanh chóng thông qua các khóa duy nhất. Điều này giúp cải thiện đáng kể hiệu suất khi xử lý khối lượng lớn dữ liệu trong hệ thống. type = OBJ_HASH tạo ra hai mục dữ liệu). encoding = OBJ_ENCODING_ZIPLIST Ý nghĩa của cấu hình này là khi một trong hai điều kiện sau đây được đáp ứngi9bet.com nhận 100k, ziplist sẽ chuyển thành dict:

hàm).

								
									hset user:100 name tielei
hset user:100 age 20

								

Mỗi lần chèn hoặc sửa đổi gây ra realloc sẽ có nhiều khả năng hơn để sao chép bộ nhớcá cược bóng đá, từ đó làm giảm hiệu suất. hset Bạn có thể chèn trường (field) và giá trị (value) được chỉ định vào một mục dữ liệu mới và thêm nó vào danh sách liên kết (ziplist). Điều này đồng nghĩa với việc mỗi lần... hset Một khi xảy ra sao chép bộ nhớxem ngoại hạng anh, chi phí sao chép bộ nhớ cũng sẽ tăng lên, vì phải sao chép một khối dữ liệu lớn hơn.

Khi có thêm dữ liệuxem ngoại hạng anh, cấu trúc ziplist ở tầng dưới cùng của hàm băm (hash) có thể sẽ chuyển đổi thành dạng dict. Nhưng câu hỏi đặt ra là, cần phải thêm bao nhiêu dữ liệu thì mới xảy ra sự chuyển đổi này? Điều này phụ thuộc vào các yếu tố như kích thước tối đa của ziplist và mật độ lưu trữ thông tin. Nếu lượng dữ liệu vượt quá giới hạn cho phép, hệ thống sẽ tự động chuyển sang sử dụng dict để đảm bảo hiệu suất và tính ổn định. Tuy nhiên, việc xác định chính xác con số cụ thể thường không được công khai rõ ràng trong tài liệu, mà thường dựa trên các thuật toán nội bộ của phần mềm.

Nhớ lại hai cấu hình Redis mà chúng tôi đề cập ở đầu bài không?

								
									hash-max-ziplist-entries 512
hash-max-ziplist-value 64

								

Bài viết tiếp theo chúng tôi sẽ giới thiệu quicklistxem ngoại hạng anh, xin hãy chờ đợi.

  • Khi số lượng mục dữ liệu (cụ thể là cặp trường-giá trị) trong hash vượt quá 512xem ngoại hạng anh, tức là khi số lượng mục trong danh sách liên kết ziplist vượt quá 1024 (hãy tham khảo t_hash.c), điều này sẽ kích hoạt một cơ chế kiểm soát đặc biệt. Điều này xảy ra bởi vì Redis cần đảm bảo hiệu suất tối ưu và khả năng quản lý bộ nhớ hiệu quả cho từng loại cấu trúc dữ liệu. Khi ngưỡng này bị vượt qua, hệ thống có thể tự động chuyển đổi cấu trúc lưu trữ từ ziplist sang hashring hoặc các dạng khác để tối ưu hóa cách truy xuất và thao tác với dữ liệu. Điều này giúp đảm bảo rằng bất kỳ yêu cầu nào cũng được xử lý nhanh chóng và hiệu quả mà không làm ảnh hưởng đến hiệu suất tổng thể của ứng dụng. hashTypeSet Một khi xảy ra sao chép bộ nhớcá cược bóng đá, chi phí sao chép bộ nhớ cũng sẽ tăng lên, vì phải sao chép một khối dữ liệu lớn hơn.
  • Khi bất kỳ giá trị nào được chèn vào hash có độ dài vượt quá 64 ký tự (hãy tham khảo t_hash.c bên dưới)cá cược bóng đá, hashTypeTryConversion Một khi xảy ra sao chép bộ nhới9bet.com nhận 100k, chi phí sao chép bộ nhớ cũng sẽ tăng lên, vì phải sao chép một khối dữ liệu lớn hơn.

Việc Redis thiết kế kiểu hash như vậy là do khi ziplist trở nên quá lớnxem ngoại hạng anh, nó sẽ gặp một số hạn chế đáng kể. Đầu tiên, việc xử lý dữ liệu trong ziplist sẽ trở nên chậm hơn bởi vì hệ thống phải duyệt qua nhiều phần tử hơn để tìm ra giá trị mong muốn. Thứ hai, mức tiêu thụ bộ nhớ có thể tăng lên bất thường, dẫn đến hiệu suất tổng thể của hệ thống bị ảnh hưởng. Cuối cùng, việc cập nhật hoặc thêm mới các phần tử vào ziplist lớn sẽ mất nhiều thời gian hơn và có thể gây ra hiện tượng treo tạm thời đối với các tác vụ khác.

  • Bài viết tiếp theo chúng tôi sẽ giới thiệu quicklistcá cược bóng đá, xin hãy chờ đợi.
  • Khi danh sách liên kết kiểu ziplist có quá nhiều mụci9bet.com nhận 100k, việc tìm kiếm một mục cụ thể sẽ trở nên rất chậm, vì để tìm được mục cần tìm, hệ thống phải duyệt qua từng phần tử trong danh sách. Điều này làm giảm đáng kể hiệu suất hoạt động, đặc biệt khi kích thước của ziplist ngày càng tăng lên.

Tóm lạixem ngoại hạng anh, ziplist được thiết kế với ý tưởng rằng các mục dữ liệu sẽ nằm liền kề nhau để tạo thành không gian bộ nhớ liên tục. Kiểu cấu trúc này không thực sự hiệu quả khi thực hiện các thao tác chỉnh sửa. Khi dữ liệu bị thay đổi, nó có thể dẫn đến việc tái phân bổ bộ nhớ realloc, từ đó có khả năng gây ra việc sao chép bộ nhớ. Điều này có thể làm ảnh hưởng đến hiệu suất và gây ra sự chậm trễ trong quá trình xử lý.


quicklist


Bài viết gốcxem ngoại hạng anh, xin vui lòng trích dẫn 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: /rc9othv0.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: Quản lý số lượng và thông báo đỏ trên ứng dụng bằng mô hình cây
Bài sau: Danh sách sách đọc của con gái

Bài viết mới nhất