Redis学习总结

文章目录

  • 前言
  • 一、全局命令
        • 1.查看所有键
        • 2.键总数
        • 3..检查键是否存在
        • 4.删除键
        • 5.键过期
        • 6.键的数据结构类型
  • 二、数据结构喝内部编码
  • 三、字符串
        • 1.字符串类型命令和时间复杂度
        • 2.内部编码
        • 3.典型使用场景
  • 四、哈希
        • 1.哈希类型命令和时间复杂度
        • 2.内部编码
        • 3.使用场景
  • 五、列表
        • 1.列表类型命令和时间复杂度
        • 2.内部编码
        • 3.使用场景
  • 六、集合
        • 1.集合类型常用命令和时间复杂度
        • 2. 内部编码
        • 3.使用场景
  • 七、有序集合
        • 1.命令和时间复杂度
        • 2.内部编码
        • 3.使用场景
  • 八、遍历键
        • 1.全量遍历键
        • 2.渐进式遍历

前言

主要写下redis得一些命令,耗时,使用场景,注意点等等。

一、全局命令

1.查看所有键

keys *

127.0.0.1:6379> keys * 
1) "python" 
2) "java" 
3) "hello"

2.键总数

dbsize

127.0.0.1:6379> dbsize 
(integer) 4

3…检查键是否存在

exists key

如果键存在则返回1,不存在则返回0。

4.删除键

del key [key ...]

返回结果为成功删除键的个数,假设删除一个不存在的键,就会返回
0。

5.键过期

expire key seconds

Redis支持对键添加过期时间,当超过过期时间后,会自动删除键。
ttl命令会返回键的剩余过期时间,它有3种返回值:

  • 大于等于0的整数:键剩余的过期时间。
  • -1:键没设置过期时间。
  • -2:键不存在
#还剩7秒 
127.0.0.1:6379> ttl hello 
(integer) 7 ... 
#还剩1秒 
127.0.0.1:6379> ttl hello 
(integer) 1 
#返回结果为-2,说明键hello已经被删除 
127.0.0.1:6379> ttl hello 
(integer) -2 
127.0.0.1:6379> get hello 
(nil)

6.键的数据结构类型

type key

如果键不存在,则返回none。

二、数据结构喝内部编码

对外,type命令会返回5种数据结构,它们分别是: string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集 合)。
Redis学习总结_第1张图片
实际上每种数据结构都有自己底层的内部编码实现,而且是多种实现, 这样Redis会在合适的场景选择合适的内部编码。
Redis学习总结_第2张图片
种内部编码实现可以在不同场景下发挥各自的优 势
例子:例如ziplist比较节省内存,但是在列表元素比较多的情况下,性能会有 所下降,这时候Redis会根据配置选项将列表类型的内部实现转换为 linkedlist。

三、字符串

1.字符串类型命令和时间复杂度

Redis学习总结_第3张图片

2.内部编码

  • int:8个字节的长整型。
  • embstr:小于等于39个字节的字符串。
  • raw:大于39个字节的字符串。
    Redis会根据当前值的类型和长度决定使用哪种内部编码实现。

3.典型使用场景

缓存功能
例如:中Redis作为缓存层,MySQL作 为存储层,绝大部分请求的数据都是从Redis中获取。由于Redis具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用。

计数
许多应用都会使用Redis作为计数的基础工具,它可以实现快速计数、
查询缓存的功能,同时数据可以异步落地到其他数据源。

共享Session
使用Redis将用户的Session进行集中管理,每次用户 更新或者查询登录信息都直接从Redis中集中获取。

限速
在进行登录时,让用户输入手机验证码,从而确定是否是用户本人。但是为了短信接口不被频繁访问,会限制用 户每分钟获取验证码的频率,例如一分钟不能超过5次。
此功能可以使用Redis来实现,下面的伪代码给出了基本实现思路:

