高校化学2924392 views
英語613639 views
LaTeX962153 views
中学英語811710 views
中学理科1630641 views
世界の国564520 views
高校国語788336 views
教育149510 views
りんご209490 views
高校倫理1440106 views
Help
Tools
NewsSpreadsheetCalendarSlidesTier ListPen ToolIllustrationCrayonPixel ArtASCII ArtPerspectiveEndless StairsGraphMind MapER DiagramFamily TreeMemeCurved TextImage EditorMosaicRetro FilterPencil SketchOCR/HighlighterMakeup EditorFaviconVideo TrimmerScrolling VideoVideo TitleColor PickerColor ExtractorBonfireFireworksWater RippleWater SplashBreaking GlassWood GrainMarble TextureCSS ButtonIcon MakerBar ChartGrouped Bar ChartStacked Bar ChartPie ChartLine ChartArea ChartStacked Area ChartScatter Plot3D Bar Chart3D Pie ChartBar Chart RaceBubble ChartPopulation PyramidPictogramEarningsCandlestick ChartInvestment RiskMortgage SimulatorCalculatorMatrix CalculatorFunction GraphPolynomial ExpansionVenn DiagramField VisualizerRubik's Cube Group TheoryTraveling SalesmanVoronoi and DelaunayFractalColumn ArithmeticDraw Math FiguresArithmetic AnimationArithmetic Word ProblemsCounting with Tree DiagramsCube NetsCross SectionsMotion PathMechanicsWavesLight and LensesThermodynamicsHow Semiconductors WorkMolecular StructuresAtomic OrbitalsElectrochemical CellsChemical EquilibriumCrystal LatticesBuffer pHComplex IonsDNA Double HelixCell DivisionMembrane ChannelsNerve ImpulseRock ClassificationWeatherConstellationsSolar and Lunar Eclipses3D ModelingFloor PlanSeismic StructuresIntersection TurnMaglevCooking AnimationOrigamiLive Viewer CountGeoJSON MapRailway MapPopulation MapCrime MapLand Price MapSchool MapShrine and Castle MapHouse of Representatives MapWord MapSolitaireReversiHakoiri MusumeChessHamburgerRippleSlide Puzzle MakerNeon PinballNovel MakerJapanese Typing PracticePiano Score EditorMusic Theory

English

Python の set vs Redis の Set 型(分散環境での集合)

Python の set はプロセス内で完結するインメモリのデータ構造だ。一方、Redis の Set は分散環境で複数のプロセスやサーバー間で共有できる。この違いがアーキテクチャ選択に大きく影響する。

基本的な違い

Python set

単一プロセス内のメモリに存在。プロセス終了で消失。高速だがスケールしない。

Redis Set

外部サーバーに存在。永続化可能。ネットワーク越しにアクセス。水平スケール可能。

Redis はデータベースとして機能し、Python set は単なるデータ構造だ。

Redis Set の基本操作

redis-py を使った基本操作を見る。

import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# 要素の追加(SADD)
r.sadd('fruits', 'apple', 'banana', 'cherry')

# 存在確認(SISMEMBER)
print(r.sismember('fruits', 'apple'))   # True
print(r.sismember('fruits', 'grape'))   # False

# 全要素取得(SMEMBERS)
print(r.smembers('fruits'))  # {'apple', 'banana', 'cherry'}

# 要素数(SCARD)
print(r.scard('fruits'))  # 3

# 削除(SREM)
r.srem('fruits', 'banana')

操作名は異なるが、Python の set とほぼ対応している。

集合演算

Redis でも和集合、積集合、差集合ができる。

import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

r.sadd('set_a', 1, 2, 3, 4)
r.sadd('set_b', 3, 4, 5, 6)

# 積集合(SINTER)
print(r.sinter('set_a', 'set_b'))  # {'3', '4'}

# 和集合(SUNION)
print(r.sunion('set_a', 'set_b'))  # {'1', '2', '3', '4', '5', '6'}

# 差集合(SDIFF)
print(r.sdiff('set_a', 'set_b'))   # {'1', '2'}

結果を新しいキーに保存する SINTERSTORE, SUNIONSTORE, SDIFFSTORE もある。

# 結果を別のキーに保存
r.sinterstore('set_intersection', 'set_a', 'set_b')
print(r.smembers('set_intersection'))  # {'3', '4'}

パフォーマンス特性

Redis へのアクセスはネットワーク越しになるため、レイテンシが発生する。

import redis
import time

r = redis.Redis(host='localhost', port=6379)

