Redis的十大数据类型简介和场景应用举例(两万字)

一、前言

    最近,博主自己去学习了一下Redis的相关技术,自己看的是b站up主:【狂神说Java】的视频。个人觉得狂神讲的很好,对于redis入门或者进阶有很大帮助。这里博主就将自己所学的内容以博客的形式进行记录。在笔记中有很多博主自己的思考和代码的编写。由于Redis的内容是十分多的,所以博主会分成几个部分来讲解。这部分就主要讲redis的数据类型,对于以前的redis有 五大基本数据类型(String、Set、Zset、Hash、List)以及三大特殊类型(BitMap、Geospatial、Hyperloglog)。但是随着技术的发展,最新的redis又增加了Stream流、BitField位域两大数据类型。这里博主就将这十大数据类型整合在一起,做一个简单的基本指令介绍。希望对各位小伙伴有所帮助。

二、Redis技术

    Redis技术,英文全称是Remote Dictionary Server(远程字典服务)。是一种NoSQL技术,这是一种基于内存的数据库,并且提供一定的持久化功能。首先在我们一般的系统中不会出现高并发的情况,所以只用MySQL这样的数据库是完全可以的。但是,一旦接入大规模的数据访问,如:一些节假日时,某些平台的商品抢购活动,会导致一瞬间有大量的访问数据的到来,如果这些访问直接访问我们的MySQL数据库,那就很有可能导致我们数据库瘫痪、甚至服务器宕机等严重失误。所以说不管是技术的了解还是真实业务的需要Redis这类缓存技术的学习都是必不可少的。

三、Jedis技术

   对于redis的学习,其实有多种方式,现在市面上大多数都是采用在Linux上执行redis相关命令。但是博主个人觉得要真正学习这门技术,还得是要在redis的客户端执行这些指令,并能集成在我们自己的项目之中。于是Jedis就成了我的首选。它是官方推荐的用于连接Java的开发工具,是用Java操作Redis的中间键。这里博主就采用Jedis来实现我们十大数据类型的基本指令。

导入相关Jar包



        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.6.3</version>
        </dependency>


    <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.7</version>
        </dependency>

四、Redis相关数据类型介绍

1、Redis中关于key的基础命令

这里是一些基础指令,相关指令都有批注,这些指令在Linux操作系统上同样可执行。

public class TestPing {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        System.out.println(jedis.ping());
        System.out.println("-------- 基本的key的操作 ------");
        System.out.println("清楚数据"+jedis.flushDB());
        System.out.println("判断某个值是否存在"+jedis.exists("name"));
        System.out.println("新增<'name','lfy'>键值对"+jedis.set("name","lfy"));
        System.out.println("新增<'age','18'>键值对"+jedis.set("age","18"));
        System.out.println("系统中的所有键:");
        Set<String> keys = jedis.keys("*");
        System.out.println(keys);
        System.out.println("----------------");
        System.out.println("删除键age:"+jedis.del("age"));
        System.out.println("判断键age是否存在:"+jedis.exists("age"));
        System.out.println("查看name键的属性的类型"+jedis.type("name"));
        System.out.println("随机返回一个key的空间"+jedis.randomKey());
        System.out.println("重命名key"+jedis.rename("name","rename"));
        System.out.println("-----------");
        System.out.println("取出修改了的name的名字"+jedis.get("name"));
        System.out.println("取出修改了的name的名字"+jedis.get("rename"));
        System.out.println("按照索引查询:"+jedis.select(0));
        System.out.println("------------");
        System.out.println("删除当前数据库中所有的key:"+jedis.flushDB());
        System.out.println("返回当前数据库库中key的数目:"+jedis.dbSize());
        System.out.println("删除所有数据库中的的key:"+jedis.flushAll());
    }
}

3ef562ad97a256dd72aad95f7b893d7e.png

2、String(字符串)类型

①基本指令代码

