Redis入门最基础贴-Reids各种数据结构及原生命令/Java代码演示

本文深入探讨Redis的六种核心数据结构:String、List、Set、SortedSet、Hash和Stream,以及GEO地理位置功能。详细讲解每种数据类型的使用场景、常用命令,并提供Java代码示例,帮助读者全面掌握Redis的高级应用。

Redis介绍

Redis是一个开源的使用C语言编写、支持万络、可基于内存亦可持久化的日志型、key-value数据据库,并提供了多种语言支持的API

Redis数据类型

Redis并不是简单的key-value存储,实际上他是一个数据结构服务器,支持不同类型的值

Key规则

1、key的长度不宜太长		//会增加内存的消耗,也会增加数据查询时候的计算成本
2、key的长度不宜过短		//可读性较差
3、通用的命名规则		//最好设计通用的命名规则,达到见名知意

Key的自动创建和删除

1、当向一个聚合数据类型中添加元素时,如果目标键不存在,就在添加元素前创建空的聚合数据类型。
> del mylist
(integer) 1
> lpush mylist 1 2 3 	//目标key已经被删除,在重新添加的时候会自动创建该key
(integer) 3

2、当从聚合数据类型中移除元素时,如果值仍然是空的,键自动被销毁。
> lpush mylist 1 2 3
(integer) 3
> exists mylist
(integer) 1
> lpop mylist
"3"
> lpop mylist
"2"
> lpop mylist
"1"
> exists mylist 	//当key值已经为空时,key被销毁
(integer) 0

3、对一个空的 key 调用一个只读的命令,比如 LLEN (返回 list 的长度),或者一个删除元素的命令,将总是产生同样的结果。该结果和对一个空的聚合类型做同个操作的结果是一样的。
> del mylist	//对于一个空的key进行查询类的操作,返回的结果是一样的
(integer) 0
> llen mylist	//获取key 的长度
(integer) 0
> lpop mylist	
(nil)

通用命令

1、del key				//删除key
2、dump key				//序列化给定的key,并返回序列化的值
3、exists key 			//检查指定key是否存在
4、expire key seconds	//为给定的key设置过期时间,以秒为单位
5、ttl key				//返回给定key的剩余存活时间(以秒为单位)
6、type key				//返回key存储对象的类型

代码演示

    @Test
    public void comm() {
        Jedis jedis = init();

        Long status = jedis.del("name");
        log.info("删除的对应key的个数:{}", status);

        byte[] dump = jedis.dump("name");
        log.info("序列化对应的key的值:{}", dump);

        Boolean exists = jedis.exists("name");
        log.info("判定指定的key是否存在:{}", exists);

        Long name = jedis.expire("name", 1000);
        log.info("给指定的可以设置过期时间:{}", name);

        Long aliveTime = jedis.ttl("name");
        log.info("剩余存活时间:{}", aliveTime);

        String type = jedis.type("name");
        log.info("给定key值的类型:{}", type);
    }
    //初始化redis链接
    private Jedis init() {
        JedisShardInfo config = new JedisShardInfo("192.168.32.28", 6379);
        config.setPassword("test2020");
        return new Jedis(config);
    }

String

String数据结构是简单的key-value类型,value可以是String,也可以是数字,是二进制安全字符串
使用场景: 微博数,粉丝数

常用的命令

	1、set		//设置指定key的值
	2、get		//获取指定key的值
	3、incr		//将key的值加一(之前没有该key以0计)
	4、decr		//将可以的值减一(之前没有该key以0计)
	5、incrby	//将key值增加一个指定数字
	6、decrby	//将key值减一个指定的数字
	7、mget		//获取多个可以的值(key若不存在会返回null元素)

代码演示

原生命令
//设置
> set name milla
OK

//获取
> get name
"milla"

//当key存在时SET会失败
> set name milla nx
(nil)

//当key不存在时成功
> set name milla xx
OK
//原子递增
> set counter 100
OK
> incr counter
(integer) 101
> incr counter
(integer) 102
> incrby counter 50
(integer) 152