# ローカル set
local_set = set(range(10000))
start = time.perf_counter()
for i in range(1000):
    i in local_set
print(f"Python set: {time.perf_counter() - start:.4f}s")

# Redis Set(事前に SADD 済み)
start = time.perf_counter()
for i in range(1000):
    r.sismember('redis_set', i)
print(f"Redis Set: {time.perf_counter() - start:.4f}s")

ローカルの set はマイクロ秒単位だが、Redis は往復で数百マイクロ秒〜数ミリ秒かかる。単純な速度では Python set が圧倒的だ。

パイプラインによる最適化

複数のコマンドをまとめて送ることで、ネットワークのオーバーヘッドを削減できる。

import redis

r = redis.Redis(host='localhost', port=6379)

# パイプラインなし(遅い)
for i in range(1000):
    r.sadd('slow_set', i)

# パイプラインあり(速い)
pipe = r.pipeline()
for i in range(1000):
    pipe.sadd('fast_set', i)
pipe.execute()

パイプラインを使えば、1000 回の往復が 1 回になる。

Redis Set の強み

Python set では実現しにくい機能がある。

永続化

RDB スナップショットや AOF でディスクに保存。再起動後もデータが残る。

TTL(有効期限)

EXPIRE でキーに有効期限を設定。セッション管理やキャッシュに有用。

分散アクセス

複数のアプリケーションサーバーから同じ Set にアクセス可能。

import redis

r = redis.Redis(host='localhost', port=6379)

# 有効期限付きの Set
r.sadd('session:user123', 'token_abc')
r.expire('session:user123', 3600)  # 1時間後に自動削除

ランダム要素の取得

Redis には set からランダムに要素を取得する機能がある。

import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

r.sadd('lottery', 'alice', 'bob', 'charlie', 'dave', 'eve')

# ランダムに 1 つ取得(削除しない)
print(r.srandmember('lottery'))

# ランダムに 2 つ取得
print(r.srandmember('lottery', 2))

# ランダムに 1 つ取得して削除(SPOP)
print(r.spop('lottery'))

Python の set で同様のことをするには、一度リストに変換して random.choice() を使う必要がある。

スキャンによる大規模 Set の走査

巨大な Set をイテレートするには SSCAN を使う。

import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# 大量のデータを追加
for i in range(100000):
    r.sadd('huge_set', f'item:{i}')

# SSCAN でカーソルベースの走査
cursor = 0
count = 0
while True:
    cursor, items = r.sscan('huge_set', cursor, count=1000)
    count += len(items)
    if cursor == 0:
        break

print(f"Total items: {count}")

SMEMBERS は全要素を一度に返すため、巨大な Set では Redis をブロックしてしまう。SSCAN なら少しずつ取得できる。

Sorted Set との違い

Redis には順序付きの Sorted Set(ZSET)もある。

import redis

r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# Sorted Set(スコア付き)
r.zadd('ranking', {'alice': 100, 'bob': 85, 'charlie': 92})

# スコア順で取得
print(r.zrange('ranking', 0, -1, withscores=True))
# [('bob', 85.0), ('charlie', 92.0), ('alice', 100.0)]

# スコアでフィルタ
print(r.zrangebyscore('ranking', 90, 100))
# ['charlie', 'alice']

Python の set にはスコアの概念がなく、heapq や別のデータ構造が必要になる。

使い分けの指針

要件Python setRedis Set
単一プロセス内で完結
複数サーバーで共有×
永続化が必要×
ミリ秒未満のレイテンシ×
数百万要素の存在確認

典型的なパターンとして、ローカルでの高速処理には Python set を使い、プロセス間・サーバー間での共有や永続化が必要な場合に Redis Set を使う。

import redis

# ハイブリッドアプローチ
class HybridSet:
    def __init__(self, redis_key):
        self.redis = redis.Redis(host='localhost', port=6379)
        self.key = redis_key
        self.local_cache = None
    
    def load(self):
        """Redis から読み込んでローカルキャッシュ"""
        self.local_cache = set(self.redis.smembers(self.key))
    
    def __contains__(self, item):
        """高速な存在確認"""
        if self.local_cache is not None:
            return item in self.local_cache
        return self.redis.sismember(self.key, item)
    
    def add(self, item):
        """両方に追加"""
        self.redis.sadd(self.key, item)
        if self.local_cache is not None:
            self.local_cache.add(item)

このようにローカルキャッシュと Redis を組み合わせることで、読み取りは高速に、書き込みは永続化されるという両方の利点を得られる。