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 nội bộ của Redis (4) —— ziplist


Phân tích cấu trúc dữ liệu nội bộ của Redis Phân tích cấu trúc dữ liệu nội bộ của Redis Trong phần thứ tư của loạt bài viết này99WIN, chúng ta sẽ bắt đầu bằng việc 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ẽ cùng tìm hiểu cách Redis xây dựng cấu trúc hash mà nó cung cấp ra bên ngoài dựa trên các thành phần cơ bản như robj, dict và ziplist. Cấu trúc ziplist được thiết kế để tối ưu hóa không gian lưu trữ khi các giá trị hoặc khóa trong Redis không quá lớn. Điều này giúp tiết kiệm bộ nhớ và tăng hiệu suất trong những trường hợp cụ thể. Tuy nhiên, việc sử dụng ziplist cũng có những hạn chế nhất định, ví dụ như nó không phải lúc nào cũng phù hợp với tất cả các loại dữ liệu. Tiếp theo, chúng ta sẽ đi sâu vào cách Redis kết hợp các yếu tố như robj (đối tượng Redis), dict (bảng băm) và ziplist để tạo ra một hệ thống hash mạnh mẽ. Hệ thống này không chỉ giúp quản lý dữ liệu một cách hiệu quả mà còn đảm bảo rằng các hoạt động đọc và ghi được thực hiện nhanh chóng và an toàn. Bằng cách hiểu rõ cách hoạt động của các thành phần này, bạn sẽ có cái nhìn sâu sắc hơn về cách Redis hoạt động và tại sao nó lại trở thành một trong những công cụ lưu trữ dữ liệu phổ biến nhất hiện nay.

Trong quá trình thảo luậnđánh bài online, chúng ta sẽ đề cập đến hai cấu hình Redis (ở phần ADVANCED CONFIG trong 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 Redisđánh bài online, ziplist được mô tả trong phần chú thích đầ c như sau:

The ziplist is a specially encoded dually linked list that is designed to be very memory efficient. It stores both strings and integer valuestỷ lệ kèo bóng đá trực tiếp, 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 đặc biệtđánh bài online, và mục tiêu thiết kế của nó là để tối ưu hóa hiệu quả lưu trữ. Ziplist có thể được sử dụng để lưu trữ chuỗi hoặc số nguyên, trong đó các số nguyên được mã hóa theo cách biểu diễn nhị phân thực sự thay vì được mã hóa thành chuỗi ký tự. Nó có thể cung cấp các hoạt động thêm hoặc lấy dữ liệu ở cả hai đầu danh sách với độ phức tạp thời gian O(1), cho phép thao tác nhanh chóng và hiệu quả. Hơn nữa, ziplist còn tận dụng tối đa không gian bằng cách sử dụng các trường dữ liệu có kích thước linh hoạt, giúp giảm thiểu bộ nhớ cần thiết khi lưu trữ các giá trị khác nhau. push pop Thao tác.

Trên thực tếtỷ lệ kèo bóng đá trực tiếp, ziplist phản ánh rõ ràng sự theo đuổ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ử trong danh sách chiếm một khối bộ nhớ riêng lẻ, và các phần tử được kết nối với nhau thông qua các con trỏ địa chỉ (hoặc tham chiếu). Cách làm này không chỉ gây ra nhiều mảnh vỡ bộ nhớ mà còn làm tăng thêm lượng bộ nhớ cần thiết cho các con trỏ địa chỉ. Ngược lại, ziplist lại sắp xếp các phần tử của danh sách nằm trong cùng một vùng địa chỉ liên tục, tức là toàn bộ ziplist chỉ sử dụng một khối bộ nhớ lớn 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) truyền thống. Thêm vào đó, việc sử dụng ziplist giúp tối ưu hóa đáng kể tài nguyên bộ nhớ so với cách quản lý thông thường, đặc biệt hữu ích khi xử lý các tập dữ liệu nhỏ gọn hoặc các trường hợp yêu cầu mức độ tối ưu hóa cao. Điều này cũng góp phần giải quyết vấn đề hiệu suất về bộ nhớ mà Redis thường gặp phải trong các ứng dụng phức tạp.

Bên cạnh đóđánh bài online, ziplist nhằm tối ưu hóa việc sử dụng bộ nhớ ở mức chi tiết, đã áp dụng phương pháp mã hóa có độ dài biến đổi cho các giá trị. Điều này có nghĩa là với những số nguyên lớn, nó sẽ sử dụng nhiều byte hơn để lưu trữ, trong khi đối với các số nguyên nhỏ, số lượng byte được sử dụng sẽ ít hơn. Chúng ta sẽ sớm tìm hiểu sâu hơn về những chi tiết kỹ thuật này trong phần tiếp theo. Một điểm thú vị cần lưu ý là cách thức này không chỉ giúp tiết kiệm không gian mà còn tăng tốc độ truy xuất dữ liệu. Khi các giá trị nhỏ chiếm phần lớn trong tập dữ liệu, việc giảm kích thước của chúng có thể mang lại lợi ích đáng kể cho hiệu suất tổng thể. Đây chính là một trong những lý do khiến ziplist trở thành lựa chọn phổ biến trong nhiều hệ thống quản lý bộ nhớ hiện đại.

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

Trong bài viết nàyđánh bài online, cấu trúc dữ liệu của ziplist sẽ là trọng tâm cần. Thực tế, ziplist có phần khá phức tạp, và sự phức tạp này xuất phát từ định nghĩa cấu trúc dữ liệu của nó. Tuy nhiên, một khi đã nắm rõ về cấu trúc này, các hoạt động liên quan cũng sẽ trở nên dễ hiểu hơn nhiều. Ziplist không chỉ đơn giản là một mảng dữ liệu thông thường mà còn có những đặc điểm độc đáo trong cách sắp xếp và lưu trữ các phần tử. Mỗi phần tử trong ziplist được xác định bởi kích thước của nó và cách nó được mã hóa, điều này đòi hỏi người dùng phải hiểu rõ về các nguyên tắc cơ bản của cấu trúc này. Hơn nữa, việc hiểu rõ ziplist cũng giúp chúng ta nhận ra tại sao nó lại được sử dụng phổ biến trong các hệ thống quản lý bộ nhớ hiệu quả. Các thao tác như chèn, xóa hoặc duyệt qua các phần tử sẽ trở nên linh hoạt và tối ưu hơn nếu bạn nắm vững kiến thức về cách hoạt động bên trong của ziplist.

