在学习命令之前,我们一定要参考Redis的官网,能够看英文官方文档,是每个程序员的"基操".
🐼Redis最核心的命令
我们说过,Redis是按照键值对的方式来存储数据的。
✅GET
cpp
GET key
通过key获取value, 如果 key 不存在,返回 nil。如果 value 的数据类型不是 string,会报错。时间复杂度O(1)
✅SET
cpp
SET key value
设置键值对[key, value], 将 string 类型的 value 设置到 key 中。如果 key 之前存在,则覆盖,⽆论原来的数据类型是什么。之前关于此 key 的 TTL 也全部失效。时间复杂度O(1)
不管是set还是get,其中key和value都是字符串
Redis不区分大小写,和MySQL一样。
为什么他俩是最核心的两个命令吗,不就是一个读,一个写,与Redis通过网络进行IO,通过这两个O(1)时间复杂度的操作,我们就能完成绝大部分操作了。
🐼Redis全局命令
我们说了,Redis是键值对结构,其中key是固定字符串,其中value表示很多数据结构类型,Redis 有 5 种数据结构,不过对于键来说有⼀些通用的命令,而value对于不同的容器我们到时候再去看。
而全局命令就是,能够搭配任何一个数据结构去使用的命令
✅KEYS
返回所有满足样式(pattern)的 key,该命令可以看到的是每一个key的模样,同时也可以允许存在通配符等,当我们不确定我们的redis中有哪些key时,可以试着来查找
cpp
KEYS pattern
来看一下官网给的pattern的介绍,主要是一些通配符的匹配规则,忘记了去官网查即可。
cpp
h?llo matches hello, hallo and hxllo # ?表示匹配任意一个字符。
h*llo matches hllo and heeeello # *表示可以匹配一个或者多个任意个字符。
h[ae]llo matches hello and hallo, but not hillo # [....]表示可以从中匹配这些字符,不再[]中的字符不行。
h[^e]llo matches hallo, hbllo, ... but not hello # 表示除e的字符其他都行
h[a-b]llo matches hallo and hbllo # 表示匹配 [a, b]之间的字符,包括边界
注意事项:
keys的时间复杂度为O(N),因此,不建议使用keys,在生产环境上,明确禁止使用keys,尤其是像keys * 这样的导致Redis挂掉的命令
🚩为啥嘞,首先生产环境就是线上环境,就是和用户交互的环境,你在线上环境搞个这,因为Redis是基于网络的,是客户端服务器的模式,而且Redis是单线程的,又不单单是服务你一个客户端,如果你这个客户端发一个key*,导致Redis只为你一个人服务了,导致其他用户的访问都去MySQL那里了,MySQL它又很"娇气",一不小心就挂了,所以,强烈禁止,不要让站在MySQL背后的哪个男人-Redis阻塞住!它在为MySQL负重前行嘞!
**✅**EXISTS
判断KEY是否存在
cpp
EXISTS key [key ...]
返回值为Key存在的个数,也就是我们可以一次性判断多个key是否存在,存在几个就返回几!
时间复杂度为O(1),官网写的是O(N)其中这个N就是key的个数,这个N是多少,不好说,但你一次性要判断的KEY不能特别多对吧,所以就是O(1).
注意下面这两种写法的区别:

