Redis核心数据结构应用场景与高性能原理刨析

Redis核心数据结构应用场景与高性能原理刨析


后续有两个概念比较模糊,后续需要进行处理

1、Redis的数据结构

Redis有5种数据结构,分别为String(字符串)、Hash(哈希)、List(列表),Set(集合)、ZSet(有序集合)。

1.1、String

1、单值缓存
set key value
get key

Redis核心数据结构应用场景与高性能原理刨析

2、对象缓存
2.1、将对象式的value抽象成一个json格式,然后就可以跟单值缓存一样了
set user:1 value
get user:1

Redis核心数据结构应用场景与高性能原理刨析

2.2、批量操作,一次存多个键值对

命令 含义
mset user:1:name lele user:1:balance 1800 批量操作存储
mget user:1:name user:1:balance 批量操作获取

Redis核心数据结构应用场景与高性能原理刨析

3、分布式锁

命令 含义
setnx product:100 true 获取当前key的锁,返回1代表取锁成功
setnx product:100 true 获取当前key的锁,返回0代表取锁失败

执行相应的业务操作,加积分什么的

命令 含义
del product:100 删除当前key
set product:100 true ex 10 nx 设置超时时间,默认为秒

这几行命令的意思是多个线程同时对同一商品进行操作,只有第一个才能获取锁成功,获取到锁之后,进行一系列的业务操作,减去相应的库存,并将这个锁释放掉,但为了防止意外,可以设置这个锁的超时时间,单位是秒。

Redis核心数据结构应用场景与高性能原理刨析

4、计数器

命令 含义
incr article:readcount:{文章id} 给当前key值的积分加1
get article:readcount:{文章id} 获取当前key值的积分

这行命令的意思是每执行一次 incr article:readcount:{文章id} 命令,就会加1,当执行get命令时,就会获取到加的总数,比如我们平时看的微信小文章中的阅读次数就可以用这个做。

Redis核心数据结构应用场景与高性能原理刨析

5、指定增量计数器
incrby key 100
给对应的key加上100

Redis核心数据结构应用场景与高性能原理刨析

1.2、Hash

1、常用操作

命令 含义
HSET key field value 存储一个哈希表key的键值
HSETNX key field value 存储一个不存在的哈希表key的键值
HMSET key field value [field value …] 在一个哈希表key中存储多个键值对
HGET key field 获取哈希表key对应的field键值
HMGET key field [field …] 批量获取哈希表key中多个field键值
HDEL key field [field …] 删除哈希表key中的field键值
HLEN key 返回哈希表key中field的数量
HGETALL key 返回哈希表key中所有的键值
HINCRBY key field increment 为哈希表key中field键的值加上增量increment

2、对象缓存

命令 含义
hmset user {userid}:name lele {userid}:balance 100 设置hash对象
hmget user {userid}:name lele {userid}:balance 100 获取hash对象

hmset user 1:name lele 1:balance 100
hmget user 1:name 1:balance

Redis核心数据结构应用场景与高性能原理刨析

Redis核心数据结构应用场景与高性能原理刨析

hgetall key
获取全部的值

Redis核心数据结构应用场景与高性能原理刨析

优缺点:

优点

1、同类数据归类整合储存,方便数据管理。
2、相比string操作消耗内存与cpu更小
3、相比string存储更节省空间

缺点:

1、过期功能不能使用在field上,只能使用在key上
2、redis集群架构下不适合大规模使用

1.3、List

1、常用操作

命令 含义
LPUSH key value [value …] 将一个或多个值value插入到key列表的表头(最左边)
RPUSH key value [value …] 将一个或多个值value插入到key列表的表尾(最右边)
LPOP key 移除并返回key列表的头元素
RPOP key 移除并返回key列表的尾元素
LRANGE key start stop 返回列表key中指定区间内的元素,区间以偏移量start和stop指定
BLPOP key [key …] timeout 从key列表表头弹出一个元素,若列表中没有元素,阻塞等待timeout秒,如果timeout=0,一直阻塞等待
BRPOP key [key …] timeout 从key列表表尾弹出一个元素,若列表中没有元素,阻塞等待timeout秒,如果timeout=0,一直阻塞等待