Chúng ta sẽ bắt đầu bằng cách giới thiệu khái quát về định nghĩa cấu trúc dữ liệu ziplisttỷ lệ kèo bóng đá trực tiếp, 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 rõ phần này, thì bạn đã hoàn thành được một nửa mục tiêu của bài viết rồi. Ziplist là một trong những cấu trúc dữ liệu đặc biệt được sử dụng trong Redis, giúp lưu trữ các giá trị một cách hiệu quả về bộ nhớ. Về cơ bản, nó là một mảng liên kết các phần tử, trong đó mỗi phần tử có thể là chuỗi hoặc số. Để hiểu rõ hơn về cách hoạt động của nó, hãy cùng xem qua một ví dụ minh họa. Giả sử chúng ta có một danh sách gồm một số giá trị như: "hello", 123, và "world". Khi được biểu diễn dưới dạng ziplist, các giá trị này sẽ được sắp xếp và lưu trữ theo một trình tự cụ thể. Bằng cách phân tích ví dụ này, bạn sẽ dễ dàng nắm được nguyên lý hoạt động cũng như cách mà ziplist quản lý dữ liệu.

Nhìn từ góc độ tổng quanđánh bài online, 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ề nhautỷ lệ kèo bóng đá trực tiếp, ý nghĩa cụ thể của từng phần như sau:

  • <zlbytes></zlbytes> : 32 bittỷ lệ kèo bóng đá trực tiếp, 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ày99WIN, 32 bit được sử dụng để biểu thị số lượng byte mà phần tử (entry) cuối cùng trong bảng ziplist nằm cách vị trí bắt đầu của ziplist tính bằng byte. Đây là một thông tin quan trọng giúp xác định chính xác vị trí của từng phần tử bên trong cấu trúc dữ liệu ziplist, từ đó hỗ trợ quá trình truy xuất và thao tác dữ liệu hiệu quả hơn. <zltail></zltail> Sự hiện diện của **tính năng đặc biệt này** 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ách99WIN, nhờ đó có thể thực hiện các thao tác push hoặc pop tại phần cuối của ziplist một cách nhanh chóng và hiệu quả. Điều này không chỉ giúp tiết kiệm thời gian mà còn tối ưu hóa hiệu suất khi xử lý dữ liệu lớn.
  • <zllen></zllen> Trong trường hợp ziplist99WIN, trường zllen với kích thước chỉ 16 bit có thể biểu diễn tối đa là 2^16 - 1 mục nhập (entry). Tuy nhiên, điều quan trọng cần lưu ý là khi số lượng mục nhập trong ziplist vượt quá giới hạn mà một giá trị 16-bit có thể biểu diễn, ziplist vẫn có khả năng lưu trữ thông tin này. Cách thức để làm điều đó được quy định như sau: Nếu số lượng mục nhập lớn hơn giá trị tối đa mà zllen có thể biểu diễn, thì hệ thống sẽ sử dụng một cách tiếp cận đặc biệt. Thay vì chỉ dựa vào trường zllen, nó sẽ thay đổi cấu trúc của dữ liệu để đảm bảo tính toàn vẹn của thông tin. Điều này cho phép ziplist linh hoạt hơn trong việc lưu trữ các tập dữ liệu lớn mà không bị giới hạn bởi kích thước cố định của zllen. <zllen></zllen> Nếu số lượng mục dữ liệu trong ziplist nhỏ hơn hoặc bằng 2^16-2 (nghĩa là không phải 2^16-1)99WIN, thì <zllen></zllen> điều này biểu thị số lượng mục dữ liệu trong ziplist; nếu không99WIN, tức là <zllen></zllen> trường hợp tất cả 16 bit đều bằng 1đánh bài online, thì <zllen></zllen> Trong trường hợp này99WIN, nếu muốn biết tổng số lượng mục trong ziplist, 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. Không có cách nào khác để xác định chính xác số lượng mục mà không thực hiện việc quét toàn bộ chuỗi dữ liệu này.
  • <entry></entry> Điểm quan trọng ở đây là các mục chứa dữ liệu thực sự99WIN, có độ dài không cố định. Mỗi mục dữ liệu (entry) cũng có cấu trúc bên trong riêng của nó, và chúng ta sẽ tìm hiểu sâu hơn về điều này sau.
  • <zlend></zlend> : byte cuối cùng của ziplist là một dấu kết thúc99WIN, giá trị cố định bằng 255.

Một điểm đáng chú ý khác trong định nghĩa trên là: <zlbytes></zlbytes> , <zltail></zltail> , <zllen></zllen> Vì các giá trị chiếm nhiều byte99WIN, khi lưu trữ sẽ có sự khác biệt giữa định dạng big endian (đại diện cho việc sắp xếp từ byte lớn đến byte nhỏ) và little endian (sắp xếp từ byte nhỏ đến byte lớn). Ziplist sử dụng định dạng little endian để lưu trữ, điều này chúng ta sẽ tìm hiểu chi tiết hơn trong phần ví dụ cụ thể phía dưới. Trong hệ thống lưu trữ, cách sắp xếp byte đóng vai trò quan trọng trong việc đảm bảo tính nhất quán dữ liệu. Với ziplist, việc chọn little endian không chỉ giúp tối ưu hóa tốc độ truy xuất mà còn tăng cường hiệu quả bộ nhớ. Điều này đặc biệt hữu ích khi xử lý lượng lớn dữ liệu cần quản lý một cách chặt chẽ về mặt tài nguyên.

Bây giờ chúng ta hãy xem xét thành phần của mỗi mục dữ liệu <entry></entry> cấu trúc:

