jedisからRedisを触ってみる

jedisからRedisを触ってみたメモです。


前回Redisをredis-cliからコマンドで操作したので、
JavaのRedisクライアントであるjedisから操作してみました。

jedis2.9.0で試してみます。
Ubuntu16上のRedis 4.0.1で試してみます。

dependency

依存関係にはjedisを追加しただけです。

pom.xml

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency>
接続確認

下記のコードで接続を確認してみます。

JedisExampleMain.java

public static void main( String[] args ) {
    Jedis jedis = new Jedis("localhost", 6379);
    jedis.set("foo", "bar");
    String value = jedis.get("foo");
    System.out.println("value: " + value);
}
protected mode設定

ホストOSからjedisでVM上のRedisに接続しようとすると下記のエラーが発生しました。
どうやらRedis3.2からデフォルトでprotected modeがONになり、外部ネットワークからは接続不可になったようです。

objc[70172]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/java (0x10b06c4c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10b1564e0). One of the two will be used. Which one is undefined.
Exception in thread "main" redis.clients.jedis.exceptions.JedisDataException: DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.
	at redis.clients.jedis.Protocol.processError(Protocol.java:127)
	at redis.clients.jedis.Protocol.process(Protocol.java:161)
	at redis.clients.jedis.Protocol.read(Protocol.java:215)
	at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
	at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:239)
	at redis.clients.jedis.Jedis.set(Jedis.java:121)
	at com.example.jedis.JedisExampleMain.main(JedisExampleMain.java:8)

protected modeをOFFにするためには、起動時のオプションに--protected-mode noを指定する必要があるようです。
(もしくは設定ファイルにprotected-mode noを記述)

$ src/redis-server --protected-mode no &

これで接続出来るようになりました。

各データ型

各データ型の使いそうなメソッドだけ試してみます。

String

set(), get()

set()で指定したキーに値を設定。
get()で指定したキーの値をフェッチ。

jedis.set("my_key", "my_value");
System.out.println(jedis.get("my_key"));
my_value
del()

del()で指定したキーの値を削除。

Long delResult = jedis.del("my_key");
System.out.println(delResult);
System.out.println(jedis.get("my_key"));
1
null
xx, nx

set()の第3引数にxx, nxを指定可能。
nx 存在しないキーの場合は設定される

jedis.del("new_key");
jedis.set("new_key", "new_val", "nx");
System.out.println(jedis.get("new_key"));
new_val

xx 存在しているキーの場合は設定される

jedis.set("new_key", "new!!!", "xx");
System.out.println(jedis.get("new_key"));
new!!!
incr(), decr()

incr()で値をインクリメント、
incrBy()で指定した値だけインクリメント

jedis.set("my_count", "100");
jedis.incr("my_count");
System.out.println(jedis.get("my_count"));
jedis.incrBy("my_count", 10);
System.out.println(jedis.get("my_count"));
101
111
decr()で値をデクリメント。

decrBy()で指定した値だけデクリメント

jedis.decr("my_count");
System.out.println(jedis.get("my_count"));
jedis.decrBy("my_count", 10);
System.out.println(jedis.get("my_count"));
110
100
mset(), mget()

mset()で複数の値を一度にセットできる。
mget()で複数の値を一度にフェッチできる。

jedis.mset("AAA", "111", "BBB", "222", "CCC", "333");
System.out.println(jedis.mget("AAA", "BBB", "CCC"));
System.out.println(jedis.mget("AAA", "BBB", "CCC", "DDD"));
[111, 222, 333]
[111, 222, 333, null]
exists()

exists()で指定したキーが存在する場合1、存在しない場合0

System.out.println(jedis.exists("AAA"));
System.out.println(jedis.exists("DDD"));
true
false
expire()

expire()で対象キーの存続時間(秒)を指定できる。
存続時間を過るとキーは自動的に削除される。

jedis.set("my_key", "abcde");
jedis.expire("my_key", 5);
System.out.println(jedis.get("my_key"));
System.out.println("Wait for 6 sec...");
Thread.sleep(6_000L);
System.out.println(jedis.get("my_key"));
abcde
Wait for 6 sec...
null

List

rpush()

rpush()でListの末尾に追加

jedis.rpush("my_list", "aaa");
jedis.rpush("my_list", "bbb");
jedis.rpush("my_list", "ccc");
jedis.rpush("my_list", "ddd");

複数の値を一度にrpush可能

jedis.del("my_list");
jedis.rpush("my_list", "aaa", "bbb", "ccc", "ddd");
lindex()

指定したインデックスの要素を取得

System.out.println(jedis.lindex("my_list", 0));
System.out.println(jedis.lindex("my_list", 2));
aaa
ccc
lrange()

