reids client 介绍

一 redis java client

redis客户端介绍
  1. redis官网整理了如下java客户端,并推荐Jedis,lettuce,Redisson。

这里写图片描述

  1. Jedis的gitHub关注比较高,spring-data-redis也是基于Jedis封装。所以推荐采用Jedis来访问redis server。
Jedis使用介绍
Redisson(了解)
  • 基于Netty实现,采用非阻塞IO,性能高
  • 支持异步请求
  • 支持连接池
  • 支持pipelining、LUA Scripting、Redis Sentinel、Redis Cluster
  • 不支持事务,官方建议以LUA Scripting代替事务
  • 支持在Redis Cluster架构下使用pipelining
  • 支持读写分离,支持读负载均衡,在主从复制和Redis Cluster架构下都可以使用
  • 内建Tomcat Session Manager,为Tomcat 6/7/8提供了会话共享功能
  • 可以与Spring Session集成,实现基于Redis的会话共享
  • 文档较丰富,有中文文档
  • 对于Jedis和Redisson的选择,同样应遵循前述的原理,尽管Jedis比起Redisson有各种各样的不足,但也应该在需要使用Redisson的高级特性时再选用Redisson,避免造成不必要的程序复杂度提升。

Redisson:
github:https://github.com/redisson/redisson
文档:https://github.com/redisson/redisson/wiki

Lettuce(了解)

Lettuce是一个可伸缩的线程安全的Redis客户端,用于同步,异步和反应使用。 多个线程可以共享同一个RedisConnection。它利用优秀netty NIO框架来高效地管理多个连接。 支持先进的Redis功能,如Sentinel,集群,流水线,自动重新连接和Redis数据模型

  • 同步,异步和反应使用
  • Redis Sentinel
  • Redis 集群
  • SSL 和 Unix 域套接字连接
  • 流媒体 API
  • CDI 和 Spring 集成
  • 编解码器(用于 UTF8 / bit / JSON 等数据表示)
  • 多个命令接口
  • 与 Java 8 和 9 兼容(自动模块不带描述符)

文档:https://github.com/lettuce-io/lettuce-core/wiki

一 Jedis功能介绍

  1. Jedis的使用可以通过wiki与github源代码的测试例子来学习使用。

  2. 如下例子介绍JedisPool构建,事物操作,pipelining多命令批量执行,lua脚本操作。其它数据结构

  3. redis3.0服务已支持集群,所以ShardedJedis操作了解。


/**
 * @author andrexu
 * @Description
 * @email xuhc@kerunyun.com
 * @created 下午8:12 2017/11/24
 */
@Slf4j
public class RedisUtilsTest {

