Redis核心数据结构应用场景与高性能原理刨析
- 一、Redis的数据结构
- 1、String
- 1.1、单值缓存
- 1.2、对象缓存
- 1.3、分布式锁
- 1.4、计数器
- 2、Hash
- 3、List
- 4、Set
- 5、ZSet
- 二、Redis高性能原理
- 1、Redis是单线程的吗?
- 2、Redis单线程快的原因
- 3、Redis单线程能够处理那么多的并发客户端连接的原因
- 三、Redis的范围查找
- 1、keys *
- 2、scan渐进式遍历键
一、Redis的数据结构
Redis一共有5种数据结构,分别是String(字符串)、Hash(哈希)、List(链表)、Set(集合)、ZSet(有序集合)
1、String
1.1、单值缓存
(1)set key value:设置key的值
(2)get key:获取指定key的值
1.2、对象缓存
将对象式的value抽象成一个JSON格式,然后进行缓存
(1)单个操作
set key value:设置key的值
get key:获取指定key的值
(2)批量操作
mset key1 value1 key2 value2
mget key1 key2
1.3、分布式锁
命令 | 含义 |
setnx product:100 true | 获取当前key的锁,返回1代表取锁成功 |
setnx product:100 true | 获取当前key的锁,返回0代表取锁失败 |
del product:100 | 删除当前key |
set product:100 true ex 10 nx | 设置超时时间,默认为秒 |
1.4、计数器
命令 | 含义 |
incr key | 给当前key值的积分加1 |
get key | 获取当前key值的积分 |
指定增量计数器:incrby key 数值–>给对应的key加上某值
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对象 |
(3)优点
①同类数据归类整合储存,方便数据管理。
②相比string操作消耗内存与cpu更小
③相比string存储更节省空间
(4)缺点
①过期功能不能使用在field上,只能使用在key上
②redis集群架构下不适合大规模使用
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)应用场景
作为分布式数据结构:
①栈Stack=LPUSH+LPOP;
②队列Queue=LPUSH+RPOP ;
③阻塞队列MQ=LPUSH+BRPOP;
4、Set
(1)常用操作
命令 | 含义 |
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)运算操作
命令 | 含义 |
SINTER key [key …] | 交集运算 |
SINTERSTORE destination key [key …] | 将交集结果存入新集合destination中 |
SUNION key [key …] | 并集运算 |
SUNIONSTORE destination key [key …] | 将并集结果存入新集合destination中 |
SDIFF key [key …] | 差集运算 |
SDIFFSTORE destination key [key …] | SDIFFSTORE destination key [key …] |
(3)案例
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}
如图所示:
5、ZSet
(1)常用操作
命令 | 含义 |
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 …] | 交集计算 |
二、Redis高性能原理
1、Redis是单线程的吗?
其实严格意义上来说,Redis不是单线程的,比如Redis的持久化、异步删除、集群数据这些都是由额外的线程来完成的。
我们平时说的Redis单线程其实是是指从其它端发送给Redis的IO操作都是由一个线程来完成的,外部访问Redis时,Redis会将这些命令排好序,然后一个一个执行。
2、Redis单线程快的原因
(1)其所有数据都是存放在内存中,所有运算都是内存级别的运算
(2)单线程避免了多线程的上下文切换
(3)IO多路复用
因此,对于耗时指令、可能导致Redis卡顿;导致其他用户请求无法得到处理。
3、Redis单线程能够处理那么多的并发客户端连接的原因
Redis的IO多路复用:Redis利用epoll来实现IO多路复用、将连接信息和时间放到队列中、一次放到文件事件分派器、事件分派器将事件发给事件处理器。
与网络通信的NIO相关:
三、Redis的范围查找
1、keys *
(1)优点是简单,初学者拿来即用。(但通常运维大神会在redis服务端禁用该命令),
(2)缺点是可能会直接导致redis服务宕机。**由于redis服务是单线程工作,每一条到达的指令都是串行执行,而Keys 命令会全量遍历缓存中的所有key,直到结束,如果redis中的key数量太多、所需要的时间就很长,**此刻请求redis服务的其它指令都将被阻塞,后台服务可能会因为超时而报错;
2、scan渐进式遍历键
SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
(1)cursor:游标;默认为0;
(2)pattern: 需要进行模糊查询的字符串;与keys的pattern相同;可省略;
(3)count:返回多少条符合的key; 可省略
(4)Type: 模糊查询返回的key的类型;可为StringHash,List,Set,
(5)Zset:可省略。
第 一次遍历时,cursor 值为 0,然后将返回结果中第一个整数值作为下一次遍历的 cursor。一直遍历 到返回的 cursor 值为 0 时结束。
注意:但是scan并非完美无瑕, 如果在scan的过程中如果有键的变化(增加、 删除、 修改) ,那 么遍历效果可能会碰到如下问题: 新增的键可能没有遍历到, 遍历出了重复的键等情况, 也就是说 scan并不能保证完整的遍历出来所有的键, 这些是我们在开发时需要考虑的。