1、官网地址
- 官网地址:Redis
- 中文官网地址:CRUG网站
2、Redis命令大全
官网命令大全
直接搜索即可
注:命令不区分大小写,而key是区分大小写的,可使用 help @类型名词 查看
3、Redis 基本数据类型
以前是 5 种数据类型,现在是 8 种啦~
- String(字符类型)
- Hash(散列类型)
- List(列表类型)
- Set(集合类型)
- SortedSet(有序集合类型,简称zset)
- Bitmap(位图)
- HyperLogLog(统计)
- GEO(地理)
3.1、string
3.1.1、获取/设置单个值
设置:SET key value
获取:SET key
3.1.2、同时设置/获取多个键值
设置:MSET key value [key value ....]
获取:MGET key [key ....]
3.1.3、数值增减
递增数字:INCR key
增加指定的整数:INCRBY key increment
递减数值:DECR key
减少指定的整数:DECRBY key decrement
3.1.4、获取字符串长度
字符串长度:STRLEN key
3.1.5、分布式锁
设置分布式锁:set key value [Ex seconds][PX milliseconds][NX|XX]
参数解释:
EX:key 在多少秒之后过期
PX:key 在多少毫秒之后过期
NX:当 key 不存在的时候,才创建 key,效果等同于setnx key value
XX:当 key 存在的时候,覆盖 key
3.1.6、应用场景
1、商品编号、订单号采用 INCR 命令生成
2、文章阅读量、点赞数和在看数
3.2、hash
3.2.1、获取/设置单个字段值
redis 中的 hash 类似于 java 中的 Map<String,Map<Object,Object>> 数据结构,即以字符串为 key,以 Map 对象为 value
添加一个 hash 对象:HSET key field value
获取 hash 对象的字段值:HGET key field
3.2.2、同时设置/获取多个字段值
添加多个 hash 对象:HMSET key field value [field value ...]
获取多个 hash 对象的字段值:HMGET key field [field ....]
3.2.3、获取所有字段值
获取 key 所对应所有的 hash 对象:HGETALL key
3.2.4、获取某个 key 内的全部数量
获取 key 对应的所有 hash 对象个数:HLEN key
3.2.5、判断是否存在
判断字段名为 field 的 hash 对象是否存在:HEXISTS key field
3.2.6、删除一个 key
删除一个 hash 对象:HDEL key
3.2.7、数值增减
如何 key 和 field 不存在,则初始值为 0,否则在之前的数值上递增:HINCRBY key field increment
3.2.8、应用场景
购物车早期版本,可在小中厂项目中使用
- 新增商品:hset shopcar:uid1024 334488 1
- 新增商品:hset shopcar:uid1024 334477 1
- 增加商品数量:hincrby shopcar:uid1024 334477 1
- 商品总数:hlen shopcar:uid1024
- 全部选择:hgetall shopcar:uid1024
3.3、list
与其说 list 是个集合,还不如说 list 是个双端队列
3.3.1、添加元素 & 查看列表
- 向 list 左边添加元素,如果 list 不存在则创建该 list:LPUSH key value [value ...]
- 向 list 右边添加元素,如果 list 不存在则创建该 list:RPUSH key value [value ....]
- 查看 list 中包含的元素:LRANGE key start stop,注:LRANGE key 0 -1 表示查看 list 中所有的元素
3.3.2、删除元素
- 从左边出队:LPOP key
- 从右边出队:RPOP key
3.3.3、获取列表中元素的个数
查看 list 中包含几个元素:LLEN key
3.3.4、应用场景
微信文章订阅公众号
- 比如我订阅了如下两个公众号,他们发布了两篇文章,文章 ID 分别为 666 和 888,可以通过执行 LPUSH likearticle:onebyId 666 888 命令推送给我
- 查看我自己的号订阅的全部文章,类似分页,下面0~10就是一次显示10条:LRANGE likearticle:onebyId 0 10
3.4、set
3.4.1、添加元素 & 删除元素 & 查看元素
- 向 set 中添加一个元素:SADD key member[member ...]
- 删除 set 中的指定元素:SREM key member [member ...]
- 获取 set 中的所有元素:SMEMBERS key
3.4.2、判断元素是否在集合中
判断指定元素是否在 set 中:SISMEMBER key member
3.4.3、获取集合中的元素个数
获取 set 中元素的个数:SCARD key
3.4.4、从集合中随机弹出元素
- 从集合中随机弹出元素,元素不删除:SRANDMEMBER key [数字]
- 从集合中随机弹出一个元素,出几个删几个:SPOP key[数字]
3.4.5、集合运算
- 集合的差集运算A-B:SDIFF key [key ...],属于A但不属于B的元素构成的集合
- 集合的交集运算A∩B:SINTER key [key ...],属于A同时也属于B的共同拥有的元素构成的集合
- 集合的并集运算AUB:SUNIOn key [key ...],属于A或者属于B的元素合并后的集合
3.4.6、应用场景
1、微信抽奖小程序
- 如果某个用户点击了立即参与按钮,则执行 sadd key useId 命令将该用户 ID 添加至 set 中
- 显示已经有多少人参与了抽奖:SCARD key
- 抽奖(从set中任意选取N个中奖人)
随机抽奖2个人,元素不删除:SRANDMEMBER key 2
随机抽奖3个人,元素会删除:SPOP key 3
2、微信朋友圈点赞
- 新增点赞:SADD pub:msgID 点赞用户ID1 点赞用户ID2
- 取消点赞:SREM pub:msgID 点赞用户ID
- 展现所有点赞过的用户:SMEMBERS pub:msgID
- 点赞用户数统计,就是常见的点赞红色数字:SCARD pub.msgID
- 判断某个朋友是否对楼主点赞过:SISMEMBER pub:msgID 用户ID
3、Bilibili 共同关注的好友
共同关注的好友:SINTER 我关注的人 Ta关注的人
4、QQ内推可能认识的人
QQ 共同好友:SINTER 我的好友 Ta的好友
3.4、zset
形象理解 zset:向有序集合中加入一个元素和该元素的分数
3.4.1、添加元素 & 删除元素 & 获取元素
- 向 zset 中添加一个带分数(权值)的元素:ZADD key score member [score member ...]
- 删除 zset 中的指定元素:ZREM key member [member ...]
- 返回索引从start到stop之间的所有元素,并按照元素分数从小到大的顺序:ZRANGE key start stop [WITHSCORES],注:如果想要获取所有元素并且从小到大排序,可写为 ZRANGE key 0 -1
3.4.2、获取元素的分数
3.4.3、获取指定分数范围的元素
获取指定分数范围的元素:ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
3.4.4、获取集合中元素的数量
获取集合中元素的数量:ZCARD key
3.4.5、获得指定分数范围内的元素个数
获得指定分数范围内的元素个数:ZCOUNT key min max
3.4.6、加某个元素的分数
增加某个元素的分数:ZINCRBY key increment member
3.4.7、按照排名范围删除元素
按照排名范围删除元素:ZREMRANGEBYRANK key start stop
3.4.8、获取元素的排名
从小到大:ZRANK key member
从大到小:ZREVRANK key member
3.4.9、应用场景
1、根据商品销售对商品进行排序显示
思路:定义商品销售排行榜(sorted set集合),key为goods:sellsort,分数为商品销售数量。
①商品编号1001的销量是9,商品编号1002的销量是15:ZADD goods:sellsort 9 1001 15 1002
②有一个客户又买了2件商品1001,商品编号1001销量加2:ZINCRBY goods:sellsort 2 1001
③求商品销量前10名:ZRANGE goods:sellsort 0 10 WITHSCORES
2、抖音热搜
①点击视频增加播放量:ZINCRBY hotvcr:20200919 1八佰,ZINCRBY hotvcr:20200919 15 八佰 2 花木兰
②展示当日排行前10条:ZREVRANGE hotvcr:20200919 0 9 WITHSCORES
3.5、redis 事务相关命令
事务介绍
- Redis的事务是通过MULTl,EXEC,DISCARD和WATCH这四个命令来完成。
- Redis的单个命令都是原子性的,所以这里确保事务性的对象是命令集合。
- Redis将命令集合序列化并确保处于一事务的命令集合连续且不被打断的执行。
- Redis不支持回滚的操作。
3.5.1、MULTI
- 用于标记事务块的开始。
- Redis会将后续的命令逐个放入队列中,然后使用EXEC命令原子化地执行这个命令序列。
- 语法:MULTI
3.5.2、EXEC
- 在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态。
- 语法:EXEC
3.5.3、DISCARD
- 清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。
- 语法:DISCARD
3.5.4、WATCH
- 当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的状态。
- 语法:WATCH key[key..…]注:该命令可以实现redis的乐观锁
3.5.5、UNWATCH
- 清除所有先前为一个事务监控的键。
- 语法:UNWATCH
3.5.6、演示事务的使用
ClienA 使用 MULTI 命令开启一个事务,并执行 SET lock 1 命令,当开启事务之后,执行命令返回 QUEUED,表示已经将该命令压入队列
ClientB 执行 SET lock 2 命令将 lock 的值设置为 2
ClientA 执行 EXEC 命令提交事务,执行 get lock 命令获取到 lock 的值为 1(ClientB 虽然加塞,但不影响 ClientA 的事务,但这不是我们想要的效果)
3.5.7、演示 WATCH 的使用
ClientA 先使用 WATCH lock 命令见监视 lock 这把锁,然后使用 MULTI 命令开启事务修改 lock 的值
ClientB 执行 set lock 4 命令将 lock 的值修改为 4
ClientA 执行 EXEC 命令提交事务,发现命令的返回值为 nil,表示事务执行失败,使用 GET lock 获取 lock 变量的值为 4(修改失败),WATCH 之后记得使用 UNWATCH 解除监视
4、Redis 缓存淘汰策略
4.1、Redis 缓存淘汰策略相关的面试题
- 生产上你们的redis内存设置多少?
- 如何配置、修改redis的内存大小?
- 如果内存满了你怎么办?
- redis 清理内存的方式?定期删除和惰性删除了解过吗
- redis 的缓存淘汰策略
- redis 的 LRU 淘汰机制了解过吗?可否手写一个 LRU 算法
4.2、redis 内存满了怎么办
4.2.1、如何查看 redis 最大占用内存
在 redis.conf 配置文件中有一个,输入 :set nu 显示行号,大约在 859行有一个 maxmemory 字段,用预设值 redis 的最大占用内存
4.2.2、redis 会占用物理机器多少内存?
如果不设置最大内存大小或者设置最大内存大小为 0,在 64 位操作系统下不限制内存大小,在32位操作系统下最多使用 3GB 内存
4.2.3、一般生产上如何配置 redis 的内存
一般推荐Redis设置内存为最大物理内存的四分之三,也就是 0.75
4.2.4、如何修改 redis 内存设置
1、通过修改文件配置(永久生效):修改 maxmemory 字段,单位为字节
2、通过命令修改(重启失效):config set maxmemory 104857600 设置 redis 最大占用内存为 100MB,config get maxmemory 获取 redis 最大占用内存
4.2.5、通过命令查看 redis 内存使用情况?
通过 info 指令可以查看 redis 内存使用情况:used_memory_human 表示实际已经占用的内存,maxmemory 表示 redis 最大占用内存
4.2.5、如果把 redis 内存打满了会发生什么? 如果 redis 内存使用超出了设置的最大值会怎样?
修改配置,故意把最大内存设置为 1byte,再通过 set k1 v1 命令下 redis 中写入数据,redis 将会报错:(error) OOM command not allowed when used memory > ‘maxmemory’
4.2.6、总结 & 结论
如果设置了 maxmemory 的选项,假如 redis 内存使用达到上限,并且 key 都没有加上过期时间,就会导致数据写爆 redis 内存。
为了避免类似情况,于是引出下一部分的内存淘汰策略
4.3、redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现?
4.3.1、redis过期策略
redis 过期策略是:定期删除+惰性删除。
所谓定期删除,指的是 redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key,检查其是否过期,如果过期就删除。
假设 redis 里放了 10w 个 key,都设置了过期时间,你每隔几百毫秒,就检查 10w 个 key,那 redis 基本上就死了,cpu 负载会很高的,消耗在你的检查过期 key 上了。注意,这里可不是每隔 100ms 就遍历所有的设置过期时间的 key,那样就是一场性能上的灾难。实际上 redis 是每隔 100ms 随机抽取一些 key 来检查和删除的。
但是问题是,定期删除可能会导致很多过期 key 到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个 key 的时候,redis 会检查一下 ,这个 key 如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。
获取 key 的时候,如果此时 key 已经过期,就删除,不会返回任何东西。
但是实际上这还是有问题的,如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期 key 堆积在内存里,导致 redis 内存块耗尽了,咋整?
答案是:走内存淘汰机制。
4.3.2、内存淘汰机制
redis 内存淘汰机制有以下几个:
- noeviction: (默认)当内存不足以容纳新写入数据时,新写入操作会报错。
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
- allkeys-lfu:对所有key使用LFU算法进行删除
- allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。
- volatile-lfu:对所有设置了过期时间的key使用LFU算法进行删除
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,删除马上要过期的key
总结
2个维度
①过期键中筛选
②所有键中筛选
4个方面
①lru
②lfu
③random
④ttl
如何配置 redis 的内存淘汰策略?
1.通过修改文件配置(永久生效):配置 maxmemory-policy 字段
2.通过命令修改(重启失效):config set maxmemory-policy allkeys-lru 命令设置内存淘汰策略,config get maxmemory-policy 命令获取当前采用的内存淘汰策略
4.3.3、redis LRU 算法
LRU 是 Least Recently Used 的缩写,即最近最少使用,是一种常用的页面置换算法,每次选择最近最久未使用的页面予以淘汰
[ LFU (Least frequently used) 最不经常使用,如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小。]
leetcode : 力扣
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?
4.3.3.1、LRU 算法设计思想
查找和插入的时间复杂度为 O(1),HashMap 没得跑了,但是 HashMap 是无序的集合,怎么样将其改造为有序集合呢?答案就是在各个 Node 节点之间增加 prev 指针和 next 指针,构成双向链表
LRU 的算法核心是哈希链表,本质就是 HashMap+DoublelinkedList 时间复杂度是O(1),哈希表+双向链表的结合体,下面这幅动图完美诠释了 HashMap+DoublelinkedList 的工作原理
4.3.3.2、LRU 算法编码实现
① 借助 JDK 自带的 linkedHashMap
linkedHashMap 的注释中写明了: linkedHashMap 非常适合用来构建 LRU 缓存