    /**
     * 连接pool构建
     *
     * @return
     */
    public static JedisPool getTestJedisPools() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        JedisPool jedisPool = new JedisPool(
                poolConfig,
                "test.redis.cnhz.shishike.com",
                6379,
                600000,
                "d64ffed6b62e4e7c:CcPEtGSkIXSESQ6R",
                18);
        return jedisPool;
    }


    /**
     * 事物内执行
     */
    @Test
    public void transactionTest() {

        Jedis jedis = getTestJedisPools().getResource();
        // batchDel(jedis);
        // JedisUtilEx
        //
        Transaction transaction = jedis.multi();

        transaction.set("test", "33333");
        transaction.set("test2", "33333");

        List<Object> result = transaction.exec();
        log.info(result.toString());
        jedis.close();
    }


    /**
     * Pipelining 一起执行一批command,减少通讯连接
     */
    @Test
    public void pipelineTest() {
        Jedis jedis = getTestJedisPools().getResource();
        Pipeline p = jedis.pipelined();

        p.set("test", "33333");
        p.set("test2", "33333");
        Response<Set<String>> allJobKey = p.keys("kry_job*");

        Response<String> testKey = p.get("test2");

        p.sync();
        log.info(String.valueOf(allJobKey.get()));
        log.info(testKey.get());
        jedis.close();
    }


    /**
     * redis中的lua脚本做了很多限制,防止随机性的发生。比如lua脚本中返回的总是有序的集合。
     * 详情见 http://doc.redisfans.com/script/eval.html - 纯函数脚本
     */
    @Test
    public void scriptFuc() throws InterruptedException {
        Jedis jedis = getTestJedisPools().getResource();
        String key = "luaTest";
        System.out.println(jedis.del(key));
        System.out.println(jedis.sadd(key, "10","3","7","40","6"));
        System.out.println(jedis.smembers(key));//这里怎么返回的值是有序的?  [3, 6, 7, 10, 40]
        System.out.println(jedis.eval("return redis.call('smembers', KEYS[1])", 1, key));//根据字母序排序  [10, 3, 40, 6, 7]
    }


    private void batchDel(Jedis jedis) {
        Set<String> set = jedis.keys("kry_job*");
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            String keyStr = it.next();
            System.out.println(keyStr);
            jedis.del(keyStr);
        }
    }

    /**
     * 多集群
     *
     * @return
     */
    private ShardedJedis getShardedJedisForCluster() {
        try {
            List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();

            String host = "redis://test.redis.cnhz.shishike.com:6379/";

            JedisShardInfo si = new JedisShardInfo(host + 1);
            si.setPassword("d64ffed6b62e4e7c:CcPEtGSkIXSESQ6R");
            shards.add(si);


            si = new JedisShardInfo(host + 2);
            si.setPassword("123456");

            shards.add(si);
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            ShardedJedis jedis = new ShardedJedis(shards);
            return jedis;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    @Test
    public void shardExpTest() {
        ShardedJedis jedis = getShardedJedisForCluster();

        for (int i = 0; i < 10; i++) {
            jedis.set("test" + i, "33333:" + i);
        }

        jedis.set("a", "bar");
        JedisShardInfo ak = jedis.getShardInfo("testl1");
        assertEquals("bar", ak);
        jedis.set("b", "bar1");
        JedisShardInfo bk = jedis.getShardInfo("testl2");
        assertEquals("testl2", bk);

    }

}

  1. jedis操作命令介绍
1.对value操作的命令

     exists(key):确认一个key是否存在

     del(key):删除一个key

     type(key):返回值的类型

     keys(pattern):返回满足给定pattern的所有key

     randomkey:随机返回key空间的一个key

     rename(oldname, newname):将key由oldname重命名为newname,若newname存在则删除newname表示的key

     dbsize:返回当前数据库中key的数目

     expire:设定一个key的活动时间(s)

     ttl:获得一个key的活动时间

     select(index):按索引查询

     move(key, dbindex):将当前数据库中的key转移到有dbindex索引的数据库

     flushdb:删除当前选择数据库中的所有key

     flushall:删除所有数据库中的所有key

2.对String操作的命令

     set(key, value):给数据库中名称为key的string赋予值value

     get(key):返回数据库中名称为key的string的value

     getset(key, value):给名称为key的string赋予上一次的value

     mget(key1, key2,…, key N):返回库中多个string(它们的名称为key1,key2…)的value

     setnx(key, value):如果不存在名称为key的string,则向库中添加string,名称为key,值为value

     setex(key, time, value):向库中添加string(名称为key,值为value)同时,设定过期时间time

     mset(key1, value1, key2, value2,…key N, value N):同时给多个string赋值,名称为key i的string赋值value i

     msetnx(key1, value1, key2, value2,…key N, value N):如果所有名称为key i的string都不存在,则向库中添加string,名称key i赋值为value i

     incr(key):名称为key的string增1操作

     incrby(key, integer):名称为key的string增加integer

     decr(key):名称为key的string减1操作

     decrby(key, integer):名称为key的string减少integer

     append(key, value):名称为key的string的值附加value

     substr(key, start, end):返回名称为key的string的value的子串

3.对List操作的命令

     rpush(key, value):在名称为key的list尾添加一个值为value的元素

     lpush(key, value):在名称为key的list头添加一个值为value的 元素

     llen(key):返回名称为key的list的长度

     lrange(key, start, end):返回名称为key的list中start至end之间的元素(下标从0开始,下同)

     ltrim(key, start, end):截取名称为key的list,保留start至end之间的元素

     lindex(key, index):返回名称为key的list中index位置的元素

     lset(key, index, value):给名称为key的list中index位置的元素赋值为value

     lrem(key, count, value):删除count个名称为key的list中值为value的元素。count为0,删除所有值为value的元素,count>0      从头至尾删除count个值为value的元素,count<0从尾到头删除|count|个值为value的元素。

     lpop(key):返回并删除名称为key的list中的首元素

     rpop(key):返回并删除名称为key的list中的尾元素

     blpop(key1, key2,… key N, timeout):lpop 命令的block版本。即当timeout为0时,若遇到名称为key i的list不存在或该list为空,则命令结束。如果 timeout>0,则遇到上述情况时,等待timeout秒,如果问题没有解决,则对key i+1开始的list执行pop操作。

     brpop(key1, key2,… key N, timeout):rpop的block版本。参考上一命令。

     rpoplpush(srckey, dstkey):返回并删除名称为srckey的list的尾元素,并将该元素添加到名称为dstkey的list的头部

4.对Set操作的命令

     sadd(key, member):向名称为key的set中添加元素member

     srem(key, member) :删除名称为key的set中的元素member

     spop(key) :随机返回并删除名称为key的set中一个元素

     smove(srckey, dstkey, member) :将member元素从名称为srckey的集合移到名称为dstkey的集合

     scard(key) :返回名称为key的set的基数

     sismember(key, member) :测试member是否是名称为key的set的元素

     sinter(key1, key2,…key N) :求交集

     sinterstore(dstkey, key1, key2,…key N) :求交集并将交集保存到dstkey的集合

     sunion(key1, key2,…key N) :求并集

     sunionstore(dstkey, key1, key2,…key N) :求并集并将并集保存到dstkey的集合

     sdiff(key1, key2,…key N) :求差集

     sdiffstore(dstkey, key1, key2,…key N) :求差集并将差集保存到dstkey的集合

     smembers(key) :返回名称为key的set的所有元素

     srandmember(key) :随机返回名称为key的set的一个元素

5.对zset(sorted set)操作的命令

     zadd(key, score, member):向名称为key的zset中添加元素member,score用于排序。如果该元素已经存在,则根据score更新该元素的顺序。

     zrem(key, member) :删除名称为key的zset中的元素member

     zincrby(key, increment, member) :如果在名称为key的zset中已经存在元素member,则该元素的score增加increment;否则向集合中添加该元素,其score的值为increment

     zrank(key, member) :返回名称为key的zset(元素已按score从小到大排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”

     zrevrank(key, member) :返回名称为key的zset(元素已按score从大到小排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”

     zrange(key, start, end):返回名称为key的zset(元素已按score从小到大排序)中的index从start到end的所有元素

     zrevrange(key, start, end):返回名称为key的zset(元素已按score从大到小排序)中的index从start到end的所有元素

     zrangebyscore(key, min, max):返回名称为key的zset中score >= min且score <= max的所有元素

     zcard(key):返回名称为key的zset的基数

     zscore(key, element):返回名称为key的zset中元素element的score

     zremrangebyrank(key, min, max):删除名称为key的zset中rank >= min且rank <= max的所有元素

     zremrangebyscore(key, min, max) :删除名称为key的zset中score >= min且score <= max的所有元素

     zunionstore / zinterstore(dstkeyN, key1,…,keyN, WEIGHTS w1,…wN, AGGREGATE SUM|MIN|MAX):对N个zset求并集和交集,并将最后的集合保存在dstkeyN中。对于集合中每一个元素的score,在进行AGGREGATE运算前,都要乘以对于的WEIGHT参数。如果没有提供WEIGHT,默认为1。默认的AGGREGATE是SUM,即结果集合中元素的score是所有集合对应元素进行 SUM运算的值,而MIN和MAX是指,结果集合中元素的score是所有集合对应元素中最小值和最大值。

6.对Hash操作的命令

     hset(key, field, value):向名称为key的hash中添加元素field<—>value

     hget(key, field):返回名称为key的hash中field对应的value

     hmget(key, field1, …,field N):返回名称为key的hash中field i对应的value

     hmset(key, field1, value1,…,field N, value N):向名称为key的hash中添加元素field i<—>value i

     hincrby(key, field, integer):将名称为key的hash中field的value增加integer

     hexists(key, field):名称为key的hash中是否存在键为field的域

     hdel(key, field):删除名称为key的hash中键为field的域

     hlen(key):返回名称为key的hash中元素个数

     hkeys(key):返回名称为key的hash中所有键

     hvals(key):返回名称为key的hash中所有键对应的value

     hgetall(key):返回名称为key的hash中所有的键(field)及其对应的value
  1. jedis redisCluster模式的访问

    • 支持自动根据key分散。
/**
 *
 * 参考文档:http://blog.csdn.net/liubenlong007/article/details/53766734
 * @author andrexu
 * @Description
 * @email xuhc@kerunyun.com
 * @created 下午2:16 2018/3/1
 */
public class RedisClusterUtilsTest {
    private static Jedis node1;
    private static Jedis node2;
    private static Jedis node3;
    private static Jedis node4;
    private static Jedis nodeSlave2;
    JedisCluster jedisCluster = null;

    private String nameKey = "andrexu";

    @Before
    public void before() {

        String[] serverArray = "172.16.30.37:7001,172.16.30.37:7002,172.16.30.37:7003,172.16.30.37:7004,172.16.30.37:7005,172.16.30.37:7006".split(",");
        Set<HostAndPort> nodes = new HashSet<>();

        for (String ipPort : serverArray) {
            String[] ipPortPair = ipPort.split(":");
            nodes.add(new HostAndPort(ipPortPair[0].trim(), Integer.valueOf(ipPortPair[1].trim())));
        }
        jedisCluster = new JedisCluster(nodes, 1000, 1000, 1, "123456", new GenericObjectPoolConfig());

    }

    /**
     * 简单字符串读写
     */
    @Test
    public void setStringData() {

        System.out.println(jedisCluster.set(nameKey, "张三"));
        System.out.println(jedisCluster.get(nameKey));
    }


    @Test
    public void showNodesTest() {

        Map<String, JedisPool>  nodes= jedisCluster.getClusterNodes();
        System.out.println(nodes);
    }

    /**
     * setnx : 如果key存在,返回0,如果不存在,则设置成功。
     * setnx的意思是set if not exist.
     */
    @Test
    public void setnxTest() {
        System.out.println(jedisCluster.setnx(nameKey, "张三"));//key不存在,返回值为1
        System.out.println(jedisCluster.get(nameKey));

        System.out.println(jedisCluster.setnx(nameKey, "张三"));//已经存在,返回值为0
        System.out.println(jedisCluster.get(nameKey));
    }



    /**
     * 批量操作key
     * keySlot算法中,如果key包含{},就会使用第一个{}内部的字符串作为hash key,这样就可以保证拥有同样{}内部字符串的key就会拥有相同slot。
     * 参考  http://brandnewuser.iteye.com/blog/2314280
     * redis.clients.util.JedisClusterCRC16#getSlot(java.lang.String)
     *
     * 注意:这样的话,本来可以hash到不同的slot中的数据都放到了同一个slot中,所以使用的时候要注意数据不要太多导致一个slot数据量过大,数据分布不均匀!
     *
     * MSET 是一个原子性(atomic)操作,所有给定 key 都会在同一时间内被设置,某些给定 key 被更新而另一些给定 key 没有改变的情况,不可能发生。
     */
    @Test
    public void msetTest() throws InterruptedException {
        /**
         * jedisCluster.mset("sf","d","aadf","as");
         * 直接这样写,会报错:redis.clients.jedis.exceptions.JedisClusterException: No way to dispatch this command to Redis Cluster because keys have different slots.
         * 这是因为key不在同一个slot中
         */

        String prefix="andrexu";
        String KEY_SPLIT=":";

        String result = jedisCluster.mset("{" + prefix + KEY_SPLIT + "}" + "name", "张三", "{" + prefix + KEY_SPLIT + "}" + "age", "23", "{" + prefix + KEY_SPLIT + "}" + "address", "adfsa", "{" + prefix + KEY_SPLIT + "}" + "score", "100");
        System.out.println(result);

        String name = jedisCluster.get("{" + prefix + KEY_SPLIT + "}" + "name");
        System.out.println(name);

        Long del = jedisCluster.del("{" + prefix + KEY_SPLIT + "}" + "age");
        System.out.println(del);

        List<String> values = jedisCluster.mget("{" + prefix + KEY_SPLIT + "}" + "name", "{" + prefix + KEY_SPLIT + "}" + "age", "{" + prefix + KEY_SPLIT + "}" + "address");
        System.out.println(values);
    }


    @Test
    public void shardExpTest() {
        JedisCluster jedis = jedisCluster;

        for (int i = 0; i < 10; i++) {
            jedis.set("test" + i, "33333:" + i);
        }

        jedis.set("a", "bar");
        String ak = jedis.get("testl1");
        assertEquals("33333:1", ak);
        jedis.set("b", "bar1");
        String bk = jedis.get("testl2");
        assertEquals("33333:2", bk);

    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值