//设置和获取多个值
> mset a 10 b 20 c 30
OK
> mget a b c
1) "10"
2) "20"
3) "30"
//判断key是否存在
> set mykey hello
OK
> exists mykey
(integer) 1
> del mykey
(integer) 1
> exists mykey
(integer) 0

//查看key值的类型
> set mykey x
OK
> type mykey
string
> del mykey
(integer) 1
> type mykey
none
//
> set mykey x
OK
> type mykey
string
> del mykey
(integer) 1
> type mykey
none

//过期时间- 精度可以精确到毫秒或秒
> set key some-value
OK
> expire key 5
(integer) 1
> get key (immediately)
"some-value"
> get key (after some time)
(nil)

//设置过期时间
> set key 100 ex 10
OK
//查看key剩余时间
> ttl key
(integer) 9
Java代码
 @Test
    public void stringType() {
        Jedis jedis = init();

        //设置key和value -返回是否设置状态OK
        String status = jedis.set("name", "milla");
        log.info("返回的结果:{}" + status);
        //获取数据
        String name = jedis.get("name");
        log.info("取出key的值:{}" + name);
        //将对应的key数值加一
        Long add = jedis.incr("count");
        log.info("加一的结果:{}" + add);
        //将对应的key数值减一
        Long sub = jedis.decr("count");
        log.info("减一的结果:{}" + sub);
        //获取多个key 的值
        List<String> mValues = jedis.mget("name", "count", "empty");
        //如果对应的key没有值会返回一个null元素
        log.info("多个key的结果:{}" + mValues);
    }

List

按插入顺序排序的字符串元素的集合。实际上相当于链表
使用场景: 微博的关注列表,粉丝列表

List上的阻塞操作

可以使用Redis来实现生产者和消费者模型,如使用LPUSH和RPOP来实现该功能。但会遇到这种情景:list是空,这时候消费者就需要轮询来获取数据,这样就会增加redis的访问压力、增加消费端的cpu时间,而很多访问都是无用的。为此redis提供了阻塞式访问 BRPOP 和 BLPOP 命令。 消费者可以在获取数据时指定如果数据不存在阻塞的时间,如果在时限内获得数据则立即返回,如果超时还没有数据则返回null, 0表示一直阻塞。
同时redis还会为所有阻塞的消费者以先后顺序排队

常用的命令

1、lpush	//将一个或者是是多个值插入列表的头部
2、rpush	//在列表中添加一个或多个值
3、lpop		//移除并获取列表的第一个元素
4、rpop		//移除列表的最后一个元素,返回值为移除的元素
5、lrange	//获取所有(一个或者是多个)给定key的值 [起止索引值-结束索引-1代表最后一个元素,-2代表倒数第二个元素,以此类对]
6、ltrim    //从左到右截取

代码演示

原生命令

> rpush mylist A		//尾部插入
(integer) 1
> rpush mylist B		//尾部插入
(integer) 2
> lpush mylist first   	//头部插入
(integer) 3
> lrange mylist 0 -1
1) "first"
2) "A"
3) "B"
> rpop mylist		//移除列表最后一个元素,如果没有更多元素会返回(nil)
"B"


> rpush mylist 1 2 3 4 5  	//添加元素
(integer) 5
> ltrim mylist 0 2 		//截取元素
OK
> lrange mylist 0 -1
1) "1"
2) "2"
3) "3"
Java代码
   @Test
    public void listType() {
        Jedis jedis = init();

        // 插入数据1 --- 2 --- 3
        Long list = jedis.rpush("list", "1", "2", "3");
        log.info("返回值,成功插入数据的个数:{}", list);
        jedis.lpush("list", "0");
        jedis.rpush("list", "4");
        //获取一个或者多个值,指定开始和结束索引
        List<String> strings = jedis.lrange("list", 0, -1);
        log.info("获取指定key中所有的元素:{}", strings);
        //移除列表中第一个元素
        String start = jedis.lpop("list");
        log.info("移除第一个元素是:{}", start);
        //移除列表中最后一个元素
        String last = jedis.rpop("list");
        log.info("移除最后一个元素是:{}", last);

        jedis.ltrim("list", 0, 1);
        List<String> trim = jedis.lrange("list", 0, -1);
        log.info("获取trim之后的list:{}", trim);
    }

Set

