Redis Java

12/20/2023 RedisJava

摘要

Redis官方文档:https://redis.io/documentation (opens new window)
Jedis 官方地址:https://github.com/redis/jedis (opens new window)
Lettuce 官方地址: Redisson 官方地址:

# 一:前言

# 二:Jedis

# 2.1 引用

在 Maven 项目中添加 Jedis 依赖包,相关版本号可以查 Maven 仓库 (opens new window)

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>5.0.2</version>
</dependency>
1
2
3
4
5

# 2.2 连接

import redis.clients.jedis.Jedis;

/**
 * Jedis 简单测试
 */
public class JedisMain {

    public static void main(String[] args) {
        // 1.构造一个 Jedis 对象
        Jedis jedis = new Jedis("192.168.214.131", 6379);
        // 2.密码认证
        jedis.auth("1472");
        // 3.测试是否连接成功
        String ping = jedis.ping();
        // 4.返回 pong 表示连接成功
        System.out.println(ping);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 2.3 连接池

使用 Jedis 对象来操作 redis 时,每次操作都需要新建/关闭 TCP 连接,连接资源开销很高,同时 Jedis 对象的个数不受限制,在极端情况下可能会造成连接泄漏,同时 Jedis 存在多线程不安全的问题。

为什么说 Jedis 线程不安全,更加详细的原因可以访问这个地址https://www.cnblogs.com/gxyandwmm/p/13485226.html

所以我们需要将 Jedis 交给线程池来管理,使用 Jedis 对象时,从连接池获取 Jedis,使用完成之后,再还给连接池。

在使用之前,需要添加 common-pool 线程池依赖包,相关版本号可以查 Maven 仓库 (opens new window)

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.11.1</version>
</dependency>
1
2
3
4
5

创建一个简单的使用线程池测试用例。

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

/**
 * 线程池简单测试
 */
public class JedisMain2 {

    public static void main(String[] args) {
        // 1. 构造一个 Jedis 连接池
        JedisPool pool = new JedisPool("192.168.214.131", 6379);
        // 2. 从连接池中获取一个 Jedis 连接
        Jedis jedis = pool.getResource();
        jedis.auth("1472");
        // 3. Jedis 操作
        String ping = jedis.ping();
        System.out.println(ping);
        // 4. 归还连接
        jedis.close();
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

实际开发过程中,初始化线程池 JedisPool 及其相关配置

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

public class RedisPoolUtils {

    private static JedisPool jedisPool = null;

    /**
     * redis 服务器地址
     */
    private final static String addr = "192.168.214.131";

    /**
     * redis 服务器端口
     */
    private final static int port = 6379;

    /**
     * redis 服务器密码
     */
    private final static String auth = "1472";


    static {
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            // 连接耗尽时是否阻塞, false 报异常,ture 阻塞直到超时, 默认 true
            config.setBlockWhenExhausted(true);
            // 设置的逐出策略类名, 默认 DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
            config.setEvictionPolicyClassName("org.apache.commons.pool2.impl.DefaultEvictionPolicy");
            // 是否启用pool的jmx管理功能, 默认 true
            config.setJmxEnabled(true);
            // MBean ObjectName = new ObjectName("org.apache.commons.pool2:type=GenericObjectPool,name=" + "pool" + i);
            // 默认为"pool"
            config.setJmxNamePrefix("pool");
            // 是否启用后进先出, 默认 true
            config.setLifo(true);
            // 最大空闲连接数, 默认8个
            config.setMaxIdle(8);
            // 最大连接数, 默认8个
            config.setMaxTotal(8);
            // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted)
            // 如果超时就抛异常,小于零:阻塞不确定的时间,默认-1
            config.setMaxWaitMillis(-1);
            // 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
            config.setMinEvictableIdleTimeMillis(1800000);
            // 最小空闲连接数, 默认0
            config.setMinIdle(0);
            // 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
            config.setNumTestsPerEvictionRun(3);
            // 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断(默认逐出策略)
            config.setSoftMinEvictableIdleTimeMillis(1800000);
            // 在获取连接的时候检查有效性, 默认false
            config.setTestOnBorrow(false);
            // 在空闲时检查有效性, 默认false
            config.setTestWhileIdle(false);
            // 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
            config.setTimeBetweenEvictionRunsMillis(-1);
            jedisPool = new JedisPool(config, addr, port, 3000, auth);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取 Jedis 资源
     *
     * @return Jedis 实例
     */
    public static Jedis getJedis() {
        if (jedisPool != null) {
            return jedisPool.getResource();
        }
        return null;
    }

    /**
     * 释放Jedis资源
     */
    public static void close(final Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

简单测试

import redis.clients.jedis.Jedis;

/**
 * 自定义线程池配置 测试
 */
public class JedisMain3 {

    public static void main(String[] args) {
        //获取 jedis 客户端
        Jedis jedis = RedisPoolUtils.getJedis();

        System.out.println("清空数据:" + jedis.flushDB());
        System.out.println("设置新键值对[hello world]:" + jedis.set("hello", "world"));
        System.out.println("获取键 hello 的值:" + jedis.get("hello"));
        RedisPoolUtils.close(jedis);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2.4 全局命令

由于 Jedis 中的 API 和 Redis 的命令高度一致,所以,Jedis 中的方法见名知意,直接使用即可。

import redis.clients.jedis.Jedis;

import java.util.Set;

/**
 * redis 全局操作命令 测试
 */
public class OverAllMain {

    public static void main(String[] args) {
        //获取 jedis 客户端
        Jedis jedis = RedisPoolUtils.getJedis();

        System.out.println("清空数据:" + jedis.flushDB());
        jedis.mset("hello", "world", "java", "redis", "a", "1", "b", "2", "c", "cc");
        Set<String> keys = jedis.keys("*");
        System.out.println("keys *:" + keys);
        System.out.println("键总数:" + jedis.dbSize());
        System.out.println("键 hello 是否存在:" + jedis.exists("hello"));
        System.out.println("删除键 hello:" + jedis.del("hello"));
        System.out.println("设置键 java 的过期时间为5s:" + jedis.expire("java", 5L));
        System.out.println("查看键 java 的剩余过期时间:" + jedis.ttl("java") + " 秒");
        System.out.println("查看键 java 的剩余过期时间:" + jedis.pttl("java") + " 毫秒");
        System.out.println("清除键 java 的过期时间:" + jedis.persist("java"));
        System.out.println("查看键 java 的过期时间:" + jedis.ttl("java"));
        System.out.println("查看键 java 的数据结构:" + jedis.type("java"));
        System.out.println("查看键 java 的内部编码:" + jedis.objectEncoding("java"));
        System.out.println("重命名键 java:" + jedis.rename("java", "java2"));
        System.out.println("重命名键 a(重名):" + jedis.renamenx("a", "c"));
        System.out.println("随机返回一个键:" + jedis.randomKey());
        RedisPoolUtils.close(jedis);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# 2.5 string

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

/**
 * redis String类型操作 测试
 */
public class StringMain {

    public static void main(String[] args) {
        Jedis jedis = RedisPoolUtils.getJedis();

        // 常用命令
        System.out.println("======================= 常用命令 =======================");
        System.out.println("清空数据:" + jedis.flushDB());
        System.out.println("设置新键值对[hello world]:" + jedis.set("hello", "world"));
        System.out.println("设置 ex:" + jedis.set("name", "ccj", new SetParams().ex(100)));
        System.out.println("设置 ex:" + jedis.setex("name", 100, "ccj"));
        System.out.println("设置 px:" + jedis.set("name", "ccj", new SetParams().px(100)));
        System.out.println("设置 nx:" + jedis.set("n1", "c1", new SetParams().nx()));
        System.out.println("设置 nx:" + jedis.setnx("n1", "c1"));
        System.out.println("设置 xx:" + jedis.set("n1", "c1", new SetParams().xx()));
        System.out.println("获取键 hello 的值:" + jedis.get("hello"));
        System.out.println("获取不存在的键 not_exist_key 的值:" + jedis.get("not_exist_key"));
        System.out.println("批量设置:" + jedis.mset("a", "1", "b", "2", "c", "cc"));
        System.out.println("批量获取:" + jedis.mget("a", "b", "c", "d"));
        System.out.println("自增(数字):" + jedis.incr("number"));
        // 会抛出 redis.clients.jedis.exceptions.JedisDataException: ERR value is not an integer or out of range 异常
        // System.out.println("自增(非数字):" + jedis.incr("c"));
        System.out.println("自增指定数字:" + jedis.incrBy("number", 3));
        System.out.println("自增指定浮点数:" + jedis.incrByFloat("number2", 3));
        System.out.println("自减(数字):" + jedis.decr("number"));
        System.out.println("自减指定数字:" + jedis.decrBy("number", 2));

        // 不常用命令
        System.out.println("======================= 不常用命令 =======================");
        System.out.println("追加值:" + jedis.append("hello", "java"));
        System.out.println("获取键 hello 的值:" + jedis.get("hello"));
        System.out.println("获取键 hello 的字符串长度:" + jedis.strlen("hello"));
        System.out.println("设置键 hello 的新值并返回原值:" + jedis.getSet("hello", "redis"));
        System.out.println("设置键 hello 设置指定位置开始的字符:" + jedis.setrange("hello", 0, "b"));
        System.out.println("获取键 hello 的值:" + jedis.get("hello"));
        System.out.println("获取部分字符串:" + jedis.getrange("hello", 0, 1));
        RedisPoolUtils.close(jedis);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

# 2.6 hash

import redis.clients.jedis.Jedis;

import java.util.HashMap;
import java.util.Map;

/**
 * redis hash(哈希)类型操作 测试
 */
public class HashMain {

    public static void main(String[] args) {
        Jedis jedis = RedisPoolUtils.getJedis();

        System.out.println("清空数据:" + jedis.flushDB());
        System.out.println("设置新哈希值键值对[name tom]:" + jedis.hset("user:1", "name", "tom"));
        System.out.println("设置field nx:" + jedis.hsetnx("user:1", "name", "tom"));
        System.out.println("获取键 name 的值:" + jedis.hget("user:1", "name"));
        Map<String, String> map = new HashMap<>();
        map.put("a", "1");
        map.put("b", "2");
        map.put("c", "cc");
        map.put("city", "gz");
        System.out.println("批量设置:" + jedis.hmset("user:1", map));

        System.out.println("计算field个数:" + jedis.hlen("user:1"));
        System.out.println("批量获取:" + jedis.hmget("user:1", "a", "c", "d"));
        System.out.println("删除field:" + jedis.hdel("user:1", "c"));
        System.out.println("判断field city 是否存在:" + jedis.hexists("user:1", "city"));

        System.out.println("获取所有field:" + jedis.hkeys("user:1"));
        System.out.println("获取所有Value:" + jedis.hvals("user:1"));
        System.out.println("获取所有field-value:" + jedis.hgetAll("user:1"));

        System.out.println("自增指定数字:" + jedis.hincrBy("user:1", "number", 3));
        System.out.println("自增指定浮点数:" + jedis.hincrByFloat("user:1", "number", 3));
        System.out.println("计算field name 值的字符串长度:" + jedis.hstrlen("user:1", "name"));
        RedisPoolUtils.close(jedis);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

# 2.7 list

import redis.clients.jedis.Jedis;
import redis.clients.jedis.args.ListPosition;

/**
 * redis list(列表)类型操作 测试
 */
public class ListMain {

    public static void main(String[] args) {
        Jedis jedis = RedisPoolUtils.getJedis();

        System.out.println("清空数据:" + jedis.flushDB());
        System.out.println("从右边插入元素:" + jedis.rpush("listkey", "c", "b", "a"));
        System.out.println("从左边插入元素:" + jedis.lpush("listkey", "a", "b", "c"));
        System.out.println("从左到右遍历:" + jedis.lrange("listkey", 0, -1));

        System.out.println("向元素b前插入元素:" + jedis.linsert("listkey", ListPosition.BEFORE, "b", "java"));
        System.out.println("从左到右遍历:" + jedis.lrange("listkey", 0, -1));

        System.out.println("获取列表下标为2的元素:" + jedis.lindex("listkey", 2));
        System.out.println("获取列表长度:" + jedis.llen("listkey"));
        System.out.println("从列表左侧弹出元素:" + jedis.lpop("listkey"));
        System.out.println("从左到右遍历:" + jedis.lrange("listkey", 0, -1));
        System.out.println("从列表右侧弹出元素:" + jedis.rpop("listkey"));
        System.out.println("从左到右遍历:" + jedis.lrange("listkey", 0, -1));

        System.out.println("删除指定元素a:" + jedis.lrem("listkey", 0, "a"));
        System.out.println("从左到右遍历:" + jedis.lrange("listkey", 0, -1));
        System.out.println("按照索引范围[1-3]修剪列表:" + jedis.ltrim("listkey", 1, 3));
        System.out.println("从左到右遍历:" + jedis.lrange("listkey", 0, -1));

        System.out.println("修改指定索引下标2的元素:" + jedis.lset("listkey", 2, "python"));
        System.out.println("从左到右遍历:" + jedis.lrange("listkey", 0, -1));
        RedisPoolUtils.close(jedis);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 2.8 set

import redis.clients.jedis.Jedis;

/**
 * redis Set(集合)类型操作 测试
 */
public class SetMain {

    public static void main(String[] args) {
        Jedis jedis = RedisPoolUtils.getJedis();

        // 集合内操作
        System.out.println("======================= 集合内操作 =======================");
        System.out.println("清空数据:" + jedis.flushDB());
        System.out.println("添加元素:" + jedis.sadd("myset", "a", "b", "c", "d", "e", "f", "g"));
        System.out.println("获取所有元素:" + jedis.smembers("myset"));
        System.out.println("删除元素a和b:" + jedis.srem("myset", "a", "b"));
        System.out.println("获取所有元素:" + jedis.smembers("myset"));

        System.out.println("计算元素个数:" + jedis.scard("myset"));
        System.out.println("判断元素c是否在集合中:" + jedis.sismember("myset", "c"));
        System.out.println("随机从集合返回2个元素:" + jedis.srandmember("myset", 2));
        System.out.println("计算元素个数:" + jedis.scard("myset"));
        System.out.println("从集合随机弹出元素:" + jedis.spop("myset"));
        System.out.println("计算元素个数:" + jedis.scard("myset"));


        // 集合间操作
        System.out.println("======================= 集合间操作 =======================");
        jedis.sadd("user:1:follow", "it", "music", "his", "sports");
        jedis.sadd("user:2:follow", "it", "news", "ent", "sports");
        System.out.println("交集:" + jedis.sinter("user:1:follow", "user:2:follow"));
        System.out.println("并集:" + jedis.sunion("user:1:follow", "user:2:follow"));
        System.out.println("差集:" + jedis.sdiff("user:1:follow", "user:2:follow"));

        // 保存交集
        jedis.sinterstore("user:1_2:inter", "user:1:follow", "user:2:follow");
        System.out.println("交集 user:1_2:inter 数据类型:" + jedis.type("user:1_2:inter"));
        System.out.println("获取所有元素:" + jedis.smembers("user:1_2:inter"));
        RedisPoolUtils.close(jedis);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 2.9 zset

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.ZAddParams;
import redis.clients.jedis.params.ZParams;

import java.util.HashMap;
import java.util.Map;

/**
 * redis zset(有序集合)类型操作 测试
 */
public class ZsetMain {

    public static void main(String[] args) {
        Jedis jedis = RedisPoolUtils.getJedis();

        // 有序集合内操作
        System.out.println("======================= 有序集合内操作 =======================");
        System.out.println("清空数据:" + jedis.flushDB());
        System.out.println("添加元素:" + jedis.zadd("user:ranking", 1, "kris"));
        Map<String, Double> members = new HashMap<>();
        members.put("mike", 91d);
        members.put("frank", 200d);
        members.put("tim", 220d);
        members.put("martin", 250d);
        System.out.println("批量添加元素:" + jedis.zadd("user:ranking", members));
        System.out.println("新增元素(添加参数):" + jedis.zadd("user:ranking", 150, "jack", new ZAddParams().nx().ch()));
        System.out.println("计算成员个数:" + jedis.zcard("user:ranking"));
        System.out.println("成员jack的分数:" + jedis.zscore("user:ranking", "jack"));

        System.out.println("计算成员jack的排名(分数从低到高):" + jedis.zrank("user:ranking", "jack"));
        System.out.println("计算成员jack的排名(分数从高到低):" + jedis.zrevrank("user:ranking", "jack"));
        System.out.println("删除成员mike:" + jedis.zrem("user:ranking", "mike"));
        System.out.println("成员jack分数增加10:" + jedis.zincrby("user:ranking", 10, "jack"));

        System.out.println("排名靠后两位:" + jedis.zrange("user:ranking", 0, 1));
        System.out.println("排名靠前两位:" + jedis.zrevrange("user:ranking", 0, 1));
        System.out.println("排名靠前两位(同时返回分数):" + jedis.zrevrangeWithScores("user:ranking", 0, 1));

        System.out.println("返回指定分数范围(160-230)的成员(分数从低到高):" + jedis.zrangeByScore("user:ranking", 160, 230));
        System.out.println("返回指定分数范围(160-230)的成员(分数从高到低):" + jedis.zrevrangeByScore("user:ranking", 230, 160));
        System.out.println("200分以上成员:" + jedis.zrangeByScoreWithScores("user:ranking", "(200", "+inf"));
        System.out.println("返回指定分数范围成(200-300)员个数:" + jedis.zcount("user:ranking", 200, 300));
        System.out.println("删除排名靠后两位:" + jedis.zremrangeByRank("user:ranking", 0, 1));
        System.out.println("删除200分以上成员:" + jedis.zremrangeByScore("user:ranking", "(200", "+inf"));


        // 有序集合间操作
        System.out.println("======================= 有序集合间操作 =======================");
        Map<String, Double> members1 = new HashMap<>();
        members1.put("kris", 1d);
        members1.put("mike", 91d);
        members1.put("frank", 200d);
        members1.put("tim", 220d);
        members1.put("martin", 250d);
        members1.put("tom", 251d);
        Map<String, Double> members2 = new HashMap<>();
        members2.put("james", 8d);
        members2.put("mike", 77d);
        members2.put("martin", 625d);
        members2.put("tom", 888d);
        jedis.zadd("user:ranking:1", members1);
        jedis.zadd("user:ranking:2", members2);

        ZParams zParams = new ZParams();
        zParams.aggregate(ZParams.Aggregate.MAX);
        zParams.weights(1, 0.5);
        System.out.println("交集:" + jedis.zinterstore("user:ranking:1_inter_2", zParams, "user:ranking:1", "user:ranking:2"));
        System.out.println("遍历:" + jedis.zrangeWithScores("user:ranking:1_inter_2", 0, -1));
        System.out.println("并集:" + jedis.zunionstore("user:ranking:1_union_2", "user:ranking:1", "user:ranking:2"));
        System.out.println("遍历:" + jedis.zrangeWithScores("user:ranking:1_union_2", 0, -1));
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

# 2.10 scan

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.ScanParams;
import redis.clients.jedis.resps.ScanResult;

import java.util.List;

/**
 * redis scan操作 测试
 */
public class ScanMain {

    public static void main(String[] args) {
        Jedis jedis = RedisPoolUtils.getJedis();
        System.out.println("清空数据:" + jedis.flushDB());
        // 插入临时数据
        for (int i = 0; i < 1234; i++) {
            jedis.set(i + "", i + "");
        }

        // 游标初始值为0
        String cursor = ScanParams.SCAN_POINTER_START;
        String key = "1*";
        ScanParams scanParams = new ScanParams();
        scanParams.match(key);
        scanParams.count(15);
        do {
            // 使用scan命令获取数据,使用cursor游标记录位置,下次循环使用
            ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
            cursor = scanResult.getCursor();// 返回0 说明遍历完成
            List<String> list = scanResult.getResult();
            System.out.println("========获取 " + list.size() + " 条数据, cursor:" + cursor + "========");
            for (String member : list) {
                System.out.print(member + "  ");
            }
            System.out.println();
        } while (!"0".equals(cursor));

        RedisPoolUtils.close(jedis);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 2.11 哨兵

哨兵模式:对 redis 服务器进行监控,如果有宕机的,就从备机里面选一个出来作为主机,实现自动切换。

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;

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

/**
 * redis 哨兵 测试
 */
public class SentinelMain {

    private static Jedis jedis;

    static {

        JedisPoolConfig config = new JedisPoolConfig();
        // 最大空闲连接数, 默认8个
        config.setMaxIdle(8);
        // 最大连接数, 默认8个
        config.setMaxTotal(8);
        // 最小空闲连接数, 默认0
        config.setMinIdle(0);
        // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常,小于零:阻塞不确定的时间,默认-1
        config.setMaxWaitMillis(3000);
        // 在获取连接的时候检查有效性,表示取出的redis对象可用, 默认false
        config.setTestOnBorrow(true);


        // redis服务器列表
        Set<String> sentinels = new HashSet<>();
        sentinels.add(new HostAndPort("172.17.0.5", 26379).toString());
        sentinels.add(new HostAndPort("172.17.0.6", 26379).toString());
        sentinels.add(new HostAndPort("172.17.0.7", 26379).toString());

        // 初始化连接池
        try (JedisSentinelPool jedisSentinelPool = new JedisSentinelPool("redis-master", sentinels, config, "qazwsx123edc")) {
            // 从池中获取一个Jedis对象
            jedis = jedisSentinelPool.getResource();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        System.out.println("清空数据:" + jedis.flushDB());
        System.out.println("设置新键值对[hello world]:" + jedis.set("hello", "world"));
        System.out.println("获取键 hello 的值:" + jedis.get("hello"));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

由于部署问题,这里的 java 代码放在了 SpringBoot 项目中,部署在 Linux 测试。

# 2.12 集群

Cluster 模式:将数据进行分片存储,避免全部节点数据都一样,浪费空间。

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.*;

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

/**
 * redis 集群 测试
 */
public class ClusterMain {
    private static JedisCluster jedisCluster;

    static {
        try {
            GenericObjectPoolConfig<Connection> config = new GenericObjectPoolConfig<>();
            // 最大空闲连接数, 默认8个
            config.setMaxIdle(8);
            // 最大连接数, 默认8个
            config.setMaxTotal(8);
            // 最小空闲连接数, 默认0
            config.setMinIdle(0);
            // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,默认-1
            config.setMaxWaitMillis(3000);
            // 在获取连接的时候检查有效性,表示取出的redis对象可用, 默认false
            config.setTestOnBorrow(true);

            Set<HostAndPort> nodes = new HashSet<>();
            nodes.add(new HostAndPort("192.168.214.131", 6383));
            nodes.add(new HostAndPort("192.168.214.131", 6384));
            nodes.add(new HostAndPort("192.168.214.131", 6385));
            nodes.add(new HostAndPort("192.168.214.131", 6386));
            nodes.add(new HostAndPort("192.168.214.131", 6387));
            nodes.add(new HostAndPort("192.168.214.131", 6388));

            jedisCluster = new JedisCluster(nodes, 1000, 1000, 5, "qazwsx123edc", config);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        System.out.println("清空数据:" + jedisCluster.flushDB());
        System.out.println("设置新键值对[hello world]:" + jedisCluster.set("hello", "world"));
        System.out.println("获取键 hello 的值:" + jedisCluster.get("hello"));
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 2.13 小结

优点:

  • 比较全面的提供了 Redis 的操作特性,也就是说你能用 redis 命令操作的,Jedis 包都也给你封装好了,直接使用即可;
  • 使用广泛,易上手。

缺点:

  • Jedis 客户端实例不是线程安全的,需要借助连接池来管理和使用 Jedis;
  • 使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到 sockets 处理完 I/O 才能执行,不支持异步。

# 三:Lettuce

spring-data-redis 的驱动包在某个版本之后底层替换为 Lettuce

Lettuce 是一个高性能基于 Java 编写的 Redis 驱动框架,底层集成了 Project Reactor 提供天然的反应式编程,通信框架集成了 Netty 使用了非阻塞 IO5.x 版本之后融合了 JDK1.8 的异步编程特性,在保证高性能的同时提供了十分丰富易用的 API5.1 版本的新特性如下:

  • 支持 Redis 的新增命令 ZPOPMIN, ZPOPMAX, BZPOPMIN, BZPOPMAX
  • 支持通过 Brave 模块跟踪 Redis 命令执行;
  • 支持 Redis Streams
  • 支持异步的主从连接;
  • 支持异步连接池;
  • 新增命令最多执行一次模式(禁止自动重连);
  • 全局命令超时设置(对异步和反应式命令也有效);
  • ... ... 等等

# 3.1 引用

在 Maven 项目中添加 Lettuce 依赖包,相关版本号可以查 Maven 仓库 (opens new window)

<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>6.2.6.RELEASE</version>
</dependency>
1
2
3
4
5

# 3.2 连接

Not found: D:\blog\ccj-blog/docs/views/notes/redis/file/java/lettuce/JedisMain.java

# 四:Redisson

# 五:RedisTemplate

# 六:参考文献

最后更新: 1/3/2024, 5:17:40 PM