Redis中常见的数据类型及其使用场景
五种常见数据类型
Redis中的数据类型指的是 value存储的数据类型,key都是以String类型存储的,value依据场景需求,能够以String、List等类型进行存储。
各数据类型介绍:
Redis数据类型对应的底层数据结构
String 类型的运用场景
常用指令
-
寄存键值:set key value [EX seconds] [PX milliseconds] [NX|XX]
-
[NX|XX] :
-
nx:假如key不存在则树立
-
xx:假如key存在则修正其值,也能够直接运用setnx/setex指令
-
-
-
获取键值:get key
-
值递加/递减:incr key
- 假如字符串中的值是数字类型的,能够运用incr指令每次递加,不是数字类型则报错。
- 一次想递加N用incrby指令,假如是浮点型数据能够用incrbyfloat指令递加。
- 相同,递减运用decr、decrby指令。
-
批量寄存键值:mset key value [key value ...]
-
批量获取键值:mget key [key ...]
-
获取值长度:strlen key
-
追加内容:append key value
-
获取部分字符:getrange key start end
缓存目标
运用 String 来缓存目标有两种办法:
- 直接缓存整个目标的 JSON,指令比方: SET user:1 '{"name":"seven", "age":18}'。
- 选用将 key 进行别离为 user:ID:特点,选用 MSET 存储,用 MGET 获取各特点值,指令比方: MSET user:1:name seven1 user:1:age 18 user:2:name seven2 user:2:age 20
惯例计数
比方核算拜访次数、点赞、转发、库存数量等等。
# 初始化文章的阅览量
> SET aritcle:readcount:1001 0
OK
#阅览量+1
> INCR aritcle:readcount:1001
(integer) 1
#阅览量+1
> INCR aritcle:readcount:1001
(integer) 2
分布式锁
之所以选用Redis来作为分布式锁,能够有几方面理由:
- redis满意的快
- redis供给了
setnx + expire
的机制,彻底符合分布式锁的完结关键 Redisson
客户端的盛行,使得依据redis的分布式锁愈加简略
SET 指令有个 NX 参数能够完结「key不存在才刺进」,能够用它来完结分布式锁:
- 假如 key 不存在,则显现刺进成功,能够用来表明加锁成功;
- 假如 key 存在,则会显现刺进失利,能够用来表明加锁失利。
一般来说,还会对分布式锁加上过期时刻,分布式锁的指令如下:
SET lock_key unique_value NX PX 10000
- lock_key 便是 key 键;
- unique_value 是客户端生成的仅有的标识;
- NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;
- PX 10000 表明设置 lock_key 的过期时刻为 10s,这是为了防止运用在运转过程中产生反常而无法开释锁。
同享 session 信息
通常情况下能够运用session信息保存用户的登录(会话)状况,由于这些 Session 信息会被保存在服务器端,假如用户一的 Session 信息被存储在服务器一,但第2次拜访时用户一被分配到服务器二,这个时分服务器并没有用户一的 Session 信息,就会呈现需求重复登录的问题。如下:
能够凭借 Redis 对这些 Session 信息进行一致的存储和办理,这样不管恳求发送到那台服务器,服务器都会去同一个 Redis 获取相关的 Session 信息,这样就处理了分布式体系下 Session 存储的问题。
List 类型的运用场景
常用指令
-
存储值:
- 左端存值:lpush key value [value ...]
- 右端存值:rpush key value [value ...]
- 索引存值:lset key index value
-
弹出元素:
- 左端弹出:lpop key
- 右端弹出:rpop key
-
获取元素个数:llen key
-
获取列表元素:
- 两头获取:lrange key start stop
- 索引获取:lindex key index
-
删去元素:
- 依据值删去:lrem key count value
- 规模删去:ltrim key start stop
音讯行列
- 音讯保序:运用 LPUSH + RPOP,对行列进行先进先出的音讯处理;满意音讯行列的保序性
- 堵塞读取:运用 BRPOP;堵塞读取行列中的数据,防止顾客不停地调用 RPOP 指令带了不必要的功用丢失
- 重复音讯处理:生产者完结大局仅有 ID;满意音讯行列的处理重复音讯的才能
- 音讯的可靠性:运用 BRPOPLPUSH让顾客程序从一个 List 中读取音讯,一起,Redis 会把这个音讯再刺进到另一个 List(能够叫作备份 List)留存;这样一来,假如顾客程序读了音讯但没能正常处理,等它重启后,就能够从备份 List 中从头读取音讯并进行处理了。满意音讯行列的可靠性
可是有两个问题:
- 生产者需求自行完结大局仅有 ID;
- 不能以消费组方式消费数据
Hash 类型
常用指令
-
寄存值:
- 单个:hset key field value
- 多个:hmset key field value [field value ...]
- 不存在时:hsetnx key field value
-
获取字段值:
- 单个:hget key field
- 多个:hmget key field [field ...]
- 获取一切键与值:hgetall key
- 获取一切字段:hkeys key
- 获取一切值:hvals key
-
判别是否存在:hexists key field
-
获取字段数量:hlen key
-
递加/减:hincrby key field increment
-
删去字段:hdel key field [field ...]
缓存目标
一般目标用 String + Json 存储,目标中某些频频改变的特点能够考虑抽出来用 Hash 类型存储。
购物车
以用户 id 为 key,产品 id 为 field,产品数量为 value,刚好构成了购物车的3个要素,如下图所示。
触及的指令如下:
- 增加产品:HSET cart:{用户id} {产品id} 1
- 增加数量:HINCRBY cart:{用户id} {产品id} 1
- 产品总数:HLEN cart:
- 删去产品:HDEL cart:
- 获取购物车一切产品:HGETALL cart:
Set 类型
聚合核算(并集、交集、差集)场景,比方点赞、一起重视、抽奖活动等。
常用指令
-
存储值:sadd key member [member ...]
-
获取一切元素:smembers key
-
随机获取:srandmember langs count
-
判别是否存在某member:sismember key member
-
获取调集中元素个数:scard key
-
删去调集元素:srem key member [member ...]
-
弹出元素:spop key [count]
点赞
能够确保一个用户只能点一个赞,现已点赞过的用户不能再点赞
# uid:1 用户对文章 article:1 点赞
> SADD article:1 uid:1
(integer) 1
# uid:2 用户对文章 article:1 点赞
> SADD article:1 uid:2
(integer) 1
# uid:3 用户对文章 article:1 点赞
> SADD article:1 uid:3
(integer) 1
# uid:1 取消了对 article:1 文章点赞。
> SREM article:1 uid:1
(integer) 1
# 获取 article:1 文章一切点赞用户 :
> SMEMBERS article:1
1) "uid:3"
2) "uid:2"
# 获取 article:1 文章的点赞用户数量:
> SCARD article:1
(integer) 2
一起重视
Set 类型支撑交集运算,所以能够用来核算一起重视的老友、大众号等。
key 能够是用户id,value 则是已重视的大众号的id。
# uid:1 用户重视大众号 id 为 5、6、7、8、9
> SADD uid:1 5 6 7 8 9
(integer) 5
# uid:2 用户重视大众号 id 为 7、8、9、10、11
> SADD uid:2 7 8 9 10 11
(integer) 5
# 获取一起重视
> SINTER uid:1 uid:2
1) "7"
2) "8"
3) "9"
# 给 uid:2 引荐 uid:1 重视的大众号:在uid:1中有可是uid:2中没有的
> SDIFF uid:1 uid:2
1) "5"
2) "6"
# 验证某个大众号是否一起被 uid:1 或 uid:2 重视:
> SISMEMBER uid:1 5
(integer) 1 # 回来0,阐明重视了
> SISMEMBER uid:2 5
(integer) 0 # 回来0,阐明没重视
抽奖活动
存储某活动中中奖的用户名 ,Set 类型由于有去重功用,能够确保同一个用户不会中奖两次。
# key为抽奖活动名,value为职工称号,把一切职工称号放入抽奖箱 :
>SADD lucky Tom Jerry John Sean Marry Lindy Sary Mark
(integer) 5
# 假如答应重复中奖,能够运用 SRANDMEMBER 指令。
# 抽取 1 个一等奖:
> SRANDMEMBER lucky 1
1) "Tom"
# 抽取 2 个二等奖:
> SRANDMEMBER lucky 2
1) "Mark"
2) "Jerry"
# 抽取 3 个三等奖:
> SRANDMEMBER lucky 3
1) "Sary"
2) "Tom"
3) "Jerry"
# 假如不答应重复中奖,能够运用 SPOP 指令。
# 抽取一等奖1个
> SPOP lucky 1
1) "Sary"
# 抽取二等奖2个
> SPOP lucky 2
1) "Jerry"
2) "Mark"
# 抽取三等奖3个
> SPOP lucky 3
1) "John"
2) "Sean"
3) "Lindy"
Zset 类型
排序场景,比方排行榜、电话和姓名排序等。
常用指令
- 存储值:zadd key [NX|XX] [CH] [INCR] score member [score member ...]
- 获取元素分数:zscore key member
- 获取排名规模:zrange key start stop [WITHSCORES]
- 获取指定分数规模排名:zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
- 增加指定元素分数:zincrby key increment member
- 获取调集元素个数:zcard key
- 获取指定规模分数个数:zcount key min max
- 删去指定元素:zrem key member [member ...]
- 获取元素排名:zrank key member
Zset结构
typedef struct zset {
dict *dict;//哈希表
zskiplist *zsl;//跳表
} zset;
zset 结构体里有两个数据结构:一个是跳表,一个是哈希表。这样的长处是既能进行高效的规模查询(如 ZRANGEBYSCORE 操作,利用了跳表),也能进行高效单点查询(如 ZSCORE 操作,利用了hash表)。
排行榜
五篇博文,别离取得赞为 200、40、100、50、150。
# arcticle:1 文章取得了200个赞
> ZADD user:seven:ranking 200 arcticle:1
(integer) 1
# arcticle:2 文章取得了40个赞
> ZADD user:seven:ranking 40 arcticle:2
(integer) 1
# arcticle:3 文章取得了100个赞
> ZADD user:seven:ranking 100 arcticle:3
(integer) 1
# arcticle:4 文章取得了50个赞
> ZADD user:seven:ranking 50 arcticle:4
(integer) 1
# arcticle:5 文章取得了150个赞
> ZADD user:seven:ranking 150 arcticle:5
(integer) 1
# 获取文章赞数最多的 3 篇文章, ZREVRANGE 指令(倒序获取有序调集 key 从start下标到stop下标的元素)
# WITHSCORES 表明把 score 也显现出来
> ZREVRANGE user:seven:ranking 0 2 WITHSCORES
1) "arcticle:1"
2) "200"
3) "arcticle:5"
4) "150"
5) "arcticle:3"
6) "100"
# 获取 100 赞到 200 赞的文章,ZRANGEBYSCORE 指令(回来有序调集中指定分数区间内的成员,分数由低到高排序)
> ZRANGEBYSCORE user:xiaolin:ranking 100 200 WITHSCORES
1) "arcticle:3"
2) "100"
3) "arcticle:5"
4) "150"
5) "arcticle:1"
6) "200"
电话,姓名排序
电话排序
# 将电话号码存储到 SortSet 中,然后依据需求来获取号段:
> ZADD phone 0 13100111100 0 13110114300 0 13132110901
(integer) 3
> ZADD phone 0 13200111100 0 13210414300 0 13252110901
(integer) 3
> ZADD phone 0 13300111100 0 13310414300 0 13352110901
(integer) 3
# 获取一切号码
> ZRANGEBYLEX phone - +
1) "13100111100"
2) "13110114300"
3) "13132110901"
4) "13200111100"
5) "13210414300"
6) "13252110901"
7) "13300111100"
8) "13310414300"
9) "13352110901"
# 获取 132 号段的号码:
> ZRANGEBYLEX phone [132 (133
1) "13200111100"
2) "13210414300"
3) "13252110901"
# 获取132、133号段的号码:
> ZRANGEBYLEX phone [132 (134
1) "13200111100"
2) "13210414300"
3) "13252110901"
4) "13300111100"
5) "13310414300"
6) "13352110901"
姓名排序
> zadd names 0 Toumas 0 Jake 0 Bluetuo 0 Gaodeng 0 Aimini 0 Aidehua
(integer) 6
# 获取一切人的姓名:
> ZRANGEBYLEX names - +
1) "Aidehua"
2) "Aimini"
3) "Bluetuo"
4) "Gaodeng"
5) "Jake"
6) "Toumas"
# 获取姓名中大写字母A最初的一切人:
> ZRANGEBYLEX names [A (B
1) "Aidehua"
2) "Aimini"
# 获取姓名中大写字母 C 到 Z 的一切人:
> ZRANGEBYLEX names [C [Z
1) "Gaodeng"
2) "Jake"
3) "Toumas"
BitMap(2.2 版新增):
介绍
适用于二值状况核算的场景。
报到
只记载报到(1)或未报到(0)
# 记载用户 4 月 3 号已报到
SETBIT uid:sign:100:202304 2 1
# 查看该用户 6 月 3 日是否报到
> GETBIT uid:sign:100:202306 3
1
# 核算用户在 6 月份的报到次数
> BITCOUNT uid:sign:100:202206
1
# 核算这个月初次打卡时刻;BITPOS key bitValue [start] [end],start end 表明要检测的规模
BITPOS uid:sign:100:202206 1
判别用户登陆状况
key = login_status 表明存储用户登陆状况调集数据, 将用户 ID 作为 offset,在线就设置为 1,下线设置 0。通过 GETBIT判别对应的用户是否在线。 5000 万用户只需求 6 MB 的空间。
# 表明ID = 10086 的用户已登陆
SETBIT login_status 10086 1
# 查看该用户是否登陆,回来值 1 表明已登录
GETBIT login_status 10086
# 登出,将 offset 对应的 value 设置成 0。
SETBIT login_status 10086 0
接连报到用户总数
把每天的日期作为 Bitmap 的 key,userId 作为 offset,若是打卡则将 offset 方位的 bit 设置成 1。key 对应的调集的每个 bit 位的数据则是一个用户在该日期的打卡记载。
那就能够设置 7 个 Bitmap,对这 7 个 Bitmap 的对应的 bit 位做 『与』运算。那么当一个 userID 在 7 个 Bitmap 对应对应的 offset 方位的 bit = 1 就阐明该用户 7 天接连打卡。成果保存到一个新 Bitmap 中,咱们再通过 BITCOUNT 核算 bit = 1 的个数便得到了接连打卡 7 天的用户总数了。
HyperLogLog(2.8 版新增)
海量数据基数核算的场景,供给不精确的去重计数。但要留意,HyperLogLog 的核算规则是依据概率完结的,不是十分精确,规范误算率是 0.81%。因而适用于海量数据的场景。
HyperLogLog 的长处是,在输入元素的数量或许体积十分十分大时,核算基数所需的内存空间总是固定的、并且是很小的。在 Redis 里边,每个 HyperLogLog 键只需求花费 12 KB 内存,就能够核算挨近 2^64 个不同元素的基数,和元素越多就越消耗内存的 Set 和 Hash 类型比较,HyperLogLog 就十分节约空间。
百万级网页 UV 计数
在核算 UV 时,能够用 PFADD 指令(用于向 HyperLogLog 中增加新元素)把拜访页面的每个用户都增加到 HyperLogLog 中。
PFADD page1:uv user1 user2 user3 user4 user5
# 能够用 PFCOUNT 指令直接取得 page1 的 UV 值,获取核算成果
PFCOUNT page1:uv
GEO(3.2 版新增)
存储地理方位信息的场景
Redis GEO 操作办法有:
- geoadd:增加地理方位的坐标。
- geopos:获取地理方位的坐标。
- geodist:核算两个方位之间的间隔。
- georadius:依据用户给定的经纬度坐标来获取指定规模内的地理方位调集。
- georadiusbymember:依据贮存在方位调集里边的某个地址获取指定规模内的地理方位调集。
- georadius:以给定的经纬度为中心, 回来键包括的方位元素傍边, 与中心的间隔不超越给定最大间隔的一切方位元素。
GEORADIUS办法参数:
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
参数阐明:
- m :米,默许单位。
- km :千米。
- mi :英里。
- ft :英尺。
- WITHDIST: 在回来方位元素的一起, 将方位元素与中心之间的间隔也同时回来。
- WITHCOORD: 将方位元素的经度和维度也同时回来。
- WITHHASH: 以 52 位有符号整数的方式, 回来方位元素通过原始 geohash 编码的有序调集分值。这个选项首要用于底层运用或许调试, 实践中的效果并不大。
- COUNT 限制回来的记载数。
- ASC: 查找成果依据间隔从近到远排序。
- DESC: 查找成果依据从远到近排序。
滴滴叫车
假定车辆 ID 是 33,经纬度方位是(116.034579,39.030452),能够用一个 GEO 调集保存一切车辆的经纬度,调集 key 是 cars:locations。
GEOADD cars:locations 116.034579 39.030452 33
当用户想要寻觅自己邻近的网约车时,LBS 运用就能够运用 GEORADIUS 指令。
例如,LBS 运用履行下面的指令时,Redis 会依据输入的用户的经纬度信息(116.054579,39.030452 ),查找以这个经纬度为中心的 5 公里内的车辆信息,并回来给 LBS 运用。
GEORADIUS cars:locations 116.054579 39.030452 5 km ASC COUNT 10
邻近的人
nearbyPeople 是一个总的 key,user_1 和 user_2 是相当于 nearbyPeople 里边的两个元素以及他们对应的经纬度,这个比方便是把 user_1 和 user_2 的经纬度存在了 nearbyPeople 这个 key 中
redis> GEOADD nearbyPeople 13.36 38.11 "user_1" 15.08 37.50 "user_2"
(integer) 2
获取 nearbyPeople 中的元素 user_1 和 user_2 这两个元素的经纬度,当然假如之前没有 geoadd 相对应元素的经纬度的话,会回来 nil
redis> GEOPOS nearbyPeople user_1 user_21) 1) "13.36138933897018433" 2) "38.11555639549629859"2) 1) "15.08726745843887329" 2) "37.50266842333162032"
获取 nearbyPeople 中 user_1 和 user_2 这两个节点之间的间隔,间隔单位能够指定,如下所示:
- m :米,默许单位。
- km :千米。
- mi :英里。
- ft :英尺。
redis> GEODIST nearbyPeople user_1 user_2"166274.1516"redis> GEODIST nearbyPeople user_1 user_2 km"166.2742"redis> GEODIST nearbyPeople user_1 user_2 mi"103.3182"
把 nearbyPeople 中的 间隔经纬度(15,37)200km 以内的元素都找出来,并且带上间隔:
redis>GEORADIUS nearbyPeople 15 37 200 km WITHDIST
1) 1) "user_1"
2) "190.4424"
2) 1) "user_2"
2) "56.4413"
Stream(5.0 版新增)
音讯行列,处理了依据 List 类型完结的音讯行列中存在的两个问题。
能够主动生成大局仅有音讯ID,并支撑以消费组方式消费数据。
面试题专栏
Java面试题专栏已上线,欢迎拜访。
- 假如你不知道简历怎样写,简历项目不知道怎样包装;
- 假如简历中有些内容你不知道该不该写上去;
- 假如有些归纳性问题你不知道怎样答;
那么能够私信我,我会尽我所能协助你。