Redisson
GitHub概要
redisson/redisson
Redisson - Valkey & Redis Java client. Real-Time Data Platform. Sync/Async/RxJava/Reactive API. Over 50 Valkey and Redis based Java objects and services: Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Spring, Tomcat, Scheduler, JCache API, Hibernate, RPC, local cache..
トピックス
スター履歴
キャッシュライブラリ
Redisson
概要
RedissonはJava向けのRedis/Valkeyクライアントライブラリで、50以上の分散オブジェクトとサービスを提供し、Nettyベースの非同期イベント駆動フレームワーク上に構築された高性能なリアルタイムデータプラットフォームです。
詳細
Redisson(レディッソン)は、RedisおよびValkeyサーバーと連携するJava向けのクライアントライブラリで、インメモリデータグリッドとして50以上の分散オブジェクトとサービスを提供します。Set、Multimap、SortedSet、Map、List、Queue、Deque、Semaphore、Lock、AtomicLong、MapReduce、Bloomフィルター、Spring統合、Tomcat統合、スケジューラー、JCache API、Hibernate、RPC、ローカルキャッシュなどの豊富な機能を備えています。Netty非同期イベント駆動クライアント・サーバーフレームワークを基盤とし、Redis 2.8以上およびJDK 1.8以上と互換性があります。同期/非同期/RxJava/ReactiveAPIをサポートし、馴染みのあるJavaコレクションとデータ構造をRedis上で利用できるため、標準インターフェースを知っているJava開発者にとって学習コストがほぼゼロです。Spring framework、Spring Boot Starter、Spring Cache、Spring Session、Spring Transaction Manager、Spring Cloud Stream、Spring Data Redisとの統合を提供し、Amazon ElastiCache、Amazon MemoryDB、Azure Cache for Redis、Redis Enterprise、Google Cloud Memorystore、その他多数のクラウドプロバイダーに対応しています。ベンチマークテストによると、Redisson PROはJedisよりも高速で、Redisの性能を55,000-75,000 ops/secから100,000-213,000 ops/secまで向上させることができます。
メリット・デメリット
メリット
- 高性能: Nettyベースで非同期処理による高速な操作
- 豊富な機能: 50以上の分散オブジェクトとサービスを提供
- 学習コスト低: 標準のJavaコレクションインターフェースを使用
- 多API対応: 同期、非同期、RxJava、Reactiveの全てをサポート
- フレームワーク統合: Spring、Hibernate等との深い統合
- クラウド対応: 主要クラウドプロバイダーとの完全互換性
- 分散機能: 分散ロック、セマフォ、MapReduce等の高度な機能
デメリット
- 複雑性: 豊富な機能により設定や使用方法が複雑になる場合がある
- メモリ使用量: Nettyとオブジェクト管理により比較的多くのメモリを使用
- 学習曲線: 高度な分散機能の習得には時間が必要
- 依存関係: Nettyやその他の依存ライブラリが必要
- オーバーヘッド: 軽量なクライアントと比較して初期化コストが高い
主要リンク
書き方の例
インストールと基本設定
<!-- Maven依存関係 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.30.0</version>
</dependency>
<!-- Spring Boot統合の場合 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.30.0</version>
</dependency>
// 基本的な接続設定
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class RedissonExample {
public static void main(String[] args) {
// 設定オブジェクトの作成
Config config = new Config();
config.useSingleServer()
.setAddress("redis://localhost:6379")
.setPassword("password")
.setDatabase(0)
.setConnectionMinimumIdleSize(10)
.setConnectionPoolSize(64)
.setIdleConnectionTimeout(10000)
.setConnectTimeout(10000)
.setTimeout(3000)
.setRetryAttempts(3)
.setRetryInterval(1500);
// Redissonクライアントの作成
RedissonClient redisson = Redisson.create(config);
// 使用後はシャットダウン
redisson.shutdown();
}
}
基本的なオブジェクト操作
import org.redisson.api.*;
public class BasicOperations {
private RedissonClient redisson;
public void basicOperations() {
// RBucket - 単一値の保存
RBucket<String> bucket = redisson.getBucket("myBucket");
bucket.set("Hello, Redisson!");
String value = bucket.get();
System.out.println(value); // "Hello, Redisson!"
// 期限付きオブジェクト
bucket.set("temporary", 60, TimeUnit.SECONDS);
// RMap - Mapインターフェース
RMap<String, String> map = redisson.getMap("myMap");
map.put("key1", "value1");
map.put("key2", "value2");
// ローカルMapのようにアクセス
String val = map.get("key1");
boolean exists = map.containsKey("key2");
map.remove("key1");
// RList - Listインターフェース
RList<String> list = redisson.getList("myList");
list.add("item1");
list.add("item2");
list.add(0, "first");
String first = list.get(0);
list.remove("item1");
// RSet - Setインターフェース
RSet<String> set = redisson.getSet("mySet");
set.add("element1");
set.add("element2");
boolean added = set.add("element1"); // false - 既に存在
// RQueue - Queueインターフェース
RQueue<String> queue = redisson.getQueue("myQueue");
queue.offer("task1");
queue.offer("task2");
String task = queue.poll(); // "task1"
}
}
非同期操作
import org.redisson.api.*;
import java.util.concurrent.CompletableFuture;
public class AsyncOperations {
private RedissonClient redisson;
public void asyncOperations() {
RBucket<String> bucket = redisson.getBucket("asyncBucket");
// 非同期でセット
RFuture<Void> setFuture = bucket.setAsync("async value");
setFuture.whenComplete((result, exception) -> {
if (exception == null) {
System.out.println("Set completed successfully");
} else {
System.err.println("Set failed: " + exception.getMessage());
}
});
// 非同期でゲット
RFuture<String> getFuture = bucket.getAsync();
getFuture.thenAccept(value -> {
System.out.println("Retrieved value: " + value);
});
// チェーンした非同期操作
bucket.setAsync("initial")
.thenCompose(v -> bucket.getAsync())
.thenCompose(value -> {
System.out.println("Current value: " + value);
return bucket.setAsync("updated");
})
.thenRun(() -> System.out.println("All operations completed"));
}
}
分散ロック
import org.redisson.api.*;
import java.util.concurrent.TimeUnit;
public class DistributedLocking {
private RedissonClient redisson;
public void lockExample() {
RLock lock = redisson.getLock("myLock");
try {
// ロック取得(ブロッキング)
lock.lock();
// クリティカルセクション
System.out.println("Critical section executed");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// ロック解放
lock.unlock();
}
}
public void tryLockExample() {
RLock lock = redisson.getLock("myTryLock");
try {
// タイムアウト付きロック取得
boolean acquired = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (acquired) {
try {
// クリティカルセクション
System.out.println("Lock acquired, executing critical section");
} finally {
lock.unlock();
}
} else {
System.out.println("Failed to acquire lock within timeout");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public void multiLockExample() {
RLock lock1 = redisson.getLock("lock1");
RLock lock2 = redisson.getLock("lock2");
RLock lock3 = redisson.getLock("lock3");
// 複数のロックを同時に取得
RLock multiLock = redisson.getMultiLock(lock1, lock2, lock3);
try {
multiLock.lock();
// 全てのロックが取得された状態での処理
System.out.println("All locks acquired");
} finally {
multiLock.unlock();
}
}
}
セマフォとカウントダウンラッチ
import org.redisson.api.*;
public class SynchronizationPrimitives {
private RedissonClient redisson;
public void semaphoreExample() {
RSemaphore semaphore = redisson.getSemaphore("mySemaphore");
try {
// 許可数を設定(初期化)
semaphore.trySetPermits(3);
// 許可を取得
semaphore.acquire();
// リソースを使用
System.out.println("Using limited resource");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// 許可を解放
semaphore.release();
}
}
public void countDownLatchExample() {
RCountDownLatch latch = redisson.getCountDownLatch("myLatch");
// ラッチの初期カウントを設定
latch.trySetCount(3);
// 別スレッドでカウントダウン
new Thread(() -> {
try {
Thread.sleep(1000);
latch.countDown();
System.out.println("Task 1 completed");
Thread.sleep(1000);
latch.countDown();
System.out.println("Task 2 completed");
Thread.sleep(1000);
latch.countDown();
System.out.println("Task 3 completed");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
try {
// 全てのタスクの完了を待機
latch.await();
System.out.println("All tasks completed!");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
Reactive/RxJava操作
import org.redisson.api.*;
import org.redisson.api.redissonrx.*;
import reactor.core.publisher.Mono;
import io.reactivex.rxjava3.core.Single;
public class ReactiveOperations {
private RedissonClient redisson;
public void reactiveExample() {
// Reactiveクライアントの取得
RedissonReactiveClient reactiveClient = redisson.reactive();
RBucketReactive<String> bucket = reactiveClient.getBucket("reactiveBucket");
// Reactive操作のチェーン
Mono<Void> operation = bucket.set("reactive value")
.then(bucket.get())
.doOnNext(value -> System.out.println("Value: " + value))
.then(bucket.delete())
.then();
// 実行
operation.subscribe(
result -> System.out.println("Operation completed"),
error -> System.err.println("Error: " + error.getMessage())
);
}
public void rxJavaExample() {
// RxJavaクライアントの取得
RedissonRxClient rxClient = redisson.rxJava();
RBucketRx<String> bucket = rxClient.getBucket("rxBucket");
// RxJava操作のチェーン
Single<String> operation = bucket.set("rx value")
.andThen(bucket.get())
.doOnSuccess(value -> System.out.println("RxJava Value: " + value));
// 実行
operation.subscribe(
result -> System.out.println("RxJava operation completed: " + result),
error -> System.err.println("RxJava error: " + error.getMessage())
);
}
}
Pub/Sub機能
import org.redisson.api.*;
public class PubSubOperations {
private RedissonClient redisson;
public void publishSubscribe() {
// パブリッシャー
RTopic topic = redisson.getTopic("myTopic");
// サブスクライバー
topic.addListener(String.class, (channel, msg) -> {
System.out.println("Received message: " + msg + " from channel: " + channel);
});
// メッセージ送信
topic.publish("Hello, subscribers!");
// パターンマッチングサブスクリプション
RPatternTopic patternTopic = redisson.getPatternTopic("news.*");
patternTopic.addListener(String.class, (pattern, channel, msg) -> {
System.out.println("Pattern: " + pattern + ", Channel: " + channel + ", Message: " + msg);
});
// パターンにマッチするチャンネルに送信
redisson.getTopic("news.sports").publish("Sports news update");
redisson.getTopic("news.tech").publish("Tech news update");
}
public void reliableTopic() {
// 信頼性のあるトピック(メッセージの永続化)
RReliableTopic topic = redisson.getReliableTopic("reliableTopic");
String listenerId = topic.addListener(String.class, (channel, msg) -> {
System.out.println("Reliable message: " + msg);
});
// メッセージ送信
topic.publish("Reliable message 1");
topic.publish("Reliable message 2");
// リスナー削除
topic.removeListener(listenerId);
}
}
トランザクション処理
import org.redisson.api.*;
public class TransactionOperations {
private RedissonClient redisson;
public void transactionExample() {
RTransaction transaction = redisson.createTransaction(TransactionOptions.defaults());
try {
// トランザクション内での操作
RBucket<String> bucket = transaction.getBucket("txBucket");
RMap<String, String> map = transaction.getMap("txMap");
bucket.set("transaction value");
map.put("key1", "value1");
map.put("key2", "value2");
// トランザクションのコミット
transaction.commit();
System.out.println("Transaction committed successfully");
} catch (Exception e) {
// エラー時はロールバック
transaction.rollback();
System.err.println("Transaction rolled back: " + e.getMessage());
}
}
public void batchOperations() {
// バッチ操作(パイプライニング)
RBatch batch = redisson.createBatch();
RBucketAsync<String> bucket1 = batch.getBucket("batch1");
RBucketAsync<String> bucket2 = batch.getBucket("batch2");
RMapAsync<String, String> map = batch.getMap("batchMap");
// バッチに操作を追加
bucket1.setAsync("batch value 1");
bucket2.setAsync("batch value 2");
map.putAsync("batchKey", "batchValue");
// バッチ実行
BatchResult<?> results = batch.execute();
System.out.println("Batch executed, results count: " + results.getResponses().size());
}
}
MapReduce処理
import org.redisson.api.*;
import org.redisson.mapreduce.*;
public class MapReduceOperations {
private RedissonClient redisson;
public void mapReduceExample() {
RMap<String, String> map = redisson.getMap("sourceMap");
// テストデータの挿入
map.put("line1", "Alice was beginning to get very tired");
map.put("line2", "of sitting by her sister on the bank");
map.put("line3", "and of having nothing to do");
// MapReduceタスクの設定
RMapReduce<String, String, String, Integer> mapReduce = map
.<String, Integer>mapReduce()
.mapper(new WordMapper())
.reducer(new WordReducer());
// 実行
Map<String, Integer> result = mapReduce.execute();
result.forEach((word, count) ->
System.out.println(word + ": " + count));
}
// Mapperクラス
public static class WordMapper implements RMapper<String, String, String, Integer> {
@Override
public void map(String key, String value, RCollector<String, Integer> collector) {
String[] words = value.toLowerCase().split("\\W+");
for (String word : words) {
if (!word.isEmpty()) {
collector.emit(word, 1);
}
}
}
}
// Reducerクラス
public static class WordReducer implements RReducer<String, Integer> {
@Override
public Integer reduce(String key, Iterator<Integer> values) {
int sum = 0;
while (values.hasNext()) {
sum += values.next();
}
return sum;
}
}
}
Spring Boot統合
// application.yml
/*
spring:
redis:
redisson:
config: |
singleServerConfig:
address: "redis://localhost:6379"
password: null
database: 0
connectionMinimumIdleSize: 10
connectionPoolSize: 64
idleConnectionTimeout: 10000
connectTimeout: 10000
timeout: 3000
*/
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.redisson.api.*;
@Service
public class RedissonService {
@Autowired
private RedissonClient redissonClient;
public void cacheUserData(String userId, Object userData) {
RBucket<Object> bucket = redissonClient.getBucket("user:" + userId);
bucket.set(userData, 1, TimeUnit.HOURS);
}
public Object getUserData(String userId) {
RBucket<Object> bucket = redissonClient.getBucket("user:" + userId);
return bucket.get();
}
public void addToUserSessions(String userId, String sessionId) {
RSet<String> sessions = redissonClient.getSet("sessions:" + userId);
sessions.add(sessionId);
sessions.expire(24, TimeUnit.HOURS);
}
public boolean isUserOnline(String userId) {
RSet<String> sessions = redissonClient.getSet("sessions:" + userId);
return !sessions.isEmpty();
}
}
パフォーマンス最適化
public class PerformanceOptimization {
public RedissonClient createOptimizedClient() {
Config config = new Config();
// 単一サーバー設定の最適化
config.useSingleServer()
.setAddress("redis://localhost:6379")
// 接続プール設定
.setConnectionPoolSize(64)
.setConnectionMinimumIdleSize(10)
// タイムアウト設定
.setConnectTimeout(10000)
.setTimeout(3000)
.setIdleConnectionTimeout(10000)
// リトライ設定
.setRetryAttempts(3)
.setRetryInterval(1500)
// DNS監視無効化(パフォーマンス向上)
.setDnsMonitoringInterval(-1);
// パフォーマンス設定
config.setThreads(16); // NIOスレッド数
config.setNettyThreads(32); // Nettyスレッド数
config.setCodec(org.redisson.codec.KryoCodec.INSTANCE); // 高速シリアライゼーション
return Redisson.create(config);
}
public void localCacheExample() {
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
RedissonClient redisson = Redisson.create(config);
// ローカルキャッシュ付きMap
LocalCachedMapOptions<String, String> options = LocalCachedMapOptions.<String, String>defaults()
.evictionPolicy(LocalCachedMapOptions.EvictionPolicy.LRU)
.cacheSize(1000)
.timeToLive(10, TimeUnit.MINUTES)
.maxIdle(5, TimeUnit.MINUTES);
RLocalCachedMap<String, String> map = redisson.getLocalCachedMap("cachedMap", options);
// 初回はRedisから取得、以降はローカルキャッシュから
map.put("key1", "value1");
String value = map.get("key1"); // ローカルキャッシュから高速取得
}
}