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

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


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ứ sáu trong loạt bài của chúng ta. Trong nội dung hôm naybacarat, chúng ta sẽ đi sâu tìm hiểu về một cấu trúc dữ liệu nội bộ của Redis — skiplist. Đây là một thành phần quan trọng đóng góp vào hiệu suất và khả năng hoạt động của hệ thống, vì vậy hãy cùng khám phá chi tiết về cách nó hoạt động và vai trò của nó trong cơ chế lưu trữ dữ liệu của Redis.

Trong Redis123win+club, việc sử dụng skiplist là để hỗ trợ việc triển khai cấu trúc dữ liệu sorted set mà Redis cung cấp ra bên ngoài. Sorted set mang đến rất nhiều thao tác phong phú, đủ để đáp ứng hầu hết các nhu cầu thực tế trong các ứng dụng khác nhau. Tuy nhiên, điều này cũng đồng nghĩa với việc sorted set có thể được xem là một trong những phần phức tạp hơ Bên cạnh đó, skiplist - cấu trúc dữ liệu mà nó sử dụng - cũng không phải là một chủ đề quá quen thuộc đối với nhiều người, bởi vì không phải trường học nào cũng dành thời gian chi tiết để giảng dạy về nó trong các khóa học về thuật toán. Do đó, để đảm bảo bài viết được dễ hiểu và đầy đủ thông tin nhất, bài này sẽ cần nhiều không gian hơn so với các phần còn lại trong loạt bài này. Điều thú vị là, mặc dù skiplist nghe có vẻ xa lạ, nhưng nó lại là một công cụ mạnh mẽ giúp tăng tốc độ truy xuất dữ liệ Trong khi cây đỏ-đen (red-black tree) hoặc các cấu trúc khác cũng có thể được dùng cho mục đích tương tự, skiplist lại nổi bật nhờ khả năng cân bằng giữa hiệu suất và đơn giản hóa việc triển khai. Điều này làm cho nó trở thành một lựa chọn lý tưởng trong môi trường như Redis, nơi mà hiệu suất và tính linh hoạt đóng vai trò cực kỳ quan trọng. Vì vậy, trong bài viết này, chúng ta sẽ đi sâu vào chi tiết về cách skiplist hoạt động, lý do tại sao nó lại phù hợp với sorted set, và làm thế nào nó giúp tối ưu hóa hiệu suất của Redis trong các tình huống cụ thể. Hy vọng qua đó, bạn sẽ có cái nhìn rõ hơn về lý do tại sao Redis lại chọn skiplist làm nền tảng chính cho sorted set của mình.

Chúng ta sẽ giới thiệu nó theo ba phần chính:

  1. Hãy cùng tìm hiểu về cấu trúc dữ liệu skiplist123win+club, một trong những thuật toán nổi tiếng và được sử dụng rộng rãi. Skiplist là một phương pháp lưu trữ và truy xuất dữ liệu hiệu quả, giúp tăng tốc độ tìm kiếm lên rất nhiều so với việc chỉ sử dụng mảng hoặc danh sách liên kết đơn giản. Tôi sẽ cố gắng giải thích nó bằng ngôn ngữ dễ hiểu nhất, như đang trò chuyện với bạn bè. Trước hết, hãy tưởng tượng bạn có một cuốn sách lớn với hàng nghìn trang và bạn muốn tìm một từ cụ thể. Thay vì đọc từng trang một, bạn có thể mở ngẫu nhiên vài trang để nhanh chóng xác định vị trí của từ đó. Skiplist hoạt động theo nguyên lý tương tự: thay vì duyệt qua từng phần tử, nó tạo ra các "cấp độ" đặc biệt giúp bạn "nhảy" qua dữ liệu một cách nhanh chóng. Về mặt kỹ thuật, mỗi nút trong skiplist không chỉ chứa dữ liệu mà còn có thể có nhiều con trỏ dẫn đến các nút khác ở các cấp độ khác nhau. Khi thực hiện tìm kiếm, thuật toán sẽ di chuyển theo hướng xuống dưới và sang phải cho đến khi tìm thấy phần tử mong muốn. Điều này làm cho quá trình tìm kiếm trở nên nhanh chóng, với độ phức tạp thời gian trung bình là O(log n), trong đó n là số lượng phần tử. Phân tích thuật toán cho thấy rằng skiplist rất linh hoạt và ổn định. Nó dễ triển khai hơn cây cân bằng (như AVL hay đỏ-đen), đồng thời vẫn đảm bảo hiệu suất tốt. Mặc dù Redis cũng sử dụng skiplist trong một số trường hợp, nhưng nội dung tôi trình bày ở đây không liên quan trực tiếp đến Redis. Tôi sẽ tiếp tục dùng ngôn ngữ đơn giản để giúp bạn hiểu rõ hơn về cách hoạt động củ
  2. Trong Redisxem ngoại hạng anh, việc triển khai skiplist cụ thể được thiết kế đặc biệt để đáp ứng các yêu cầu của tập hợp đã sắp xếp (sorted set). So với phiên bản skiplist cổ điển, phiên bản trong Redis đã thực hiện một số điều chỉnh đáng kể. Những thay đổi này giúp tối ưu hóa hiệu suất và đảm bảo rằng skiplist có thể xử lý hiệu quả các hoạt động như chèn, xóa và tìm kiếm trong tập hợp đã sắp xếp mà không làm giảm tính linh hoạt hoặc độ chính xác của cấu trúc dữ liệu.
  3. Bạn có thể tìm hiểu cách sorted set được xây dựng dựa trên sự kết hợp của skiplistbacarat, dict và ziplist. Đầu tiên, sorted set sử dụng skiplist để sắp xếp các phần tử theo thứ tự tăng dần hoặc giảm dần, giúp việc tìm kiếm trở nên nhanh chóng và hiệu quả. Skiplist cho phép truy xuất trực tiếp đến các phần tử mà không cần duyệt qua toàn bộ danh sách. Bên cạnh đó, dict (dictionary) đóng vai trò như một bản đồ ánh xạ giữa các thành viên của sorted set và điểm số tương ứng của chúng. Điều này giúp xác định vị trí của mỗi phần tử trong skiplist một cách dễ dàng và nhanh chóng. Cuối cùng, ziplist được sử dụng khi số lượng phần tử trong sorted set nhỏ. Ziplist lưu trữ dữ liệu theo dạng mảng liên tiếp, giúp tiết kiệm bộ nhớ và tăng tốc độ xử lý khi số lượng phần tử ít. Tuy nhiên, khi số lượng phần tử tăng lên, ziplist sẽ tự động chuyển sang sử dụng skiplist để đảm bảo hiệu suất tối ưu. Nhờ sự kết hợp thông minh của ba cấu trúc dữ liệu này, sorted set có khả năng quản lý dữ liệu một cách linh hoạt và hiệu quả, đáp ứng tốt nhiều yêu cầu khác nhau trong việc lưu trữ và xử lý thông tin.

Trong quá trình thảo luận123win+club, chúng ta sẽ đề cập đến hai cấu hình Redis (ở phần CẤU HÌNH NÂNG CAO trong tệp redis.conf):

								
									zset-max-ziplist-entries 128
zset-max-ziplist-value 64

								

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

Lưu ý: Việc phân tích mã nguồn trong bài viết này dựa trên nhánh 3.2 của mã nguồn Redis.

Giới thiệu về cấu trúc dữ liệu skiplist

Skiplist về bản chất cũng là một loại cấu trúc tìm kiếm123win+club, được thiết kế để giải quyết các vấn đề tìm kiếm trong thuật toán (Searching). Cụ thể, nó giúp xác định nhanh chóng vị trí của một key đã cho (hoặc giá trị tương ứng với key đó) trong tập dữ liệu. Với khả năng tối ưu hóa việc duyệt qua các phần tử, skiplist trở thành một lựa chọn hiệu quả khi cần xử lý các thao tác tìm kiếm liên tục và nhanh chóng.

Phân tích cấu trúc dữ liệu bên trong Redis Phần đầu tiên Trong phần giới thiệu về dictbacarat, chúng ta đã từng thảo luận rằng các phương pháp giải quyết vấn đề tìm kiếm thường được chia thành hai nhóm lớn: một nhóm dựa trên các cây cân bằng và một nhóm khác dựa trên bảng băm (hash table). Tuy nhiên, skiplist lại khá đặc biệt, vì nó không thể được xếp vào bất kỳ nhóm nào trong hai nhóm này. Nó có những đặc điểm độc đáo khiến nó trở thành một lựa chọn thú vị trong việc xử lý dữ liệu.

Loại cấu trúc dữ liệu này do ... William Pugh Phát minh ra nó123win+club, lần đầu tiên xuất hiện trong bài báo năm 1990 của ông ... Skip Lists: A Probabilistic Alternative to Balanced Trees "..." Các bạn quan tâm đến chi tiết có thể tải bản gốc của bài báo để đọc.

Skiplist123win+club, như tên gọi của nó, trước hết là một danh sách. Thực tế, nó được phát triển dựa trên ý tưởng của danh sách liên kết có thứ tự. Tuy nhiên, khác với danh sách liên kết thông thường, skiplist sử dụng nhiều lớp hàng đợi xếp chồng lên nhau, cho phép truy xuất dữ liệu nhanh hơn nhờ các thao tác nhảy giữa các nút. Điều này làm cho việc tìm kiếm trở nên hiệu quả hơn rất nhiều so với việc duyệt tuần tự từng phần tử trong danh sách liên kết cơ bản.

Trước tiênxem ngoại hạng anh, hãy nhìn vào một danh sách liên kết đã sắp xếp, như hình dưới đây (nút màu xám ở phía bên trái biểu thị một nút đầu rỗng):