不重复且无序的集合
使用场景: 共同的关注、共同的喜好、二度好友等

常用的命令

1、sadd			//向集合中添加一个或多个成员
2、spop			//移除并返回集合中的一个随机元素(集合是无序的)
3、smembers		//返回集合中全部的元素
4、sunion		//返回给定集合的并集
5、sinter		//返回指定集合的交集
6、sismember 	//检查某元素是否在某集合中
7、scard		//获取集合中成员的数量

代码演示

原生命令
> sadd myset 1 2 3	//批量添加
(integer) 3
> smembers myset	//获取所有元素
1. 3
2. 1
3. 2
> sismember myset 3		//检查3是否在set中
(integer) 1
> sismember myset 30	//检查30是否在set中
(integer) 0

> sadd news:1000:tags 1 2 3 5 8
(integer) 5
> sadd news:2000:tags 3 7 5 
(integer) 3```
> sadd news:3000:tags 2 3 5 
(integer) 3```
> sinter news:1000:tags news:2000:tags news:3000:tags //三个集合的交集
1.3
2.5
> sunion news:1000:tags news:2000:tags news:3000:tags //三个集合的并集
1.1
2.2
3.3
4.5
5.7
6.8

> scard news:1000:tags 	//获取集合中成员的数量
> 5
Java代码
    @Test
    public void setType() {
        // 取出两个人共同关注的好友
        Jedis jedis = init();

        // 每个人维护一个set
        jedis.sadd("news1", "userC", "userD", "userE");
        jedis.sadd("news2", "userC", "userE", "userF");
        // 取出共同关注
        Set<String> sinter = jedis.sinter("news1", "news2");
        log.info("关注新闻1和新闻2的用户: {}", sinter);

        // 检索给新闻点赞/转发的
        jedis.sadd("news_zan", "userC", "userD", "userE");
        jedis.sadd("news_zhuan", "userE", "userF");
        // 取出共同人群
        Set<String> union = jedis.sunion("news_zan", "news_zhuan");
        log.info("关注某新闻(点赞和转发)的所有人:{}", union);

        Boolean sismember = jedis.sismember("new1", "userC");
        log.info("指定key的集合中是否包含某元素:{}", sismember);

        Set<String> members = jedis.smembers("news1");
        log.info("关注新闻1的所有的用户:{}", members);
        String member = jedis.spop("news1");
        log.info("随机移除一个关注新闻1的用户:{}", member);

        Long count = jedis.scard("new1");
        log.info("获取集合中成员的数量:{}", count);
    }

Sorted Set

与Set比较类似,但是Sorted Set是可以通过用户提供的优先级(score)参数为成员进行排序–自动有序
使用场景: 排行榜、按照用户投票和时间排序

常用的命令

1、zadd			//向集合中添加一个或多个成员,或者更新已经存在的优先级
2、zrange		//通过索引区间返回有序集合中指定区间内的成员
3、zrem			//移除有序集合中的一个或多个成员
4、zcard		//获取有序集合的成员数量
5、zcount 		//计算指定score区间内的成员的数量

代码演示

原生命令
> zadd exam_rank 82.0 lilei 90 tom 96 lily 89 jerry 100 milla	//增加了5个元素
> (integer) 5
> zrange exam_rank 0 -1		//获取所有的元素 已经排序了
 1)  "lilei"
 2)  "jerry"
 3)  "tom"
 4)  "lily"
 5)  "milla"
 > zrange exam_rank 0  -1 withscores	//获取所有的元素[显示分数] 已经排序了
 1)  "lilei" 82
 2)  "jerry" 89
 3)  "tom"   90
 4)  "lily"  96
 5)  "milla" 100
> zrangebyscore exam_rank 96 100	//指定区间的元素
 1)  "lily"
 2)  "milla"
> zcard exam_rank  	//集合成员数量
(interger)5
> zcount exam_rank 90 100	//指定区间的集合成员数量
(interger)3
> zrem exam_rank milla	//删除指定集合元素
(interger)1
> zrange exam_rank 0 -1 	//查询所有
 1)  "lilei"
 2)  "jerry"
 3)  "tom"
 4)  "lily"