public class TestString {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);


        jedis.flushDB();
        System.out.println("---------添加数据----------");
        System.out.println(jedis.set("key1", "id1"));
        System.out.println(jedis.set("key2", "id2"));
        System.out.println(jedis.set("key3", "id3"));
        User user = new User();
        user.setName("小明");
        user.setAge(18);
        System.out.println("添加User对象:"+jedis.set("user", JSON.toJSONString(user)));
        System.out.println("获取User对象:"+jedis.get("user"));
        System.out.println(jedis.keys("*"));
        System.out.println("删除key2"+jedis.del("key2"));
        System.out.println("获取key2"+jedis.get("key2"));
        System.out.println("修改key1的值"+jedis.set("key1","123"));
        System.out.println("获得key1的值"+jedis.get("key1"));
        System.out.println("在key3的值后面加值:"+jedis.append("key3","456"));
        System.out.println("获得key3的值:"+jedis.get("key3"));
        System.out.println("添加多个键值对:"+jedis.mset("key01","value01","key02","value02"));
        System.out.println("获得多个键值对:"+jedis.mget("key01","key02"));
        System.out.println("删除多个键值对:"+jedis.del("key1","key01"));

        System.out.println("----------新增键值对覆盖原来的值----------");
        jedis.flushDB();
        //如果key1不存在就创建key1并,设置值为666,如果存在就覆盖原来的值
        System.out.println(jedis.setnx("key1","666"));
        System.out.println("获得key1的值:"+jedis.get("key1"));

        System.out.println("-----------新增键值对并设置有效时间-----------");
        //判断过期时间,如果60秒内不存在就过期
        System.out.println(jedis.setex("key1",60,"888"));
        System.out.println(jedis.get("key1"));

        System.out.println("---------获取原来的值,更新为新的值----------");
        //先get再set,获取完值后再设置
        System.out.println(jedis.getSet("key2","9090asfgfhgfh"));
        System.out.println(jedis.get("key2"));
        System.out.println("获取key2的值的字串:"+jedis.getrange("key2",2,5));

    }
}

9024b49f8471fa072d14f4edac16b094.png

②应用场景举例

a.数据缓存

我们可以用String类型将数据存入缓存,来提高读取速度,减少后端数据库的压力。

b.计数器

我们在一些文章里的浏览量和一些网站的粉丝数、点赞数、评论数、播放数等统计多单位的数量。

c、对象存储

我们可以用redis存储json对象(上面代码有),用于一些复杂的商品、用户信息的存储。

d、验证码

在String类型中可以设置多少秒内,数据过期的功能。这个和我们验证码的功能刚好可以结合起来,产生 了验证码我们先存在redis中,设置时间,并在规定时间中对用户填入的验证码进行校验。后面博主会做一期关于阿里云短信验证的作品,到时候可以将redis集合在其中,大家持续关注哦。

e、分布式锁

String类型中的setnx:不存在再设置。设置成功返回值为1,设置失败返回值为0。判断key是否存在和是否设置value是两个原子性操作,这个在分布式锁中常常应用。

3、List(列表)类型

①:基本指令代码

public class TestList {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();

        System.out.println("--------添加一个list--------");
        jedis.lpush("collections","ArrayList","Vector","HashMap","WeakHashMap");
        jedis.lpush("collections","HashSet");
        jedis.lpush("collections","TreeSet");
        jedis.lpush("collections","TreeMap");
        //-1代表查询所有
        System.out.println("获取collections中的内容:"+jedis.lrange("collections",0,-1));
        jedis.rpush("right","1","2","3");
        jedis.rpush("right","4");
        System.out.println("获取right中的内容:"+jedis.lrange("right",0,-1));
        //注意 lpush与rpush的区别分别表示从队列(将list看作一个队列)的左边进和右边进
        //而lrange()表示从左边出。
        System.out.println("collections区间0~3的内容:"+jedis.lrange("collections",0,3));

        System.out.println("---------删除列表中指定的值--------");
        //删除列表指定个数,第二个数为删除的个数(有重复时),后add进入的值先被珊,类似于出栈操作。
        System.out.println("删除指定的元素:"+jedis.lrem("collections",2,"HashMap"));
        System.out.println("获取collections中的内容:"+jedis.lrange("collections",0,-1));
        System.out.println("删除区间0~3之外的个数:"+jedis.ltrim("collections",0,3));
        System.out.println("获取collections中的内容:"+jedis.lrange("collections",0,-1));
        System.out.println("collections列表左端出栈(lpop)"+jedis.lpop("collections"));
        System.out.println("获取collections中的内容:"+jedis.lrange("collections",0,-1));
        System.out.println("collections列表右端出栈(rpop)"+jedis.rpop("collections"));

