ふくふくHukuhuku Inc.
EP.12Algorithms 9分公開: 2026-05-11

Skip List:Redis の Sorted Set を支える確率的なソート済みデータ構造

「ソート済みリストに O(log n) で挿入・検索・順位取得」を確率的に実現する Skip List。Redis ZSET / LevelDB / RocksDB の中身を Python で実装。

#Skip List#Redis#確率的データ構造
CO📔 Google Colab で開く(上から順にセルを実行)
シェア

Redis のリーダーボード「ゲームのランキング上位 100」「フォロワー数順のユーザリスト」「最新ツイート 50 件」 ── これらすべて Sorted Set (ZSET) で実装され、その中身が Skip List。連結リストにショートカット層を確率的に追加するだけで O(log n) を実現する、エレガントなデータ構造。

1. これで何が動いているか

  • Redis Sorted Set (ZSET): ランキング / 時系列
  • LevelDB / RocksDB: MemTable の中核
  • Apache Cassandra: SSTable インデックス
  • Java ConcurrentSkipListMap: スレッドセーフな TreeMap
  • etcd: メモリ内インデックス

2. 仕組みのざっくり

  • 最下層 (level 0): ソート済みの連結リスト
  • 上の層: 各要素を確率 p (典型 0.5) で持ち上げた要素のリスト
  • 検索: 最上層から開始、「次が目的より小さければ進む、大きければ下の層へ」
  • 挿入: 検索しながら経路を記録 → ランダムな高さで全層に挿入
  • 期待計算量: 平均高さ 1/(1-p) = 2、検索 O(log n)

3. Python 実装 (動作確認済)

純粋 Python の Skip List
Python
import random
class SkipNode:    def __init__(self, key, level):        self.key = key        self.forward = [None] * (level + 1)
class SkipList:    def __init__(self, max_level=16, p=0.5):        self.max_level = max_level        self.p = p        self.header = SkipNode(None, max_level)        self.level = 0
    def random_level(self):        '''高さを確率的に決定 (期待値 1/(1-p))'''        lvl = 0        while random.random() < self.p and lvl < self.max_level:            lvl += 1        return lvl
    def insert(self, key):        update = [None] * (self.max_level + 1)        cur = self.header        # 検索しながら経路 (update) を記録        for i in range(self.level, -1, -1):            while cur.forward[i] and cur.forward[i].key < key:                cur = cur.forward[i]            update[i] = cur
        lvl = self.random_level()        if lvl > self.level:            for i in range(self.level + 1, lvl + 1):                update[i] = self.header            self.level = lvl
        new = SkipNode(key, lvl)        for i in range(lvl + 1):            new.forward[i] = update[i].forward[i]            update[i].forward[i] = new
    def search(self, key):        cur = self.header        for i in range(self.level, -1, -1):            while cur.forward[i] and cur.forward[i].key < key:                cur = cur.forward[i]        cur = cur.forward[0]        return cur is not None and cur.key == key
    def display(self):        '''各層を可視化'''        for i in range(self.level, -1, -1):            keys = []            cur = self.header.forward[i]            while cur:                keys.append(cur.key)                cur = cur.forward[i]            print(f'Level {i}: {keys}')
# 動作確認random.seed(0)sl = SkipList()for x in [3, 6, 7, 9, 12, 19, 17, 26, 21, 25]:    sl.insert(x)
sl.display()print(f'\n19 がある? {sl.search(19)}')print(f'100 がある? {sl.search(100)}')
# 実機実行例:# Level 0: [3, 6, 7, 9, 12, 17, 19, 21, 25, 26]# Level 1: [3, 6, 12, 19, 25]# ...# 19 がある? True, 100 がある? False

4. Redis ZSET との関係

redis-py で Skip List ベースの Sorted Set
Python
# pip install redisimport redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# ZADD で score 付きでメンバー追加r.zadd('leaderboard', {    'alice': 95, 'bob': 80, 'carol': 100, 'dave': 75, 'eve': 88})
# 上位 3 件 (内部的には Skip List で O(log n))top3 = r.zrevrange('leaderboard', 0, 2, withscores=True)print(f'TOP 3: {top3}')
# alice の順位 (内部的には Skip List で O(log n))rank = r.zrevrank('leaderboard', 'alice')print(f'alice の順位: {rank + 1}')
# score 80-95 の範囲mid = r.zrangebyscore('leaderboard', 80, 95)print(f'80-95 の範囲: {mid}')

5. 計算量比較

操作Skip ListRed-Black TreeHash Table
検索O(log n)O(log n)O(1)
挿入O(log n)O(log n)O(1)
削除O(log n)O(log n)O(1)
順位取得O(log n)O(log n)不可
範囲取得O(log n + k)O(log n + k)O(n)
実装難易度難 (回転)

6. メリットとデメリット

  • メリット: 実装が圧倒的に簡単 (回転なし)
  • メリット: Lock-Free / 並列化が容易 (各層を独立に操作可)
  • メリット: 「順位」「範囲」操作が高速
  • デメリット: ランダム性に依存 (最悪 O(n)、ただし確率的に稀)
  • デメリット: メモリ局所性が悪い (B-Tree より遅い場合あり)

7. 次の話

EP.13 では LSM Tree (Log-Structured Merge Tree) を扱います。Cassandra / RocksDB / BigTable / DynamoDB が使う、書込み性能を最大化するストレージエンジンの仕組み。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp