Redis

Redis启动方法

1. redis-server和redis-cli默认启动

进入redis安装目录的src下

cd /usr/local/redis-5.0.4/src/

启动服务

 → /usr/local/redis-5.0.4/src$ ./redis-server
5693:C 10 Oct 2019 17:06:43.321 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
5693:C 10 Oct 2019 17:06:43.323 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=5693, just started
5693:C 10 Oct 2019 17:06:43.323 # Warning: no config file specified, using the default config. In order to specify a config file use ./redis-server /path/to/redis.conf
5693:M 10 Oct 2019 17:06:43.328 * Increased maximum number of open files to 10032 (it was originally set to 256).
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 5.0.4 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 5693
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           http://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  `-._    `-._`-.__.-'_.-'    _.-'
      `-._    `-.__.-'    _.-'
          `-._        _.-'
              `-.__.-'

5693:M 10 Oct 2019 17:06:43.334 # Server initialized
5693:M 10 Oct 2019 17:06:43.334 * Ready to accept connections

链接redis

 → /usr/local/redis-5.0.4/src$ ./redis-cli
127.0.0.1:6379> keys *
(empty list or set)

指定端口启动

# 指定6390端口启动redis服务
./redis-server --port 6390
./redis-cli -p 6390

指定某个配置文件启动

./redis-server ../redis.conf

链接远程的redis

# 6390为端口号 127.0.0.1 为本地ip, 链接远程redis直接更换为远程服务器的ip
./redis-cli -p 6390 -h 127.0.0.1

配置密码

修改redis的配置文件以设置密码

## ruiredis为密码
requirepass ruiredis

使用配置文件启动

./redis-server ../redis.conf

链接redis, 如果不输入密码或者输入错误密码, 也可以链接到redis, 但是查看keys或者做别的操作会提示

 → /usr/local/redis-5.0.4/src$ ./redis-cli
127.0.0.1:6379> keys *
(error) NOAUTH Authentication required.
127.0.0.1:6379>

输入密码链接

 → /usr/local/redis-5.0.4/src$ ./redis-cli -a ruiredis
127.0.0.1:6379> keys *
1) "a"
127.0.0.1:6379>

Redis键命令

  • 存储一个值
set key value
127.0.0.1:6379> set name lisi
OK
  • 获取一个值
get key
127.0.0.1:6379> get name
"lisi"
  • 获取存储列表所有的key
keys * 
127.0.0.1:6379> keys *
1) "c"
2) "b"
3) "name"
4) "a"
  • 判断一个key是否存在
exists key
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists 9
(integer) 0
  • 判断key的剩余生存时间
# -1代表永久有效, -2代表不存在
127.0.0.1:6379> ttl name
(integer) -1 
  • 给一个key设置有效期
# 给name设置20s的有效期
127.0.0.1:6379> expire name 20
(integer) 1
127.0.0.1:6379> ttl name
(integer) 18
  • 获取key的类型
127.0.0.1:6379> type b
string
  • 将key 重命名
# 如果rename是一个存在的key, 那么会将已有的key的value覆盖掉
127.0.0.1:6379> rename b d
OK
127.0.0.1:6379> keys *
1) "c"
2) "a"
3) "d"
# 如果renamenx一个存在的key, 那么会覆盖失败, 0为覆盖失败, 1是覆盖成功
127.0.0.1:6379> renamenx b c
(integer) 0
127.0.0.1:6379> renamenx b m
(integer) 1

Redis数据结构

1 .字符串

  • setex: 设置有效期为秒
# 设置key为c, value为d, 有效期为100s
127.0.0.1:6379> setex c 100 d
OK
127.0.0.1:6379> get c
"d"
127.0.0.1:6379> ttl c
(integer) 91
  • psetex: 设置有效期为毫秒
# 设置key为d, value为m, 有效期为10000ms
127.0.0.1:6379> psetex d 10000 m
OK
127.0.0.1:6379> get d
"m"
127.0.0.1:6379> ttl d
(integer) 4
  • getrange: 获取范围内的字符串, 闭合区间, 包含边界的元素