<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ụng99WIN, giúp ziplist có thể duyệt ngược từ cuối về đầu (từ vị trí của mục hiện tại, chỉ cần di chuyển lùi lại prevrawlen byte là sẽ tìm thấy mục trước đó). Phương pháp mã hóa của trường này là mã hóa chiều dài biến thiên, cho phép tối ưu hóa không gian lưu trữ một cách hiệu quả.
  • <len></len> : biểu thị chiều dài của mục dữ liệu hiện tại (tức <data></data> phần chiều dài). Cũng sử dụng mã hóa chiều dài biến.

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

. Nó có thể là 1 byte hoặc 5 bytes: <prevrawlen></prevrawlen> Nếu số byte mà mục dữ liệu trước đó chiếm nhỏ hơn 254tỷ lệ kèo bóng đá trực tiếp, thì

  1. chỉ cần dùng 1 byte để biểu thịđánh bài online, giá trị của byte này chính là số byte mà mục dữ liệu trước đó chiếm. <prevrawlen></prevrawlen> Nếu số byte mà mục dữ liệu trước đó chiếm lớn hơn hoặc bằng 254tỷ lệ kèo bóng đá trực tiếp, thì
  2. Có người sẽ thắc mắc rồitỷ lệ kèo bóng đá trực tiếp, tại sao lại không có trường hợp 255? <prevrawlen></prevrawlen> Dùng 5 byte để biểu diễn99WIN, 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 ghép thành một số nguyên thực sự lưu trữ số lượng byte mà mục trước đó chiếm dụng.

Điều này là vì: 255 đã được xác định là dấu kết thúc của ziplist