2、常用数据结构也可以用redis的list轻松实现
Stack(栈) = LPUSH + LPOP
Queue(队列)= LPUSH + RPOP Blocking
MQ(阻塞队列)= LPUSH + BRPOP
这个阻塞队列相当于,我要去里面拿东西,但是现在里面没有了,所以我就执行 BRPOP 让队列阻塞,知道队列中有了,我再执行,可以设置超时时间。

3、案例
平时我们刷的微博或者朋友圈等 ,每次的消息都是发的时间最晚的在最上面,这种用redis也可以实现
比如小明现在关注了 A 和 B ,当前,A 先发了一条消息,随后,B 也发了一条消息,B 发消息的时间比 A 的晚,那么在小明这里的排序应该是 B的消息在上面,A 的消息在下面,这里其实是当有消息时,我们可以用 LPUSH 命令将消息放到队列中,然后使用 LRANGE 命令将需要展示的消息展示出来。但是这里我们就会发现一个问题,每一个人都会有自己的队列,然后通过 LRANGE 命令去操作它,这里之前解决办法有两种方案,一种是push,一种是pull。
push是每发一条消息,会把自己的消息id全部告诉粉丝,然后粉丝存到自己的队列中,这种在一个人有上亿粉丝时就不太适用了,但是有些业务场景可以做成先发给在线粉丝,非在线的后续可以慢慢发。
pull是每发一条消息,然后把这个消息放到一个公共的地方,粉丝们自己去访问然后放到自己的队列中。

1.4、Set

1、Set常用操作

命令 含义
SADD key member [member …] 往集合key中存入元素,元素存在则忽略,若key不存在则新建
SREM key member [member …] 从集合key中删除元素
SMEMBERS key 获取集合key中所有元素
SCARD key 获取集合key的元素个数
SISMEMBER key member 判断member元素是否存在于集合key中
SRANDMEMBER key [count] 从集合key中选出count个元素,元素不从key中删除
SPOP key [count] 从集合key中选出count个元素,元素从key中删除

2、Set运算操作

命令 含义
SINTER key [key …] 交集运算
SINTERSTORE destination key [key …] 将交集结果存入新集合destination中
SUNION key [key …] 并集运算
SUNIONSTORE destination key [key …] 将并集结果存入新集合destination中
SDIFF key [key …] 差集运算
SDIFFSTORE destination key [key …] 将差集结果存入新集合destination中

3、案例
抽奖,比如抽奖是下面这张图

Redis核心数据结构应用场景与高性能原理刨析

当有一个人点击抽奖时,我们执行 sadd key {userId} 命令去给set集合里面添加一个用户id。
当点击查看所有参与用户的时候,我们可以使用命令 smembers key 去显示出所有的用户。
抽取中奖用户的时候我们可以使用命令 SRANDMEMBER key [count] 或者 SPOP key [count],
SRANDMEMBER key [count] 命令只适用于抽一次奖的那种,因为筛选完之后,筛选出的用户还在set集合中;
SPOP key [count] 命令适合于多次抽奖,比如一、二、三等奖,因为筛选完之后,筛选到的用户会被移除set集合。

关注模型

me关注的人:{A1,A2,A4,A6,A8}
A1关注的人:{A2,A3,A4}
A2关注的人:{A1,A4,A5,A6}
A3关注的人:{A6,A7,A8}
A4关注的人:{A2,A5}

me和A2的共同关注:{A1,A4,A6}
A1关注的人也关注着A2:{A4} --也就是看A1关注的每一个人的关注列表中有没有A2
命令如下图:

Redis核心数据结构应用场景与高性能原理刨析

1.5、ZSet

1、Set常用操作