phoneNum = "138xxxxxxxx"; 
key = "shortMsg:limit:" + phoneNum; 
// SET key value EX 60 NX 
isExists = redis.set(key,1,"EX 60","NX"); 
if(isExists != null || redis.incr(key) <=5){
    // 通过 
}else{ 
       // 限速 
 }

四、哈希

1.哈希类型命令和时间复杂度

Redis学习总结_第4张图片

2.内部编码

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries 配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64 字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的 结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。
  • hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使 用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而 hashtable的读写时间复杂度为O(1)。

3.使用场景

当作数据库表格使用理解,就是去模拟关系型复杂查询开发困难,维护成本高;一行一个哈希。

五、列表

1.列表类型命令和时间复杂度

Redis学习总结_第5张图片

Redis学习总结_第6张图片

2.内部编码

  • ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置 (默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时 (默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使
    用。
  • linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用 linkedlist作为列表的内部实现。

Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节 点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一 种更为优秀的内部编码实现,它的设计原理可以参考Redis的另一个作者 Matt Stancliff的博客:https://matt.sh/redis-quicklist

3.使用场景

消息队列
Redis的lpush+brpop命令组合即可实现阻塞队列,生产 者客户端使用lrpush从列表左侧插入元素,多个消费者客户端使用brpop命令 阻塞式的“抢”列表尾部的元素,多个客户端保证了消费的负载均衡和高可用
性。

文章列表
1)每篇文章使用哈希结构存储,例如每篇文章有3个属性title、 timestamp、content。
2)向用户文章列表添加文章,user:{id}:articles作为用户文章列表的键。
3)分页获取用户文章列表,例如下面伪代码获取用户id=1的前10篇文章。
tip:使用列表类型保存和获取文章列表会存在两个问题。第一,如果每次分 页获取的文章个数较多,需要执行多次hgetall操作,此时可以考虑使用 Pipeline(第3章会介绍)批量获取,或者考虑将文章数据序列化为字符串类 型,使用mget批量获取。第二,分页获取文章列表时,lrange命令在列表两
端性能较好,但是如果列表较大,获取列表中间范围的元素性能会变差,此 时可以考虑将列表做二级拆分,或者使用Redis3.2的quicklist内部编码实现, 它结合ziplist和linkedlist的特点,获取列表中间范围的元素时也可以高效完
成。

开发提示
实际上列表的使用场景很多,在选择时可以参考以下口诀:

  • lpush+lpop=Stack(栈)
  • lpush+rpop=Queue(队列)
  • lpsh+ltrim=Capped Collection(有限集合)
  • lpush+brpop=Message Queue(消息队列)

六、集合

1.集合类型常用命令和时间复杂度

集合命令可以按照集合内和集合间两个维度进行分类
Redis学习总结_第7张图片

2. 内部编码

  • intset(整数集合):当集合中的元素都是整数且元素个数小于set-maxintset-entries配置(默认512个)时,Redis会选用intset来作为集合的内部实
    现,从而减少内存的使用。
  • hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使 用hashtable作为集合的内部实现。

)当元素个数较少且都为整数时,内部编码为intset;当元素个数超过512个,内部编码变为hashtable;当某个元素不为整数时,内部编码也会变为hashtable。

3.使用场景

集合类型比较典型的使用场景是标签(tag)。

  • sadd=Tagging(标签)
  • spop/srandmember=Random item(生成随机数,比如抽奖)
  • sadd+sinter=Social Graph(社交需求)

七、有序集合

它保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。
但是它和列表使用索引下标作为 排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依 据。
有序集合中的元素不能重复,但是score可以重复,就和一个班里的同学学号不能重复,但是考试成绩可以相同。
Redis学习总结_第8张图片

1.命令和时间复杂度

Redis学习总结_第9张图片

2.内部编码

  • ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplistentries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配 置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使用。
  • skiplist(跳跃表):当ziplist条件不满足时,有序集合会使用skiplist作 为内部实现,因为此时ziplist的读写效率会下降。