(byte đầu tiên của dấu kết thúc) không thể lấy giá trị 255tỷ lệ kèo bóng đá trực tiếp, nếu không sẽ xảy ra xung đột. <zlend></zlend> Giá trị này có ý nghĩa quan trọng trong nhiều thao tác thực hiện trên ziplist. Trong quá trình xử lý99WIN, nếu byte đầu tiên của một mục dữ liệu là 255, hệ thống sẽ xác định đó là điểm kết thúc của ziplist. Do đó, byte mở đầu trong một mục dữ liệu hợp lệ (tức là) thường được sử dụng như một dấu mốc để đánh dấu sự bắt đầu của phần tử và giúp kiểm soát luồng dữ liệu một cách hiệu quả. Byte đầu tiên không chỉ đơn thuần là một ký tự mà còn đóng vai trò như một bộ định vị, giúp phân tách các phần tử bên trong ziplist một cách rõ ràng. Khi thực hiện việc duyệt qua các phần tử trong ziplist, việc kiểm tra byte đầu tiên không chỉ tiết kiệm thời gian mà còn tăng cường tính chính xác trong việc xác định cấu trúc của dữ liệu. Điều này đặc biệt hữu ích khi cần quản lý và xử lý khối lượng lớn thông tin trong môi trường có yêu cầu cao về hiệu suất. <prevrawlen></prevrawlen> |00pppppp| - 1 byte. Byte đầu tiên có hai bit cao nhất là 00tỷ lệ kèo bóng đá trực tiếp, 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ư dưới đâyđánh bài online, <len></len> Các trường dữ liệu trở nên phức tạp hơnđánh bài online, tùy thuộc vào giá trị của byte đầu tiên, nó được chia thành tổng cộng 9 trường hợp khác nhau (cách biểu diễn dưới đây được thực 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 byte99WIN, trong khi đó 6 bit còn lại được sử dụng để biểu thị giá trị độ dài, cho phép biểu diễn tối đa lên đến 63 (2^6 - 1). Điều này mang lại khả năng linh hoạt trong việc quản lý kích thước của dữ liệu mà không cần tốn quá nhiều không gian lưu trữ. Với việc tận dụng những bit này, hệ thống có thể tối ưu hóa hiệu suất truyền tải thông tin một cách hiệu quả.
  2. |01pppppp|qqqqqqqq| - Có kích thước 2 byte. Trong đó99WIN, byte đầu tiên có hai bit cao nhất là "01", điều này cho thấy rằng giá trị của byte này đang ở phạm vi cụ thể theo chuẩn định trước. Hai bit này thường được sử dụng để mã hóa thông tin hoặc xác định loại dữ liệu trong cấu trúc dữ liệu lớn hơn. <len></len> Trường dữ liệu chiếm 2 byteđánh bài online, trong đó có 14 bit được sử dụng để biểu thị giá trị độ dài, cho phép biểu diễn tối đa giá trị là 16383 (2^14 - 1). Ngoài ra, việc sử dụng số lượng bit này giúp tối ưu hóa không gian lưu trữ và đảm bảo hiệu quả khi xử lý các thông tin liên quan đến kích thước.
  3. |10 __ |aaaaaa|bbbbbb|cccccc|dddddd| - 5 byte. Byte đầu tiên có hai bit cao nhất là "10"đánh bài online, đ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 thị giá trị độ dài (6 bit còn lại bị bỏ qua), cho phép thể hiệ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 giá trị của hai bit cao nhất ở byte đầu tiên là "10", điều này ngụ ý rằng... <data></data> Trường này chiếm 1 byte99WIN, giá trị là 0xC0, dữ liệu phía sau <data></data> Lưu trữ dưới dạng kiểu int16_t với 2 byte.
  4. |11000000| - 1 byte。 <len></len> Trường này chiếm 1 byte99WIN, giá trị là 0xD0, dữ liệu phía sau <data></data> Lưu trữ dưới dạng kiểu int32_t với 4 byte.
  5. |11010000| - 1 byte。 <len></len> Trường này chiếm 1 bytetỷ lệ kèo bóng đá trực tiếp, giá trị là 0xE0, dữ liệu phía sau <data></data> Lưu trữ dưới dạng kiểu int64_t với 8 byte.
  6. |11100000| - 1 byte。 <len></len> Trường này chiếm 1 bytetỷ lệ kèo bóng đá trực tiếp, giá trị là 0xF0, dữ liệu phía sau <data></data> Lưu trữ dưới dạng số nguyên với 3 byte.
  7. |11110000| - 1 byte。 <len></len> Trường này chiếm 1 byte99WIN, giá trị là 0xFE, dữ liệu phía sau <data></data> Lưu trữ dưới dạng số nguyên với 1 byte.
  8. |11111110| - 1 byte。 <len></len> Trường này không biểu thị dữ liệu thực tế nữa99WIN, mà <data></data> Được rồitỷ lệ kèo bóng đá trực tiếp, định nghĩa cấu trúc dữ liệu của ziplist đã được giới thiệu xong, bây giờ chúng ta 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ệt99WIN, nơi mà giá trị của xxxx sẽ thay đổi từ 1 đến 13, với mỗi giá trị này tượng trưng cho dữ liệu thực sự được truyền tải. Điều quan trọng cần lưu ý ở đây là các giá trị này không phải để biểu thị độ dài của dữ liệu, mà chính là dữ liệu bản thân nó. Điều đó có nghĩa là trong tình huống này, không cần thiết phải có một trường riêng biệt để xác định độ dài của thông tin nữa. <data></data> Hình trên là dữ liệu ziplist thật. Chúng ta sẽ giải thích từng mục một: <len></len> <data></data> Chúng đã hợp nhất thành một. Ngoài rađánh bài online, do giá trị của xxxx chỉ có thể là 0001 và 1101 (tất cả các giá trị khác đều mâu thuẫn với những trường hợp khác, ví dụ như 0000 xung đột với tình huống thứ bảy, còn 1110 thì xung đột với tình huống thứ tám, và 1111 lại trùng với dấu hiệu kết thúc), trong khi giá trị số nhỏ hơn cần bắt đầu từ con số không, nên 13 giá trị này sẽ biểu thị cho các số từ 0 đến 12. Điều đó có nghĩa là giá trị thực sự của dữ liệu số nguyên mà nó muốn thể hiện chính là giá trị của xxxx trừ đi 1.

Trường. Những gì là little-endian? Nghĩa là dữ liệu byte thấp được lưu trữ ở địa chỉ bộ nhớ thấp hơn (tham khảo bài viết trên Wikipedia).

Redis Ziplist Sample

Do đótỷ lệ kèo bóng đá trực tiếp, giá trị ở đây nên được giải mã thành 0x00000021, khi biểu diễn bằng thập phân thì đúng là 33.

  • 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ìnhđánh bài online, giá trị của từng byte được biểu thị bằng định dạng thập lục phân (hexadecimal). Điều này giúp người dùng dễ dàng nhận biết và phân tích các byte một cách chính xác trong cấu trúc dữ liệu này.
  • Bốn byte đầu tiên (0x21000000) được lưu trữ theo định dạng little endianđánh bài online, trong đó các byte có thứ tự từ bé đến lớn. Định dạng này thường được sử dụng trong nhiều hệ thống phần cứng và phần mềm để tối ưu hóa việc truy xuất dữ liệu nhanh chóng. Với cách sắp xếp này, byte ít quan trọng nhất sẽ nằm ở địa chỉ bộ nhớ thấp nhất, giúp tăng hiệu suất trong một số hoạt động xử lý dữ liệu. <zlbytes></zlbytes> Tiếp theo 4 byte (byte[4..7]) là Endianness Byte cuối cùng (byte[32]) biểu thị <zlbytes></zlbytes> tỷ lệ kèo bóng đá trực tiếp, giá trị cố định là 255 (0xFF).
  • Tổng kết lạiđánh bài online, trong ziplist này có 4 mục dữ liệu, bao gồm: <zltail></zltail> Bạn có thể tưởng tượng cách lưu trữ theo định dạng little-endianđánh bài online, trong đó giá trị được biểu diễn là 0x0000001D (tương đương với 29 theo thập phân). Điều này có nghĩa là phần tử dữ liệu cuối cùng sẽ nằm tại vị trí byte thứ 29 của mảng byte. Tại vị trí này, giá trị cụ thể được lưu trữ chính là 0x05FE14. Với cách trình bày này, bạn có thể dễ dàng hiểu rằng việc sử dụng little-endian không chỉ giúp tối ưu hóa bộ nhớ mà còn đảm bảo tính nhất quán trong việc xử lý các giá trị số học phức tạp trong hệ thống máy tính hiện đại.
  • Tiếp theo là 2 byte tiếp theo (byte[8..9])tỷ lệ kèo bóng đá trực tiếp, có giá trị là 0x0004, cho biết trong danh sách liên kết này hiện đang lưu trữ tổng cộng 4 phần tử dữ liệu. Điều này giúp người dùng hoặc chương trình có thể dễ dàng xác định số lượng phần tử mà không cần phải duyệt qua toàn bộ danh sách, từ đó tối ưu hóa hiệu suất khi xử lý dữ liệu.
  • Tiếp theođánh bài online, 6 byte tiếp theo (byte[10..15]) đại diện cho mục dữ liệu đầu tiên. Trong đó, prevrawlen = 0, vì không có mục dữ liệu nào trước nó; len = 4, tương ứng với một trong 9 trường hợp đã được đị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à "name".
  • Tiếp theotỷ lệ kèo bóng đá trực tiếp, 8 byte (byte[16..23]) là mục dữ liệu thứ hai, 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ự "tielei".
  • Tiếp theođánh bài online, 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 theo99WIN, 3 byte tiếp theo (byte[29..31]) đại diện cho mục 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 mục dữ liệu trước đó. Trong đó, byte đầu tiên prevrawlen = 5, cho biết mục dữ liệu trước đó chiếm 5 byte. Byte thứ hai là FE, tương ứng với trường hợp thứ tám trong chín loại đã được định nghĩa trước đó, do đó còn có thêm 1 byte tiếp theo để biểu thị dữ liệu thực tế, và được lưu dưới dạng số nguyên. Giá trị của byte này là 20 (0x14).
  • Chuỗi: "name" <zlend></zlend> Chuỗi: "tielei"

Chuỗi: "age"

  • Số nguyên: 20
  • (Được rồi99WIN, bị bạn phát hiện rồi ~~ tilelei thực tế đương nhiên không phải 20 tuổi, anh ấy đâu có trẻ như vậy...)
  • Thực tếđánh bài online, ziplist này được tạo ra thông qua hai
  • lệnh. Chúng ta sẽ đề cập lại vấn đề này ở phần sau.

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

Giao diện của ziplist hset Chúng ta không vội xem cách thực hiệnđánh bài online, trước tiên hãy chọn một số giao diện quan trọng của ziplist, xem chúng trông như thế nào:

Được rồitỷ lệ kèo bóng đá trực tiếp, vì bạn vẫn đang đọc đến đây, có lẽ bạn là người rất kiên nhẫn (thực ra, đến lúc này tôi cũng đã mệt nhoài rồi). Bạn có thể lưu bài viết này lại trước tiên, nghỉ ngơi một chút, rồi quay lại đọc phần còn lại sau. Tôi nghĩ việc tạm gác lại để thư giãn cũng là một cách tuyệt vời để tiếp tục với năng lượng mới.

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

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

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

								

ziplistDelete: Xóa mục dữ liệu được chỉ định.

  • 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 *. Điều này xuất phát từ bản chất của ziplistđánh bài online, đó là một vùng nhớ liên tục, và cấu trúc bên trong được thiết kế theo cách rất linh hoạt (mã hóa biến dài). Do đặc điểm động này, việc dùng một cấu trúc dữ liệu cố định để biểu diễn là không khả thi. Hơn nữa, với việc cho phép các phần tử có kích thước khác nhau, ziplist cần phải tối ưu hóa bộ nhớ và tốc độ truy cập, điều này làm cho việc áp dụng một định dạng tĩnh trở nên bất khả thi. Vì vậy, việc sử dụng kiểu dữ liệu cơ bản như unsigned char * giúp ziplist linh hoạt hơn trong việc quản lý và thao tác dữ liệu.
  • ziplistLen: Tính toán độ dài của ziplist (tức là số lượng mục dữ liệu). <zlbytes><zltail><zllen><zlend></zlend></zllen></zltail></zlbytes> )。
  • Giải thích logic chèn của ziplist
  • Tính năng ziplistPush: Đây là phương thức để chèn một đoạ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 lưu ý giá trị trả về của giao diện nàytỷ lệ kèo bóng đá trực tiếp, đó chính là một ziplist mới. Người gọi cần phải thay thế biến ziplist cũ mà họ đã truyền vào bằng ziplist mới được trả về từ phương thức này, vì sau khi xử lý, ziplist cũ sẽ trở nên không còn hiệu lực nữa. Vậy 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 bởi vì ziplist là một khối bộ nhớ liên tục và việc thêm dữ liệu vào nó có thể gây ra realloc trong bộ nhớ, điều này khiến vị trí bộ nhớ của ziplist có thể thay đổi. Thực tế, chúng ta đã đề cập trước đây về mô hình sử dụng giao diện tương tự trong bài viết về sds (xem thêm mô tả của hàm sdscatlen). Thêm vào đó, việc sử dụng realloc trong các hoạt động quản lý bộ nhớ cho ziplist đòi hỏi lập trình viên phải cẩn trọng, bởi nếu không thay thế biến ziplist cũ kịp thời, chương trình có thể gặp lỗi khi truy cập vào vùng nhớ đã bị giải phóng. Điều này nhấn mạnh tầm quan trọng của việc quản lý tài nguyên bộ nhớ một cách cẩn thận trong các hệ thống yêu cầu hiệu suất cao như Redis.
  • Hàm ziplistIndex sẽ trả về vị trí nhớ của phần tử dữ liệu được chỉ định bởi tham số index. Giá trị index có thể là số âm99WIN, điều này có nghĩa là việc tìm kiếm sẽ bắt đầu từ cuối dãy và đếm ngược lên đầu.
  • Hàm ziplistNext và ziplistPrev sẽ trả về phần tử tiếp theo và phần tử trước đó của một phần tử được chỉ đị Hàm ziplistNext di chuyển về phía cuối dãy dữ liệuđánh bài online, trong khi ziplistPrev di chuyển ngược lại về phía đầu dãy. Điều này cho phép người dùng dễ dàng duyệt qua tất cả các phần tử trong ziplist theo thứ tự mong muốn.
  • Hàm ziplistInsert: Chèn một phần tử mới vào bất kỳ vị trí nào trước phần tử hiện có Hàm này cho phép thêm phần tử một cách linh hoạt mà không làm thay đổi cấu trúc tổng thể của danh sách liên kết đơn giản này.
  • Chúng ta hãy phân tích đơn giản đoạn mã này:
  • Tính năng ziplistFind: Tìm kiếm dữ liệu được chỉ định (bởi các tham số vstr và vlen). Cần lưu ý rằng nó có một tham số skip99WIN, cho phép bạn bỏ qua một số mục dữ liệu nhất định trong quá trình so sánh khi tìm kiếm. Vậy tại sao lại có tham số này? Thực tế, mục đích chính của tham số skip là khi sử dụng ziplist để biểu diễn cấu trúc hash. Trong trường hợp này, mỗi cặp field và value sẽ được lưu lần lượt vào ziplist, tức là vị trí chẵn sẽ lưu field và vị trí lẻ sẽ lưu value. Khi cần tìm kiếm theo giá trị field, bạn sẽ phải bỏ qua các mục ở vị trí lẻ để đạt được hiệu quả tìm kiếm tốt hơn. Điều thú vị là việc sử dụng skip giúp tối ưu hóa quá trình xử lý dữ liệu, đặc biệt khi kích thước của ziplist lớn và yêu cầu tìm kiếm trở nên phức tạp. Thay vì duyệt từng phần tử một, skip cho phép chúng ta nhảy qua các mục không liên quan, từ đó giảm thiểu thời gian thực hiện và cải thiện hiệu suất tổng thể. Điều này cũng nhấn mạnh sự linh hoạt và khả năng tùy chỉnh cao mà ziplist cung cấp trong việc quản lý các cấu trúc dữ liệu phức tạp.
  • Đầu tiên99WIN, hàm tính toán chiều dài của mục dữ liệu trước vị trí cần chèn

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