# 获取key为word的前5个字符
127.0.0.1:6379> set word wordwordword
OK
127.0.0.1:6379> get word
"wordwordword"
127.0.0.1:6379> getrange word 0 4
"wordw"
  • getset: 返回旧的value并设置一个新的value
127.0.0.1:6379> set rui rui
OK
127.0.0.1:6379> get rui
"rui"
127.0.0.1:6379> getset rui rui123
"rui"
127.0.0.1:6379> get rui
"rui123"
  • mset: 同时设置多个key
127.0.0.1:6379> mset r1 1 r2 2 r3 3
OK
127.0.0.1:6379> mget r1 r2 r3
1) "1"
2) "2"
3) "3"
  • setnx: 当key不存在的时候set成功, 返回1, 当key存在的时候set失败, 返回0
127.0.0.1:6379> keys *
1) "word"
2) "a"
3) "m"
4) "r2"
5) "r1"
6) "rui"
7) "r3"
127.0.0.1:6379> setnx rui 123
(integer) 0
127.0.0.1:6379> setnx rui123 345
(integer) 1
  • msetnx: 当key不存在的时候set成功, 当key存在的时候set失败, 且具有事务的原子性, 要么全部成功, 要么全部失败
127.0.0.1:6379> msetnx rui 123 uuu iii
(integer) 0
127.0.0.1:6379> msetnx ruic 123 uuu iii
(integer) 1
  • incr, decr: integer类型增加和减少
127.0.0.1:6379> set int 1
OK
127.0.0.1:6379> incr int # 一次加1
(integer) 2
127.0.0.1:6379> incr int
(integer) 3
127.0.0.1:6379> incr int
(integer) 4
127.0.0.1:6379> get int
"4"
127.0.0.1:6379> incrby int 100 # 一次性加100
(integer) 104
127.0.0.1:6379> get int
"104"
127.0.0.1:6379> decr int
(integer) 103
127.0.0.1:6379> decr int
(integer) 102
127.0.0.1:6379> decrby int 100
(integer) 2
  • append: 拼接字符串
127.0.0.1:6379> append int wtf
(integer) 4
127.0.0.1:6379> get int
"2wtf"

2. 哈希hash

  • hset: 存储一个hash类型的数据
# map是key, name是map的key, lisi是map的value
127.0.0.1:6379[1]> hset map name lisi
(integer) 1
127.0.0.1:6379[1]> type map
hash
  • hexists: 判断map内的key是否存在
127.0.0.1:6379[1]> hexists map name
(integer) 1
127.0.0.1:6379[1]> hexists map namecccc
(integer) 0
  • hget: 获取一个hash类型的数据
127.0.0.1:6379[1]> hget map name
"lisi"
127.0.0.1:6379[1]> hget map cccc
(nil)
  • hgetall: 获取hash的所有数据
127.0.0.1:6379[1]> hset map age 19
(integer) 1
127.0.0.1:6379[1]> hgetall map
1) "name"
2) "lisi"
3) "age"
4) "19"
  • hkeys: 获取所有的key
127.0.0.1:6379[1]> hkeys map
1) "name"
2) "age"
  • hvals: 获取所有的value
127.0.0.1:6379[1]> hvals map
1) "lisi"
2) "19"
  • hlen: 获取hash类型数据的长度
127.0.0.1:6379[1]> hlen map
(integer) 2
  • hmget: 一次性获取多个hash类型数据key的值
127.0.0.1:6379[1]> hmget map name age
1) "lisi"
2) "19"
  • hmset: 一次性设置多个数据
127.0.0.1:6379[1]> hmset map newname zhangsan newage 28
OK
  • hdel: 删除hash数据类型数据的值
127.0.0.1:6379[1]> hdel map newname newage
(integer) 2

3. 列表list