        System.out.println("---------collections的长度-----------");
        System.out.println("collections的长度为:"+jedis.llen("collections"));
        System.out.println("获取下标为2的元素:"+jedis.lindex("collections",2));

        System.out.println("---------排序sort---------");
        jedis.lpush("sortList","2","3","8","1","9");
        System.out.println("sortList排序前:"+jedis.lrange("sortList",0,-1));
        System.out.println(jedis.sort("sortList"));
        System.out.println("sortList排序后:"+jedis.lrange("sortList",0,-1));

    }

}

f7614ce6eb27ae7545d7a6b45b79f8a8.png②:应用场景举例

a.消息队列

如果我们对List使用Lpush、Rpop就是队列的结构,使用Lpush、Lpop就是栈的结构。其实就是和RabbitMQ实现的功能差不多。等下后面讲Stream数据类型时,就会发现和Stream的工作原理和List是一样的,只是Redis官方特意出了一个Stream类型,专门用于消息的传输。

b.消息排队

比如我们在QQ、或者微信要显示别人给你发的动态的点赞顺序,依次显示,像这种有顺序的业务我们就可以通过List来实现。

4、Set(集合)类型

①:基本指令代码

public class TestSet {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        System.out.println("----------向集合中添加元素(不重复)----------");
        System.out.println(jedis.sadd("eleSet","e1","e2","e3","e4","e5"));
        System.out.println(jedis.sadd("eleSet","e6"));
        System.out.println(jedis.sadd("eleSet","e6"));
        System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
        //删除多个元素依次类推
        System.out.println("删除一个元素e1"+jedis.srem("eleSet","e1"));
        System.out.println("eleSet的所有元素为:"+jedis.smembers("eleSet"));
        System.out.println("随机移除集合中的一个元素:"+jedis.spop("eleSet"));
        System.out.println("随机移除集合中的一个元素:"+jedis.spop("eleSet"));
        System.out.println("eleSet中包含的元素的个数:"+jedis.scard("eleSet"));
        System.out.println("判断e3是否在eleSet中:"+jedis.sismember("eleSet","e3"));
        System.out.println("-----------------------");
        System.out.println(jedis.sadd("eleSet1","e1","e2","e3","e4","e5","e6","e7"));
        System.out.println(jedis.sadd("eleSet2","e1","e2","e3","e4","e5"));
        System.out.println("将elsSet1中删除e1并存入eleSet3中:"+jedis.smove("eleSet1","eleSet3","e1"));
        System.out.println("将elsSet2中删除e2并存入eleSet3中:"+jedis.smove("eleSet1","eleSet3","e2"));
        System.out.println("eleSet中的元素:"+jedis.smembers("eleSet3"));
        System.out.println("-----------集合运算-----------");
        System.out.println("elsSet1和eleSet2的交集:"+jedis.sinter("eleSet1","eleSet2"));
        System.out.println("elsSet1和eleSet2的并集:"+jedis.sunion("eleSet1","eleSet2"));
        System.out.println("elsSet1和eleSet2的差集:"+jedis.sdiff("eleSet1","eleSet2"));
        //求交集并将交集保存到dstkey的集合
        jedis.sinterstore("eleSet4","eleSet1","eleSet2");
        System.out.println("eleSet4中的元素:"+jedis.smembers("eleSet4"));
    }
}

164bd950c14afddf8d6208c0e923b496.png

②:应用场景举例

a.共同关注、共同爱好、推荐好友

采用set中的并集实现。

b.避免键名冲突

在设置键名时,应避免使用过于简单或易于冲突的名称。最好使用具有唯一标识符的键名,如前缀加随机数、时间戳等,以确保不会发生键名冲突的情况。

5、Zset(有序集合)类型

①:基本指令代码

