关于java:谈谈Redis五种数据结构及真实应用场景

13次阅读

共计 6060 个字符,预计需要花费 16 分钟才能阅读完成。

<img src=”http://www.image.jincou.com/ad4dae3acbc54f07809725290417c1b4″ width=”550″ height=”582″>

前言

如果问你 redis 有哪些数据结构,你必定能够一口气说出五种根本数据结构: String(字符串)Hash(哈希)List(列表)Set(汇合)zset(有序汇合)

你或者还晓得它还有三种非凡的数据结构类型:Geospatial、Hyperloglog、Bitmap。

但如果问你在理论我的项目中用了哪些数据结构。你是不是感觉如同大大部分只是用了 String 的数据结构,就算缓存一个对象,也只是通过 JSONObject.toJSONString(object)将它转为 String 存储。取的时候在把这个 json 字符串转为对象。

那么既然 redis 提供了 5 种根本数据结构,必定都有特定的利用场合。

接下来会针对 5 种根本数据类型,来演示在理论开发中的利用场景。

一、String(字符串)

1. 简介

String 类型是 Redis 中最根本、最罕用的数据类型,甚至被很多玩家当成 Redis 惟一的数据类型去应用。String 类型在 Redis 中是二进制平安 (binary safe) 的, 这意味着 String 值关怀二进制的字符串,不关怀具体格局,你能够用它存储 json 格局或 JPEG 图片格式的字符串。

2、外部编码

如果存储数字的话,是用 int(8 字节长整型)类型的编码; 如果存储非数字,小于等于 39 字节的字符串,是embstr 编码;大于 39 个字节,则是 raw 编码。

无关 redis 的数据外部编码抽空整顿一篇文章独自写

3、应用场景

(1) 存储一些配置数据

在前后分离式开发中,有些数据尽管存储在数据库,然而更改特地少。比方有个 全国地区表。以后端发动申请后,后盾如果每次都从关系型数据库读取,会影响网站整体性能。

咱们能够在第一次拜访的时候,将所有地区信息存储到 redis 字符串中,再次申请,间接从数据库中读取地区的 json 字符串,返回给前端。

(2) 缓存对象

将对象转为 json 存储,比方商品信息,用户信息。

(3) 数据统计