# 创建一个key为list的List, 先加入的在最下面
127.0.0.1:6379[2]> lpush list 1 2 3 4 5 6 7 8
(integer) 8
# 获取key
127.0.0.1:6379[2]> keys *
1) "list"
127.0.0.1:6379[2]> type list
list
# 获取key为list的数据长度
127.0.0.1:6379[2]> llen list
(integer) 8
# 获取前3个数据,  闭合区间, 包含边界的元素
127.0.0.1:6379[2]> lrange list 0 2
1) "8"
2) "7"
3) "6"
# 将最上面的数据修改为100
127.0.0.1:6379[2]> lset list 0 100
OK
127.0.0.1:6379[2]> lrange list 0 2
1) "100"
2) "7"
3) "6"
# 获取索引为5的数据
127.0.0.1:6379[2]> lindex list 5
"3"
# 删除最上面的元素
127.0.0.1:6379[2]> lpop list
"100"
# 删除最下面的元素
127.0.0.1:6379[2]> rpop list
"1"
127.0.0.1:6379[2]> lrange list 0 100
1) "7"
2) "6"
3) "5"
4) "4"
5) "3"
6) "2"

4. 集合set

# 创建一个set, key为set
127.0.0.1:6379[3]> sadd set a b c d
(integer) 4
127.0.0.1:6379[3]> keys *
1) "set"
127.0.0.1:6379[3]> type set
set
# 给set添加一个元素, 如果存在返回0, 如果不存在返回1
127.0.0.1:6379[3]> sadd set a
(integer) 0
# 返回集合内元素的数量
127.0.0.1:6379[3]> scard set
(integer) 4
127.0.0.1:6379[3]> sadd set2 c d e f
(integer) 4
127.0.0.1:6379[3]> keys *
1) "set2"
2) "set"
# 获取一个集合内的所有元素
127.0.0.1:6379[3]> smembers set
1) "c"
2) "b"
3) "a"
4) "d"
127.0.0.1:6379[3]> smembers set2
1) "c"
2) "e"
3) "d"
4) "f"
# 获取set比set2多的元素,
127.0.0.1:6379[3]> sdiff set set2
1) "b"
2) "a"
# 获取set2比set多的元素,
127.0.0.1:6379[3]> sdiff set2 set
1) "e"
2) "f"
# 获取两个集合的交集
127.0.0.1:6379[3]> sinter set set2
1) "c"
2) "d"
# 获取两个集合的并集
127.0.0.1:6379[3]> sunion set set2
1) "b"
2) "a"
3) "f"
4) "c"
5) "d"
6) "e"
# 从集合里随机获取2个元素
127.0.0.1:6379[3]> srandmember set 2
1) "b"
2) "d"
127.0.0.1:6379[3]> srandmember set 2
1) "c"
2) "d"
# 判断a是否为集合set内部的元素, 如果是返回1, 如果不是返回0
127.0.0.1:6379[3]> sismember set a
(integer) 1
127.0.0.1:6379[3]> sismember set m
(integer) 0
# 移除一个或者多个成员, 移除a和b
127.0.0.1:6379[3]> srem set a b
(integer) 2
127.0.0.1:6379[3]> smembers set
1) "c"
2) "d"
# 移除并且返回 一个随机元素
127.0.0.1:6379[3]> spop set2
"c"
127.0.0.1:6379[3]> smembers set2
1) "e"
2) "d"
3) "f"

5. 有序集合sortedset

# 创建一个有序集合, key为sortedset1 , 内部的key为a b c, value为100 200 300
127.0.0.1:6379[4]> zadd sortedset1 100 a 200 b 300 c
(integer) 3
127.0.0.1:6379[4]> keys *
1) "sortedset1"
127.0.0.1:6379[4]> type sortedset1
zset
127.0.0.1:6379[4]> rename sortedset1 sortedset
OK
# 获取sortedset的元素个数
127.0.0.1:6379[4]> zcard sortedset
(integer) 3
# 获取a的value
127.0.0.1:6379[4]> zscore sortedset a
"100"
# 获取b的value
127.0.0.1:6379[4]> zscore sortedset b
"200"
# 获取c的value
127.0.0.1:6379[4]> zscore sortedset c
"300"
# 获取d的value, 没有不存在返回nil
127.0.0.1:6379[4]> zscore sortedset d
(nil)
# 获取value在[0, 200]的元素的个数
127.0.0.1:6379[4]> zcount sortedset 0 200
(integer) 2
# 获取value在[0, 300]的元素的个数
127.0.0.1:6379[4]> zcount sortedset 0 300
(integer) 3
# 获取a的索引
127.0.0.1:6379[4]> zrank sortedset a
(integer) 0
# 获取b的索引
127.0.0.1:6379[4]> zrank sortedset b
(integer) 1
127.0.0.1:6379[4]> zrank sortedset c
(integer) 2
# 获取a的增加1000
127.0.0.1:6379[4]> zincrby sortedset 1000 a
"1000"
# 获取前101个元素的key, 闭合区间
127.0.0.1:6379[4]> zrange sortedset 0 100
1) "a"
2) "b"
3) "c"
127.0.0.1:6379[4]> zrange sortedset 0 1
1) "a"
2) "b"
127.0.0.1:6379[4]> zrange sortedset 0 1 withscores
1) "a"
2) "100"
3) "b"
4) "200"