public class TestZset {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        //注意Zset与Set只是多了一个计数位
        //添加一个值
        jedis.zadd("MySet",1,"一");
        jedis.zadd("MySet",2,"二");
        jedis.zadd("MySet",4,"三");
        jedis.zadd("MySet",3,"五");
        jedis.zadd("MySet",6,"四");
        System.out.println("获取MySet中的值:"+jedis.zrange("MySet",0,-1));
        //使用zadd(String key,Map<String,Double>)添加多个数据
        HashMap<String, Double> map = new HashMap();
        map.put("小明",123.00);
        map.put("小刚",456.00);
        map.put("小李",789.00);
        jedis.zadd("Salary",map);
        Set<String> sets = jedis.zrangeByScore("Salary", "-inf", "+inf");
        Iterator<String> iterator = sets.iterator();
        while (iterator.hasNext()){
            System.out.println("排序小到大:"+iterator.next());
        }
        //查询456之内的人
        Set<String> salary = jedis.zrangeByScore("Salary", "-inf", "457");
        Iterator<String> iterator1 = salary.iterator();
        while (iterator1.hasNext()){
            System.out.println("查询工资在456之内的人:"+iterator1.next());
        }

        //从大到小排序
        System.out.println(jedis.zrevrange("Salary", 0, -1));

        //用zcount用户获取指定区间的成员数量
        long mySet = jedis.zcount("MySet", 1, 5);
        System.out.println(mySet);
    }
}

0c65be8d6a25ac137a201ec1cc5712fb.png

②:应用场景举例

a.有关排序的业务

如存储班级成绩表、部门工资表、排行榜应用、取TOP N测试等。

b.代权重执行

如普通消息与重要消息分别带上权重执行。

6、Hash(哈希)类型

①:基本指令代码

public class TestHash {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        HashMap<String,String> map = new HashMap<>();
        map.put("key1","value1");
        map.put("key2","value2");
        map.put("key3","value3");
        map.put("key4","value4");
        //添加名称为hash(key)的hash元素
        jedis.hmset("hash",map);
        //向名称为hash中添加key为key5,value为value5的元素
        jedis.hset("hash","key5","value5");
        System.out.println("散列hash的所有键值对为:"+jedis.hgetAll("hash"));
        System.out.println("散列hash的所有键为:"+jedis.hkeys("hash"));
        System.out.println("散列hash的所有值为:"+jedis.hvals("hash"));
        System.out.println("删除一个或者多个键值对:"+jedis.hdel("hash","key1","key2"));
        System.out.println("散列表中的键值对的个数:"+jedis.hlen("hash"));
        System.out.println("判断hash中key1是否存在:"+jedis.hexists("hash","key1"));
        System.out.println("判断hash中key3是否存在:"+jedis.hexists("hash","key3"));
        System.out.println("获取hash中的值:"+jedis.hmget("hash","key3"));
        System.out.println("获取hash中的值:"+jedis.hmget("hash","key3","key2","key4"));
    }
}

59d39e6c9ba43f7024df8ddf5f3d40ac.png

②:应用场景举例

a.存储对象

b.购物车

如购物车中商品的信息存储,数量的加减(incrby),商品的单选、多选、全选(hmget(),hgetall())等。

7、Geospatial(地理位置)类型

①:基本指令代码

public class TestGeospatial {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        System.out.println("添加单个城市的地理位置:"+jedis.geoadd("city",104.0489567,30.39456,"chengdu"));
        HashMap<String, GeoCoordinate> map = new HashMap();
        //添加多个城市地址位置
        map.put("pengzhou",new GeoCoordinate(103.56789,30.58623));
        map.put("qionglai",new GeoCoordinate(103.27389,30.24856));
        map.put("guizhou",new GeoCoordinate(106.42763,26.34561));
        map.put("kunming",new GeoCoordinate(102.42963,25.02963));
        System.out.println("添加多个城市地址位置:"+jedis.geoadd("city",map));
        System.out.println("获取单个地址地理位置:"+jedis.geopos("city","chengdu"));
        System.out.println("获取多个地址地理位置:"+jedis.geopos("city","chengdu","guizhou"));
        System.out.println("计算两个城市之间的距离:"+jedis.geodist("city","chengdu","guizhou"));

        System.out.println("-------------------------------");
        System.out.println("以成都为中心 查询直线距离500km的城市:"+jedis.georadius("city",104.04,30.39,500, GeoUnit.KM));
        System.out.println("找出指定元素类的其他元素:"+jedis.georadiusByMember("city","pengzhou",200, GeoUnit.KM));

