【Redis】Redis 的常用命令

本贴最后更新于 602 天前,其中的信息可能已经沧海桑田

Redis 常见命令

启用 redis:redis-cli -h [ip] -p [6379] -a [password]

名词解释

原子操作(命令):可以理解为只有这条命令执行完毕才会进行下一个命令操作,有点阻塞的意思。

位图:这里的位图并不是图形图像中的“位图(像素图片)”,而是一种数据储存概念,即“位图数据结构”。
例如假设我们想记录用户本周 7 天内签到记录,可以使用字符串 “0101101”:第 1 位表示第 1 天,0 表示未签到,1 表示已签到。

通用命令

keys 命令

计算 redis 所有的键:keys *

keys pu* (找出以 pu 开头的所有 key 的集合)

keys pu? (找出以 pu 开头,长度为 3 的 key 的集合)

keys pu[a-g]* (找出以 pu 开头,后面为 a-g 范围内的字母,*结尾并不限制的 key 的集合)

key 相关命令

计算 key 的总数:dbsize

判断一个 key 是否存在:exists key

删除 key:del key1 key2

设置 key 在 5 秒以后过期:expire key 5 (对于字符串类型比较特别,如果调用了 set 方法重新赋值,那么原来的过期时间会自动删除)

查看 key 的剩余过期时间:ttl key (如果返回-2 则表示 key 不存在,如果返回-1 则表示该 key 存在且永远不过期)

删除 key 的过期时间(key 不再会过期):persist key

查看 key 的类型:type key (返回类型可能为:stringhashlistsetzsetnone)

Object 命令

1、查看某个 keyredis 内部中记录的编码类型:object encoding key

int:整数

raw:一般字符串

embstr:在 2.8 版本里不存在、3.0 版本里一般字符串小于 39 字节字符串、3.2 版本里小于 44 字节的字符串

zipmap:比较小的嘻哈表

hashtable:任意哈希表

ziplist:节约大小并且较小的列

linkedlist:任意列表

intset:值储存数字的小集合

hashtable:任意集合

ziplist:比较小的有序集合

skiplist:任何有序集合

2、查看某个 key 空闲时间(没有被读取或写入):object idletime key

返回空闲秒数

3、查看引用所储存值的次数:object refcount key

暂时没明白含义

type 命令

查看某个 key 对外的类型:type key

返回值有:none (key 不存在)、string (字符串)、hash (哈希表)、list (列表)、set (集合)、zset (有序集)

字符串(string)类型操作

设置(新增)key

不管 key 是否存在,都设置(新增)keyset key value

key 不存在,才设置:setnx key value | set key value nx

key xx 存在,才设置:set key value xx

key 存在的情况下,设置值 value 和过期时间(假设 8 秒):set key value ex 8 xx

设置 key 的值并且设置过期时间的另外一种写法:setex key 8 value nx|xx

整型(整数)运算

自增 1:incr key (若 key 不存在,自增后 key=1)

自减 1:decr key (若 key 不存在,自减后 key=-1)

自增 n:incrby n (若 key 不存在,增加后 key=n)

自减 n:decrby n (若 key 不存在,自减呼 key=-n)

浮点数运算

自增浮点数(例如 3.5):incrbyfloat key 3.5

批量获取或设置

批量获取:mget key1 key2 key3

批量设置:mset key1 value1 key2 value2 key3 value3

获取和修改 value 相关

设置 key 新的 newvalue,并且返回旧的 valuegetset key newvalue

追加 value 到旧的 value 尾部:append key value (返回追加后的长度)

注意:这里是追加到尾部,并不是增加,假设 key 当前值为 38,append key 9 执行以后,key 的值为 389)

返回字符串的长度:strlen key (注意使用 UTF-8 编码一个汉字为 3 个字节、使用 GB2312 一个汉字为 2 个字节)

获取某个范围的值(例如获取下标 0-5 范围内的字符):getrange key 0 5

注意:返回前 6 个字符,因为第 1 个字符下标为 0,下标为 5 其实是第 6 个字符。若取值范围超出则会忽略超出部分。

设置某个下标的值(例如设置下标 5 的值):setrange key 5 value (将下标 5 的值改为 value,并返回修改之后 key 的长度)

注意:如果设置下标超出范围,则会将中间空白的范围自动填充\x00。\x00 表示不可见的字符。

哈希(hash)类型操作

设置(新增)hash

设置 keyvaluehset key value

获得 keyvaluehget key

删除 keyvaluehedel key

keyfield 不存在时,添加并设置:hsetnx key field value

注意:哈希值操作中,不存在 hset key field value nx|xx 这个操作

批量设置或获取

设置 key 的多对 fieldvaluehmset key field1 value1 field2 value2

获得 key 的多个 valuehmget key field1 field2

返回 key 的所有 fieldvaluehgetall key

返回 key 的所有 valushvals key

返回 key 的所有 fieldhkeys key

判断子项(field)是否存在

判断 keyfield 是否存在:hexists key field``