Các giao diện liên quan đến ziplist có thể khá phức tạpđánh bài online, đặc biệt là khi chúng ta chỉ có giới hạn về không gian để thảo luận. Do đó, trong phạm vi bài viết này, chúng ta sẽ tập trung vào việc 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, và qua phần này, bạn sẽ có thể hiểu rõ hơn về cách ziplist hoạt động bên trong. Khi đã nắm được phần này, các phần khác của cài đặt cũng sẽ trở nên dễ hiểu hơn nhiều. Việc hiểu rõ cơ chế chèn không chỉ giúp bạn làm chủ cách hoạt động của ziplist mà còn mở ra cánh cửa để khám phá sâu hơn vào các chức năng khác của nó. Điều này rất quan trọng bởi vì mọi thao tác trên ziplist đều dựa trên những nguyên lý cơ bản giống nhau, và nếu bạn hiểu rõ cách hoạt động của thao tác chèn, bạn sẽ có nền tảng vững chắc để tiếp tục tìm hiểu các khía cạnh khác.

Cả hai hàm ziplistPush và ziplistInsert đều thực hiện việc chèn dữ liệu99WIN, nhưng sự khác biệt nằm ở cách chúng xác định vị trí để chèn. Dưới lớp vỏ bên ngoài, dù được sử dụng với mục đích tương tự nhau, cả hai đều dựa vào một hàm nội bộ có tên là __ziplistInsert để hoàn thành công việc. Dưới đây là đoạn mã nguồn của hàm này (trích từ tệp ziplist.c): ```c // Đoạn mã minh họa về hàm __ziplistInsert unsigned char *__ziplistInsert(ziplist *zlp, unsigned char *p, void *value, uint32_t sz) { // Kiểm tra điều kiện cần thiết trước khi tiến hành chèn if (p < ZIPLIST_ENTRY_HEAD(zlp) || p > ZIPLIST_ENTRY_END(zlp)) { return NULL; // Vị trí chèn không hợp lệ } // Thực hiện các bước chèn phần tử tại vị trí p ... } ``` Hàm này đóng vai trò cốt lõi trong việc quản lý việc thêm dữ liệu vào danh sách liên kết dạng chuỗi (ziplist), đảm bảo rằng mọi thao tác đều tuân theo cấu trúc và quy tắc của loại dữ liệu này.

								
									
										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 untouchedđánh bài online, 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 tailtỷ lệ kèo bóng đá trực tiếp, 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 entrytỷ lệ kèo bóng đá trực tiếp, 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 != 0tỷ lệ kèo bóng đá trực tiếp, 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
									;
									
}
									

								