        System.out.println("--------------------------------");
        System.out.println("获取全部元素:"+jedis.zrange("city",0,-1));
        System.out.println("删除指定元素:"+jedis.zrem("city","kunming"));
        System.out.println("获取全部元素:"+jedis.zrange("city",0,-1));
        jedis.flushDB();
    }
}

ed93e878881413840e4062b9ac837357.png

②:应用场景举例

a.附近的人

现在很多APP的聊天软件都有“附近的人”、“找朋友”等功能,都可以通过geospatial来 实现。

b.打车定位

现在的打车软件的记录出租车坐标位置、查询附近的出租车等功能也都可以通过geospatial相关指令来实现。

8、Hyperloglog(基数统计)类型

①:基本指令代码

基数:不重复的元素

如:A{1,3,5,7,8,7},B{1,3,5,7},基数=5.

public class TestHyperloglog {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        System.out.println("创建第一组元素:"+jedis.pfadd("log_one","a","b","c"));
        System.out.println("统计log_one的元素个数:"+jedis.pfcount("log_one"));
        System.out.println("创建第二组元素 :  "+jedis.pfadd("log_two", "c", "d", "e", "f"));
        System.out.println("统计第二组元素数量  :  "+jedis.pfcount("log_two"));
        System.out.println("合并第一组和第二组数据  :  "+jedis.pfmerge("log_three", "log_one", "log_two"));
        System.out.println("合并后log_three的元素数量  :  "+jedis.pfcount("log_three"));

    }
}

5e2799bd242d04c2513ea1c7dd60cb74.png

②:应用场景举例

a.统计网页的UV(Unique Visitors 独立访客)

当一个人访问网站多次时,还是算作一个人。如果用set存储大量id,会比较麻烦,因为只是做一个统计,如果用Hyperloglog,占用的内存固定,2的64次方的不同元素,只需要12kb的内存,更有优势。(Hyperloglog有0.81%错误率,可以忽略)

9、BitMap(位图)类型

①:基本指令代码

BitMap是位存储方式(只有0和1两个状态)。

public class TestBitMap {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        System.out.println("举例:  user为用户   1,2,3,4,5,6,7 代表周一到周末  false-未打卡  true-打卡");
        System.out.println("  周一  :  "+jedis.setbit("user", 1, true));
        System.out.println("  周二  :  "+jedis.setbit("user", 2, true));
        System.out.println("  周三  :  "+jedis.setbit("user", 3, true));
        System.out.println("  周四  :  "+jedis.setbit("user", 4, true));
        System.out.println("  周五  :  "+jedis.setbit("user", 5, true));
        System.out.println("  周六  :  "+jedis.setbit("user", 6, false));
        System.out.println("  周日  :  "+jedis.setbit("user", 7, false));
        System.out.println("查看某一天是否有打卡");
        System.out.println("  查看周三  :  "+jedis.getbit("user", 3));
        System.out.println("  查看周六  :  "+jedis.getbit("user", 6));
        System.out.println("统计打卡的天数");
        System.out.println(+jedis.bitcount("user"));
        jedis.flushDB();
    }
}

b7f8b18a301117409da48b5fb6eb71e4.png

②:应用场景举例

a.快速筛选用户

b.统计用户信息

可以通过BitMap来统计活跃与不活跃、登录与不登陆等只有两个状态的情况。

c.上班打卡情况

10、Stream(流)类型

①:简单介绍

   由于Stream这种数据类型是Redis在5.0中新增加的数据类型,所以先做一个介绍。Stream是Redis推出的一种专门用来处理消息队列场景的高级数据结构。(消息队列就是队列,有着先进先出的特点,可以让我们的代码有序执行,避免出现混乱的情况)其实上面讲的List数据类型就具备了支持MQ对应的处理模型。但还出一个Stream类型,除了Redis公司为了专门做一个消息队列来抢占其他市场上的消息中间键(kafka,RabbitMQ)的市场份额以为,还完善了之前List做消息队列时的一些不足之处。

②:Stream的优点

    相比与List类型的消息队列,List类型只能处理点对点的模式,但是对于Redis的发布者模式(类似于我们关注的公众号向我们发送消息的模式)有一个缺点就是消息无法持久化,如果网络断开、Redis宕机时,数据就会丢失。而且没有ack机制来确保数据的可靠性等问题。而Stream都成功解决了这个问题,在后面的代码中会有演示。