Redis连接池

public class RedisPool {
    // jedis连接词
    private static JedisPool pool;
    // 最大连接数
    private static Integer maxTotal = Integer.parseInt(PropertiesUtil.getProperty
            ("redis.max.total", "20"));
    // 最大的Jedispool中最大的idle状态(空闲的)的Jedis实例的个数
    private static Integer maxIdle = Integer.parseInt(PropertiesUtil.getProperty
            ("redis.max.idle", "20"));
    // 最小的Jedispool中最大的idle状态(空闲的)的Jedis实例的个数
    private static Integer minIdle = Integer.parseInt(PropertiesUtil.getProperty
            ("redis.min.idle", "10"));
    // 在brrow一个jedis实例的时候, 是否要进行验证, 如果赋值为true, 那么表示不需要. 从连接池获取链接
    private static Boolean testOnBrrow = Boolean.parseBoolean(PropertiesUtil
            .getProperty
            ("redis.test.testOnBrrow", "true"));
    // 在return一个jedis实例的时候, 是否要进行验证, 如果赋值为true, 那么不需要, 链接归还连接池
    private static Boolean testOnReturn = Boolean.parseBoolean(PropertiesUtil
            .getProperty("redis.test.testOnReturn", "true"));
    // redis的ip
    private static String ip = PropertiesUtil.getProperty("redis.ip");
    // redis的port
    private static Integer port = Integer.parseInt(PropertiesUtil.getProperty
            ("redis.port"));
    private static void initPool() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxTotal);
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setTestOnBorrow(testOnBrrow);
        jedisPoolConfig.setTestOnReturn(testOnReturn);
        // 链接耗尽的时候 是否阻塞. false会抛出异常, true会阻塞一直到超时, 默认为true
        jedisPoolConfig.setBlockWhenExhausted(true);
        pool = new JedisPool(jedisPoolConfig, ip, port, 1000 * 2);
    }
    // 随着tomcat的启动而初始化
    static {
        initPool();
    }
    // 获取jedis实例
    public static Jedis getJedis() {
        return pool.getResource();
    }
    // 回收jedis
    public static void returnResource(Jedis jedis) {
        pool.returnResource(jedis);
    }
    // 回收坏的链接
    public static void returnBrokenResource(Jedis jedis) {
        pool.returnBrokenResource(jedis);
    }

    public static void main(String[] args) {
        Jedis jedis = getJedis();
        jedis.set("ruirui", "dayday");
        returnResource(jedis);
        pool.destroy();
    }
}

Redis API工具类

public class RedisPoolUntil {
    private static Logger logger = LoggerFactory.getLogger(CategoryServiceImpl
            .class);

    /**
     * 封装set方法
     * @param key
     * @param value
     * @return
     */
    public static String set(String key, String value) {
        Jedis jedis = null;
        String result = null;
        try {
            jedis = RedisPool.getJedis();
            result = jedis.set(key, value);
        } catch (Exception e) {
            logger.error("set key:{} value:{} error:", key, value, e);
            RedisPool.returnBrokenResource(jedis);
            return result;
        }
        RedisPool.returnResource(jedis);
        return result;
    }