🚩第一种写法和第二种分两次写,有啥区别吗?
其实,有本质的区别,我们知道我们的redis是客户端服务器通信的,我们每操控的一条命令,都是基于网络发给redis-server的,然后redis-server处理完再发给我们。相比于操控在内存,是很慢的,走网络就有开销,比如os什么时候刷新网卡?网卡IO效率还慢,还得封装分用。那么你说一次命令完成的事情,走两次,哪个好?
redis也意识到了这一点,所以redis支持一次性一个命令来操作多个key完成多种操作。
✅DEL
删除指定的 key
cpp
DEL key [key ...]
返回值依旧是成功删除掉key的个数。时间复杂度为O(1)
🚩这里需要注意一下,就是之前学的MySQL,是很忌讳你去删除东西的,这里Redis呢?在Redis中删除东西,要看你Redis是缓存还是被当做数据库使用了,如果你仅仅是一个热点数据,全量数据在MySQL中,那么删除几个key,问题不大,不过删多了,可能导致热点数据命中率不高,进而导致MySQL挂掉。但是如果Redis被当做数据库使用,那么此时就不敢随意删除了,一旦删除,可能不能进行回滚操作,导致数据丢失了~归根结底,还是不要乱删数据!
✅EXPIRE
为已经存在指定的 key 添加秒级的过期时间(Time To Live TTL)
返回1表示设置成功。0 表示设置失败。时间复杂度为O(1)
cpp
EXPIRE key seconds
怎么理解这个过期时间,就是如果你的key超过这个过期时间,就会被自动删除~
✅TTL
全称为time to live
获取已经存在的指定 key 的过期时间,秒级
返回值:剩余过期时间。-1 表示没有关联过期时间,-2 表示key 不存在。时间复杂度为O(1)
cpp
TTL key
通长这个命令配合expire来使用,来查看过期时间的。
✅TYPE
返回 key 对应value的数据类型
时间复杂度为O(1)
cpp
TYPE key
返回值包括: returned are: string, list, set, zset, hash, stream, and vectorset.
对于以上value的操作,操作差别很大, 命令是完全不同的,但是我们都可以使用type来看看对应的key的value到底是啥~
🐼扩展知识(面试题)
Redis中的key的过期时间是怎么实现的?Redis中的key那么多,你怎么知道哪些key要被删除,哪些key不被删除,哪些key还没过期?如果我们直接遍历所有的key,那么"杀伤力"不亚于key *
主要有两种方式:
✅定期删除
每次抽取一部分,进行验证过期时间,保证这个抽取过程是足够快的!为什么要保证足够快呢?或者说为什么要进行抽取呢?原因是Redis是单线程程序,主要任务是执行每个命令的任务,如果抽取过程占用的时间太久,导致其他客户端的连接是阻塞的,就类似与执行key*咯
✅惰性删除
假设这个key已经过期,但是暂时还没删它,key还存在,紧接着,redis-cli后面又再一次访问到了这个key,然后再将它删除,并返回给客户端nil。
虽然上述两种操作可以删除过期的key,但是效果一般,并不能保证所有key及时删除。不过redis进行了一写淘汰策略的机制。
这里有一个有意思的故事:
为什么没有使用定时器?
网上有人说,Redis的删除key的策略如果是根据定时器来完成的,那么不靠谱。真的是这样吗?
其实不然,Redis的作者并没有实现定时器,猜测因为Redis完全就是一个线程的基调,引入定时器,就意味着引入了多线程。为了不打破作者的初衷,这里并没有引入多线程。也没有引入定时器了!
定时器真的不靠谱吗?我们来看一下常用的两种定时器:
⌚️根据优先级队列实现的定时器
优先级队列就是按照指定的优先级进行先入先出的,啥叫优先级高,自定义的~
在redis删除key的场景中,就可以通过"过期时间越早,优先级越高的方式",那么队首元素,就是最早的key,最先过期的,所以我们只需要分配一个线程,取出队首元素,看看是不是需要删除即可,如果队首都没过期,那么所有key都没过期~此时,我们就不需要遍历key了
⌚️根据时间轮来实现定时器
把时间划分为一个个小段,就是一个小格子,小段的粒度,看具体的场景。比如,将时间段为每100ms一次,而每一个格子上挂接这一个个链表,这一个个链表的节点,可能是 一个任务,每隔100ms,小格子都移动一格,尝试把对应任务执行一下,如图:

如果删除key操作使用定时器看起来是高效的,而没有使用定时器也不是网上所说的,还是Redis的初衷就是单线程的基调~