redis-plus-plus
キャッシュライブラリ
redis-plus-plus
概要
redis-plus-plusは、C++11以降に対応したモダンなRedisクライアントライブラリで、hiredisベースの高性能実装とSTLライクなインターフェースを提供し、C++アプリケーションでのRedis操作を簡単かつ効率的に実現します。
詳細
redis-plus-plus(レディス・プラス・プラス)はhiredisライブラリをベースとして構築されたC++用のRedisクライアントライブラリで、C++11以降の標準をサポートしています。RedisだけでなくValkey、DragonflyDB、KeyDBなどのRESP(Redis Serialization Protocol)対応のkey-valueストアでも動作します。主要機能には、ほぼ全てのRedisコマンド、コネクションプール、Redisスクリプティング、スレッドセーフティ(特記事項を除く)、Redis Pub/Sub、パイプライニング、トランザクション、Redis Cluster、Redis Sentinel、STLライクなインターフェース、汎用コマンドインターフェース、Redis Stream、TLS/SSL対応、同期・非同期インターフェース、コルーチンサポートなどがあります。「本番環境で使用可能で、多くのコンパイラで完全にテストされている」と評価されており、Redis 5.0および対応するクラスターに任意のコマンドを送信できる汎用コマンドインターフェースも提供しています。Conan等のパッケージマネージャーでも利用可能で、C++の現代的な開発環境に適応しています。
メリット・デメリット
メリット
- モダンC++対応: C++11以降の標準機能を活用した設計
- 高性能: hiredisベースによる高速なRedis通信
- 豊富な機能: Cluster、Sentinel、Stream、TLS等の全機能対応
- STLライク: C++開発者に馴染みやすいインターフェース
- スレッドセーフ: マルチスレッド環境での安全な使用
- 汎用性: Redis以外のRESP対応ストアでも動作
- 非同期対応: 同期・非同期・コルーチンの全てをサポート
デメリット
- C++複雑性: C++特有の複雑さとコンパイル時間の長さ
- 依存関係: hiredisライブラリへの依存とビルド設定の複雑さ
- 学習コスト: RedisとC++両方の深い理解が必要
- デバッグ困難: C++の低レベル特性によるデバッグの複雑さ
- プラットフォーム制約: C++コンパイラとプラットフォーム固有の制約
主要リンク
- redis-plus-plus GitHub リポジトリ
- redis-plus-plus Conan パッケージ
- hiredis GitHub リポジトリ
- Redis 公式クライアントライブラリ一覧
- Redis 公式サイト
書き方の例
基本接続とセットアップ
#include <sw/redis++/redis++.h>
using namespace sw::redis;
try {
// 基本接続
Redis redis("tcp://127.0.0.1:6379");
// 設定付き接続
ConnectionOptions connection_options;
connection_options.host = "127.0.0.1";
connection_options.port = 6379;
connection_options.password = "auth";
connection_options.db = 1;
ConnectionPoolOptions pool_options;
pool_options.size = 3;
Redis redis_with_pool(connection_options, pool_options);
} catch (const Error &e) {
std::cerr << "Connection error: " << e.what() << std::endl;
}
基本的なキー・バリュー操作
#include <iostream>
#include <sw/redis++/redis++.h>
int main() {
try {
sw::redis::Redis redis("tcp://127.0.0.1:6379");
// 文字列の設定と取得
redis.set("key", "value");
auto val = redis.get("key");
if (val) {
std::cout << "Value: " << *val << std::endl;
}
// 数値の操作
redis.set("counter", "0");
auto count = redis.incr("counter");
std::cout << "Counter: " << count << std::endl;
// 期限付きキー
redis.setex("temp_key", std::chrono::seconds(60), "temp_value");
// キーの存在確認
if (redis.exists("key")) {
std::cout << "Key exists" << std::endl;
}
// キーの削除
redis.del("key");
} catch (const sw::redis::Error &e) {
std::cerr << "Redis error: " << e.what() << std::endl;
}
return 0;
}
リストとセット操作
// リスト操作
redis.lpush("list", {"a", "b", "c"});
redis.rpush("list", {"d", "e"});
std::vector<std::string> list_items;
redis.lrange("list", 0, -1, std::back_inserter(list_items));
for (const auto& item : list_items) {
std::cout << item << " ";
}
// セット操作
redis.sadd("set", {"member1", "member2", "member3"});
std::unordered_set<std::string> set_members;
redis.smembers("set", std::inserter(set_members, set_members.begin()));
for (const auto& member : set_members) {
std::cout << member << " ";
}
// セットの演算
redis.sadd("set1", {"a", "b", "c"});
redis.sadd("set2", {"b", "c", "d"});
std::unordered_set<std::string> intersection;
redis.sinter({"set1", "set2"}, std::inserter(intersection, intersection.begin()));
ハッシュ操作
// ハッシュフィールドの設定
redis.hset("user:1", "name", "John");
redis.hset("user:1", "email", "[email protected]");
redis.hset("user:1", "age", "30");
// 複数フィールドの一括設定
std::unordered_map<std::string, std::string> user_data = {
{"name", "Alice"},
{"email", "[email protected]"},
{"age", "25"}
};
redis.hmset("user:2", user_data.begin(), user_data.end());
// ハッシュの取得
auto name = redis.hget("user:1", "name");
if (name) {
std::cout << "Name: " << *name << std::endl;
}
// 全フィールドの取得
std::unordered_map<std::string, std::string> all_fields;
redis.hgetall("user:1", std::inserter(all_fields, all_fields.begin()));
for (const auto& [field, value] : all_fields) {
std::cout << field << ": " << value << std::endl;
}
パイプライニング
// パイプラインで複数コマンドを効率的に実行
auto pipe = redis.pipeline();
auto future1 = pipe.set("key1", "value1");
auto future2 = pipe.set("key2", "value2");
auto future3 = pipe.get("key1");
auto future4 = pipe.get("key2");
pipe.exec();
// 結果の取得
try {
future1.get(); // set の結果
future2.get(); // set の結果
auto val1 = future3.get(); // get の結果
auto val2 = future4.get(); // get の結果
if (val1) std::cout << "Key1: " << *val1 << std::endl;
if (val2) std::cout << "Key2: " << *val2 << std::endl;
} catch (const sw::redis::Error &e) {
std::cerr << "Pipeline error: " << e.what() << std::endl;
}
トランザクション
// トランザクション処理
auto tx = redis.transaction();
tx.multi();
tx.set("account:1:balance", "100");
tx.set("account:2:balance", "200");
tx.decrby("account:1:balance", 50);
tx.incrby("account:2:balance", 50);
auto results = tx.exec();
if (results) {
std::cout << "Transaction successful" << std::endl;
} else {
std::cout << "Transaction failed" << std::endl;
}
Redis Cluster操作
#include <sw/redis++/redis++.h>
// Redis Cluster接続
ConnectionOptions connection_options;
connection_options.host = "127.0.0.1";
connection_options.port = 7000;
ConnectionPoolOptions pool_options;
pool_options.size = 3;
RedisCluster cluster(connection_options, pool_options);
// クラスターでの操作
cluster.set("key", "value");
auto val = cluster.get("key");
// クラスター情報の取得
auto nodes = cluster.cluster_nodes();
std::cout << "Cluster nodes: " << nodes << std::endl;
Pub/Sub機能
// Publisher
redis.publish("channel1", "Hello, World!");
// Subscriber
auto sub = redis.subscriber();
sub.subscribe("channel1");
sub.consume();
while (true) {
try {
auto msg = sub.consume();
if (msg.type == sw::redis::Subscriber::MsgType::MESSAGE) {
std::cout << "Received: " << msg.msg << std::endl;
}
} catch (const sw::redis::TimeoutError &e) {
// タイムアウト処理
continue;
} catch (const sw::redis::Error &e) {
std::cerr << "Subscriber error: " << e.what() << std::endl;
break;
}
}
非同期操作
#include <future>
#include <sw/redis++/redis++.h>
// 非同期でのRedis操作
auto async_redis = std::async(std::launch::async, [&redis]() {
try {
redis.set("async_key", "async_value");
auto val = redis.get("async_key");
return val ? *val : std::string("Not found");
} catch (const sw::redis::Error &e) {
return std::string("Error: ") + e.what();
}
});
// 他の処理を実行
std::cout << "Doing other work..." << std::endl;
// 非同期処理の結果を取得
auto result = async_redis.get();
std::cout << "Async result: " << result << std::endl;
エラーハンドリング
#include <sw/redis++/redis++.h>
void robust_redis_operation() {
try {
Redis redis("tcp://127.0.0.1:6379");
// 操作実行
redis.set("test_key", "test_value");
auto val = redis.get("test_key");
} catch (const ConnectionError &e) {
std::cerr << "Connection error: " << e.what() << std::endl;
// 再接続ロジック
} catch (const TimeoutError &e) {
std::cerr << "Timeout error: " << e.what() << std::endl;
// タイムアウト処理
} catch (const ReplyError &e) {
std::cerr << "Redis reply error: " << e.what() << std::endl;
// Redis固有のエラー処理
} catch (const Error &e) {
std::cerr << "Generic Redis error: " << e.what() << std::endl;
// 汎用エラー処理
} catch (const std::exception &e) {
std::cerr << "Standard exception: " << e.what() << std::endl;
}
}