    /**
     * 重新给key设置过期时间
     * @param key
     * @param exTime 单位为秒
     * @return
     */
    public static Long expire(String key, int exTime) {
        Jedis jedis = null;
        Long result = null;
        try {
            jedis = RedisPool.getJedis();
            result = jedis.expire(key, exTime);
        } catch (Exception e) {
            logger.error("expire key:{} value:{} error:", key, e);
            RedisPool.returnBrokenResource(jedis);
            return result;
        }
        RedisPool.returnResource(jedis);
        return result;
    }
    /**
     * 设置过期时间
     * @param key
     * @param value
     * @param exTime 单位是秒
     * @return
     */
    public static String setEx(String key, String value, int exTime) {
        Jedis jedis = null;
        String result = null;
        try {
            jedis = RedisPool.getJedis();
            result = jedis.setex(key, exTime, value);
        } catch (Exception e) {
            logger.error("setex key:{} value:{} error:", key, value, e);
            RedisPool.returnBrokenResource(jedis);
            return result;
        }
        RedisPool.returnResource(jedis);
        return result;
    }
    /**
     * 封装get方法
     * @param key
     * @return
     */
    public static String get(String key) {
        Jedis jedis = null;
        String result = null;
        try {
            jedis = RedisPool.getJedis();
            result = jedis.get(key);
        } catch (Exception e) {
            logger.error("get key:{} error:", key, e);
            RedisPool.returnBrokenResource(jedis);
            return result;
        }
        RedisPool.returnResource(jedis);
        return result;
    }

    /**
     * 删除key
     * @param key
     * @return
     */
    public static Long del(String key) {
        Jedis jedis = null;
        Long result = null;
        try {
            jedis = RedisPool.getJedis();
            result = jedis.del(key);
        } catch (Exception e) {
            logger.error("del:{} error:", key, e);
            RedisPool.returnBrokenResource(jedis);
            return result;
        }
        RedisPool.returnResource(jedis);
        return result;
    }
}

Redis分布式

Redis分布式算法原理

传统分布式算法
例子

假设有一个图片 test.jpg,我们有 3 个服务器, 服务器1 ,服务器2 ,服务器3
4 个 redis 节点

  • Redis0
  • Redis1
  • Redis2
  • Redis3
    20个数据


    image

    这上面 1-20 大家就可以认识是 对应数据 hash 之后的结果,然后对这些结果用 4 取模(因为这里有 4 个 Redis 节点).
    1 % 4 = 1 所以将该数据放在 Redis1
    2 % 4 = 2 所以将该数据放在 Redis2
    3 % 4 = 3 所以将该数据放在 Redis3
    4 % 4 = 0 所以将该数据放在 Redis0
    同理,后面的其他数据应该这样放置,如下图


    image

    但是,突然我们发现Redis 的节点不够用了(需要增加节点),或者Redis负载非常低(需要删除节点)。
    这里我们来增加一个节点 Redis4,增加之后的数据再节点上的分部如下图:
    image

    你会发现,只有 redis0 命中了值 20,redis1命中了1,redis2 命中了2,redis3命中了3,命中率为 4/20 = 20%

Consistent hashing一致性算法原理

这个算法有一个环形hash空间的概念,通常hash算法都是将value映射在一个32位的key值当中,那么把数据首尾相接就会形成一个圆形,取值范围为0 ~ 2^32-1,这个圆环就是环形hash空间。如下图:


image

将对象映射到 圆形hash空间

  • 我们hash 4个 对象 obj1-obj4
  • 通过hash 函数计算出hash 值的key
    落在 环形 hash 空间上的情况如图


    image

    将cache 映射到环形 hash空间

  • 将对象和 cache 都映射到同一个hash 空间,并且使用相同的hash 算法,如下图:


    image

    现在我们就把数据对象和cache 都映射到 hash空间上了,接下来就是要考虑如何将这个对象映射到cache 上面,看下面的图,沿着环形顺时针走,从key1开始,可将obj1 映射到keyA上,obj2 映射到keyC ,obj3映射到keyC,obj4映射到keyB上


    image

下面来看看移除和添加cache 节点有什么变化


image

将cacheB移除,obj4就只能顺时针找到 cacheC了,所以移除一个cache节点,影响的是从该cache节点逆时针开始碰到第一个节点的范围对象,环状的其他区域数据节点都不会影响,如图:


image

在 obj2和obj3直接添加一个 cacheD ,如图,我们可以看到obj2 顺时针就会映射到cacheD上,同时受到影响的也是从添加的cache节点逆时针碰到第一个节点的范围