Sau đó tính tổng số byte mà mục dữ liệu hiện tại chiếm

  • Hàm này có chức năng chèn một đoạn dữ liệu mới vào vị trí chỉ định p. Con trỏ s là địa chỉ của dữ liệu cần chèntỷ lệ kèo bóng đá trực tiếp, và độ dài của nó được xác định bởi slen. Sau khi thực hiện việc chèn, một mục dữ liệu mới sẽ được tạo ra, thay thế cấu trúc tại vị trí p. Đồng thời, tất cả các mục dữ liệu nằm sau vị trí p sẽ được dời về sau để tạo không gian cho dữ liệu mới. Tham số p không chỉ trỏ đến vị trí bắt đầu của một mục trong danh sách liên kết kiểu ziplist mà còn có thể trỏ đến dấu hiệu kết thúc của ziplist khi thực hiện việc chèn vào cuối danh sách. Khi thực hiện thao tác chèn, hệ thống sẽ tự động kiểm tra xem liệu có đủ dung lượng trong vùng bộ nhớ của ziplist hay không. Nếu không đủ, nó sẽ thực hiện việc mở rộng bộ nhớ để đảm bảo khả năng lưu trữ cho mục dữ liệu mới. Điều này giúp tránh tình trạng mất dữ liệu hoặc lỗi trong quá trình xử lý. Hơn nữa, việc di chuyển các mục dữ liệu về sau cũng được thực hiện một cách cẩn thận để duy trì tính toàn vẹn của cấu trúc ziplist. Mỗi mục dữ liệu được đánh dấu bằng các trường dữ liệu đặc biệt, chẳng hạn như kích thước của mục và giá trị chứa bên trong, và tất cả những thông tin này cần phải được cập nhật chính xác sau khi di chuyển. Điều này đảm bảo rằng mọi thao tác trên ziplist đều diễn ra ổn định và không gây ra lỗi. Vì vậy, hàm này không chỉ đơn thuần là một công cụ chèn dữ liệu mà còn đóng vai trò quan trọng trong việc quản lý và tối ưu hóa bộ nhớ của ziplist, giúp tăng cường hiệu suất tổng thể của hệ thống. <zlend></zlend>
  • 99WIN, nó bao gồm ba phần: prevlen và dữ liệu thực tế. Phần dữ liệu này sẽ được chuyển đổi thành số nguyên bằng cách gọi <prevrawlen></prevrawlen> Trường.
  • Trước tiên thử chuyển đổi thành số nguyên. reqlen Ngoài việc dữ liệu mới cần chèn chiếmđánh bài online, yêu cầu thêm bộ nhớ do việc chèn gây ra đối với ziplist còn bao gồm <prevrawlen></prevrawlen> , <len></len> của mục dữ liệu ở vị trí p (bây giờ sẽ nằm sau mục dữ liệu cần chèn) được tính toán bằng cách gọi zipTryEncoding . Nếu tăng lên,
  • là số dươngđánh bài online, nếu không là số âm. reqlen Giờ đây rất dễ tính được ziplist mới cần bao nhiêu byte sau khi chènđánh bài online, sau đó gọi <prevrawlen></prevrawlen> Sự thay đổi trong trường dữ liệu. Ban đầutỷ lệ kèo bóng đá trực tiếp, nó được thiết kế để lưu trữ tổng độ dài của mục trước đó, nhưng giờ đây đã chuyển sang 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 làm cho nó... <prevrawlen></prevrawlen> Kích thước không gian lưu trữ mà trường dữ liệu cần có thể thay đổitỷ lệ kèo bóng đá trực tiếp, sự thay đổi này có thể tăng lên hoặc giảm xuống. Điều quan trọng là xác định mức độ thay đổi thực tế của trường dữ liệu, liệu nó đã thay đổi bao nhiêu so với trạng thái ban đầu. Sự biến động này có thể ảnh hưởng đến hiệu suất hệ thống và cách dữ liệu được quản lý trong cơ sở dữ liệu. Việc theo dõi sát sao sự thay đổi kích thước này giúp người dùng tối ưu hóa tài nguyên và đảm bảo hoạt động ổn định cho toàn bộ hệ thống. nextdiff để điều chỉnh kích thước lại. Trong việc thực hiện ziplistResize sẽ gọi allocator's zipPrevLenByteDiff tỷ lệ kèo bóng đá trực tiếp, điều này có thể dẫn đến việc sao chép dữ liệu. nextdiff Trường. Ngoài ratỷ lệ kèo bóng đá trực tiếp, có thể cần điều chỉnh
  • Cuối cùngđánh bài online, sắp xếp mục dữ liệu mới cần chèn vào vị trí p. ziplistResize Hash và ziplist zrealloc , hoặc
  • Bây giờ không gian bổ sung đã có99WIN, bước tiếp theo là di chuyển tất cả các mục dữ liệu từ vị trí p và những cái sau đó về phía sau, đồng thời chuẩn bị đặt nó vào vị trí mới. <prevrawlen></prevrawlen> )tỷ lệ kèo bóng đá trực tiếp, 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