命令 含义
ZADD key score member [[score member]…] 往有序集合key中加入带分值元素
ZREM key member [member …] 从有序集合key中删除元素
ZSCORE key member 返回有序集合key中元素member的分值
ZINCRBY key increment member 为有序集合key中元素member的分值加上increment
ZCARD key 返回有序集合key中元素个数
ZRANGE key start stop [WITHSCORES] 正序获取有序集合key从start下标到stop下标的元素
ZREVRANGE key start stop [WITHSCORES] 倒序获取有序集合key从start下标到stop下标的元素

2、Zset集合操作

命令 含义
ZUNIONSTORE destkey numkeys key [key …] 并集计算
ZINTERSTORE destkey numkeys key [key …] 交集计算

3、案例

Redis核心数据结构应用场景与高性能原理刨析

这个微博热搜相信我们也是经常见到的,他们上面的排行以及阅读量都可以用redis来实现。
1、可以通过ZINCRBY key increment member命令给文章加上分值,也就是打开了多少次,默认是1。

Redis核心数据结构应用场景与高性能原理刨析

2、获取当天的排行可以用倒叙排序展示的命令
ZREVRANGE key start stop [WITHSCORES]
[WITHSCORES] 这个的意思是加上分值
我这里是乱码,能说明意思就好

Redis核心数据结构应用场景与高性能原理刨析

3、如果要展示七天的总和,我们可以求并集再排序
zunionstore hotNews:20180813-20180819 7 hotNews:20180813 hotNews:20180814 hotNews:20180815 hotNews:20180816 hotNews:20180817 hotNews:20180818 hotNews:20180819
这行命令的意思是对7个key求并集并存储到 hotNews:20180813-20180819 key中

Redis核心数据结构应用场景与高性能原理刨析

2、Redis高性能原理

2.1、Redis是单线程的吗?

其实严格意义上来说,Redis不是单线程的,比如Redis的持久化、异步删除、集群数据这些都是由额外的线程来完成的。
我们平时说的Redis单线程其实是是指从其它端发送给Redis的IO操作都是由一个线程来完成的,外部访问Redis时,Redis会将这些命令排好序,然后一个一个执行。

2.2、Redis单线程为什么还能这么快?

因为Redis的操作是在内存上的,所有的操作都是内存级别的,而且单线程也避免了多线程之间的线程切换损耗,但是在操作时一定要注意,尤其是bigKey,一旦操作失误就会造成Redis卡顿。

2.3、Redis 单线程如何处理那么多的并发客户端连接?

Redis的IO多路复用:redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,依次放到文件事件分派器,事件分派器将事件分发给事件处理器。
这块这个概念还是有点模糊,后续进行深层次的学习可能会有其它博客解释

Redis核心数据结构应用场景与高性能原理刨析

3、Redis高级命令

3.1、keys *

遍历全库,效率比较低,可支持通配符,如下图所示:

Redis核心数据结构应用场景与高性能原理刨析

3.2、scan渐进式遍历键

SCAN cursor [MATCH pattern] [COUNT count] scan 参数提供了三个参数,第一个是 cursor 整数值(hash桶的索引值),第二个是 key 的正则模式, 第三个是一次遍历的key的数量(参考值,底层遍历的数量不一定),并不是符合条件的结果数量。第 一次遍历时,cursor 值为0,然后将返回结果中第一个整数值作为下一次遍历的 cursor。一直遍历 到返回的 cursor 值为 0 时结束。
注意:但是scan并非完美无瑕, 如果在scan的过程中如果有键的变化(增加、 删除、 修改) ,那 么遍历效果可能会碰到如下问题:
新增的键可能没有遍历到, 遍历出了重复的键等情况(有可能在遍历期间又插入新数据,产生了rehash()), 也就是说 scan并不能保证完整的遍历出来所有的键, 这些是我们在开发时需要考虑的。
这块这个概念还是有点模糊,后续进行深层次的学习可能会有其它博客解释
代码示例如下:

Redis核心数据结构应用场景与高性能原理刨析

从图中就可以看出,每一次的结果不一定都是固定的,每一次返回的数字都是下一次扫描的起点游标,直到返回0结束。

上一篇:velocity的foreach遍历数据


下一篇:ORACLE里约束,查询修改及禁用主键、外键的方法