image

从上面我们可以看到,cache 的变动,对应数据对象的影响很小。
Hash倾斜性

但是呢,要知道理想和现实的差距,我们理想的环状空间是均匀分布的,如图:


image

现实却是这样的情况:


如果用上面的hash 算法,大量的数据对象会映射在 A 节点上,而BC节点很少,这样就导致A节点很忙,BC却很是清闲,这就是因为Hash 的倾斜性造成的。


image
虚拟节点

如何解决Hash 倾斜性导致的问题呢?从而引入了虚拟节点


image

比如有 obj1 和 obj 2 两个对象 对其进行 hash 计算,这里增加了 6 个虚拟节点,hash 之后分布落在了 V2,V5上,然后对虚拟节点进行 rehash ,这时 V1,V2映射在 N1上,V3,V4映射在N2上,V5,V6 映射在N3上,obj就映射在了 N1上,obj2映射在N3上。
引入了 虚拟节点,现在 环状空间是什么样子的呢?看下图


image

ABC分别都有对应的影子节点,这时候数据对象的映射就相对均匀了,但是要知道,虚拟节点也有是hash 倾斜性的,这就要在真实节点和虚拟节点之间做一个平衡,分配一个很好的比例,随着节点越来越多,数据越来越多,那么这个分布就会越来越均匀了,在删除节点和添加节点的时候也会把影响降到最小。

Consistent hashing命中率

(1-n/(n + m)) * 100%

  • n为服务器台数
  • m为新增服务器台数

分布式Redis连接池

public class RedisShardedPool {
    private static ShardedJedisPool pool;//sharded jedis连接池
    private static Integer maxTotal = Integer.parseInt(PropertiesUtil.getProperty("redis.max.total","20")); //最大连接数
    private static Integer maxIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.max.idle","20"));//在jedispool中最大的idle状态(空闲的)的jedis实例的个数
    private static Integer minIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.min.idle","20"));//在jedispool中最小的idle状态(空闲的)的jedis实例的个数

    private static Boolean testOnBorrow = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.borrow","true"));//在borrow一个jedis实例的时候,是否要进行验证操作,如果赋值true。则得到的jedis实例肯定是可以用的。
    private static Boolean testOnReturn = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.return","true"));//在return一个jedis实例的时候,是否要进行验证操作,如果赋值true。则放回jedispool的jedis实例肯定是可以用的。

    private static String redis1Ip = PropertiesUtil.getProperty("redis1.ip");
    private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));
    private static String redis2Ip = PropertiesUtil.getProperty("redis2.ip");
    private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));

    private static void initPool(){
        JedisPoolConfig config = new JedisPoolConfig();

        config.setMaxTotal(maxTotal);
        config.setMaxIdle(maxIdle);
        config.setMinIdle(minIdle);

        config.setTestOnBorrow(testOnBorrow);
        config.setTestOnReturn(testOnReturn);

        config.setBlockWhenExhausted(true);//连接耗尽的时候,是否阻塞,false会抛出异常,true阻塞直到超时。默认为true。

        JedisShardInfo info1 = new JedisShardInfo(redis1Ip,redis1Port,1000*2);

        JedisShardInfo info2 = new JedisShardInfo(redis2Ip,redis2Port,1000*2);

        List<JedisShardInfo> jedisShardInfoList = new ArrayList<JedisShardInfo>(2);

        jedisShardInfoList.add(info1);
        jedisShardInfoList.add(info2);

        pool = new ShardedJedisPool(config,jedisShardInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN);
    }

    static{
        initPool();
    }

    public static ShardedJedis getJedis(){
        return pool.getResource();
    }


    public static void returnBrokenResource(ShardedJedis jedis){
        pool.returnBrokenResource(jedis);
    }



    public static void returnResource(ShardedJedis jedis){
        pool.returnResource(jedis);
    }


    public static void main(String[] args) {
        ShardedJedis jedis = pool.getResource();

        for(int i =0;i<10;i++){
            jedis.set("key"+i,"value"+i);
        }
        returnResource(jedis);

//        pool.destroy();//临时调用,销毁连接池中的所有连接
        System.out.println("program is end");


    }
}

分布式Redis API工具类