Java代码
    @Test
    public void SortedSetType() {
        Jedis jedis = init();
        String ranksKeyName = "exam_rank";
        jedis.zadd(ranksKeyName, 100.0, "milla");
        jedis.zadd(ranksKeyName, 82.0, "lilei");
        jedis.zadd(ranksKeyName, 90, "tom");
        jedis.zadd(ranksKeyName, 96, "lily");
        jedis.zadd(ranksKeyName, 89, "jerry");

        Set<String> stringSet = jedis.zrevrange(ranksKeyName, 0, 2);
        log.info("返回前三名:{}", stringSet);

        Long zcount = jedis.zcount(ranksKeyName, 85, 100);
        log.info("超过85分的数量:{} ", zcount);

        Long zcard = jedis.zcard(ranksKeyName);
        log.info("获取成员的数量:{} ", zcard);
        //移除tom
        Long zrem = jedis.zrem(ranksKeyName, "tom");
        log.info("移除一个成员:{} ", zrem);
        //获取所有的成员,指定起止索引 -1代表到最后
        Set<String> zrange = jedis.zrange(ranksKeyName, 0, -1);
        log.info("移除一个成员:{} ", zrange);
    }

Hash

由field和关联的value组成的map。field和value都是字符串的
使用场景: 存储部分变更数据,如用户信息

常用的命令

1、hset			//设置指定的key和value
2、hget			//获取指定key的值
3、hmget		//获取一个或多个属性的值
4、hgetall		//获取key中多个属性的值

代码演示

原生命令
> hset user name tom age 18 userId 1001	//增加用户信息{name:"tome",age:18,userId:"1001"}
(integere)3
> hget user userId	//获取其中的userId属性
"1001"
> hget user name age no-such-field 	//获取key的多个属性,不存在返回nil
 1)tom
 2)18
 3)(nil)
> hgetall user	//获取所有的user属性
 1)  "name"
 2)  "tom"
 3)  "age"
 4)  "18"
 5)  "userId"
 6)  "1001"
Java代码
    // 类似:在redis里面存储一个hashmap
    // 推荐的方式,无特殊需求是,一般的缓存都用这个
    @Test
    public void hashType() {
        Jedis jedis = init();
        jedis.hset("user", "name", "tom");
        jedis.hset("user", "age", "18");
        jedis.hset("user", "userId", "10001");
        String name = jedis.hget("user", "name");
        log.info("获取用户姓名:{}", name);
        List<String> hmget = jedis.hmget("user", "name", "age");
        log.info("获取key下面的多个属性值:{}", hmget);
        Map<String, String> userMap = jedis.hgetAll("user");
        log.info("获取用户信息列表:{}", userMap);
    }

GEO

GEO3.2版本开始对GEO(地理位置)的支持
使用场景: LBS应用开发

常用的命令

1、geoadd			//增加地理位置坐标,可批量添加
2、geodist			//获取两个地理位置的距离
3、geohash			//获取某地理位置的geohash值
4、geopos			//获取指定位置的坐标,可批量获取
5、georadius		//根据给定地理位置坐标获取指定范围内地理位置的集合(中心点由输入的经度和纬度决定)
6、georadiusbymember 	//根据给定成员的位置获取指定范围内地理位置的集合(中心点由给定位置元素决定)

代码演示

原生命令
//后续补充
Java代码
@Test
    public void geoType() {
        Jedis jedis = init();
        jedis.geoadd("test", 20.3, 56.9, "拱宸桥");
        jedis.geoadd("test", 80.3, 16.9, "滨和地铁站");

        Double geodist = jedis.geodist("test", "拱宸桥", "滨和地铁站");
        log.info("获取两个地理位置的距离: {}", geodist);

        List<String> geohash = jedis.geohash("test", "拱宸桥");
        log.info("获取一/多个地理位置的geoHash集合: {}", geohash);

        List<GeoCoordinate> geopos = jedis.geopos("test", "拱宸桥", "滨和地铁站");
        log.info("获取一/多个地理位置的地理位置集合: {}", geopos);

        //类似与画一个圆,圆内所有的房源信息等等
        List<GeoRadiusResponse> radiusList = jedis.georadius("test", 21.3, 50.2, 200000, GeoUnit.KM);
        log.info("根据给定地理位置坐标获取指定范围内的地理位置集合: {}", radiusList);

        List<GeoRadiusResponse> geoRadiusResponses = jedis.georadiusByMember("test", "滨和地铁站", 9000000, GeoUnit.KM);
        log.info("根据指定成员的位置获取至额定范围内的位置信息集合: {}", geoRadiusResponses);
    }