Biểu đồ cấu trúc danh sách liên kết đã sắp xếp

Trong một danh sách liên kết như vậyxem ngoại hạng anh, nếu chúng ta muốn tìm kiếm một giá trị cụ thể, thì cần phải bắt đầu từ đầu danh sách và so sánh lần lượt từng nút cho đến khi tìm thấy nút chứa giá trị đó hoặc gặp nút có giá trị lớn hơn giá trị cần tìm (trường hợp không tìm thấy). Điều này có nghĩa là độ phức tạp thời gian sẽ là O(n). Tương tự, khi chúng ta muốn chèn thêm một giá trị mới, cũng cần thực hiện quá trình tìm kiếm giống như trên để xác định vị trí chính xác mà giá trị mới cần được chèn vào.

Giả sử rằng chúng ta thêm một con trỏ giữa mỗi hai nút kế tiếpxem ngoại hạng anh, cho phép con trỏ chỉ xuống nút tiếp theo, như hình dưới đây:

Danh sách liên kết đã sắp xếp với con trỏ nhảy giữa mỗi hai nút

Kết quả là tất cả các con trỏ mới được kết nối với nhau thành một danh sách liên kết hoàn toàn mớixem ngoại hạng anh, nhưng số lượng nút trong danh sách này chỉ bằng một nửa so với danh sách gốc (trong hình trên là các nút có giá trị 7, 19 và 26). Hiện tại, khi muốn tìm kiếm dữ liệu, chúng ta có thể bắt đầu bằng cách duyệt qua danh sách liên kết mới này. Khi gặp một nút có giá trị lớn hơn giá trị cần tìm, hãy quay lại danh sách ban đầu để tiếp tục tìm kiếm. Ví dụ, nếu muốn tìm giá trị 23, quá trình tìm kiếm sẽ đi theo hướng mà các mũi tên màu đỏ trong hình dưới đây đã chỉ ra.

Một ví dụ về đường đi tìm kiếm

  • 23 trước tiên so sánh với 7xem ngoại hạng anh, sau đó với 19, lớn hơn cả hai, tiếp tục so sánh tiếp.
  • Khi so sánh 23 và 26xem ngoại hạng anh, nhỏ hơn 26, do đó quay lại chuỗi ban đầu và so sánh với 22.
  • Bạn có thể thấy rằng 23 lớn hơn 22xem ngoại hạng anh, vì vậy hãy tiếp tục di chuyển theo mũi tên ở phía dưới để so sánh với số 26. Khi đối chiếu, bạn sẽ nhận ra rằng 23 nhỏ hơn 26, điều này cho thấy dữ liệu cần tìm là 23 không tồn tại trong danh sách liên kết gốc. Đồng thời, vị trí chèn thích hợp cho số 23 sẽ nằm giữa 22 và 26.

Trong quá trình tìm kiếm này123win+club, nhờ có sự thêm vào của con trỏ mới, chúng ta không cần phải so sánh từng bước với tất cả các nút trong danh sách liên kết nữa. Số lượng nút cần so sánh chỉ còn khoảng một nửa so với trước đây. Điều này giúp tiết kiệm đáng kể thời gian và công sức trong việc xử lý dữ liệu.

Chúng ta có thể áp dụng cách tương tự để tiếp tục thêm một con trỏ vào mỗi cặp nút mới xuất hiện trong chuỗi liên kết ở tầng trênxem ngoại hạng anh, từ đó tạo ra một chuỗi liên kết thứ ba. Hãy tham khảo hình minh họa dưới đây: [Trong phần hình ảnh, giả sử bạn sẽ có một biểu đồ hoặc sơ đồ giải thích cách hoạt động của các lớp chuỗi liên kết] Mỗi lần lặp lại quá trình này không chỉ làm tăng độ phức tạp của cấu trúc dữ liệu mà còn giúp tối ưu hóa hiệu suất tìm kiếm và thao tác dữ liệu. Với cách sắp xếp hợp lý, chúng ta có thể giảm đáng kể thời gian thực thi các lệnh xử lý trong hệ thống.

Hai lớp con trỏ nhảy

Trong cấu trúc bảng liên kết ba tầng mới nàyxem ngoại hạng anh, nếu chúng ta vẫn đang tìm kiếm số 23, thì khi bắt đầu từ bảng liên kết ở tầng trên cùng, điều đầu tiên cần so sánh sẽ là số 19. Khi nhận ra rằng 23 lớn hơn 19, chúng ta ngay lập tức biết rằng chỉ cần tiếp tục tìm kiếm từ phần sau của vị trí 19 và hoàn toàn có thể bỏ qua tất cả các nút trước nó. Có thể tưởng tượng rằng, khi bảng liên kết đủ dài, cách tìm kiếm dựa trên nhiều lớp bảng liên kết này sẽ giúp chúng ta bỏ qua rất nhiều nút ở các tầng dưới, từ đó làm tăng đáng kể tốc độ tìm kiếm. Đặc biệt, với một lượng lớn dữ liệu, cấu trúc này không chỉ tiết kiệm thời gian mà còn tối ưu hóa hiệu suất hoạt động của hệ thống. Giả sử bạn đang xử lý một cơ sở dữ liệu khổng lồ, thay vì phải duyệt từng bước từ đầu đến cuối, việc sử dụng bảng liên kết nhiều tầng cho phép bạn nhanh chóng xác định phạm vi cần tìm kiếm, giảm thiểu công sức kiểm tra không cần thiết. Điều này đặc biệt hữu ích trong các ứng dụng yêu cầu tốc độ xử lý cao như hệ thống quản lý thông tin, mạng xã hội hoặc phân tích dữ liệu lớn.

Skiplist được thiết kế dựa trên ý tưởng của danh sách liên kết đa lớp này. Thực tếbacarat, theo cách tạo danh sách liên kết như đã mô tả ở trên, số lượng các nút ở mỗi lớp phía trên sẽ bằng một nửa số lượng nút ở lớp dưới cùng, điều này làm cho quá trình tìm kiếm trở nên rất giống với việc thực hiện tìm kiếm nhị phân, giúp giảm thời gian phức tạp của việc tìm kiếm xuống mức O(log n). Tuy nhiên, phương pháp này gặp vấn đề lớn khi thêm dữ liệu mới. Khi một nút mới được chèn vào, sự cân bằng 2:1 giữa các nút ở các lớp liền kề sẽ bị phá vỡ. Nếu muốn duy trì tỷ lệ này, tất cả các nút sau khi chèn (bao gồm cả nút vừa được thêm vào) phải được điều chỉnh lại, dẫn đến việc thời gian phức tạp độ trở lại mức O(n). Vấn đề tương tự cũng xảy ra khi xóa dữ liệu. Sự bất ổn này khiến skiplist cần một cơ chế đặc biệt để giữ cho cấu trúc luôn cân bằng mà không làm ảnh hưởng quá nhiều đến hiệu suất tổng thể. Các kỹ thuật bổ sung như xác suất ngẫu nhiên hoặc các thuật toán tối ưu hóa khác đã được áp dụng trong skiplist để giải quyết những hạn chế này, từ đó tạo ra một cấu trúc dữ liệu linh hoạt và hiệu quả trong nhiều trường hợp sử dụng thực tế.

Để giải quyết vấn đề nàyxem ngoại hạng anh, skiplist không yêu cầu số lượng nút giữa các danh sách liên kết liền kề phải tuân theo mối quan hệ nhất định mà thay vào đó sẽ ngẫu nhiên xác định một mức độ (level) cho từng nút. Ví dụ, nếu một nút được chọn có mức độ là 3, nó sẽ được thêm vào cả ba danh sách liên kết từ tầng thứ nhất đến tầng thứ ba. Để làm rõ hơn, hình ảnh dưới đây minh họa quá trình hình thành skiplist thông qua từng bước chèn nút. Hình ảnh minh họa này cho thấy cách mà mỗi nút mới được thêm vào dựa trên xác suất ngẫu nhiên của nó, từ đó tạo ra cấu trúc phân tầng linh hoạt và hiệu quả trong việc tìm kiếm và truy xuất dữ liệu. Mỗi bước chèn không chỉ giúp duy trì sự cân bằng của cấu trúc mà còn tối ưu hóa thời gian thực thi các thao tác cơ bản như thêm, xóa hoặc tìm kiếm.

Quá trình hình thành skiplist khi chèn

Qua quá trình tạo và chèn của Skip List123win+club, có thể nhận thấy rằng số tầng (level) của mỗi nút là ngẫu nhiên, và việc chèn thêm một nút mới sẽ không ảnh hưởng đến số tầng của các nút khác. Do đó, thao tác chèn chỉ cần chỉnh sửa các con trỏ trước và sau nút được chèn, mà không cần phải điều chỉnh nhiều nút khác. Điều này làm giảm độ phức tạp của thao tác chèn. Thực tế, đây là một tính năng quan trọng của Skip List, giúp nó vượt trội hơn so với các giải pháp sử dụng cây cân bằng về hiệu suất khi chèn. Chúng ta sẽ đề cập lại điều này trong phần sau.