指定した範囲の要素を取得。
第2引数は開始するインデックス、第3引数は終了するインデックス。

System.out.println(jedis.lrange("my_list", 0, 2));
System.out.println(jedis.lrange("my_list", 1, 3));
System.out.println(jedis.lrange("my_list", 0, -1));
[aaa, bbb, ccc]
[bbb, ccc, ddd]
[aaa, bbb, ccc, ddd]
lpush()

リストの先頭に指定された要素を挿入。

jedis.lpush("my_list", "aa");
System.out.println(jedis.lrange("my_list", 0, -1));
[aa, aaa, bbb, ccc, ddd]
lpop(), rpop()

lpop()でリストの最初の要素を削除して取得。
rpop()でリストの末尾の要素を削除して取得。

System.out.println(jedis.lpop("my_list"));
System.out.println(jedis.lrange("my_list", 0, -1));
System.out.println(jedis.rpop("my_list"));
System.out.println(jedis.lrange("my_list", 0, -1));
aa
[aaa, bbb, ccc, ddd]
ddd
[aaa, bbb, ccc]
ltrim()

指定した範囲を残すようにリストをトリムする。
第2引数は開始するインデックス、第3引数は終了するインデックス。

jedis.del("my_list");
jedis.rpush("my_list", "aaa", "bbb", "ccc", "ddd");
System.out.println(jedis.lrange("my_list", 0, -1));
jedis.ltrim("my_list", 0, 1);
System.out.println(jedis.lrange("my_list", 0, -1));
[aaa, bbb, ccc, ddd]
[aaa, bbb]

Set

sadd(), smembers()

sadd()でセットに要素を追加。
smembers()でセットに含まれるすべての要素を取得。

jedis.sadd("my_set", "AAA");
jedis.sadd("my_set", "BBB");
jedis.sadd("my_set", "CCC");
jedis.sadd("my_set", "AAA");
System.out.println(jedis.smembers("my_set"));
[CCC, AAA, BBB]

複数の要素を一度に追加可能

jedis.del("my_set");
jedis.sadd("my_set", "AAA", "BBB", "CCC");
System.out.println(jedis.smembers("my_set"));
[CCC, AAA, BBB]
spop()

spop()でランダムに要素を削除して取得。

jedis.spop("my_set");
System.out.println(jedis.smembers("my_set"));
[CCC, AAA]
sismember()

指定した要素が格納されているかどうか。
格納されている場合は1、そうでない場合0

jedis.del("my_set");
jedis.sadd("my_set", "AAA", "BBB", "CCC");
Boolean existAAA = jedis.sismember("my_set", "AAA");
System.out.println(existAAA);
Boolean existDDD = jedis.sismember("my_set", "DDD");
System.out.println(existDDD);
true
false
srem()

指定した要素を削除.

jedis.del("my_set");
jedis.sadd("my_set", "AAA", "BBB", "CCC");
jedis.srem("my_set", "BBB");
System.out.println(jedis.smembers("my_set"));
[CCC, AAA]

Hash

hset(), hget(), hgetall(), hdel()

hset()で指定したフィールドに値を設定。
hget()で指定したフィールドの値を取得。
hgetAll()で指定したキーのすべてのフィールドと値を取得。
hdel()で指定したフィールドを削除。

jedis.hset("my_hash", "aaa", "111");
jedis.hset("my_hash", "bbb", "222");
jedis.hset("my_hash", "ccc", "333");
System.out.println(jedis.hget("my_hash", "aaa"));
System.out.println(jedis.hget("my_hash", "bbb"));
System.out.println(jedis.hgetAll("my_hash"));
jedis.hdel("my_hash", "ccc");
System.out.println(jedis.hgetAll("my_hash"));
111
222
{aaa=111, ccc=333, bbb=222}
{aaa=111, bbb=222}
hmset(), hmget()

hmset()で指定したフィールドと値をまとめて設定。
hmget()で指定したフィールドの値をまとめて取得。

jedis.del("my_hash");
Map<String, String> map = new HashMap<>();
map.put("aaa", "111");
map.put("bbb", "222");
map.put("ccc", "333");
jedis.hmset("my_hash", map);
System.out.println(jedis.hmget("my_hash", "aaa", "bbb", "ccc"));
[111, 222, 333]
hincrBy()

指定したフィールドの値をインクリメント。

System.out.println(jedis.hget("my_hash", "aaa"));
jedis.hincrBy("my_hash", "aaa", 1);
System.out.println(jedis.hget("my_hash", "aaa"));
jedis.hincrBy("my_hash", "aaa", 100);
System.out.println(jedis.hget("my_hash", "aaa"));
111
112
212

Zset

zadd()