redis 整型能够用来记录网站访问量,某个文件的下载量, 签到人数、视频访问量等等。(自增自减

(4) 工夫内限度申请次数

比方已登录用户申请短信验证码,验证码在 5 分钟内无效的场景。
当用户首次申请了短信接口,将用户 id 存储到 redis 曾经发送短信的字符串中,并且设置过期工夫为 5 分钟。当该用户再次申请短信接口,发现曾经存在该用户发送短信记录,则不再发送短信。

(5) 订单号(全局惟一)

有时候你须要去生成一个全局惟一值的时候能够通过 redis 生成。要害命令:incrby(原子自增)。

SET order_no 2001 -- 假如订单号从 2001 开始, 这里 vlaue 必须是 int 类型
INCRBY order_no 1 -- 自增 1,这个时候返回 2002

(6) 分布式 session

当咱们用 nginx 做负载平衡的时候,如果咱们每个从服务器上都各自存储本人的 session,那么当切换了服务器后,session 信息会因为不共享而会失落,咱们不得不思考第三利用来存储 session。

通过咱们用关系型数据库或者 Redis 等非关系型数据库。关系型数据库存储和读取性能远远无奈跟 Redis 等非关系型数据库比。

4、常用命令

-- 增
set mykey "test"       -- 为键设置新值,并笼罩原有值
setex mykey 10 "hello" -- 设置指定 Key 的过期工夫为 10 秒, 在存活工夫能够获取 value
mset key3 "stephen" key4 "liu" -- 批量设置键
-- 删
del mykey -- 删除已有键
-- 改
incr mykey      -- 值减少 1, 若该 key 不存在, 创立 key, 初始值设为 0, 减少后后果为 1
decrby mykey 5  -- 值缩小 5
-- 查 
exists mykey   -- 判断该键是否存在,存在返回 1,否则返回 0
get mykey      -- 获取 Key 对应的 value
mget key3 key4 -- 批量获取键

二、Hash(哈希)

1、简介

Hash 的数据结构咱们能够简略了解为 java 中的 Map<String,Map<String,String>, 这种构造就特地适宜存储对象,下面的 String 的类型的确也能够存储对象,但每次批改对象中的某一个属性,都要拿出整个 json 字符串在批改这个属性,之后在从新插入,而 hash 的接口特点让咱们能够只批改该对象的某一个属性。

hash 数据类型在存储上述类型的数据时具备比 String 类型更灵便、更快的劣势,具体的说,应用 String 类型存储,必然须要转换和解析 json 格局的字符串,即使不须要转换,在内存开销方面,还是 hash 占优势。

2、外部编码

哈希类型元素个数小于 512 个,所有值小于 64 字节的话,应用 ziplist 编码, 否则应用 hashtable 编码。

4、应用场景

(1) Redisson 分布式锁

Redisson 在实现分布式锁的时候,外部的用的数据就是 hash 而不是 String。因为 Redisson 为了实现可重入加锁机制。所以在 hash 中存入了以后线程 ID。

(2) 购物车列表

用户 id 为 key商品 id 为 field商品数量为 value,恰好形成了购物车的 3 个因素,如下图所示。

<img src=”http://www.image.jincou.com/98b0d46aa5054c4daed3c11a3325dc09″ width=”400″ height=”450″>

这里波及的命令如下

hset cart:{用户 id} {商品 id} 1  # 增加商品

hincrby cart:{用户 id} {商品 id} 1 # 减少数量

hlen cart:{用户 id} # 获取商品总数

hdel cart:{用户 id} {商品 id} # 删除商品

hgetall cart:{用户 id} 获取购物车所有商品

阐明: 以后仅仅是将商品 ID 存储到了 Redis 中, 在回显商品具体信息的时候, 还须要拿着商品 id 查问一次数据库。

(3) 缓存对象

hash 类型的 (key, field, value) 的构造与对象的 (对象 id, 属性, 值) 的构造类似,也能够用来存储对象。

在介绍 String 类型的利用场景时有所介绍,String + json 也是存储对象的一种形式,那么存储对象时,到底用 String + json 还是用 hash 呢?

两种存储形式的比照如下表所示。

string + json hash
效率 很高
容量
灵活性
序列化 简略 简单

个别对象用 string + json 存储,对象中某些频繁变动的属性能够思考抽出来用 hash 存储

3、常用命令

-- 增
hset key field1 "s"   -- 若字段 field1 不存在, 创立该键及与其关联的 Hash, Hash 中,key 为 field1 , 并设 value 为 s,若字段 field1 存在, 则笼罩 
hmset key field1 "hello" field2 "world" -- 一次性设置多个字段    
-- 删
hdel key field1 -- 删除 key 键中字段名为 field1 的字段
del key  -- 删除键    
-- 改 
hincrby key field 1 -- 给 field 的值加 1
-- 查
hget key field1 -- 获取键值为 key, 字段为 field1 的值
hlen key        -- 获取 key 键的字段数量
hmget key field1 field2 field3 -- 一次性获取多个字段
hgetall key -- 返回 key 键的所有 field 值及 value 值
hkeys key   -- 获取 key 键中所有字段的 field 值
hvals key   -- 获取 key 键中所有字段的 value 值

三、List(列表)

1、简介

List 类型是依照 插入程序排序的字符串链表,一个列表最多能够存储 2^32- 1 个元素。咱们能够简略了解为就相当于 java 中的LinkesdList

和数据结构中的一般链表一样,咱们能够在其头部 (left) 和尾部 (right) 增加新的元素。在插入时,如果该键并不存在,Redis 将为该键创立一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。

<img src=”http://www.image.jincou.com/ecb51b2337954a1090f9a8e9e312414a” width=”650″ height=”220″>

2、外部编码

如果列表的元素个数小于 512 个,列表每个元素的值都小于 64 字节(默认),应用 ziplist 编码,否则应用 linkedlist 编码。

在 Redis 3.2 之后就都改用 ziplist+ 链表的混合结构,称之为 quicklist(疾速链表)。

2、应用场景

有人会思考用 list 数据结构来做一些朋友圈的点赞列表、评论列表、排行榜。也不是不能够但我集体感觉这些性能用 set 或 zset 来做会更加适合,上面会具体举例。

(1) 音讯队列

lpop 和 rpush(或者反过来,lpush 和 rpop)能实现队列的性能。

但如果是这样你发现 redis 作为音讯队列是不平安的,它 不能反复生产,一旦生产就会被删除 , 同时做消费者确认 ACK 也麻烦 所以个别在理论开发中个别很少用 redis 中音讯队列,因为当初曾经有 Kafka、NSQ、RabbitMQ 等成熟的音讯队列了,它们的性能更加欠缺。

4、常用命令

-- 增 
lpush mykey a b -- 若 key 不存在, 创立该键及与其关联的 List, 顺次插入 a ,b,若 List 类型的 key 存在, 则插入 value 中
rpush mykey a b -- 在链表尾部先插入 b, 在插入 a(lpush list a b 那么读的时候是 b,a 的程序,而 rpush 是怎么放怎么读出来
-- 删
del mykey       -- 删除已有键 
-- 改
lset mykey 1 e        -- 从头开始, 将索引为 1 的元素值, 设置为新值 e, 若索引越界, 则返回错误信息
-- 查
lrange mykey 0 -1 -- 取链表中的全副元素,其中 0 示意第一个元素,- 1 示意最初一个元素。lrange mykey 0 2  -- 从头开始, 取索引为 0,1,2 的元素
lpop mykey        -- 获取头部元素, 并且弹出头部元素, 出栈

四、set(汇合)

1、简介

Redis 中的 set 和 Java 中的 HashSet 有些相似,它外部的键值对是 无序的、惟一的。它的外部实现相当于一个非凡的字典,字典中所有的 value 都是一个值 NULL。当汇合中最初一个元素被移除之后,数据结构被主动删除,内存被回收。

2、编码

如果汇合中的元素都是整数且元素个数小于 512 个,应用 intset 编码,否则应用 hashtable 编码。

利用场景

既然 set 的汇合的个性是:无序的、惟一的。那么咱们思考在一些惟一的场景下应用它。

(1) 抽奖流动

存储某流动中中奖的用户 ID,因为有去重性能,能够保障同一个用户不会中奖两次。

sadd user 1 2 3 4 5  -- 把所有员工 (名称或编号) 放入抽奖箱   

srandmember user 1   -- 抽取一个一等奖(员工能够反复参加抽奖)

spop user 1   -- 抽取一个一等奖(员工不能够反复参加抽奖)

srandmember user 3 -- 抽取 3 个二等奖

smembers user -- 查看以后抽奖箱中参所有员工

scard user  -- 查看以后抽奖箱中参加抽奖的人数

(2) 点赞

保障一个用户只能点一个赞。key 能够是某某文章、微信朋友圈的文章 id

sadd key userId  -- 点赞(/ 珍藏) 

srem key userId  -- 勾销点赞(/ 珍藏)smembers key     -- 获取所有点赞 (/ 珍藏) 用户  

card key  -- 获取点赞用户数量

sismember key userId -- 判断是否点赞(/ 珍藏)

(3) 好友人脉

key 能够是 用户 id

sadd userId1 1 2 3 4 5 
sadd userId2 4 5 6 7 8 -- 某个 user 的好友 id 放入汇合

sinter userId1 userId2 -- 获取独特好友

sdiff userId1 userId2 -- 给 user2 举荐 user1 的好友

sismember userId1 5
sismember userId2 5 -- 验证某个用户是否同时被 user1 和 user2 关注

4、常用命令

-- 增
sadd myset a b c -- 若 key 不存在, 创立该键及与其关联的 set, 顺次插入 a ,b,c。若 key 存在, 则插入 value 中, 若 a 在 myset 中曾经存在, 则插入了 b 和 c 两个新成员。-- 删
spop myset       -- 尾部的 b 被移出, 事实上 b 并不是之前插入的第一个或最初一个成员
srem myset a d f -- 若 f 不存在, 移出 a、d , 并返回 2

-- 改
smove myset myset2 a -- 将 a 从 myset 移到 myset2,-- 查
sismember myset a -- 判断 a 是否曾经存在,返回值为 1 示意存在。smembers myset    -- 查看 set 中的内容
scard myset       -- 获取 Set 汇合中元素的数量
srandmember myset -- 随机的返回某一成员

五、zset(有序汇合)

1、简介

Sorted-Sets 中的每一个成员都会有一个分数 (score) 与之关联,Redis 正是通过分数来为汇合中的成员进行从小到大的排序。成员是惟一的,然而分数 (score) 却是能够反复的

2、数据编码

当有序汇合的元素个数小于 128 个,每个元素的值小于 64 字节时,应用 ziplist 编码,否则应用skiplist(跳跃表)编码

3、利用场景

既然是 有序的,不可反复的列表,那么就能够做一些排行榜相干的场景。

1) 排行榜(商品销量,视频评分,用户游戏分数)

  1. 新闻热搜。

4、常用命令

-- 增
zadd key 2 "two" 3 "three" -- 增加两个分数别离是 2 和 3 的两个成员
-- 删
zrem key one two  -- 删除多个成员变量, 返回删除的数量
-- 改
zincrby key 2 one -- 将成员 one 的分数减少 2,并返回该成员更新后的分数(分数扭转后相应它的 index 也会扭转)-- 查 
zrange key 0 -1 WITHSCORES -- 返回所有成员和分数, 不加 WITHSCORES, 只返回成员
zrange key start stop -- 依照元素的分值从小到大的程序返回从 start 到 stop 之间的所有元素
zscore key three -- 获取成员 three 的分数
zrangebyscore key 1 2 -- 获取分数满足表达式 1 < score <= 2 的成员
zcard key       -- 获取 myzset 键中成员的数量
zcount key 1 2  -- 获取分数满足表达式 1 <= score <= 2 的成员的数量
zrank key member -- 获取元素的排名,从小到大
zrevrank key member -- 获取元素的排名,从大到小

这篇文章就先写到这里,无关 redis 的数据外部编码,抽空在独自写一篇文章。

申明 : 公众号如需转载该篇文章, 那麻烦在文章的头部 申明是转至公众号: 后端元宇宙。尊重作者辛苦劳动果实嘛。同时也能够问自己要该文章 markdown 原稿和原图片。其它状况一律禁止转载哦!

本文由博客一文多发平台 OpenWrite 公布!

正文完
 0