redis-plus-plus

C++キャッシュライブラリRedisクライアントNoSQL

キャッシュライブラリ

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++コンパイラとプラットフォーム固有の制約

主要リンク

書き方の例

基本接続とセットアップ

#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;
    }
}