当元素个数较少且每个元素较小时,内部编码为skiplist;当元素个数超过128个,内部编码变为ziplist;当某个元素大于64字节时,内部编码也会变为hashtable

3.使用场景

有序集合比较典型的使用场景就是排行榜系统。例如视频网站需要对用
户上传的视频做排行榜,榜单的维度可能是多个方面的:按照时间、按照播
放数量、按照获得的赞数。

使用赞数这个维度作为例子,主要实现下列功能
(1)添加用户赞数

例如用户mike上传了一个视频,并获得了3个赞,可以使用有序集合的 zadd和zincrby功能
zadd user:ranking:2016_03_15 mike 3
如果之后再获得一个赞,可以使用zincrby:
zincrby user:ranking:2016_03_15 mike 1

(2)取消用户赞数

由于各种原因(例如用户注销、用户作弊)需要将用户删除,此时需要 将用户从榜单中删除掉,可以使用zrem。
zrem user:ranking:2016_03_15 mike

(3)展示获取赞数最多的十个用户

zrevrangebyrank user:ranking:2016_03_15 0 9

(4)展示用户信息以及用户分数

此功能将用户名作为键后缀,将用户信息保存在哈希类型中,至于用户 的分数和排名可以使用zscore和zrank两个功能.

hgetall user:info:tom 
zscore user:ranking:2016_03_15 mike 
zrank user:ranking:2016_03_15 mike

八、遍历键

1.全量遍历键

keys pattern
  • *代表匹配任意字符。
  • 代表匹配一个字符。
  • []代表匹配部分字符,例如[1,3]代表匹配1,3,[1-10]代表匹配1到10的任意数字。
  • \x用来做转义,例如要匹配星号、问号需要进行转义。

考虑到Redis的单线程架构就不那么美妙了,如果Redis包含了 大量的键,执行keys命令很可能会造成Redis阻塞,所以一般建议不要在生 产环境下使用keys命令。

可以在以下三种情况使用:

  • 在一个不对外提供服务的Redis从节点上执行,这样不会阻塞到客户端 的请求,但是会影响到主从复制,有关主从复制我们将在第6章进行详细介绍。
  • 如果确认键值总数确实比较少,可以执行该命令。
  • 使用下面要介绍的scan命令渐进式的遍历所有键,可以有效防止阻塞。

2.渐进式遍历

scan cursor [match pattern] [count number]

  • cursor是必需参数,实际上cursor是一个游标,第一次遍历从0开始,每 次scan遍历完都会返回当前游标的值,直到游标值为0,表示遍历结束。
  • match pattern是可选参数,它的作用的是做模式的匹配,这点和keys的模式匹配很像。
  • count number是可选参数,它的作用是表明每次要遍历的键个数,默认 值是10,此参数可以适当增大。

使用时参考如下伪代码

String key = "myset"; 
// 定义pattern 
String pattern = "old:user*"; 
// 游标每次从0开始 
String cursor = "0"; 
while (true) {
    // 获取扫描结果    
    ScanResult scanResult = redis.sscan(key, cursor, pattern);    
    List elements = scanResult.getResult();    
    if (elements != null && elements.size() > 0) { 
           // 批量删除        
           redis.srem(key, elements);    
    }    
    // 获取新的游标    
    cursor = scanResult.getStringCursor();   
    // 如果游标为0表示遍历结束    
    if ("0".equals(cursor)) {
        break;    
    } 
}

除了scan以外,Redis提供了面向哈希类型、集合类型、有序集合的扫 描遍历命令,解决诸如hgetall、smembers、zrange可能产生的阻塞问题,对 应的命令分别是hscan、sscan、zscan,它们的用法和scan基本类似。

渐进式遍历可以有效的解决keys命令可能产生的阻塞问题,但是scan并 非完美无瑕,如果在scan的过程中如果有键的变化(增加、删除、修改),那么遍历效果可能会碰到如下问题:新增的键可能没有遍历到,遍历出了重 复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键,这些是
我们在开发时需要考虑的。

你可能感兴趣的:(Redis)