Redisをメインデータベースとして無理やり使う際の苦しみ
長年、開発者の間でRedisはキャッシュ専用のものだと決めつけられてきました。文字列を保存したり、カウンターをインクリメントしたり、あるいはせいぜいシンプルなHashを保存する程度です。しかし、プロジェクトが大きくなり、住所や趣味が複雑にネストされたユーザープロファイルのようなデータを保存する必要が出てくると、純粋なRedisだけでは限界が見え始めます。
Redis Coreを無理に使おうとすると、通常2つの罠に陥ります。
- 罠1: オブジェクト全体をJSON文字列に変換して1つのキーに放り込む。名前や電話番号を1つ変更したいだけでも、JSON全体を取得し、パースして修正し、再び書き戻す必要があります。オブジェクトが数MBある場合、この操作はCPUと帯域を激しく消費します。
- 罠2: Hashに細分化する。この方法は高速ですが、検索能力が皆無です。
SCANのような基本コマンドだけでは、「東京都に住んでいて5万円以上使った顧客リストを抽出して」といった要求をRedisに投げることはできません。
Redis OM (Object Mapping) は、この問題を解決するために登場しました。これはMongoDBにおけるMongooseや、SQLにおけるSQLAlchemyのようなものだと考えてください。ただし、**RedisJSON**と**RediSearch**による圧倒的なスピードの上で動作します。
なぜRedis OMを使うべきなのか?
泥臭いコードを書く代わりに、Redis OMではモデル(Schema)を明確に定義できます。複雑なRedisコマンドを、非常に読みやすい純粋なオブジェクトのメソッドへと変換してくれます。
主なメリットは以下の通りです:
- 全文検索(Full-text search)を瞬時に行うための自動インデックス作成。
- オブジェクト全体を取得することなく、ネストされたデータ構造(Nested structures)に直接クエリを実行可能。
- 開発時間を大幅に短縮(純粋なドライバーを使う場合に比べて、ボイラープレートコードを約30〜50%削減)。
- PythonとNode.jsの両方を強力にサポート。
「鉄板」の環境構築
Redis OMを使うには、標準のRedisでは不十分です。検索やJSON処理モジュールが最初から統合された「フルオプション」版である**Redis Stack**が必要です。
Dockerを使えば、一行のコマンドで最も素早くデプロイできます:
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
実行後、localhost:8001を開いてください。Redis Insightのインターフェースにより、ターミナルでコマンドを打つ代わりに、直感的にデータを視覚化して確認できます。
Pythonでの実践 (Redis-om-python)
まず、pipでライブラリをインストールします:
pip install redis-om
人事管理システムを例に考えてみましょう。検索のために個人情報と経歴を保存する必要があります。モデルの定義方法はPydanticに非常によく似ています:
from redis_om import HashModel, Field, Migrator
from typing import Optional
from pydantic import EmailStr
class Employee(HashModel):
name: str = Field(index=True)
email: EmailStr = Field(index=True)
age: int = Field(index=True)
bio: str = Field(index=True, full_text_search=True)
city: str = Field(index=True)
# 最も重要なステップ:インデックスの作成
Migrator().run()
Migrator().run() コマンドを忘れないでください。これがないと、Redisはデータの検索方法を認識できず、検索結果は常にゼロになってしまいます。
データを追加し、複雑なクエリを実行してみます:
# 新しい従業員を保存
emp = Employee(name="田中 太郎", email="[email protected]", age=30, bio="5年の経験を持つバックエンドエンジニア", city="東京")
emp.save()
# 東京在住で25歳以上の人を検索
results = Employee.find((Employee.city == "東京") & (Employee.age > 25)).all()
Node.jsでの実装 (Redis-om-node)
Node.jsユーザーの場合、Async/Awaitを使用した構文はさらにスムーズです。
npm install redis-om
以下は、Schemaの設定と在庫商品の検索方法です:
import { Client, Entity, Schema } from 'redis-om';
const client = new Client();
await client.open('redis://localhost:6379');
class Product extends Entity {}
const productSchema = new Schema(Product, {
name: { type: 'string', indexed: true },
description: { type: 'string', textSearch: true },
price: { type: 'number', indexed: true }
});
const repo = client.fetchRepository(productSchema);
await repo.createIndex();
// 価格が2000ドル未満で、説明に「薄型軽量」を含むノートPCを検索
const products = await repo.search()
.where('description').matches('薄型軽量')
.and('price').is.lessThan(2000)
.return.all();
ネストされたデータの処理 (Nested Objects)
これこそがRedis OMの秘密兵器です。現実のデータは複雑なツリー構造を持っていることが多いです。例えば、1つの注文に複数の商品が含まれ、各商品に独自のカテゴリがある場合などです。
このようなネスト構造をテストするために、CSVからJSONへ素早く変換する必要がある場合は、toolcraft.app/ja/tools/data/csv-to-json のツールを使用してください。ブラウザ上で処理されるため、データは常に安全です。
ネストされたデータを保存するには、HashModelからJsonModelに変更します:
from redis_om import JsonModel
from typing import List
class Address(JsonModel):
street: str
zip_code: str
class Customer(JsonModel):
name: str = Field(index=True)
addresses: List[Address] # 非常に柔軟なネスト構造
RedisJSONのおかげで、オブジェクトの隅々まで深くクエリを実行でき、速度はサブミリ秒(1ミリ秒未満)を維持できます。
プロジェクトからの実践的な経験
秒間数千リクエストを処理するシステムにRedis OMを導入した経験から、いくつかのアドバイスがあります:
- RAM管理: 全文検索は非常に強力ですが、RAMを大量に消費します。
textSearch: trueは、商品の説明やユーザーの経歴など、本当に必要なフィールドにのみ設定してください。 - データ制約: Redis OMは型に対して厳格です。Modelで
intと定義した場所にstringを投入すると、アプリはすぐにクラッシュします。入力データのバリデーションは徹底しましょう。 - JSONの深さ: ネストはサポートされていますが、3〜4階層を超えて多用しすぎないようにしてください。構造がフラットなほど、クエリのパフォーマンスは安定します。
- インデックス: Schemaに変更を加えた新しいバージョンをデプロイする際は、必ずインデックス作成コマンドを再実行してください。
まとめ
Redis OMはRedisを単なる一時的なメモリ保存場所から、新たなレベルへと引き上げました。超高速なレスポンス、柔軟な検索、そして現代的なJSON構造を必要とする場合、これは間違いなく第一の選択肢となるでしょう。
もちろん、複雑なリレーショナルデータには依然としてSQLの利点があります。しかし、スピードと柔軟性が求められる課題において、Redis Stack + Redis OMのコンボは、あなたの開発武器庫の中で非常に強力なツールになるはずです。

