Jedis

キャッシュライブラリJavaRedisパフォーマンス分散キャッシュ接続プーリング

GitHub概要

redis/jedis

Redis Java client

スター12,173
ウォッチ675
フォーク3,902
作成日:2010年6月11日
言語:Java
ライセンス:MIT License

トピックス

javajedisredisredis-clientredis-cluster

スター履歴

redis/jedis Star History
データ取得日時: 2025/10/22 09:55

キャッシュライブラリ

Jedis

概要

JedisはJava用の公式Redisクライアントライブラリで、高性能な接続プーリング、Redis Cluster、Sentinel、RediSearch、RedisJSONなどのRedis全機能をサポートします。

詳細

JedisはRedis Labsが開発・メンテナンスしているJava向けの公式Redisクライアントライブラリです。シンプルで直感的なAPIを提供しながら、Redisの全機能にアクセスできます。JedisPoolを使った効率的な接続管理、Redis Clusterでの分散処理、Redis Sentinelによる高可用性、パイプライニングとトランザクション、Pub/Subメッセージング、Lua scriptingサポートなど、エンタープライズレベルの機能を網羅しています。JedisPooledクラスにより、従来のtry-with-resourcesパターンを簡略化し、より簡潔なコードでRedis操作が可能です。RediSearch、RedisJSON、RedisTimeSeries等のRedisモジュールにも対応し、高度な検索やJSON操作、時系列データ処理も実現できます。Maven、Gradleでの簡単な導入、豊富なドキュメント、アクティブなコミュニティサポートにより、Javaエコシステムでの標準的なRedisクライアントとして広く採用されています。

メリット・デメリット

メリット

  • 公式サポート: Redis Labsによる公式開発・メンテナンス
  • 豊富な機能: Redis全機能とモジュール(RediSearch、RedisJSON等)サポート
  • 高性能接続プーリング: JedisPoolによる効率的なリソース管理
  • 簡潔なAPI: 直感的で使いやすいJava APIデザイン
  • クラスタサポート: Redis Clusterの自動検出とフェイルオーバー
  • エンタープライズ対応: Sentinel、TLS、認証機能の完全サポート
  • アクティブなコミュニティ: 豊富なドキュメントとサンプルコード

デメリット

  • 同期処理中心: 非同期処理のサポートが限定的
  • 接続管理の複雑さ: プールサイズやタイムアウト設定の最適化が必要
  • Javaエコシステム限定: Java/JVM言語以外では使用不可
  • 依存関係: Commons PoolやApache HTTP Client等の依存ライブラリ
  • エラーハンドリング: 接続エラー時の再試行ロジックが必要

主要リンク

書き方の例

基本的な設定とMaven dependency

<!-- pom.xml -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>5.1.0</version>
</dependency>

JedisPooledを使った簡単な操作

import redis.clients.jedis.JedisPooled;

public class RedisExample {
    public static void main(String[] args) {
        // JedisPooledで接続(推奨方法)
        JedisPooled jedis = new JedisPooled("localhost", 6379);
        
        // 基本的な操作
        jedis.set("key", "value");
        String value = jedis.get("key");
        System.out.println(value); // "value"
        
        // 期限付きキー設定
        jedis.setex("tempkey", 60, "temporary value"); // 60秒で有効期限切れ
        
        // リスト操作
        jedis.lpush("mylist", "element1", "element2", "element3");
        List<String> list = jedis.lrange("mylist", 0, -1);
        System.out.println(list);
        
        // セット操作
        jedis.sadd("myset", "member1", "member2", "member3");
        Set<String> members = jedis.smembers("myset");
        System.out.println(members);
    }
}

JedisPoolを使った接続管理

import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Jedis;

public class JedisPoolExample {
    private static JedisPool pool;
    
    static {
        // プール設定
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(20); // 最大接続数
        poolConfig.setMaxIdle(10);  // 最大アイドル接続数
        poolConfig.setMinIdle(2);   // 最小アイドル接続数
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(true);
        
        // プール初期化
        pool = new JedisPool(poolConfig, "localhost", 6379, 2000, "password");
    }
    
    public void performOperations() {
        // try-with-resourcesで安全な接続管理
        try (Jedis jedis = pool.getResource()) {
            jedis.set("pooled-key", "pooled-value");
            String value = jedis.get("pooled-key");
            System.out.println("Retrieved: " + value);
            
            // 複雑な操作
            jedis.hset("user:1", "name", "John Doe");
            jedis.hset("user:1", "email", "[email protected]");
            jedis.hset("user:1", "age", "30");
            
            Map<String, String> user = jedis.hgetall("user:1");
            System.out.println("User data: " + user);
        }
    }
    
    public static void close() {
        if (pool != null) {
            pool.close();
        }
    }
}

Redis Cluster操作

import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.HostAndPort;

import java.util.HashSet;
import java.util.Set;