返回 key 拥有的 field 数量:hlen key

子项(field)自增

field 自增(若不存在则创建):hincrby k field n

field 浮点数自增:hincrbyfloat key field float

列表(list)类型操作

设置(新增)

当新增时 list 并不存在,则默认新增该 list

获取

返回列表总长度:llen listkey

获取索引对应的值:lindex listkey index

获取某范围内索引对应的值:lrange listkey n m (若 0 -1,则表示从 0 到最后一个)

设置某个索引对应的值:lset listkey index newValue

删除

删除 count 个列表中某个值:lrem listkey count value

注意:

count=0,则遍历列表,删除所有 value

count>0,则从左开始遍历,删除最多 count 个符合的 value

count<0,则从右开始遍历,删除最多 count 个符合的 value

增加或弹出

从右侧(结尾)插入值:rpush listkey value1 value2

从左侧(开头)插入值:lpush listkey value1 value2

从右侧弹出值:rpop listkey

从左侧弹出值:lpop listkey

在指定的值 前 | 后 插入新的值:linsert listkey before|after value newValue (会遍历整个列,发现符合 value 就插入一次)

按照索引范围修剪(保留)列表:ltrim listkey start end

lpop 阻塞(等待有可用值)版本:blpop listkey timeout (timeout 为阻塞超时时间,若 timeout=0 表示永远不阻塞)

rpop 阻塞(等待有可用值)版本:brpop listkey timeout (timeout 为阻塞超时时间,若 timeout=0 表示永远不阻塞)

实用口诀

lpush + lpop = stack(栈 先进后出) 永远在列表最前面插入和弹出

lpush + rpop = queue(队列 先进先出) 在最前面插入,最后面弹出

lpush + ltrim = capped collection 在最前面插入,修剪队列 -> 控制列的数量

lpush + brpop = message queue 在最前面插入,最后面阻塞弹出 -> 实现消息队列

集合(set)类型操作

注意:集合中没有 get set 这些函数、集合里的内容不允许有重复。

新增或删除

向集合 key 中添加集合内容 element:sadd key element (若 element 存在则添加失败)

删除集合 key 中的某个元素:srem key element

获取

获取集合中元素的个数:scard key

判断某元素是否在集合内容中:sismember key value

随机获取集合中的 count 个元素:srandmember key count

随机弹出(删除)集合中 1 个元素:spop key

获取集合全部元素:smembers key

两个集合间的操作

获取重合的集合内容(交集):sinter follow1 follow2

获取差异的集合内容(差集):sdiff follow1 follow2

获取全部的集合内容(合集):sunion follow1 follow2

有序集合(set)类型操作

概念

有序集合的“值”的构成:score 分数值(可重复) + element 值(不可重复)

注意

无论 stringhashlistset,他们赋值通常都是 field + value,但是有序集合的顺序和他们相反:score + element。

新增

添加有序集合的对值:zadd key score1 element1 score2 element2 (score 可重复,element 不可重复)

注意:该执行复杂程度是 O(logN)

删除

删除有序集合的值:zrem key element1 element2 ...

获取相关

获取有序集合内容总个数:zcard key

获取元素的分数:zscore key element

按照 score 来自增 score

增加或减少某元素的分数:zincrby key count element (element 是唯一不可重复的)

按照 score 来获取排名

获取某 element 在集合中的排名(根据 score 的值升序排列,即从小到大):zrank key element

某范围内(按照 score 或按照索引)的相关操作,默认均为按 score 升序

获取某个范围内集合的值:zrange key start end withscores (0 第一个,-1 最后一个)

注意:该执行复杂程度是 O(log(n)+m),其中 n 为有序集合中元素总个数,m 为 startend 之间元素的总个数

获取指定分数范围内的所有值:zrangebyscore key minScore maxScore

获取指定分数范围内值的个数:zcount key minScore maxScore

删除指点排名内的升序元素:zremrangebyrank key start end

删除指定分数内的升序元素:zremrangebyscore key minScore maxScore

降序相关操作

降序 rev 开头,从高到低

zrevrankzrevrangezrevrangebyscore

两个有序集合相关操作

两个有序集合交集排名:zinterstore

两个有序集合合集排名:zunionstore

缓存更新策略

1、LRU/LFU/FIFO 算法剔除:

例如 maxmemory-policy 最大内存策略配置

优点:维护成本低、缺点:数据一致性最差

2、超时剔除:

例如给 key 设置 expire 过期时间

优点:维护成本低、缺点:数据一致性较差

3、主动更新:

开发控制生命周期

优点:数据一致强、缺点:维护成本高

实际项目中的策略:

1、若数据一致性要求不高,采用最大内存和淘汰策略。

2、若对数据一致性要求高,采用超时剔除和主动更新结合,还要加上最大内存和淘汰策略。

问:选择了主动更新为啥还要加上超时剔除?

答:因为万一主动更新失败,让超时剔除作为保底。当然最大内存和淘汰策略是更加基础的保底。

缓存穿透问题

名词解释:缓存穿透

