介绍
Redis一共有几种数据类型?(注意是数据类型不是数据结构)
一共是八种,String、Hash、Set、List、Zset、Hyperloglog、Geo、Streams。
1、为什么要把数据放在内存中?
- 内存的速度更快,10WQPS
- 减少计算的时间,减轻数据库压力
2、如果是用内存的数据结构作为缓存,为什么不用HashMap或者Memcached?
- 更丰富的数据类型
- 支持多种编程语言
- 功能丰富:持久化机制、内存淘汰策略、事务、发布订阅、pipeline、LUA
- 支持集群、分布式
3、Memcached和redis的区别是什么?
Memcached只能存储KV、没有持久化机制、不支持主从复制、是多线程的。
Reids默认有16个库(0-15),可以在配置文件redis.conf中修改。
databases16
RedisKey的最大长度限制是512M,值得限制不同,有的是用长度限制的,有的是用个数限制的。
String
介绍
最基本也是最常用的数据类型就是String。get和set命令就是String的操作命令,Redis的字符串被叫做二进制安全的字符串(Binary-safestrings)。
String可以存储三种类型,INT(整数)、float(单精度浮点数)、string(字符串)。
操作命令
下面是它的一些操作命令
#存值(如果对同一个keyset多次会直接覆盖旧值)setjack2673#取值getjack#查看所有键keys*#获取键总数(生产环境数据量大,慎用)dbsize#查看键是否存在existsjack#删除键deljacktonny#重命名键renamejacktonny#查看类型typejack#获取指定范围的字符getrangejack01#获取值长度strlenjack#字符串追加内容appendjackgood#设置多个值(批量操作,原子性)msetjack2673tonny2674#获取多个值mgetjacktonny#设置值,如果key存在,则不成功setnxjackshuaige#基于此实现分布式锁setkeyvalue[expirationEXseconds|PXmilliseconds][NX|XX]#(整数)值递增(值不存在会得到1)incrjackincrbyjack100#(整数)值递减decrjackdecrbyjack100#浮点数增量setmf2.6incrbyfloatmf7.3
1、缓存
String类型,这是最常用的,可以缓存一些热点数据,比如首页新闻,可以显著提升热点数据的访问速度,同时减轻DB压力。
2、分布式数据共享
String类型,因为Redis是分布式的独立服务,可以在多个应用之间共享。
例如:分布式Session
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency>
3、分布式锁
参考《介绍几种常见的分布式锁写法》
4、全局ID
INT类型,INCRBY,利用原子性
incrbyuserid1000
(分表分库的场景,一次性拿一段)
5、计数器
INT类型,INCR方法
例如:文章的阅读量,微博点赞数,允许一定的延迟,先写入Redis再定时同步到数据库
6、限流
INT类型,INCR方法
以访问者的IP和其他信息作为key,访问一次增加一次计数,超过次数则返回false。
Hash
介绍
现在有一张teacher表
假设我们还是通过String类型存储的话,存储的时候就要把Teacher实体类进行序列化,然后作为value只存储进去;修改的时候,也需要把value先取出来进行反序列化,比如把年龄更改为21岁,然后再序列化,再存进去,十分的繁琐,增加开销。
我们需要单独获取、修改一个值,这时我们可以通过key分层的方式来实现,如下表:
但是这样key会很多,key也很长,占用空间,有没有更好的办法,这时候就用到我们的Hash类型,如下两张表所示:
这样也便于集中管理,划分的粒度不同,可以按照实际场景,key的过期时间,灵活度考虑选取哪一种存储方式。
Hash用来存储多个无序的键值对,最大存储数量2^32-1(40亿左右)。
优点:
- 把所有相关的值聚集到一个Key中,节省内存空间
- 只使用一个Key,减少Key冲突
- 当需要批量获取值的时候,只需要使用一个命令,减少内存/IO/CPU的消耗
缺点:
- Field不能单独设置过期时间
- 需要考虑数据量分布的问题(field非常多的时候,无法分布到多个节点)
操作命令
#设置、批量设置值hseth1f6hseth1e5hmseth1a1b2c3d4#取值hgeth1a#批量取值hmgeth1abcd#获取所有fieldhkeysh1#获取所有field的值hvalsh1#返回哈希表中,所有的字段和值hgetallh1#删除fieldhdelh1a#获取哈希表中字段的数量hlenh1
应用场景
String可以做的事情,Hash都可以做。再补充一个场景,购物车:
key:用户id;field:商品id;value:商品数量;
+1:hincr;
-1:hdecr;
删除:hincrbykeyfield-1;
全选:hgetall;
商品数:hlen;
List
介绍
存储有序的字符串(从左到右),元素可以重复,最大存储数量2^32-1(40亿左右)。
下面通过画图来演示一下入队列,出队列
操作命令
#左推lpushqueuealpushqueuebc#右推rpushqueuede#左边移除并返回列表的第一个元素lpopqueue#右边移除并返回列表的第一个元素rpopqueue#通过索引获取列表中的元素lindexqueue0#返回列表中指定区间内的元素lrangequeue0-1
应用场景
1、列表
例如用户的消息列表、网站的公告列表、活动列表、博客的文章列表、评论列表等,通过LRANGE取出一页,按顺序显示。
2、队列/栈
List还可以当做分布式环境的队列/栈使用。
队列:先进先出,rpush和blpop
栈:先进后出,rpush和brpop
这里介绍两个阻塞的弹出操作:blpop/brpop,可以设置超时时间(单位:秒)。
blpop:blpopkey1timeout,移出并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。brpop:brpopkey1timeout,移出并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
Set
介绍
Set存储String类型的无序集合,最大存储数量2^32-1(40亿左右)。
如下图所示:
操作命令
#添加一个或多个元素saddmysetabcdefg#获取所有元素smembersmyset#统计元素个数scardmyset#随机获取一个元素srandmembermyset#随机弹出一个元素spopmyset#移除一个或者多个元素sremmysetdef#查看元素是否存在sismembermyseta#获取差集sdiffset1set2#获取交集sinterset1set2#获取并集sunionset1set2
应用场景
1、抽奖
随机获取元素:spopmyset
2、点赞、签到、打卡
我们以微博举例子,假设这条微博的ID是t1001,用户ID是u6001,
用dianzan:t1001来维护t1001这条微博的所有点赞用户。
点赞了这条微博:sadddianzan:t1001u6001
取消点赞:sremdianzan:t1001u6001
是否点赞:sismemberdianzan:t1001u6001
点赞的所有用户:smembersdianzan:t1001
点赞数:scarddianzan:t1001
比关系型数据库简单了许多。
3、商品标签
用tags:i8001来维护商品所有的标签。
saddtags:i8001画面清晰细腻
saddtags:i8001真彩清晰显示屏
saddtags:i8001流畅至极
4、商品筛选
华为P40上线了,支持民族品牌,加到各个标签中去。
saddbrand:huaweip40
saddos:androidp40
saddscreensize:6.0-6.24p40
买的时候筛选,牌子是华为,操作系统是安卓,屏幕大小在6.0-6.24之间的,取交集:
sinterbrand:huaweios:androidscreensize:6.0-6.24
ZSet
介绍
sortedset存储有序的元素。每个元素都有个score,按照score从小到大排序。score相同时,按照key的ASCII码排序。
操作命令
#添加元素zaddmyzset10java20php30ruby40cpp50python#获取全部元素zrangemyset0-1withscoreszrevrangemyzset0-1withscores#根据分数区间获取元素zrangebyscoremyzset2030#移除元素(也可以根据scorerank删除)zremmyzsetphpcpp#统计元素个数zcardmyzset#分值增加zincrbymyzset5python#根据分值min和max统计个数zcountmyzset2060#获取python排名zrankmyzsetpython#获取元素分数zscoremyzsetpython
应用场景
排行榜
今天是2021年5月23号,建一个key为hotSearch:20210523的zset。
放羊大叔这条新闻的id是n1234,每点击一下:zincrbyhotSearch:202105231n1234
获取热搜排行榜前十条:zrevrangehotSearch:20210523010withscores
袁老国士无双,一路走好,中华民族的儿女不会忘记您!
BitMaps
介绍
BitMaps是在字符串类型上定义地位操作,一个字节由8个二进制位组成。如下图所示:
m的ASCII码是109,对应的二进制数据是01101101
操作命令
#设置字符串key为k1,value为micsetk1mic#取k1的第七位,结果是0getbitk16#取k1的第八位为0,此时的ASCII码是108,对应字母是lsetbitk170#所以取出来值为licgetk1#统计二进制中1的个数,一共是12个bitcountk1#获取第一个1或者0的位置bitposk11bitposk11
应用场景
- BITOPANDdestkeykey[key...],对一个或多个key求逻辑并,并将结果保存到destkey。
- BITOPORdestkeykey[key...],对一个或多个key求逻辑或,并将结果保存到destkey。
- BITOPXORdestkeykey[key...],对一个或多个key求逻辑异或,并将结果保存到destkey。
- BITOPNOTdestkeykey,对给定key求逻辑非,并将结果保存到destkey。
连续七天在线用户
#存值(如果对同一个keyset多次会直接覆盖旧值)setjack2673#取值getjack#查看所有键keys*#获取键总数(生产环境数据量大,慎用)dbsize#查看键是否存在existsjack#删除键deljacktonny#重命名键renamejacktonny#查看类型typejack#获取指定范围的字符getrangejack01#获取值长度strlenjack#字符串追加内容appendjackgood#设置多个值(批量操作,原子性)msetjack2673tonny2674#获取多个值mgetjacktonny#设置值,如果key存在,则不成功setnxjackshuaige#基于此实现分布式锁setkeyvalue[expirationEXseconds|PXmilliseconds][NX|XX]#(整数)值递增(值不存在会得到1)incrjackincrbyjack100#(整数)值递减decrjackdecrbyjack100#浮点数增量setmf2.6incrbyfloatmf7.30
那么在算连续七天在线用户就是:
BITOPAND7_both_online_usersfirstdayseconddaythirddayfourthdayfifthdaysixthdayseventhday
还可以应用访问统计、在线用户统计等等。
Hyperloglog
Hyperloglog提供了一种不太精确的基数统计方法,用来统计一个集合中不重复的元素个数,比如统计网站的UV,或者应用的日活、月活,存在一定的误差。
在Redis中实现的Hyperloglog,只需要12k内存就能统计2^64个数据。
#存值(如果对同一个keyset多次会直接覆盖旧值)setjack2673#取值getjack#查看所有键keys*#获取键总数(生产环境数据量大,慎用)dbsize#查看键是否存在existsjack#删除键deljacktonny#重命名键renamejacktonny#查看类型typejack#获取指定范围的字符getrangejack01#获取值长度strlenjack#字符串追加内容appendjackgood#设置多个值(批量操作,原子性)msetjack2673tonny2674#获取多个值mgetjacktonny#设置值,如果key存在,则不成功setnxjackshuaige#基于此实现分布式锁setkeyvalue[expirationEXseconds|PXmilliseconds][NX|XX]#(整数)值递增(值不存在会得到1)incrjackincrbyjack100#(整数)值递减decrjackdecrbyjack100#浮点数增量setmf2.6incrbyfloatmf7.31
源码在:com/xhj/jedis/HyperLogLogTest.java
Geo
现在有这样一个需求,获取半径1公里以内的门店,那么我们就要把门店的经纬度存起来,如果存在数据库的话,一个字段存经度,一个字段存维度,计算距离比较复杂。现在我们通过Redis的Geo存储十分方便。
#存值(如果对同一个keyset多次会直接覆盖旧值)setjack2673#取值getjack#查看所有键keys*#获取键总数(生产环境数据量大,慎用)dbsize#查看键是否存在existsjack#删除键deljacktonny#重命名键renamejacktonny#查看类型typejack#获取指定范围的字符getrangejack01#获取值长度strlenjack#字符串追加内容appendjackgood#设置多个值(批量操作,原子性)msetjack2673tonny2674#获取多个值mgetjacktonny#设置值,如果key存在,则不成功setnxjackshuaige#基于此实现分布式锁setkeyvalue[expirationEXseconds|PXmilliseconds][NX|XX]#(整数)值递增(值不存在会得到1)incrjackincrbyjack100#(整数)值递减decrjackdecrbyjack100#浮点数增量setmf2.6incrbyfloatmf7.32
可以增加地址位置信息、获取地址位置信息、计算两个位置的距离、获取指定范围内的地理位置集合等等。源码在:com/xhj/jedis/GeoTest.java
#存值(如果对同一个keyset多次会直接覆盖旧值)setjack2673#取值getjack#查看所有键keys*#获取键总数(生产环境数据量大,慎用)dbsize#查看键是否存在existsjack#删除键deljacktonny#重命名键renamejacktonny#查看类型typejack#获取指定范围的字符getrangejack01#获取值长度strlenjack#字符串追加内容appendjackgood#设置多个值(批量操作,原子性)msetjack2673tonny2674#获取多个值mgetjacktonny#设置值,如果key存在,则不成功setnxjackshuaige#基于此实现分布式锁setkeyvalue[expirationEXseconds|PXmilliseconds][NX|XX]#(整数)值递增(值不存在会得到1)incrjackincrbyjack100#(整数)值递减decrjackdecrbyjack100#浮点数增量setmf2.6incrbyfloatmf7.33
Streams
5.0推出的数据类型。支持多播的可持久化的消息队列,用于实现发布订阅功能,借鉴了Kafka的设计。
应用场景总结
- 缓存—提升热点数据的访问速度
- 共享数据—数据的存储和共享的问题
- 全局ID—分布式全局ID的生成方案(分库分表)
- 分布式锁—进程间共享数据的原子操作保证
- 在线用户统计和计数
- 队列、栈—跨进程的队列/栈
- 消息队列—异步解耦的消息机制
- 服务注册与发现—RPC通信机制的服务协调中心(Dubbo支持Redis)
- 购物车
- 新浪用户消息时间线
- 抽奖逻辑(礼物、转发)
- 点赞、签到、打卡
- 商品标签
- 用户(商品)关注(推荐)模型
- 电商产品筛选
- 排行榜