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

3万+

被折叠的 条评论
为什么被折叠?