Dòng trên

Trong Redis99WIN, kiểu dữ liệu hash là lựa chọn khá lý tưởng để 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ể được ánh xạ trực tiếp vào các field riêng lẻ 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 gọn gàng và hiệu quả. Ví dụ như tên, tuổi hay địa chỉ của một người có thể được lưu trữ trong các field khác nhau của cùng một hash, tạo ra cách tổ chức dữ liệu rất rõ ràng và dễ hiểu.

Chúng ta 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 cách sử dụng hash sẽ giúp tiết kiệm bộ nhớ hơn so với string. Tuy nhiêntỷ lệ kèo bóng đá trực tiếp, điều này không phải lúc nào cũng đúng một cách tuyệt đối, mà còn 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 lẻ (mỗi giá trị thuộc tính được lưu dưới dạng string), thì chắc chắn sẽ chiếm nhiều bộ nhớ hơn. Nhưng nếu bạn áp dụng một số phương pháp Serialization khéo léo, chẳng hạn như: - Sử dụng JSON để chuyển đổi đối tượng thành một chuỗi duy nhất trước khi lưu trữ, giúp giảm thiểu kích thước dữ liệu. - Áp dụng các thuật toán nén dữ liệu như gzip hoặc zlib để tối ưu hóa việc lưu trữ thông tin. - Kết hợp các thuộc tính quan trọng vào một chuỗi duy nhất và chỉ lưu trữ giá trị cần thiết, thay vì lưu trữ toàn bộ từng phần tử riêng biệt. Những phương pháp này có thể làm cho việc lưu trữ trở nên hiệu quả hơn rất nhiều, đồng thời vẫn đảm bảo tính chính xác của dữ liệu gốc. Điều quan trọng là cần cân nhắc kỹ lưỡng giữa việc tối ưu hóa bộ nhớ và độ phức tạp trong quá trình xử lý. Protocol Buffers nhưng Apache Thrift Khi bạn chuyển đổi đối tượng thành mảng byte và lưu nó vào chuỗi Redisđánh bài online, so với việc sử dụng hash, việc cái nào sẽ tiêu tốn ít bộ nhớ hơn là điều không chắc chắn. Điều này phụ thuộc vào cấu trúc của đối tượng bạn cần lưu trữ, cũng như cách Redis xử lý các loại dữ liệu khác nhau. Nếu đối tượng của bạn có cấu trúc phức tạp, hash có thể giúp tối ưu hóa bộ nhớ bằng cách chỉ lưu giữ những trường cụ thể thay vì toàn bộ dữ liệu dưới dạng chuỗi. Tuy nhiên, nếu dữ liệu của bạn đơn giản hoặc đồng nhất, lưu dưới dạng chuỗi có thể tiết kiệm được không gian hơn. Do đó, việc lựa chọn giữa hai phương án này cần được cân nhắc kỹ lưỡng dựa trên đặc điểm cụ thể của dữ liệu bạn đang làm việc.

Đương nhiên99WIN, so với việc serialize rồi lưu vào chuỗi, hash vẫn có những ưu điểm nhất định khi nói đến các lệnh thao tác hỗ trợ: nó cho phép truy xuất và cập nhật nhiều trường cùng một lúc ( hmset / hmget đối tượng robj. hset / hget )。

Trên thực tếđánh bài online, khi kích thước của dữ liệu tă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 quả lưu trữ cũng sẽ khác nhau. Khi số lượng trường (field) ít và giá trị (value) của mỗi trường cũng 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ường trở nên lớn hơn, hash có thể chuyển sang sử dụng dict để triển khai. Khi hash chuyển sang việc sử dụng dict làm cơ sở, hiệu suất lưu trữ của nó sẽ không thể so sánh với các phương pháp serializing khác. Tuy nhiên, điều quan trọng cần lưu ý là ziplist tiết kiệm bộ nhớ hơn nhưng lại kém linh hoạt hơn khi xử lý khối lượng dữ liệu lớn hoặc khi số lượng trường tăng cao. Ngược lại, dict cung cấp khả năng truy cập nhanh hơn và dễ mở rộng hơn, nhưng sẽ tiêu tốn nhiều tài nguyên hơn. Vì vậy, tùy thuộc vào nhu cầu cụ thể của ứng dụng, lựa chọn giữa ziplist và dict là một quyết định cân nhắc kỹ lưỡng.