Stream

Stream5.0版本开始的新结构 “流”
使用场景: 消费者生产者场景(类似MQ)

常用的命令

1、xadd			//往指定的流添加信息
2、xlen			//获取stream流中的消息数量
3、xdel			//删除流中的消息
4、xrange		//返回流中满足给定id范围的消息
5、xread		//从一个或者多个流中读取消息
6、xinfo 		//检索关于流和关联的消费者组的不同的信息

代码演示

原生命令
	// stream 流,5.0新特性,redisTemplate、jedis还没有支持,Redisson和Lettuce支持了
    // springboot中默认的redis客户端Lettuce
    > XADD mystream * sensor-id 1234 temperature 19.8 	// 添加 
	> XRANGE mystream - + COUNT 2     					// 遍历
	> XREAD COUNT 2 STREAMS mystream 0     				// 消费
	> XREAD BLOCK 0 STREAMS mystream $				    // 阻塞式消费
	> XGROUP CREATE mystream mygroup $			    	// 创建消费者组
	> XREADGROUP GROUP mygroup Alice COUNT 1 STREAMS mystream     // 分组消费
	> XACK mystream mygroup 1526569495631-0			    // 消费确认
	> XPENDING mystream mygroup - + 10					// 查看未确认的消息
	> XCLAIM mystream mygroup Alice 3600000 1526569498055-0	    // 重新认领消费
    // XINFO 查看stream信息,监控
Java代码
    /**
     * 生产者
     *
     * @throws InterruptedException
     */
    @Test
    public void producer() throws InterruptedException {
        RedisClient redisClient = RedisClient.create("redis://192.168.16.28:6379");
        StatefulRedisConnection<String, String> connect = redisClient.connect();
        RedisCommands<String, String> stream = connect.sync();
        stream.xadd("stream_sms_send", "smsid", "10001", "content", "收到短信请回复");
        stream.xdel("stream_sms_send", "smsid");

    }

    // 普通消费 -- 最后一条消息
    @Test
    /**
     * 消费者
     */
    public void consumer() {
        RedisClient redisClient = RedisClient.create("redis://192.168.16.28:6379");
        StatefulRedisConnection<String, String> connect = redisClient.connect();
        RedisCommands<String, String> redisSyncCommands = connect.sync();
        List<StreamMessage<String, String>> stream_sms_send = redisSyncCommands.xread(XReadArgs.StreamOffset.from("stream_sms_send", "0"));
        for (StreamMessage<String, String> stringStringStreamMessage : stream_sms_send) {
            System.out.println(stringStringStreamMessage);
        }
    }

    @Test
    public void createGroup() {
        RedisClient redisClient = RedisClient.create("redis://192.168.16.28:6379");
        StatefulRedisConnection<String, String> connect = redisClient.connect();
        RedisCommands<String, String> redisSyncCommands = connect.sync();
        // 创建分组
        redisSyncCommands.xgroupCreate(XReadArgs.StreamOffset.from("stream_sms_send", "0"), "group_1");
    }

    @Test
    public void consumerGroup() {
        RedisClient redisClient = RedisClient.create("redis://192.168.16.28:6379");
        StatefulRedisConnection<String, String> connect = redisClient.connect();
        RedisCommands<String, String> redisSyncCommands = connect.sync();
        // 按组消费
        List<StreamMessage<String, String>> xreadgroup = redisSyncCommands.xreadgroup(Consumer.from("group_1", "consumer_1"), XReadArgs.StreamOffset.lastConsumed("stream_sms_send"));
        for (StreamMessage<String, String> message : xreadgroup) {
            System.out.println(message);
            // 告知redis,消息已经完成了消费
            redisSyncCommands.xack("stream_sms_send", "group_1", message.getId());
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值