public class ClusterExample {
    public static void main(String[] args) {
        // クラスタノード設定
        Set<HostAndPort> jedisClusterNodes = new HashSet<>();
        jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379));
        jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7380));
        jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7381));
        
        // クラスタ接続
        JedisCluster jedisCluster = new JedisCluster(jedisClusterNodes);
        
        // クラスタでのデータ操作
        jedisCluster.set("cluster-key", "cluster-value");
        String value = jedisCluster.get("cluster-key");
        System.out.println("Cluster value: " + value);
        
        // 複数のキーを同じハッシュスロットに配置
        jedisCluster.set("{user:1000}:profile", "user profile data");
        jedisCluster.set("{user:1000}:preferences", "user preferences");
        
        // パフォーマンスカウンタ
        jedisCluster.incr("page_views");
        Long views = jedisCluster.get("page_views");
        System.out.println("Page views: " + views);
        
        jedisCluster.close();
    }
}

パイプライニングとトランザクション

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.Response;

public class AdvancedOperations {
    public void demonstratePipelining() {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            // パイプライニングで複数コマンドを一括実行
            Pipeline pipeline = jedis.pipelined();
            
            Response<String> set1 = pipeline.set("pipe1", "value1");
            Response<String> set2 = pipeline.set("pipe2", "value2");
            Response<Long> incr = pipeline.incr("counter");
            Response<List<String>> lrange = pipeline.lrange("mylist", 0, -1);
            
            // 一括実行
            pipeline.sync();
            
            // 結果取得
            System.out.println("Set result: " + set1.get());
            System.out.println("Counter value: " + incr.get());
            System.out.println("List contents: " + lrange.get());
        }
    }
    
    public void demonstrateTransaction() {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            // 楽観的ロックでのトランザクション
            String watchKey = "balance:1000";
            
            jedis.watch(watchKey);
            String balance = jedis.get(watchKey);
            int currentBalance = Integer.parseInt(balance != null ? balance : "0");
            
            if (currentBalance >= 100) {
                Transaction tx = jedis.multi();
                tx.decrBy(watchKey, 100);
                tx.incrBy("balance:2000", 100);
                
                List<Object> results = tx.exec();
                if (results == null) {
                    System.out.println("Transaction failed due to concurrent modification");
                } else {
                    System.out.println("Transaction successful: " + results);
                }
            } else {
                jedis.unwatch();
                System.out.println("Insufficient balance");
            }
        }
    }
}

Pub/Subメッセージング

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;

public class PubSubExample {
    
    // カスタムメッセージリスナー
    static class MessageListener extends JedisPubSub {
        @Override
        public void onMessage(String channel, String message) {
            System.out.println("Received message: " + message + " from channel: " + channel);
        }
        
        @Override
        public void onSubscribe(String channel, int subscribedChannels) {
            System.out.println("Subscribed to channel: " + channel);
        }
        
        @Override
        public void onUnsubscribe(String channel, int subscribedChannels) {
            System.out.println("Unsubscribed from channel: " + channel);
        }
        
        @Override
        public void onPMessage(String pattern, String channel, String message) {
            System.out.println("Pattern message: " + message + " from " + channel + " matching " + pattern);
        }
    }
    
    public static void main(String[] args) {
        // Publisher
        Thread publisherThread = new Thread(() -> {
            try (Jedis jedis = new Jedis("localhost", 6379)) {
                for (int i = 0; i < 10; i++) {
                    jedis.publish("news", "Breaking news #" + i);
                    jedis.publish("weather", "Weather update #" + i);
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        // Subscriber
        Thread subscriberThread = new Thread(() -> {
            try (Jedis jedis = new Jedis("localhost", 6379)) {
                MessageListener listener = new MessageListener();
                jedis.subscribe(listener, "news", "weather");
            }
        });
        
        subscriberThread.start();
        publisherThread.start();
        
        try {
            publisherThread.join();
            subscriberThread.interrupt();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

RediSearchとRedisJSONの使用

import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.search.*;
import redis.clients.jedis.json.Path;
import com.google.gson.Gson;

public class ModulesExample {
    
    static class Product {
        public String name;
        public String category;
        public double price;
        public String description;
        
        public Product(String name, String category, double price, String description) {
            this.name = name;
            this.category = category;
            this.price = price;
            this.description = description;
        }
    }
    
    public static void main(String[] args) {
        JedisPooled jedis = new JedisPooled("localhost", 6379);
        Gson gson = new Gson();
        
        // RedisJSON操作
        Product product = new Product("Laptop", "Electronics", 999.99, "High-performance laptop");
        jedis.jsonSet("product:1", gson.toJson(product));
        
        String productJson = jedis.jsonGet("product:1");
        System.out.println("Stored product: " + productJson);
        
        // RediSearch インデックス作成
        Schema schema = new Schema()
                .addTextField("name", 5.0)
                .addTextField("description", 1.0)
                .addTagField("category")
                .addNumericField("price");
                
        IndexDefinition def = new IndexDefinition()
                .setPrefixes(new String[]{"product:"});
                
        try {
            jedis.ftCreate("product-index", IndexOptions.defaultOptions().setDefinition(def), schema);
        } catch (Exception e) {
            // インデックスが既に存在する場合
            System.out.println("Index already exists");
        }
        
        // 検索実行
        Query query = new Query("laptop")
                .addFilter(new Query.NumericFilter("price", 500, 1500))
                .limit(0, 10);
                
        SearchResult result = jedis.ftSearch("product-index", query);
        System.out.println("Search results: " + result.getDocuments());
        
        jedis.close();
    }
}