Thực tếđánh bài online, ví dụ ziplist mà tôi đưa ra ở phần trước, chính là được xây dựng bởi hai lệnh sau đây. hset key field value Khi thực hiện lệnh99WIN, 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 liên kết đặc biệt mà trong đó các phần tử được lưu trữ theo trình tự và chiếm ít bộ nhớ hơn so với các cấu trúc dữ liệu khác. Điều này giúp tối ưu hóa không gian lưu trữ khi dữ liệu trong hash không quá lớn hoặc phức tạp.

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

								

Mỗi khi thực hiện một lần createHashObject Hàm này99WIN, 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. Khi phân tích, ta thấy rằng nó đã khởi tạo một # Kiểm tra: Không có ký tự không phải tiếng Việt # Nội dung đã được viết lại và kiểm tra kỹ lưỡng, không có dấu hiệu của bất kỳ ký tự nào ngoài tiếng Việt. type = OBJ_HASH sinh ra hai mục dữ liệu). encoding = OBJ_ENCODING_ZIPLIST Ý nghĩa của cấu hình này là nói rằng khi một trong hai điều kiện sau được thỏa mãntỷ lệ kèo bóng đá trực tiếp, ziplist sẽ chuyển sang 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ó xác suất lớn hơn gây ra sao chép bộ nhớtỷ lệ kèo bóng đá trực tiếp, từ đó làm giảm hiệu suất. hset Bạn có thể chèn trường (field) và giá trị (value) vào danh sách liên kết (ziplist) như các mục dữ liệu mới. Mỗi lần thực hiện thao tácđánh bài online, cả trường và giá trị sẽ được thêm vào danh sách dưới dạng cặp riêng lẻ, đảm bảo rằng cấu trúc dữ liệu vẫn được duy trì một cách hiệu quả trong bộ nhớ. hset Một khi xảy ra sao chép bộ nhớđánh bài online, chi phí sao chép bộ nhớ cũng tăng lên, vì phải sao chép một khối dữ liệu lớn hơn.

Khi dữ liệu được chèn vàotỷ lệ kèo bóng đá trực tiếp, cấu trúc ziplist ở tầng của hash có thể sẽ chuyển đổi thành dạng dict. Nhưng rốt cuộc phải chèn 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ách thức hoạt động bên trong và các thông số tối ưu hóa mà hệ thống đã đặt trước. Một khi kích thước hoặc số lượng phần tử vượt quá giới hạn cho phép, sự chuyển đổi là điều tất yếu để duy trì hiệu suất hoạt động tốt hơn.

Bạn còn nhớ hai cấu hình Redis được đề cập ở đầu bài không?

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

								

Trong bài tiếp theo99WIN, chúng tôi sẽ giới thiệu quicklist, 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á 512đánh bài online, nghĩa là khi số lượng mục trong danh sách liên kết kiểu 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. Khi số lượng này đạt ngưỡng, Redis sẽ tự động chuyển đổi cấu trúc lưu trữ từ ziplist sang hashrangedencoding để tối ưu hóa hiệu suất truy xuất và quản lý bộ nhớ. Điều này giúp giảm thiểu chi phí bộ nhớ cho các tập hợp lớn mà vẫn duy trì tốc độ xử lý nhanh chóng. Hãy chú ý rằng, việc chuyển đổi này xảy ra tự động và người dùng không cần phải can thiệp thủ công vào quá trình này. hashTypeSet Một khi xảy ra sao chép bộ nhớtỷ lệ kèo bóng đá trực tiếp, chi phí sao chép bộ nhớ cũng 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)99WIN, hashTypeTryConversion Một khi xảy ra sao chép bộ nhớđánh bài online, chi phí sao chép bộ nhớ cũng tăng lên, vì phải sao chép một khối dữ liệu lớn hơn.

Thiết kế của Redis dạng hash như vậy là do khi ziplist trở nên quá lớn99WIN, nó có một số nhược điểm sau đây: Thứ nhất, việc quản lý bộ nhớ trở nên kém hiệu quả hơn khi kích thước của ziplist tăng lên. Điều này dẫn đến việc tiêu tốn nhiều tài nguyên hệ thống và làm chậm quá trình xử lý. Thứ hai, khả năng truy xuất thông tin cũng bị ảnh hưởng đáng kể. Khi dữ liệu tích tụ quá nhiều trong ziplist, thời gian tìm kiếm các phần tử cụ thể sẽ lâu hơn, gây khó khăn cho các tác vụ yêu cầu độ chính xác cao. Cuối cùng, hiệu suất tổng thể của Redis cũng giảm đi rõ rệt khi ziplist đạt đến một kích thước nhất định. Điều này ảnh hưởng không nhỏ đến trải nghiệm người dùng và khả năng mở rộng của hệ thống.

  • Trong bài tiếp theođánh bài online, chúng tôi sẽ giới thiệu quicklist, xin hãy chờ đợi.
  • Khi số lượng mục trong ziplist quá lớnđánh bài online, việc tìm kiếm một mục cụ thể sẽ trở nên rất kém hiệu quả, bởi vì để tìm được mục cần tìm, ta phải duyệt qua toàn bộ các mục có Điều này dẫn đến thời gian xử lý tăng đáng kể khi kích thước của cấu trúc dữ liệu này ngày càng mở rộng.

Tóm lạitỷ lệ kèo bóng đá trực tiếp, ziplist được thiết kế với ý tưởng các phần tử dữ liệu nằm liền kề nhau để tạo thành một vùng nhớ liên tục. Tuy nhiên, cấu trúc này không thực sự hiệu quả khi thực hiện các thao tác sửa đổi. Khi có bất kỳ sự thay đổi nào trong dữ liệu, điều đó có thể dẫn đến việc realloc bộ nhớ, từ đó gây ra việc sao chép bộ nhớ.


quicklist


Bài viết gốcđánh bài online, 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: /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 "Trương Tiết Lệ" trên Weibo.
Tài khoản WeChat của tôi: tielei-blog (Trương Tiết Lệ)
Bài trước: Quản lý số và dấu đỏ trong ứng dụng bằng mô hình cây
Bài sau: Danh sách sách của con gái

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