e73bc2610fef0261e3ad280e03c108cb.png

③:Stream类型包含的功能和指令

  这里分为生产者和消费者两部分来介绍。

a.生产者

   对于生产者来说通过xadd()指令生产消息放入队列中,且每个消息都有一个消息ID,由毫秒时间戳和序列号组成。当消息生成后可以通过xrange()获取消息队列(可指定范围)或者xrevrange()反向获取,消息ID从大到小。除此之外还有,xdel()删除消息,xlen()获取Stream消息中的长度,xtrim()限制Stream的长度,如果已经超长会进行截取。对于xread()消息的读取分为阻塞读和非堵塞读两种情况获取消息列表。

b.消费者

   在消费者中有xgroup()创建消费组、xreadgroup()、xack()、xpengding()等等指令,都会在代码中进行展示。

c.四个特殊符号

- +:最小和最大可能出现的id;

$:表示消费者的最新消息;

*:用于xadd()指令中,让系统自动生成消息ID;

>:用于xreadgroup()指令,表示迄今还没有发送给使用者的消息。

④:基本指令代码(该代码中有上述所有指令的相应批注和解释)

public class TestStream {
    public static void main(String[] args) {
        //redis-stream 就是redis版本的MQ,消息中间键
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        System.out.println("-----------添加消息到队列的末尾------------");
        HashMap<String, String> map = new HashMap();
        HashMap<String, String> map2 = new HashMap();
        HashMap<String, String> map3 = new HashMap();
        HashMap<String, String> map4 = new HashMap();
        map.put("name","李四");
        map.put("name2","张三");
        map.put("name3","王五");
        map2.put("id","1");
        map2.put("id2","2");
        map2.put("id3","3");
        map3.put("age","18");
        map3.put("age2","34");
        map3.put("age3","12");
        System.out.println(jedis.ping());
        StreamEntryID myStream1 = jedis.xadd("MyStream", StreamEntryID.NEW_ENTRY, map);
        StreamEntryID myStream2 = jedis.xadd("MyStream", StreamEntryID.NEW_ENTRY, map2);
        StreamEntryID myStream3 = jedis.xadd("MyStream", StreamEntryID.NEW_ENTRY, map3);
        System.out.println("生成第一条信息ID:"+myStream1);
        System.out.println("生成第二条信息ID:"+myStream2);
        System.out.println("生成第三条信息ID:"+myStream3);
        //System.out.println("输出MyStream中的值:"+jedis.xrange("MyStream",));
        List<StreamEntry> myStream = jedis.xrange("MyStream", myStream1, myStream3,3);
        for (StreamEntry streamEntry : myStream) {
            System.out.println(streamEntry.toString());
        }
        //反转
        System.out.println("--------反转----------");
        List<StreamEntry> myStream4 = jedis.xrevrange("MyStream", myStream3, myStream1, 3);
        for (StreamEntry streamEntry : myStream4) {
            System.out.println(streamEntry.toString());
        }
        System.out.println("删除一个myStream1:"+jedis.xdel("MyStream",myStream1));
        map4.put("sex","男");
        //再添加一条信息
        StreamEntryID myStream5 = jedis.xadd("MyStream", StreamEntryID.NEW_ENTRY, map4);
//        long len = jedis.xtrim("MyStream", 2, false);
//        System.out.println("截取MyStream:"+len);
        System.out.println("求MyStream的长度:"+jedis.xlen("MyStream"));
        System.out.println("---------读消息--------");
        //在读消息时,有多种读法,我这里是通过设定读取的条数和从那条消息以后开始读来读,通过查看xread()这个方法也可以知道有多种读法
        HashMap<String, StreamEntryID>Entry = new HashMap<>();
        Entry.put("MyStream",myStream2);
        //在myStream2以后读取一条信息
        List<Map.Entry<String, List<StreamEntry>>> xread = jedis.xread(XReadParams.xReadParams().count(1), Entry);
        Map.Entry<String, List<StreamEntry>> stringListEntry = xread.get(0);
        List<StreamEntry> value = stringListEntry.getValue();
        for (StreamEntry streamEntry : value) {
            System.out.println(streamEntry);
        }
        //消费者分组读的目的,让组内的多个消费者共同分担读取信息,每个消费者读取部分消息,从而实现读取负载在多个消费者间是均衡分布的。
        System.out.println("--------消费者分组读取收据----------");
        //创建groupA、groupB两个分组
        String first = jedis.xgroupCreate("MyStream", "groupA", myStream1, true);
        String second = jedis.xgroupCreate("MyStream", "groupB", myStream1, true);
        HashMap<String, StreamEntryID> entry = new HashMap<>();
        entry.put("MyStream",StreamEntryID.UNRECEIVED_ENTRY);
        System.out.println("------------   测试groupA   ---------");
        //同一个组中consumer1读取的消息,consumer2、consumer3....不可再读,不同组之间成员可再读
        //如下面例子所示,现在MyStream中还有3条消息,consumer1读了2条,consumer2读了1条,则consumer3读到的数据为空
        System.out.println("------groupA中consumer1读两条条数据---------");
        List<Map.Entry<String, List<StreamEntry>>> entries = jedis.xreadGroup("groupA", "consumer1", XReadGroupParams.xReadGroupParams().count(2), entry);
        Map.Entry<String, List<StreamEntry>> stringListEntry1 = entries.get(0);
        List<StreamEntry> value1 = stringListEntry1.getValue();
        for (StreamEntry streamEntry : value1) {
            System.out.println(streamEntry);
        }
        System.out.println("------groupA中consumer2读第一条数据---------");
        List<Map.Entry<String, List<StreamEntry>>> entries1 = jedis.xreadGroup("groupA", "consumer2", XReadGroupParams.xReadGroupParams().count(1), entry);
        Map.Entry<String, List<StreamEntry>> stringListEntry2 = entries1.get(0);
        List<StreamEntry> value2 = stringListEntry2.getValue();
        for (StreamEntry streamEntry : value2) {
            if(streamEntry==null){
                System.out.println("值为空");
            }else{
                System.out.println(streamEntry);
            }
        }
        System.out.println("------groupA中consumer3读第三条数据---------");
        List<Map.Entry<String, List<StreamEntry>>> entries2 = jedis.xreadGroup("groupA", "consumer3", XReadGroupParams.xReadGroupParams(), entry);
        if(entries2==null){
            System.out.println("数组值为空");
        }else{
            System.out.println("值不为空");
        }
        System.out.println("查看每个消费组内所有的消费者的消息(已读、未确认的)");
        StreamPendingSummary xpending = jedis.xpending("MyStream", "groupA");
        System.out.println("总数:"+xpending.getTotal()+" MinID: "+xpending.getMinId()+" MaxID: "+xpending.getMaxId()+" 消息: "+xpending.getConsumerMessageCount());
        System.out.println("-----ack命令已经读取,已确认-----");
        System.out.println("查看consumer1读了那几条数据,以streamEntryID为标识");
        List<StreamPendingEntry> xpending1 = jedis.xpending("MyStream", "groupA", myStream1, myStream5, 10, "consumer1");
        for (StreamPendingEntry streamPendingEntry : xpending1) {
            System.out.println(streamPendingEntry.toString());
        }
        System.out.println("将myStream2进行ack提交:"+jedis.xack("MyStream", "groupA", myStream2));
        System.out.println("---------groupA中consumer1提交ack后的查看xpending中的数据------------");
        StreamPendingSummary xpending2 = jedis.xpending("MyStream", "groupA");
        //在groupA中consumer1提交ack后的查看xpending中的数据时,我们会发现消息总数会减一,因为当发出ack()命令后,相当于通知Stream消息已经处理完成,否则消息不会被清楚会备案。
        System.out.println("总数:"+xpending2.getTotal()+" MinID: "+xpending2.getMinId()+" MaxID: "+xpending2.getMaxId()+" 消息: "+xpending2.getConsumerMessageCount());

        System.out.println("---------XINFO打印Stream/Consumer/Group详细信息---------");
        System.out.println("打印Consumer信息:");
        List<StreamConsumersInfo> streamConsumersInfos = jedis.xinfoConsumers("MyStream", "groupA");
        for (StreamConsumersInfo streamConsumersInfo : streamConsumersInfos) {
            System.out.println(streamConsumersInfo.getConsumerInfo());
        }
        System.out.println("打印Stream信息:");
        StreamInfo myStream6 = jedis.xinfoStream("MyStream");
        System.out.println(myStream6.getStreamInfo());
        System.out.println("打印Group信息:");
        List<StreamGroupInfo> myStream7 = jedis.xinfoGroup("MyStream");
        for (StreamGroupInfo streamGroupInfo : myStream7) {
            System.out.println(streamGroupInfo.getGroupInfo());
        }

    }
}