通常一个合理的访问流程是:客户端请求 -> Redis 缓存层 -> 若没有缓存 -> 请求真实数据库 -> 反馈客户端同时将数据写入缓存层

假设客户端的绝大多数请求在缓存层里找不到,被迫需要去请求真实数据库,相当于每次跳过了缓存层,把这种现象称为“缓存穿透”。

造成原因

1、业务代码自身有问题:从数据库拿到的数据,其实没写入缓存层

2、遭遇恶意攻击、爬虫:大量意想不到的请求

如何发现?

1、使用 Redis 后,检查业务性能提高了多少,是否符合预期。

2、设定相关指标:总调度数、缓存层命中数、存储层(数据库)命中数

可以通过 redis 后台(如果有),查看上述统计,观察并优化缓存命中率。

Redis 实用技巧

缓存空对象

假设客户端请求的数据本身不存在(或短期内不存在),业务代码请求储存层(数据库)之后(并没有拿到任何结果),可以给 Redis 内写入一个空对象,并设置过期时间。
这样短期内客户端再请求这条不存在的数据时,可以从缓存层拿到(尽管是空对象),从而暂时性减少存储层的压力。

缺点:数据短期不一致

互斥锁(分布式锁)

假设瞬间大量客户端同时请求一条相同的数据,例如第 1、第 2、第 3...第 n 客户端,业务代码在处理第 1 个客户端请求时,给这条数据请求加上一个锁 lock,之后的第 2、第 3...第 n 客户端的请求会“挂起并等待中”。当第 1 个客户端请求经过真实存储层(数据库)获取并写入缓存层后,将数据请求解锁 unlock,再依次批量将请求结果反馈给第 2...第 n 个客户端。

缺点:若第 1 个客户端的请求一直未顺利执行完毕,则会造成后面第 2...第 n 客户端一直处于“卡顿”中,存在死锁风险。

解决方法:添加锁时增加一个过期时间,比如 3 秒,这样无论第 1 个客户端的流程是否顺利执行完毕,3 秒后都会解锁。

问题又来了,假设第 1 个客户端的执行流程没走完就进行了解锁(自动到期),这时第 2 个客户端也开始执行该请求,也会增加一把锁,那将来解锁环节是解的哪把锁(是第 1 个客户还是第 2 个客户的锁)?

解决方法:相对更加靠谱一些,就是在上锁时增加一个随机数,解锁时对比这个随机数来区分就是是哪把锁。

热点 key 永不过期

对于热点 key,需要一直存在缓存层中。如果给 key 没有设置过期时间,那么会造成这个 key 的值一直不更新,数据不一致的情况。

如果给 key 设置过期时间,key 会在过期时间结束时被删除掉,直到下一次重新请求并写入缓存中。

如何保证缓存层里永远有一份 key 呢?

实现方法是:当有客户端请求这个 key 时,业务代码去判断 key 的过期剩余时间。假设剩余过期时间小于 8 秒,先把缓存层中 key 的值反馈给客户端,

同时偷偷的进行一次存储层(数据库)数据请求,并将结果更新到缓存层中(依然设定有过期时间)。

缺点:逻辑过期时间增加代码维护成本。

布隆过滤器

1970 年伯顿·布隆提出,2020 年我依然听不懂的一个原理。

Redis 键值设计原则

key 名设计

1、可读性和可管理性:业务名(或数据库名) + : + 表名 + : + id,例如:mykoa:user:1

可管理性其中包含将来批量删除以 XX 开头的缓存数据。

2、简洁性:保证语义的前提下,控制 key 的长度。例如 user:{uid}:friends:messages:{mid} 简化为 u:{uid}:fr:m:{mid}

注意:建议在 redis3.0key 不要超过 39 字节、3.2 以上版本不要超过 44 字节

3、不要包含特殊字符:例如空格、换行、单引号以及其他转义字符

value 值设计

1、value 不要过于大

string 类型控制在 10k 以内、其他类型(hash/list/set/zset)元素个数不要超过 5000

2、根据需求,采用合理的数据结构

3、设置声明周期(通过给 key 设置过期时间)

注意:过期时间不宜集中,容易造成缓存穿透和雪崩

Redis 命令技巧

1、若数据量过大,不建议使用 hgetalllrangesmemberszrangesinter。可以使用 hscansscanzscan 代替。

注意:上述讲的是获取而不是设置,推荐使用 hmse t 这样的批量设置命令

2、禁止线上使用 keysflushallflushdb 等(可通过 redisrename 机制禁掉命令)。若线上真的需要,可使用 scan 相关代替。

3、合理使用 select(按数字进行 redis 多数据库划分)

注意:无论如何 redis 终究是一个单线程,所以无论怎么划分依然是会整体相互影响。

4、不建议过多使用 redis 事务功能。redis 不支持回滚,如果事务操作出错,无法回滚(恢复到上一个正常的状态)。

  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    284 引用 • 247 回帖 • 176 关注
  • SQL
    124 引用 • 296 回帖 • 3 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...