Dựa trên cấu trúc skiplist trong hình ảnh được cung cấpxem ngoại hạng anh, chúng ta có thể dễ dàng hiểu vì sao tên của cấu trúc dữ liệu này lại được đặt như vậy. Khi dịch sang tiếng Việt, skiplist có thể được gọi là "bảng nhảy" hoặc "bảng vượt qua", ám chỉ rằng ngoài danh sách liên kết ở tầng thứ nhất (tầng cơ sở), nó sẽ tạo ra nhiều tầng danh sách liên kết thưa thớt hơn. Ở các tầng này, các con trỏ cố ý bỏ qua một số nút (và càng ở các tầng cao hơn, số lượng nút bị bỏ qua sẽ càng nhiều). Điều này cho phép chúng ta tìm kiếm dữ liệu bằng cách bắt đầu từ các tầng cao hơn, rồi dần dần di chuyển xuống tầng thấp hơn, cuối cùng đến tầng thứ nhất để xác định chính xác vị trí của dữ liệu. Trong quá trình này, chúng ta đã bỏ qua một số nút, nhờ đó làm tăng tốc độ tìm kiếm. Ngoài ra, việc sử dụng skiplist không chỉ giúp cải thiện hiệu suất tìm kiếm mà còn mang lại khả năng thêm/xóa phần tử dễ dàng hơn so với các cấu trúc dữ liệu truyền thống khác. Chính sự linh hoạt và hiệu quả này đã khiến skiplist trở thành một lựa chọn phổ biến trong các ứng dụng cần xử lý dữ liệu lớn và yêu cầu tốc độ cao.

Skiplist mới được tạo ra này bao gồm tổng cộng 4 danh sách liên kếtbacarat, và bây giờ giả sử chúng ta vẫn đang tìm kiếm giá trị 23 trong đó, hình ảnh bên dưới cho thấy đường đi tìm kiếm: Hình ảnh chỉ ra rằng khi thực hiện tìm kiếm, các nút được duyệt qua sẽ được đánh dấu rõ ràng. Đầu tiên, chúng ta bắt đầu từ nút ở tầng cao nhất của skiplist, tiến hành so sánh và di chuyển sang trái hoặc xuống dưới sao cho giá trị cần tìm (23) luôn nằm bên phải của nút hiện tại. Quá trình này tiếp tục cho đến khi chúng ta tìm thấy giá trị mong muốn hoặc xác định rằng nó không tồn tạ Skiplist là một cấu trúc dữ liệu rất hiệu quả vì nó giảm thiểu số lần so sánh cần thiết trong quá trình tìm kiếm thông qua việc sử dụng nhiều cấp độ danh sách liên kết. Điều này giúp tăng tốc đáng kể so với việc sử dụng một danh sách liên kết đơn thuần.

Đường đi tìm kiếm trên skiplist

Điều cần lưu ý làbacarat, quá trình chèn các nút đã được trình bày trước đó thực tế cũng đòi hỏi một bước tìm kiếm tương tự trước khi thực hiện việc chèn. Đầu tiên, bạn phải xác định vị trí chính xác để chèn bằng cách tìm kiếm trong cấu trúc dữ liệu, sau đó mới tiến hành hoàn thành thao tác chèn. Quy trình này không chỉ giúp đảm bảo tính đúng đắn mà còn tăng hiệu quả hoạt động của toàn bộ hệ thống.

Cho đến thời điểm nàybacarat, chúng ta đã hiểu rõ về các thao tác tìm kiếm và chè Thao tác xóa cũng tương tự như thao tác chèn, và có lẽ bạn cũng dễ dàng tưởng tượng ra cách thực hiện nó. Tất cả những thao tác này chắc chắn không quá khó để bạn viết thành mã nguồn, giúp chúng trở nên cụ thể và hữu ích hơn bao giờ hết.

Trong thực tếbacarat, cấu trúc dữ liệu skip list mỗi nút nên chứa cả phần key và phần value. Ở phần trước, chúng ta chưa phân biệt rõ ràng giữa key và value, nhưng trên thực tế, danh sách sẽ được sắp xếp theo thứ tự của các key, và quá trình tìm kiếm cũng dựa vào việc so sánh các key. Điều này có nghĩa là khi bạn duyệt qua danh sách, hệ thống sẽ sử dụng key để xác định vị trí của từng phần tử một cách hiệu quả, từ đó giúp tối ưu hóa thời gian truy xuất thông tin.

Tuy nhiênbacarat, nếu đây là lần đầu tiên bạn tiếp cận với cấu trúc dữ liệu skiplist, chắc chắn bạn sẽ đặt ra một câu hỏi: Việc chèn nút vào trong skiplist sử dụng việc sinh ngẫu nhiên số tầng cho mỗi nút, chỉ dựa trên một thao tác ngẫu nhiên đơn giản như vậy, liệu có đảm bảo được hiệu suất tìm kiếm tốt không? Để trả lời câu hỏi này, chúng ta cần phân tích các đặc tính thống kê củ Trong thực tế, khi nói về skiplist, nhiều người thường cảm thấy tò mò về cách hoạt động của nó. Cấu trúc này không chỉ phụ thuộc vào việc ngẫu nhiên chọn số tầng cho mỗi nút mà còn liên quan đến việc sắp xếp hợp lý các phần tử trong từng lớp. Chính sự kết hợp giữa yếu tố ngẫu nhiên và cơ chế sắp xếp này đã tạo nên khả năng tìm kiếm nhanh chóng mà skiplist mang lại. Vì vậy, việc hiểu rõ về mặt thống kê là vô cùng quan trọng để đánh giá hiệu suất thực sự của nó.

Trước khi tiến hành phân tíchbacarat, chúng ta cần nhấn mạnh rằng việc tính toán số ngẫu nhiên trong quá trình thực hiện thao tác chèn là một bước vô cùng quan trọng. Bước này có tác động lớn đến các đặc tính thống kê củ Số ngẫu nhiên này không phải là loại số ngẫu nhiên thông thường tuân theo phân phối đều mà nó được tính toán theo quy trình như sau:

  • Đầu tiên123win+club, mỗi nút chắc chắn có con trỏ tầng thứ nhất (mỗi nút đều nằm trong danh sách liên kết tầng thứ nhất).
  • Nếu một nút đã có con trỏ ở cấp độ thứ i (với i >= 1)xem ngoại hạng anh, tức là nó đã xuất hiện trong danh sách liên kết từ cấp độ 1 đến cấp độ i, thì xác suất để nó cũng có con trỏ ở cấp độ tiếp theo (cấp độ i+1) là p. Điều này ngụ ý rằng mỗi khi nút này tiến lên một cấp độ mới, nó sẽ tiếp tục duy trì sự hiện diện ở cấp độ đó với xác suất được xác định trước, tạo ra một chuỗi các quyết định ngẫu nhiên nhưng có cấu trúc.
  • Số tầng tối đa của các nút không được vượt quá một giá trị tối đa123win+club, ký hiệu là MaxLevel.

Pseudocode để tính toán số tầng ngẫu nhiên như sau:

								
									
										randomLevel
									()
									
    level
									 :=
									 1
									
    // random() trả về một số ngẫu nhiên trong khoảng [0...1)
    while
									 random
									()
									 <
									 p
									 and
									 level
									 <
									 MaxLevel
									 do
									
        level
									 :=
									 level
									 +
									 1
									
    return
									 level
									

								

Trong mã giả của hàm randomLevel()xem ngoại hạng anh, có hai tham số được đề cập, đó là p và MaxLevel. Trong cơ chế thực hiện skiplist của Redis, hai tham số này có giá trị cụ thể như sau: Tham số p (probability) thường được đặt ở mức 0.25, nghĩa là xác suất để một nút xuất hiện ở cấp độ tiếp theo sẽ là 1/4. Điều này giúp duy trì sự cân bằng giữa chiều cao của skiplist và hiệu suất tìm kiếm. Tham số MaxLevel lại đóng vai trò là giới hạn tối đa của các lớ Giá trị này trong Redis thường được thiết lập ở mức 32, cho phép tối ưu hóa không gian và đảm bảo rằng các hoạt động trên skiplist diễn ra nhanh chóng mà không gây quá tải về bộ nhớ. Việc lựa chọn những giá trị này không chỉ giúp tối ưu hóa hiệu suất mà còn đảm bảo rằng skiplist hoạt động ổn định và hiệu quả trong môi trường Redis.

								
									p = 1/4
MaxLevel = 32

								

Phân tích hiệu suất thuật toán của skiplist

Trong phần này123win+club, chúng ta sẽ phân tích sơ lược về độ phức tạp thời gian và không gian của cấu trúc dữ liệu skiplist để có thể hiểu rõ hơn về hiệu suất hoạt động của nó. Nếu bạn không quá bận tâm đến việc phân tích kỹ lưỡng về hiệu năng của thuật toán, thì bạn hoàn toàn có thể bỏ qua đoạn văn này mà không vấn đề gì. Skiplist là một cấu trúc dữ liệu khá thú vị, được thiết kế để hỗ trợ tìm kiếm nhanh chóng trong các tập dữ liệu lớn. Độ phức tạp thời gian trung bình của skiplist cho các thao tác như tìm kiếm, chèn và xóa thường nằm ở mức O(log n), nhờ vào việc sử dụng các cấp lớp xếp chồng lên nhau. Điều này giúp skiplist đạt được tốc độ gần giống với cây nhị phân cân bằng (balanced binary tree) nhưng với cài đặt đơn giản hơn nhiều. Tuy nhiên, đổi lại, skiplist cần thêm không gian lưu trữ để quản lý các cấp lớp. Mỗi nút trong skiplist có thể chứa nhiều con trỏ dẫn đến các nút ở các tầng khác nhau, điều này làm cho độ phức tạp không gian của skiplist có thể tăng lên đến O(n log n). Dù vậy, thực tế cho thấy rằng số lượng lớp trung bình thường khá thấp, do đó không gian sử dụng thực tế vẫn tương đối hợp lý. Nếu bạn chỉ muốn hiểu về khái niệm cơ bản của skiplist mà không cần đi sâu vào các chi tiết kỹ thuật, thì đừng lo lắng nếu bạn bỏ qua phần phân tích này. Có rất nhiều khía cạnh khác của skiplist mà bạn có thể khám phá sau này!