@Slf4j
public class RedisShardedPoolUtil {

    /**
     * 设置key的有效期,单位是秒
     * @param key
     * @param exTime
     * @return
     */
    public static Long expire(String key,int exTime){
        ShardedJedis jedis = null;
        Long result = null;
        try {
            jedis = RedisShardedPool.getJedis();
            result = jedis.expire(key,exTime);
        } catch (Exception e) {
            log.error("expire key:{} error",key,e);
            RedisShardedPool.returnBrokenResource(jedis);
            return result;
        }
        RedisShardedPool.returnResource(jedis);
        return result;
    }

    //exTime的单位是秒
    public static String setEx(String key,String value,int exTime){
        ShardedJedis jedis = null;
        String result = null;
        try {
            jedis = RedisShardedPool.getJedis();
            result = jedis.setex(key,exTime,value);
        } catch (Exception e) {
            log.error("setex key:{} value:{} error",key,value,e);
            RedisShardedPool.returnBrokenResource(jedis);
            return result;
        }
        RedisShardedPool.returnResource(jedis);
        return result;
    }

    public static String set(String key,String value){
        ShardedJedis jedis = null;
        String result = null;

        try {
            jedis = RedisShardedPool.getJedis();
            result = jedis.set(key,value);
        } catch (Exception e) {
            log.error("set key:{} value:{} error",key,value,e);
            RedisShardedPool.returnBrokenResource(jedis);
            return result;
        }
        RedisShardedPool.returnResource(jedis);
        return result;
    }

    public static String getSet(String key,String value){
        ShardedJedis jedis = null;
        String result = null;

        try {
            jedis = RedisShardedPool.getJedis();
            result = jedis.getSet(key,value);
        } catch (Exception e) {
            log.error("getset key:{} value:{} error",key,value,e);
            RedisShardedPool.returnBrokenResource(jedis);
            return result;
        }
        RedisShardedPool.returnResource(jedis);
        return result;
    }

    public static String get(String key){
        ShardedJedis jedis = null;
        String result = null;
        try {
            jedis = RedisShardedPool.getJedis();
            result = jedis.get(key);
        } catch (Exception e) {
            log.error("get key:{} error",key,e);
            RedisShardedPool.returnBrokenResource(jedis);
            return result;
        }
        RedisShardedPool.returnResource(jedis);
        return result;
    }

    public static Long del(String key){
        ShardedJedis jedis = null;
        Long result = null;
        try {
            jedis = RedisShardedPool.getJedis();
            result = jedis.del(key);
        } catch (Exception e) {
            log.error("del key:{} error",key,e);
            RedisShardedPool.returnBrokenResource(jedis);
            return result;
        }
        RedisShardedPool.returnResource(jedis);
        return result;
    }

    public static Long setnx(String key,String value){
        ShardedJedis jedis = null;
        Long result = null;

        try {
            jedis = RedisShardedPool.getJedis();
            result = jedis.setnx(key,value);
        } catch (Exception e) {
            log.error("setnx key:{} value:{} error",key,value,e);
            RedisShardedPool.returnBrokenResource(jedis);
            return result;
        }
        RedisShardedPool.returnResource(jedis);
        return result;
    }


    public static void main(String[] args) {
        ShardedJedis jedis = RedisShardedPool.getJedis();
        RedisPoolUtil.set("keyTest","value");
        String value = RedisPoolUtil.get("keyTest");
        RedisPoolUtil.setEx("keyex","valueex",60*10);
        RedisPoolUtil.expire("keyTest",60*20);
        RedisPoolUtil.del("keyTest");
        String aaa = RedisPoolUtil.get(null);
        System.out.println(aaa);
        System.out.println("end");
    }
}

集群和分布式的区别

小饭店原来只有一个厨师,切菜洗菜备料炒菜全干。后来客人多了,厨房一个厨师忙不过来,又请了个厨师,两个厨师都能炒一样的菜,这两个厨师的关系是集群。
为了让厨师专心炒菜,把菜做到极致,又请了个配菜师负责切菜,备菜,备料,厨师和配菜师的关系是分布式,一个配菜师也忙不过来了,
又请了个配菜师,两个配菜师关系是集群

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容