zsetに第1引数で指定したスコアで、第2引数のメンバを登録。

jedis.zadd("my_zset", 111, "member1");
jedis.zadd("my_zset", 222, "member2");
jedis.zadd("my_zset", 333, "member3");

まとめて登録も可能

jedis.del("my_zset");
Map<String, Double> map = new HashMap<>();
map.put("member1", 111D);
map.put("member2", 222D);
map.put("member3", 333D);
jedis.zadd("my_zset", map);
zrange(), zrangeWithScores()

zrange()で指定した範囲のメンバを返す。
第2引数は開始するインデックス、第3引数は終了するインデックス。
zrangeWithScores()でスコアも同時に返す。

System.out.println(jedis.zrange("my_zset", 0, -1));
jedis.zrangeWithScores("my_zset", 0, -1)
        .forEach(t -> System.out.println(t.getElement() + " : " + t.getScore()));
[member1, member2, member3]
member1 : 111.0
member2 : 222.0
member3 : 333.0
zrangeByScore(), zrangeByScoreWithScores()

zrangeByScore()で第2引数と第3引数の間のスコアを持つ要素を返す。
zrangeByScoreWithScores()でスコアも同時に返す。

System.out.println(jedis.zrangeByScore("my_zset", 0, 150));
jedis.zrangeByScoreWithScores("my_zset", 0, 150)
        .forEach(t -> System.out.println(t.getElement() + " : " + t.getScore()));
jedis.zrangeByScoreWithScores("my_zset", 100, 230)
        .forEach(t -> System.out.println(t.getElement() + " : " + t.getScore()));
[member1]

member1 : 111.0

member1 : 111.0
member2 : 222.0

redis-cliだと、第2引数と第3引数には無限大(+inf/-inf)を指定することが可能だが、
jedisではDouble.MIN_VALUE/MAX_VALUEを使用する

System.out.println(jedis.zrangeByScore("my_zset", Double.MIN_VALUE, 200));
System.out.println(jedis.zrangeByScore("my_zset", 200, Double.MAX_VALUE));
[member1]

[member2, member3]
zrem()

指定されたメンバを削除する。

System.out.println(jedis.zrange("my_zset", 0, -1));
jedis.zrem("my_zset", "member1");
System.out.println(jedis.zrange("my_zset", 0, -1));
[member1, member2, member3]

[member2, member3]
zremrangeByScore()

第1引数と第2引数の間のスコアを持つ要素を削除する。

jedis.del("my_zset");
jedis.zadd("my_zset", map);
jedis.zrangeWithScores("my_zset", 0, -1)
        .forEach(t -> System.out.println(t.getElement() + " : " + t.getScore()));
jedis.zremrangeByScore("my_zset", 200, 400);
jedis.zrangeWithScores("my_zset", 0, -1)
        .forEach(t -> System.out.println(t.getElement() + " : " + t.getScore()));
member1 : 111.0
member2 : 222.0
member3 : 333.0

member1 : 111.0

トランザクション

multi(), exec()

multi()でトランザクションを開始し、後続のコマンドはキューに入ります。
exec()でキューにあるすべてのコマンドを実行し、トランザクションを終了します。

System.out.println(jedis.lrange("user_list", 0, -1));
System.out.println(jedis.get("counter"));

Transaction t = jedis.multi();
t.rpush("user_list", "bob");
t.incr("counter");
t.rpush("user_list", "alice");
t.incr("counter");
t.exec();

System.out.println(jedis.lrange("user_list", 0, -1));
System.out.println(jedis.get("counter"));
[]
null

[bob, alice]
2
discard()

キューに入れられたすべてのコマンドをトランザクション内でフラッシュします。
トランザクションは解除されます。

System.out.println(jedis.get("counter"));

Transaction t2 = jedis.multi();
t2.incr("counter");
t2.incr("counter");
t2.discard();

System.out.println(jedis.get("counter"));
null
null
watch()

watch()で指定したキーを監視します。
監視していたキーが他のクライアントから更新されると、exec()した際にエラーになります。

System.out.println(jedis.get("counter"));

jedis.watch("counter");
Transaction t3 = jedis.multi();
t3.incr("counter");
t3.exec();

System.out.println(jedis.get("counter"));
null
1
unwatch()

watch()で監視対象となったすべてのキーをフラッシュします。
exec()かdiscard()を呼び出した場合は自動でフラッシュされます。(手動でunwatch不要)

System.out.println(jedis.get("counter"));

jedis.watch("counter");
jedis.unwatch();
Transaction t4 = jedis.multi();
t4.incr("counter");
t4.exec();

System.out.println(jedis.get("counter"));
null
1


テストコードは下記にあげときました。
github.com


終わり。