Chúng ta hãy bắt đầu bằng cách tính toán số lượng trung bình các con trỏ được chứa trong mỗi nút (mong đợi xác suất). Số lượng con trỏ mà một nút chứa chính là chi phí bổ sung (overhead) của thuật toán trong không gian123win+club, và nó có thể được sử dụng để đo lường độ phức tạp về mặt không gian. Mỗi nút trong cấu trúc dữ liệu này không chỉ đơn thuần là nơi lưu trữ thông tin mà còn đóng vai trò như một mắt xích quan trọng kết nối với các nút khác. Việc đánh giá số lượng trung bình các con trỏ sẽ giúp chúng ta hiểu rõ hơn về cách thuật toán này quản lý tài nguyên trong bộ nhớ và liệu nó có hiệu quả hay không trong việc tối ưu hóa không gian. Ngoài ra, khi tính toán overhead này, chúng ta cũng cần cân nhắc đến việc liệu thuật toán có thể tự động điều chỉnh hoặc tối ưu hóa số lượng con trỏ theo từng trường hợp cụ thể hay không. Điều này không chỉ ảnh hưởng đến hiệu suất tổng thể mà còn quyết định khả năng mở rộng của thuật toán khi đối mặt với khối lượng dữ liệu lớn hơn.

Dựa trên mã giả của hàm randomLevel()bacarat, chúng ta có thể dễ dàng nhận thấy rằng việc tạo ra các lớp nút ở mức cao hơn sẽ có xác suất xảy ra thấp hơn. Để phân tích cụ thể hơn, chúng ta có thể xem xét như sau: Trong cấu trúc Skip List, mỗi lần gọi hàm randomLevel(), một giá trị ngẫu nhiên sẽ được sinh ra để xác định chiều cao của nút mới. Khi giá trị ngẫu nhiên đạt đến một giới hạn nhất định (thường là một giá trị mũ hai), nó sẽ quyết định xem nút đó có được thêm vào một lớp cao hơn hay không. Điều này dẫn đến thực tế rằng, các lớp cao hơn chỉ xuất hiện với tần suất thấp hơn so với các lớp thấp hơn. Về mặt định lượng, xác suất để một nút nằm ở một lớp thứ k (k > 1) thường giảm theo một chuỗi nhân cấp số nhân, ví dụ: xác suất cho lớp thứ hai thường bằng một nửa xác suất cho lớp đầu tiên, và xác suất cho lớp thứ ba lại bằng một nửa xác suất của lớp thứ hai, và cứ thế tiếp tục. Điều này tạo nên một sự phân bố hình thang, nơi các lớp thấp hơn thường xuyên xuất hiện hơn, trong khi các lớp cao hơn chỉ xuất hiện hiếm hoi nhưng đóng vai trò quan trọng trong việc tối ưu hóa hiệu suất tìm kiếm.

  • Số tầng của các nút ít nhất là 1. Và số tầng lớn hơn 1 của các nút tuân theo một phân phối xác suất.
  • Xác suất của việc số tầng của một nút bằng 1 là 1-p.
  • Xác suất của việc số tầng của một nút lớn hơn hoặc bằng 2 là pbacarat, và xác suất của việc số tầng của một nút bằng 2 chính xác là p(1-p).
  • Xác suất của việc số tầng của một nút lớn hơn hoặc bằng 3 là p 2 xem ngoại hạng anh, và xác suất của việc số tầng của một nút bằng 3 chính xác là p 2 (1-p)。
  • Xác suất của việc số tầng của một nút lớn hơn hoặc bằng 4 là p 3 123win+club, và xác suất của việc số tầng của một nút bằng 4 chính xác là p 3 (1-p)。
  • ……

Do đóxem ngoại hạng anh, số tầng trung bình của một nút (cũng chính là số lượng con trỏ trung bình mà nó chứa), được tính như sau:

Tính toán số tầng trung bình của skiplist

Bây giờ rất dễ dàng tính toán được:

  • Khi p = 1/2xem ngoại hạng anh, số lượng con trỏ trung bình của mỗi nút là 2;
  • Khi p = 1/4bacarat, số lượng trung bình các con trỏ được chứa trong mỗi nút sẽ là 1,33. Điều này cũng phản ánh chi phí về mặt không gian khi thực hiệ Trong cấu trúc dữ liệu skiplist, việc thiết lập giá trị p = 1/4 giúp cân bằng giữa hiệu suất tìm kiếm và độ phức tạp không gian lưu trữ. Với mỗi nút, xác suất xuất hiện một con trỏ mới giảm dần theo cấp số nhân, từ đó tạo ra một cấu trúc phân tầng hiệu quả. Vì vậy, mức tiêu tốn bộ nhớ trung bình cho mỗi nút được tính toán chính xác ở con số 1,33, giúp tối ưu hóa hiệu suất hoạt động của skiplist trong hệ thống Redis.

Tiếp theoxem ngoại hạng anh, để phân tích độ phức tạp thời gian, chúng ta sẽ tính toán chiều dài trung bình của quá trình tìm kiế Chiều dài tìm kiếm được xác định bởi số lần nhảy vượt qua các nút trên đường đi tìm kiếm, và số lần so sánh trong quá trình tìm kiếm sẽ bằng với chiều dài tìm kiếm cộng thêm 1. Lấy ví dụ về đường đi tìm kiếm số 23 được đánh dấu trước đó trong hình, bắt đầu từ nút đầu tiên ở góc trên bên trái và tiếp tục đến nút 22, chiều dài tìm kiếm trong trường hợp này là 6. Chúng ta có thể thấy rõ hơn rằng mỗi bước nhảy đại diện cho một quyết định mà thuật toán đưa ra trong quá trình tìm kiếm, và mỗi bước này đều yêu cầu một phép so sánh để xác định xem liệu cần di chuyển xuống hàng thấp hơn hay tiếp tục tiến về phía phải trên cùng một hàng. Điều này giải thích tại sao việc tính toán chính xác số lần so sánh rất quan trọng đối với hiệu suất tổng thể củ

Để tính toán độ dài của việc tìm kiếmbacarat, chúng ta cần sử dụng một chút mẹo nhỏ. Chúng ta nhận thấy rằng khi mỗi nút được chèn vào, số tầng của nó được xác định bởi hàm randomLevel(). Hơn nữa, cách tính ngẫu nhiên này không phụ thuộc vào các nút khác, và mỗi lần chèn là hoàn toàn độc lập. Do đó, về mặt thống kê, cấu trúc của skip list không phụ thuộc vào thứ tự chèn các nút. Ngoài ra, điều thú vị là cấu trúc skip list cho phép việc tìm kiếm trở nên hiệu quả hơn so với danh sách liên kết thông thường. Khi một nút được thêm vào, hàm randomLevel() tạo ra một xác suất mà có thể làm tăng hoặc giảm số tầng của nút đó. Điều này có nghĩa là cấu trúc của skip list luôn được tối ưu hóa để đảm bảo rằng các nút quan trọng sẽ nằm ở tầng cao hơn, từ đó giúp việc tìm kiếm diễn ra nhanh chóng hơn.

Với cách này123win+club, để tính toán độ dài của quá trình tìm kiếm, chúng ta có thể xem xét lại hành trình tìm kiếm từ góc nhìn ngược lại. Bắt đầu từ nút cuối cùng ở hàng thứ nhất trong góc dưới bên phải, sau đó di chuyển ngược lên và sang trái theo đường đi tìm kiếm, giống như việc leo cầu thang vậy. Giả sử rằng khi quay lại một nút nào đó, nó mới được chèn vào – điều này, dù về mặt logic có vẻ thay đổi thứ tự chèn của các nút, nhưng về mặt thống kê, không làm ảnh hưởng đến cấu trúc tổng thể của danh sách nhảy (skiplist). Thêm vào đó, vì mỗi nút có xác suất độc lập được chọn để nằm ở bất kỳ cấp bậc nào, nên dù chúng ta đảo ngược quá trình, sự phân bố của các cấp vẫn giữ nguyên. Điều này cho phép chúng ta hiểu rõ hơn về tính ngẫu nhiên và ổn định của skiplist trong việc tối ưu hóa thời gian truy xuất dữ liệu.

Giả sử hiện tại chúng ta đang đứng tại một nút x thuộc tầng thứ i và cần di chuyển lên trên và sang trái thêm k tầng. Ở thời điểm nàyxem ngoại hạng anh, có hai khả năng xảy ra: Hoặc là chúng ta tiếp tục di chuyển theo hướng thẳng đứng lên trên trong suốt quãng đường, hoặc là chúng ta sẽ phải thực hiện một chuỗi các bước xoay tréo để đạt được mục tiêu. Dù lựa chọn nào đi chăng nữa, cả hai phương án đều đòi hỏi sự cẩn trọng và chiến lược rõ ràng để đảm bảo không bỏ lỡ bất kỳ cơ hội nào trong quá trình tiến lên.

  • Nếu nút x có con trỏ tầng thứ (i+1)123win+club, thì chúng ta cần di chuyển lên. Xác suất của trường hợp này là p.
  • Nếu nút x không có con trỏ tầng thứ (i+1)123win+club, thì chúng ta cần di chuyển sang trái. Xác suất của trường hợp này là (1-p).

Hai trường hợp này được minh họa trong hình dưới đây:

Quay ngược đường đi tìm kiếm trên skiplist

Sử dụng C(k) để biểu thị độ dài trung bình của đường đi tìm kiếm cần phải đi qua khi leo lên k cấpxem ngoại hạng anh, thì:

								Bạn có thể thấy rằng giá trị ban đầu C(0) bằng 0. Còn đối với C(k)bacarat, nó được tính theo công thức sau: C(k) = (1-p) × (chiều dài tìm kiếm trong trường hợp b ở hình trên) + p × (chiều dài tìm kiếm trong trường hợp c ở hình trên). Trong đó, tham số p là xác suất xảy ra của một sự kiện nào đó, và (1-p) đại diện cho xác suất của trường hợp còn lại. Điều này cho phép chúng ta xác định độ dài trung bình cần thiết để thực hiện việc tìm kiếm dựa trên hai kịch bản khác nhau được minh họa trong hình vẽ.

Thay thế123win+club, thu được một phương trình sai phân và giản lược:

								
									C(k)=(1-p)(C(k)+1) + p(C(k-1)+1)
C(k)=1/p+C(k-1)
C(k)=k/p

								

Kết quả này cho thấy rằng mỗi khi chúng ta di chuyển lên một cấp độbacarat, cần phải di chuyển thêm 1/p bước trên đường đi tìm kiếm. Hơn nữa, tổng số cấp độ mà chúng ta cần leo lên sẽ bằng với số lượng tầng của danh sách nhảy (skiplist) trừ đi một.

Tiếp theo123win+club, chúng ta cần phân tích xem khi skiplist có n nút, giá trị kỳ vọng của tổng số tầng sẽ là bao nhiêu. Đây là một câu hỏi khá dễ hiểu nếu nhìn nhận trực quan. Dựa trên thuật toán xác định độ sâu của các nút, chúng ta có thể suy ra rằng: Trong skiplist, mỗi nút có khả năng xuất hiện ở bất kỳ tầng nào từ 1 đến k (k là số tầng tối đa). Xác suất để một nút nằm ở tầng i được tính theo công thức 1/2^i, nghĩa là xác suất giảm đi một nửa sau mỗi tầng. Điều này cho phép chúng ta xây dựng mô hình phân phối xác suất cho các tầng củ Do đó, khi có n nút, chúng ta có thể tính toán giá trị kỳ vọng bằng cách nhân số lượng nút với xác suất mà một nút xuất hiện ở từng tầng. Kết quả cuối cùng sẽ cho thấy rằng tổng số tầng của skiplist tăng dần nhưng vẫn duy trì được hiệu quả trong việc tìm kiếm và chèn/xóa nút.

  • Danh sách liên kết tầng thứ nhất cố định có n nút;
  • Danh sách liên kết tầng thứ hai trung bình có n*p nút;
  • Danh sách liên kết tầng thứ ba trung bình có n*p 2 nút;

Vì vậyxem ngoại hạng anh, từ tầng 1 đến tầng cao nhất, số lượng nút trung bình trên mỗi danh sách liên kết tạo thành một cấp số nhân giảm dần. Có thể dễ dàng tính toán được rằng giá trị trung bình của tổng số tầng bằng log. Điều thú vị là cấu trúc này cho phép hệ thống hoạt động hiệu quả trong việc tìm kiếm và cập nhật dữ liệu. Với mỗi tầng, số lượng nút giảm dần theo tỷ lệ cố định, tạo ra một cơ chế tối ưu hóa về mặt tài nguyên. Điều này đặc biệt hữu ích khi xử lý khối lượng lớn thông tin, giúp giảm thiểu thời gian truy xuất và tăng tốc độ phản hồi đáng kể. 1/p nútbacarat, và số lượng nút trung bình của tầng cao nhất là 1/p.

Tổng quát123win+club, nếu tính nhanh, độ dài tìm kiếm trung bình gần bằng:

  • C(log 1/p n-1)=(log 1/p n-1)/p

Tức làbacarat, độ phức tạp thời gian trung bình là O(log n).

Đương nhiênxem ngoại hạng anh, phân tích độ phức tạp thời gian ở đây vẫn còn tương đối sơ lược. Ví dụ, khi di chuyển ngược theo đường dẫn tìm kiếm về phía bên trái và phía trên, có thể bạn sẽ chạm đến nút đầu tiên bên trái trước, sau đó đi lên dọc theo chuỗi các nút này; hoặc cũng có thể bạn chạm đến nút ở tầng cao nhất trước, rồi tiếp tục di chuyển sang trái theo chuỗi liên kết ở tầng đó. Tuy nhiên, những chi tiết này không làm thay đổi kết quả độ phức tạp thời gian trung bình cuối cùng. Ngoài ra, độ phức tạp thời gian được đưa ra ở đây chỉ là một giá trị trung bình xác suất, nhưng thực tế hoàn toàn có thể tính toán một phân phối xác suất chính xác hơn. Để hiểu rõ hơn về vấn đề này, bạn vui lòng tham khảo thêm. William Pugh Bài báo của ... Skip Lists: A Probabilistic Alternative to Balanced Trees 》。

So sánh skiplist với cây cân bằng và bảng băm

  • Các phần tử trong skiplist và các loại cây cân bằng (như AVLxem ngoại hạng anh, cây đỏ-đen, v.v.) được sắp xếp theo thứ tự, trong khi bảng băm không có thứ tự. Do đó, trên bảng băm chỉ có thể thực hiện tìm kiếm cho một khóa đơn lẻ, không phù hợp để thực hiện tìm kiếm phạm vi. Tìm kiếm phạm vi có nghĩa là tìm tất cả các nút có giá trị nằm giữa hai giá trị cụ thể đã được chỉ định.
  • Khi thực hiện tìm kiếm trong phạm vi123win+club, thao tác trên cây cân bằng (balanced tree) phức tạp hơn so vớ Trên cây cân bằng, sau khi chúng ta đã tìm được giá trị nhỏ nhất trong phạm vi được chỉ định, cần phải tiếp tục duyệt theo thứ tự inorder để tìm các nút khác có giá trị không vượt quá giới hạn lớn của phạm vi. Nếu không thực hiện một số chỉnh sửa đối với cây cân bằng, việc duyệt theo thứ tự inorder ở đây sẽ không dễ dàng thực hiện. Ngược lại, việc tìm kiếm phạm vi trên skiplist rất đơn giản, chỉ cần duyệt qua danh sách liên kết ở tầng đầu tiên một vài bước sau khi đã tìm được giá trị nhỏ nhất là có thể hoàn thành. Ngoài ra, việc duy trì và cập nhật cây cân bằng thường yêu cầu nhiều thao tác phức tạp hơn, đặc biệt là khi thêm hoặc xóa các nút. Điều này làm cho việc quản lý cây cân bằng trở nên khó khăn hơn so với skiplist, vốn được thiết kế để tối ưu hóa các hoạt động tìm kiếm, chèn và xóa nhờ cấu trúc đa lớp của nó. Skiplist cũng có lợi thế về hiệu suất trong trường hợp tải dữ liệu lớn, vì nó cho phép tìm kiếm nhanh chóng mà không cần quan tâm đến sự cân bằng phức tạp như trong cây.
  • Việc chèn và xoá trong cây cân bằng có thể dẫn đến việc điều chỉnh lại các nhánh conxem ngoại hạng anh, khiến logic trở nên phức tạp. Ngược lại, đối với danh sách bậc (skiplist), thao tác chèn và xoá chỉ yêu cầu sửa đổi một số con trỏ của các nút liền kề, làm cho quy trình này không chỉ đơn giản mà còn rất nhanh chóng. Danh sách bậc tận dụng cơ chế lớp layer để tạo ra cấu trúc dữ liệu linh hoạt hơn, cho phép các thao tác tìm kiếm, chèn và xoá được thực hiện dễ dàng nhờ vào sự hỗ trợ của các liên kết đa chiều. Điều này giúp tiết kiệm thời gian và công sức khi xử lý các tập dữ liệu lớn, đồng thời giảm thiểu nguy cơ lỗi trong quá trình triển khai.
  • Từ góc độ sử dụng bộ nhớ mà nói123win+club, skiplist linh hoạt hơn so với cây cân bằng. Thông thường, mỗi nút trong cây cân bằng sẽ chứa 2 con trỏ (mỗi con trỏ dẫn đến cây con bên trái và bên phải), còn số lượng con trỏ trung bình của mỗi nút trong skiplist là 1/(1-p), cụ thể phụ thuộc vào giá trị của tham số p. Nếu như trong cách triển khai của Redis, giá trị p được đặt là 1/4, thì trung bình mỗi nút trong skiplist chỉ có khoảng 1,33 con trỏ, điều này giúp nó vượt trội hơn so với cây cân bằng về hiệu quả bộ nhớ. Ngoài ra, một ưu điểm khác của skiplist là khả năng dễ dàng mở rộng để tăng cường hiệu suất khi tìm kiếm. Trong khi cây cân bằng yêu cầu cấu trúc cố định và các phép xoay phức tạp khi thêm hoặc xóa nút, skiplist lại cho phép thêm bớt nút một cách nhanh chóng mà không cần phải điều chỉnh toàn bộ cấu trúc. Điều này làm cho skiplist trở thành lựa chọn lý tưởng trong nhiều trường hợp, đặc biệt là khi hệ thống cần xử lý khối lượng lớn dữ liệu mà vẫn đảm bảo tốc độ truy xuất ổn định.
  • Việc tìm kiếm một key trong skip list và cây cân bằng đều có độ phức tạp thời gian là O(log n)xem ngoại hạng anh, tương đương nhau về hiệu suất cơ bản. Tuy nhiên, bảng băm (hash table) khi duy trì được xác suất xung đột hash ở mức thấp, thì độ phức tạp tìm kiếm của nó sẽ gần như O(1), điều này cho phép nó hoạt động với hiệu suất cao hơn đáng kể. Chính vì lý do đó, các cấu trúc dữ liệu như Map hoặc dictionary mà chúng ta thường sử dụng trong thực tế hầu hết đều được xây dựng dựa trên nguyên lý bảng băm. Ngoài ra, việc chọn loại cấu trúc dữ liệu nào cũng phụ thuộc vào yêu cầu cụ thể của ứng dụng. Ví dụ, nếu bạn cần thao tác với dữ liệu có thứ tự hoặc phạm vi truy vấn linh hoạt, skip list hay cây cân bằng có thể là lựa chọn tốt hơn. Nhưng khi chủ yếu quan tâm đến tốc độ truy xuất nhanh và không quá quan trọng thứ tự của các phần tử, bảng băm chắc chắn là giải pháp tối ưu. Điều này giúp giải thích tại sao nhiều hệ thống hiện đại đều ưu tiên sử dụng bảng băm làm nền tảng chính để lưu trữ và quản lý dữ liệu dạng key-value.
  • Về độ khó của việc thực hiện thuật toán123win+club, skiplist đơn giản hơn nhiều so với cây cân bằng.