dc4f1cc39f9547f60dbe31991abe455f.png

⑤:应用场景举例

a.消息队列

可以用于减少响应时间,降低系统的耦合性,用于一些秒杀和促销活动。

11、BitField(位域)类型

BitField位域可以将Redis字符串看作一个由二进制组成的数组,并对这个数组中任意偏移量进行访问。主要作用位域修改(get,set指令)、溢出控制(wrap、sat、fall指令)。该数据类型了解即可。

①:基本指令代码

public class TestBitField {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        jedis.flushDB();
        System.out.println("---------BitField中的Get指令----------");
        jedis.set("fieldkey","hello");
        //i表示有符号,u表示无符号
        List<Long> bitfield = jedis.bitfield("fieldkey", "get", "i8", "0");
        List<Long> bitfield2 = jedis.bitfield("fieldkey", "get", "i8", "8");
        List<Long> bitfield3 = jedis.bitfield("fieldkey", "get", "i8", "16");
        List<Long> bitfield4 = jedis.bitfield("fieldkey", "get", "i8", "24");
        List<Long> bitfield5 = jedis.bitfield("fieldkey", "get", "i8", "32");
        for (Long aLong : bitfield) {
            System.out.println(aLong.byteValue());
        }
        for (Long aLong : bitfield2) {
            System.out.println(aLong.byteValue());
        }
        for (Long aLong : bitfield3) {
            System.out.println(aLong.byteValue());
        }
        for (Long aLong : bitfield4) {
            System.out.println(aLong.byteValue());
        }
        for (Long aLong : bitfield5) {
            System.out.println(aLong.byteValue());
        }
        System.out.println("---------BitField中的Set指令----------");
        System.out.println("将hello中的e改成x:");
        //从第九位开始,将接下来8个位用有字符号数120(字母x)替换
        List<Long> bitfield6 = jedis.bitfield("fieldkey", "set", "i8", "8", "120");
        System.out.println(jedis.get("fieldkey"));
        //不停的加
        System.out.println("---------BitField中的Incrby指令----------");
        //从第三位开始,对接下来的4位无符号数+1
        List<Long> bitfield7 = jedis.bitfield("fieldkey", "incrby", "u4", "2", "1");
        for (Long aLong : bitfield7) {
            System.out.println(aLong.byteValue());
        }
        jedis.bitfield("fieldkey", "incrby", "u4", "2", "1");
        jedis.bitfield("fieldkey", "incrby", "u4", "2", "1");
        jedis.bitfield("fieldkey", "incrby", "u4", "2", "1");
        jedis.bitfield("fieldkey", "incrby", "u4", "2", "1");
        List<Long> bitfield8 = jedis.bitfield("fieldkey", "incrby", "u4", "2", "1");
        //不停的加,产出数值。这时默认情况下,incrby使用wrap参数
        //wrap采用回绕方法处理有符号和无符号整数溢出情况
        System.out.println("-------incrby使用wrap参数----");
        for (Long aLong : bitfield8) {
            System.out.println(aLong.byteValue());
        }

    }
}

ee2977fe29d98da5dc066ffac19a1457.png

②:应用场景举例

a.位域修改和溢出控制

五、总结

    到这儿也就是结束了关于这十大数据类型的介绍。那也做个总结吧,其实从刚开始学Redis到写完这篇文章是花了很多时间的,并不是看一遍就能写的。大多数小伙伴也知道现在市面上关于Redis的讲解大都是在Linux系统上操作指令的,但是我们真实的运用肯定是用的Java代码。所以博主也是看了很多视频、官网资料才写完这部分关于Redis的数据类型的介绍。如果这篇文章对各位小伙伴有帮助,莫忘了点赞、收藏哦!

 

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值