Thực hiện skiplist trong Redis

Trong phần nàybacarat, chúng ta sẽ thảo luận về việc thực hiệ

Trong Redisxem ngoại hạng anh, skiplist được sử dụng để triển khai một cấu trúc dữ liệu mà người dùng có thể tương tác trực tiếp: tập hợp đã sắp xếp (sorted set). Thực tế, sorted set không chỉ dựa vào skiplist mà còn kết hợp cả ziplist và dict. Chúng ta sẽ tìm hiểu thêm về mối quan hệ giữa các cấu trúc này trong chương sau. Hiện tại, hãy dành chút thời gian để khám phá những lệnh quan trọng củ Những lệnh này đóng vai trò vô cùng quan trọng đối với cách skiplist được thực hiệ Skiplist không chỉ là nền tảng cốt lõi mà còn hỗ trợ hiệu quả cho việc truy xuất nhanh chóng các phần tử Điều này đặc biệt hữu ích khi bạn cần thêm, xóa hoặc tìm kiếm các phần tử dựa trên thứ tự giá trị của chúng. Các lệnh như ZADD, ZREM, ZRANGE hay ZSCORE đều phụ thuộc vào cách hoạt động củ Bên cạnh đó, ziplist được sử dụng khi số lượng phần tử trong sorted set nhỏ, giúp tiết kiệm bộ nhớ hơn. Trong trường hợp dữ liệu lớn hơn, dict sẽ được ưu tiên để đảm bảo hiệu suất truy xuất. Điều này cho thấy sự linh hoạt và tối ưu hóa của Redis trong việc lựa chọn cấu trúc dữ liệu phù hợp với từng tình huống cụ thể.

Ví dụ lệnh cho sorted set

Sorted set (tập hợp đã sắp xếp) là một loại tập dữ liệu có thứ tự123win+club, rất lý tưởng cho các tình huống sử dụng như bảng xếp hạng. Với đặc tính giữ thứ tự của các phần tử theo giá trị, nó giúp người dùng dễ dàng truy xuất thông tin theo thứ tự ưu tiên hoặc điểm số, từ đó tạo ra trải nghiệm liền mạch trong các ứng dụng yêu cầu tính năng phân cấp hoặc đánh giá. Ví dụ điển hình là một bảng xếp hạng trò chơi, nơi người dùng luôn muốn nhìn thấy tên người chiến thắng ở đầu danh sách một cách nhanh chóng và chính xác.

Bây giờ chúng ta hãy xem qua một ví dụ123win+club, sử dụng tập hợp đã sắp xếp (sorted set) để lưu trữ bảng điểm của môn học đại số (algebra). Dữ liệu gốc như sau:

  • Alice 87.5
  • Bob 89.0
  • Charles 65.5
  • David 78.0
  • Emily 93.5
  • Fred 87.5

Dữ liệu này liệt kê tên và điểm số của từng học sinh. Tiếp theo123win+club, chúng ta sẽ lưu dữ liệu này vào tập hợp đã sắp xếp (sorted set) như sau:

Ví dụ lệnh cho sorted set

Đối với các lệnh trênbacarat, những điểm cần chú ý bao gồm:

  • Bạn đã nhập tên và điểm số (score) của 6 học sinh vào một tập hợp đã sắp xếp (sorted set) với khóa (key) là "algebra"bacarat, sử dụng 6 lệnh zadd. Hãy lưu ý rằng Alice và Fred có cùng điểm số, cụ thể là 87,5 điểm.
  • Lệnh `zrevrank` được sử dụng để kiểm tra thứ hạng của Alice (trong lệnh nàyxem ngoại hạng anh, chữ "rev" có nghĩa là sắp xếp theo chiều ngược lại, tức từ lớn đến nhỏ), và kết quả trả về là 3. Những người đứng trước Alice lần lượt là Emily, Bob và Fred. Tuy nhiên, cần lưu ý rằng thứ hạng (rank) được đếm bắt đầu từ 0, do đó Alice có thứ hạng là 3. Thực tế, Alice và Fred có cùng điểm số, trong trường hợp này, tập hợp đã sắp xếp sẽ dựa vào thứ tự từ điển để xác định thứ hạng. Khi sắp xếp theo chiều ngược lại, Fred xuất hiện trước Alice.
  • Lệnh zscore truy vấn điểm số tương ứng của Charles.
  • Lệnh zrevrange truy vấn 4 người từ lớn đến nhỏ theo thứ hạng 0-3.
  • Lệnh zrevrangebyscore được sử dụng để tìm kiếm tất cả các học sinh có điểm số nằm trong khoảng từ 80bacarat,0 đến 90,0 và sắp xếp kết quả theo thứ tự điểm số giảm dần, từ cao xuống thấp. Điều này giúp dễ dàng nhận ra những học sinh đạt điểm xuất sắc nhất trong phạm vi đã cho.

Tóm lại123win+club, mỗi phần tử trong sorted set chủ yếu thể hiện 3 thuộc tính:

  • Dữ liệu chính nó (trong ví dụ trướcxem ngoại hạng anh, chúng tôi đã lưu tên làm dữ liệu).
  • Mỗi dữ liệu tương ứng với một điểm số (score).
  • Dựa trên giá trị điểm số và thứ tự từ điển của dữ liệuxem ngoại hạng anh, mỗi mục dữ liệu sẽ được cấp một thứ hạng (rank). Bạn có thể lựa chọn sắp xếp theo thứ tự tăng dần hoặc giảm dần. Trong quá trình này, dữ liệu đầu tiên sẽ được so sánh dựa trên điểm số. Nếu có trường hợp hai hoặc nhiều dữ liệu có cùng điểm số, thứ tự từ điển của tên hoặc mã định danh sẽ được sử dụng để xác định thứ hạng. Điều này đảm bảo rằng mọi mục đều có một vị trí rõ ràng trong bảng xếp hạng. Ví dụ, nếu bạn chọn sắp xếp theo thứ tự tăng dần, các mục có điểm thấp hơn sẽ đứng đầu danh sách, trong khi nếu chọn sắp xếp theo thứ tự giảm dần, các mục có điểm cao nhất sẽ xuất hiện trước tiên. Đây là cách linh hoạt để phân loại thông tin dựa trên tiêu chí mà bạn đặt ra.

Đặc thù của việc thực hiện skiplist trong Redis

Chúng ta hãy phân tích sơ lược một vài lệnh truy vấn xuất hiện trước đó:

  • zrevrank tìm kiếm dữ liệu tương ứng với thứ hạngbacarat, điều này không được hỗ trợ trong skiplist đã đề cập trước đó.
  • Lệnh zscore truy vấn điểm số tương ứng của dữ liệuxem ngoại hạng anh, điều này không được hỗ trợ bở
  • Bạn có thể sử dụng lệnh zrevrange để lấy ra các giá trị nằm trong khoảng xếp hạng được chỉ định. Tuy nhiên123win+club, điều này không được hỗ trợ trong skiplist mà chúng ta đã đề cập trước đó.
  • Bạn có thể sử dụng lệnh zrevrangebyscore để tìm kiếm tập hợp dữ liệu theo khoảng giá trị điểm số123win+club, đây là một trong những thao tác tìm kiếm phạm vi điển hình được hỗ trợ bởi cấu trúc skip list (ở đây, score tương đương với key). Lệnh này không chỉ giúp bạn lọc dữ liệu nhanh chóng mà còn tận dụng hiệu quả của skip list trong việc quản lý và sắp xếp các phần tử dựa trên điểm số.

Thực tếbacarat, việc thực hiện sorted set trong Redis như sau:

  • Khi số lượng dữ liệu ít123win+club, sorted set được thực hiện bằng một ziplist.
  • Khi lượng dữ liệu tăng lênxem ngoại hạng anh, sorted set thường được thực hiện bằng cách kết hợp một dict và mộ Về cơ bản, dict được sử dụng để tra cứu mối quan hệ giữa dữ liệu và điểm số, trong khi skiplist giúp tìm kiếm dữ liệu dựa trên điểm số (có thể là tìm kiếm theo phạm vi). Skiplist không chỉ nhanh chóng mà còn cho phép thao tác có thứ tự, làm cho việc quản lý dữ liệu trở nên hiệu quả hơn rất nhiều so với các cấu trúc truyền thống khác. Điều này giúp sorted set trở thành một công cụ mạnh mẽ trong việc sắp xếp và truy xuất dữ liệu phức tạp.

Cấu trúc của sorted set sẽ được thảo luận chi tiết hơn trong chương tiếp theo. Hiện tạibacarat, chúng ta hãy tập trung tìm hiểu mối liên hệ giữa sorted set và skiplist:

  • Tìm kiếm zscore không được cung cấp bởi skiplist mà bởi dict.
  • Để hỗ trợ việc xếp hạng (rank)bacarat, Redis đã mở rộng cấu trúc skip list, cho phép tìm kiếm dữ liệu theo thứ tự xếp hạng một cách nhanh chóng. Đồng thời, khi tìm thấy dữ liệu dựa trên điểm số (score), việc xác định vị trí xếp hạng của nó cũng trở nên vô cùng dễ dàng. Đặc biệt, thao tác tìm kiếm theo thứ tự xếp hạng này có độ phức tạp thời gian chỉ ở mức O(log n), đảm bảo hiệu suất cao và tối ưu hóa hiệu quả trong quá trình xử lý dữ liệu.
  • Tìm kiếm zrevrange dựa trên thứ hạng để lấy dữ liệuxem ngoại hạng anh, được cung cấp bởi skiplist mở rộng.
  • Zrevrank đầu tiên sẽ truy xuất điểm số từ dict dựa trên dữ liệu đã choxem ngoại hạng anh, sau đó sử dụng điểm số này để tìm kiế Khi tìm thấy, nó cũng sẽ đồng thời xác định được thứ hạng tương ứng.

Quy trình tìm kiếm vừa rồi cũng ám chỉ độ phức tạp thời gian của các hoạt động:

  • Tìm kiếm zscore chỉ cần kiểm tra một dictxem ngoại hạng anh, vì vậy độ phức tạp thời gian là O(1)
  • Các lệnh như zrevrankxem ngoại hạng anh, zrevrange và zrevrangebyscore đều yêu cầu truy vấn danh sách nhảy (skiplist), do đó, độ phức tạp thời gian của zrevrank là O(log n). Trong khi đó, zrevrange và zrevrangebyscore có độ phức tạp thời gian là O(log(n) + M), trong đó M đại diện cho số lượng phần tử được trả về trong kết quả hiện tại. Điều này có nghĩa là việc tìm kiếm không chỉ phụ thuộc vào kích thước của tập dữ liệu mà còn chịu ảnh hưởng từ số lượng phần tử cần lấy ra, làm tăng hiệu suất tổng thể khi thực hiện các thao tác này.

Tóm lạibacarat, skiplist trong Redis so với skiplist cổ điển mà chúng ta đã đề cập trước đó có một số điểm khác biệt như sau:

  • Trong cấu trúc Skip Listbacarat, điểm số (score) được phép lặp lại, có nghĩa là các khóa (key) trong Skip List cũng có thể xuất hiện nhiều lần. Điều này khác biệt so với phiên bản Skip List cổ điển ban đầu được giới thiệu, nơi mà các khóa luôn phải duy nhất và không được phép trùng lặp. Sự thay đổi này mang lại sự linh hoạt lớn hơn cho việc quản lý dữ liệu, cho phép xây dựng các ứng dụng phức tạp hơn mà không cần ràng buộc quá chặt chẽ vào tính duy nhất của khóa.
  • Khi thực hiện so sánh123win+club, không chỉ so sánh điểm số (tương đương với key trong skiplist) mà còn cần xem xét cả nội dung dữ liệu. Trong cách triển khai skiplist của Redis, chính nội dung dữ liệu mới là yếu tố xác định duy nhất một phần tử, chứ không phải key. Ngoài ra, khi có nhiều phần tử có cùng điểm số, cần phải sắp xếp chúng dựa trên nội dung dữ liệu theo thứ tự từ điển. Điều này đảm bảo rằng mỗi phần tử luôn được phân biệt một cách rõ ràng và chính xác, ngay cả khi các giá trị điểm số trùng lặp.
  • Tầng 1 của danh sách liên kết không phải là một danh sách liên kết đơn hướng mà là một danh sách liên kết hai chiều. Điều này giúp việc lấy các phần tử trong một phạm vi theo thứ tự ngược trở nên dễ dàng hơn. Với cấu trúc hai chiềubacarat, việc di chuyển qua lại giữa các nút trở nên linh hoạt và hiệu quả, cho phép người dùng truy xuất dữ liệu theo nhiều hướng khác nhau một cách nhanh chóng.
  • Trong skiplistbacarat, rất dễ tính toán thứ hạng (rank) của từng phần tử.

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

								
									
										#define ZSKIPLIST_MAXLEVEL 32
#define ZSKIPLIST_P 0.25

									
typedef
									 struct
									 zskiplistNode
									 {
									
    robj
									 *
									obj
									;
									
    double
									 score
									;
									
    struct
									 zskiplistNode
									 *
									backward
									;
									
    struct
									 zskiplistLevel
									 {
									
        struct
									 zskiplistNode
									 *
									forward
									;
									
        unsigned
									 int
									 span
									;
									
    }
									 level
									[];
									
}
									 zskiplistNode
									;
									

typedef
									 struct
									 zskiplist
									 {
									
    struct
									 zskiplistNode
									 *
									header
									,
									 *
									tail
									;
									
    unsigned
									 long
									 length
									;
									
    int
									 level
									;
									
}
									 zskiplist
									;
									

								

Đoạn mã này xuất phát từ server.h123win+club, chúng ta sẽ phân tích ngắn gọn:

  • Trong phần mở đầuxem ngoại hạng anh, hai hằng số ZSKIPLIST_MAXLEVEL và ZSKIPLIST_P đã được xác định, lần lượt đại diện cho hai tham số mà chúng ta đã đề cập trước đó trong cấu trúc skiplist: tham số MaxLevel và tham số p. Trong đó, ZSKIPLIST_MAXLEVEL biểu thị mức tối đa mà một nút có thể đạt được trong skiplist, còn ZSKIPLIST_P là xác suất quyết định một nút sẽ được thêm vào các tầng trên cao hơn trong cấu trúc này. Điều này đóng vai trò quan trọng trong việc đảm bảo hiệu quả của thuật toán tìm kiếm và chèn.
  • Định nghĩa cấu trúc nú
    • Trường obj lưu trữ dữ liệu của nútxem ngoại hạng anh, và kiểu của nó là một chuỗi robj. Ban đầu, một chuỗi robj có thể lưu trữ không phải là sds mà là kiểu long, nhưng lệnh zadd sẽ giải mã dữ liệu trước khi chèn nó vào skiplist, do đó trường obj ở đây chắc chắn sẽ chứa một sds. Để biết thêm chi tiết về robj, bạn có thể tham khảo bài viết thứ ba trong loạt bài này: " Phân tích sâu cấu trúc dữ liệu bên trong Redis (3) —— robj Mục đích của việc làm như vậy có lẽ là để thuận tiện hơn trong quá trình tìm kiếm và so sánh theo thứ tự từ điển của dữ liệu. Hơn nữabacarat, khả năng phần dữ liệu trong skiplist là số cũng không quá cao.
    • Trường score là điểm số tương ứng với dữ liệu.
    • Trường dữ liệu backward là con trỏ hướng về nút trước đó trong danh sách liên kết (còn được gọi là con trỏ ngược). Mỗi nút chỉ có một con trỏ ngượcbacarat, do đó chỉ có lớp danh sách liên kết đầu tiên mới thực sự là danh sách liên kết hai chiều. Điều này có nghĩa là khi bạn di chuyển về phía trước trong danh sách, bạn sẽ không thể quay lại các nút đã qua mà không sử dụng thông tin từ trường backward này.
    • Trong cấu trúc nàyxem ngoại hạng anh, mảng level[] lưu trữ các con trỏ trỏ đến các nút tiếp theo ở mỗi cấp của danh sách liên kết đa lớp (skip list). Mỗi cấp sẽ có một con trỏ sau (back pointer), được biểu thị bởi trườ Bên cạnh đó, mỗi con trỏ sau này còn đi kèm với một giá trị span, cho biết con trỏ hiện tại bao phủ bao nhiêu nút trong danh sách. Giá trị span đóng vai trò quan trọng trong việc tính toán thứ hạng (rank) của phần tử, đây là một cải tiến mà Redis đã thực hiện trê Điều cần lưu ý là mảng level[] ở đây là một mảng linh hoạt (flexible array), tức là nó không có kích thước cố định và có thể thay đổi tùy thuộc vào yêu cầu của cấu trúc dữ liệu. flexible array member Vì vậybacarat, bộ nhớ dành cho phần này không nằm trong cấu trúc zskiplistNode mà sẽ được cấp phát riêng khi cần chèn thêm nút. Chính điều này cũng khiến số lượng con trỏ của mỗi nút trong skiplist là không cố định. Kết luận mà chúng ta đã phân tích trước đây — rằng số lượng con trỏ của mỗi nút trong skiplist trung bình là 1/(1-p) — mới có ý nghĩa. Nếu số lượng con trỏ của các nút luôn cố định, thì cách tính toán này sẽ không còn chính xác và có thể gây ra sự sai lệch trong việc quản lý tài nguyên bộ nhớ.
  • Định nghĩa cấu trúc skiplist thực sựxem ngoại hạng anh, nó bao gồm:
    • Con trỏ đầu header và con trỏ cuối tail.
    • Chiều dài của danh sách liên kết được ký hiệu là length123win+club, tức là tổng số lượng nút có trong danh sách. Hãy chú ý rằng khi một skiplist mới được tạo ra, nó sẽ bao gồm một con trỏ đầu trống, và con trỏ này không được tính vào giá trị length. Con trỏ đầu tiên này chỉ đóng vai trò như một điểm bắt đầu, không chứa dữ liệu thực sự mà chỉ phục vụ cho việc định hướng đến các nút tiếp theo trong cấu trú
    • Level biểu thị tổng số tầng của skiplist123win+club, tức là giá trị tối đa của số tầng của tất cả các nút.

Hình ảnh dưới đây sử dụng bảng điểm của một lớp học đại số được chèn trước đó làm ví dụ123win+club, minh họa cấu trúc có thể của một skiplist trong Redis:

Ví dụ cấu trúc skiplist trong Redis

Lưu ý: Các số trong dấu ngoặc nhỏ phía trên mũi tên chỉ báo giá trị của span tương ứng. Điều này có nghĩa là mũi tên hiện tại vượt qua bao nhiêu nútxem ngoại hạng anh, nhưng việc đếm này không tính đến nút xuất phát của mũi tên mà chỉ tính đến nút đích cuối cùng.

Giả sử chúng ta đang tìm kiếm phần tử có score = 89.0 trong skiplist (tức là điểm số của Bob) và theo dõi con đường tìm kiếmbacarat, chúng ta sẽ vượt qua các mũi tên được đánh dấu đỏ trên đồ thị. Tổng các giá trị span của những mũi tên này sẽ cho ta thứ hạng của Bob, tính bằng cách cộng (2 + 2 + 1) rồi trừ đi 1, vì thứ hạng bắt đầu từ 0. Điều này áp dụng cho thứ hạng tăng dần. Nếu muốn tính thứ hạng giảm dần, chúng ta chỉ cần lấy độ dài của skiplist và trừ đi tổng các giá trị span đã tính trước đó, tức là 6 - (2 + 2 + 1) = 1. Điều này giúp xác định vị trí của Bob dựa trên thứ hạng giảm dần so với toàn bộ danh sách.

Rõ ràng123win+club, trong quá trình tìm kiếm một skiplist, bằng cách cộng dồn giá trị span, chúng ta có thể dễ dàng tính được thứ hạng của phần tử. Ngược lại, nếu muốn tìm kiếm dữ liệu dựa trên thứ hạng đã cho (giống như zrange và zrevrange), ta cũng có thể liên tục cộng dồn span và luôn đảm bảo tổng không vượt quá thứ hạng đã chỉ định. Bằng cách này, ta sẽ có được một đường dẫn tìm kiếm với độ phức tạp thời gian O(log n). Điều thú vị là việc này không chỉ giúp tối ưu hóa quá trình tìm kiếm mà còn tăng cường hiệu quả quản lý dữ liệu trong cấu trú

Sorted set trong Redis

Chúng ta đã đề cập trước đây rằng sorted set trong Redis được xây dựng dựa trên skiplistxem ngoại hạng anh, dict và ziplist:

  • Khi số lượng dữ liệu ít123win+club, sorted set được thực hiện bằng một ziplist.
  • Khi lượng dữ liệu tăng lênxem ngoại hạng anh, tập hợp đã sắp xếp (sorted set) được thực hiện thông qua một cấu trúc dữ liệu gọi là zset. Cấu trúc này bao gồm một từ điển (dict) và một danh sách nhảy (skiplist). Từ điển được sử dụng để tra cứu mối quan hệ giữa dữ liệu và điểm số (score), trong khi danh sách nhảy giúp tìm kiếm dữ liệu dựa trên điểm số (có thể là tìm kiếm trong phạm vi). Điều này cho phép việc truy xuất dữ liệu trở nên nhanh chóng và hiệu quả hơn, đặc biệt khi phải xử lý khối lượng lớn thông tin phức tạp.

Hãy cùng tìm hiểu sơ lược về trường hợp đầu tiên mà chúng ta sẽ đề cập đến — đó là việc thực hiện sorted set dựa trên ziplist. Trong phần trước của chuỗi bài viết này123win+club, chúng ta đã từng đề cập đến... (Đây là một cách mở đầu linh hoạt hơn để tiếp cận chủ đề, đồng thời tạo sự kết nối với nội dung đã được trình bày trước đó.) Trong quá trình triển khai Redis, ziplist thường được sử dụng khi các tập dữ liệu nhỏ và đơn giản. Khi đó, cấu trúc ziplist giúp tối ưu hóa bộ nhớ và tăng hiệu suất cho các hoạt động cơ bản. Tuy nhiên, khi kích thước hoặc số lượng phần tử trong sorted set gia tăng, việc chuyển sang sử dụng linked list hoặc skip list có thể mang lại lợi ích hơn. Bạn có thấy điều này phù hợp với nhu cầu sử dụng của mình không? Bài viết về ziplist Trong phần trước123win+club, chúng ta đã đề cập rằng ziplist là một khối bộ nhớ liên tục gồm nhiều mục dữ liệu. Do mỗi phần tử của sorted set bao gồm cả dữ liệu và score, khi sử dụng lệnh zadd để thêm vào một cặp (dữ liệu, score), ở mức cơ sở, hệ thống sẽ chèn hai mục dữ liệu tương ứng vào ziplist: dữ liệu được đặt trước và score được đặt sau. Điều này giúp tối ưu hóa không gian lưu trữ trong trường hợp các phần tử có kích thước nhỏ và số lượng phần tử không quá lớn.

Một trong những ưu điểm chính của ziplist là tiết kiệm bộ nhớ123win+club, nhưng việc tìm kiếm trên nó chỉ có thể thực hiện theo thứ tự (có thể từ đầu đến cuối hoặc ngược lại). Do đó, các thao tác truy vấn khác nhau của sorted set sẽ tiến hành duyệt qua ziplist từ đầu đến cuối (hoặc từ cuối đến đầu), từng bước di chuyển hai phần tử tại một thời điểm, để "bước qua" mỗi cặp (dữ liệu, điểm số). Ngoài ra, do bản chất tuần tự của ziplist, hiệu suất tìm kiếm trong trường hợp này phụ thuộc rất nhiều vào vị trí của phần tử cần tìm. Nếu phần tử nằm ở gần cuối danh sách, số lần duyệt qua các phần tử có thể khá lớn, ảnh hưởng đến hiệu quả tổng thể của hoạt động truy vấn. Điều này cũng lý giải vì sao Redis thường chỉ sử dụng ziplist cho các trường hợp dữ liệu nhỏ và đơn giản, nhằm tối ưu hóa về mặt tài nguyên hệ thống.

Khi các phần tử được chèn vàobacarat, cấu trúc dữ liệu ziplist của sorted set có thể sẽ được chuyển đổi sang phiên bản đầy đủ của zset (chi tiết về quá trình chuyển đổi có thể tìm thấy trong t_zset.c ở hàm zsetConvert). Nhưng rốt cuộc thì cần phải chèn bao nhiêu phần tử thì mới xảy ra việc chuyển đổi này?

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

								
									zset-max-ziplist-entries 128
zset-max-ziplist-value 64

								

Cấu hình này có nghĩa là khi một trong hai điều kiện sau đây được đáp ứngxem ngoại hạng anh, ziplist sẽ được chuyển đổi thành zset (các điều kiện cụ thể có thể được tìm thấy trong mã nguồn t_zset.c, ở hàm zaddGenericCommand liên quan):

  • Khi số lượng phần tử trong tập hợp đã sắp xếp (sorted set) vượt quá 128xem ngoại hạng anh, nghĩa là số cặp dữ liệu (data, score) nhiều hơn 128 và đồng thời số mục trong cấu trúc ziplist vượt quá 256, thì hệ thống sẽ bắt đầu chuyển đổi cách lưu trữ để tối ưu hóa hiệu suất.
  • Khi bất kỳ dữ liệu nào được chèn vào sorted set có độ dài vượt quá 64.

Cuối cùngbacarat, định nghĩa mã nguồn của cấu trúc zset như sau:

								
									
										typedef
									 struct
									 zset
									 {
									
    dict
									 *
									dict
									;
									
    zskiplist
									 *
									zsl
									;
									
}
									 zset
									;
									

								

Tại sao Redis sử dụng skiplist thay vì cây cân bằng?

giữ mọi thứ thật đơn giản và hiệu quả

There are a few reasons:

1) They are not very memory intensive. It’s up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees.

2) A sorted set is often target of many ZRANGE or ZREVRANGE operations123win+club, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees.

3) They are simpler to implement123win+club, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.

Nguồn gốc câu nói này:

https://news.ycombinator.com/item?id=1171423

Những lý do được tóm tắt từ ba khía cạnh chính là mức sử dụng bộ nhớbacarat, khả năng hỗ trợ tìm kiếm phạm vi và độ dễ dàng trong việc triển khai – những chủ đề này chúng ta đã thực sự đề cập đến ở phần trước. Bên cạnh đó, mỗi khía cạnh này đều mang lại những lợi ích và thách thức riêng biệt. Về mặt sử dụng bộ nhớ, việc tối ưu hóa không chỉ giúp cải thiện hiệu suất mà còn giảm chi phí tài nguyên. Đối với khả năng tìm kiếm phạm vi, điều này đặc biệt quan trọng khi bạn cần xử lý các tập dữ liệu lớn hoặc yêu cầu tính toán phức tạp. Cuối cùng, độ dễ dàng trong việc triển khai cũng ảnh hưởng trực tiếp đến thời gian và nguồn lực cần thiết để phát triển hệ thống.


Trong phần tiếp theo của loạt bài viết nàybacarat, chúng ta sẽ cùng tìm hiểu về intset và mối liên hệ của nó với kiểu dữ liệu set mà Redis cung cấp ra bên ngoài. Đừng bỏ lỡ nhé!

(Kết thúc)

Các bài viết được chọn lọc khác


Bài viết gốc123win+club, 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: /0mbkgjj0.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: Bạn có cần hiểu công nghệ học sâu và mạng thần kinh không?
Bài sau: [Khoa học viễn tưởng] Cô gái ngà voi