【Redis】4.基本全局命令

文章目录

  • [2. Redis 常见数据类型](#2. Redis 常见数据类型)
    • [2.1 基础知识](#2.1 基础知识)
      • [2.1.1 基本命令](#2.1.1 基本命令)
        • [2.1.1.1 SET](#2.1.1.1 SET)
        • [2.1.1.2 GET](#2.1.1.2 GET)
      • [2.1.2 基本全局命令](#2.1.2 基本全局命令)
        • [2.1.2.1 KEYS](#2.1.2.1 KEYS)
        • [2.1.2.2 EXISTS](#2.1.2.2 EXISTS)
        • [2.1.2.3 DEL](#2.1.2.3 DEL)
        • [2.1.2.4 EXPIRE](#2.1.2.4 EXPIRE)
        • [2.1.2.5 TTL](#2.1.2.5 TTL)
        • [2.1.2.6 TYPE](#2.1.2.6 TYPE)

2. Redis 常见数据类型

Redis 提供了 5 种数据结构,理解每种数据结构的特点对于 Redis 开发运维非常重要,同时掌握每种数据结构的常见命令,会在使用 Redis 的时候做到游刃有余。本章内容如下:

  • 预备知识:几个全局(generic)命令,数据结构和内部编码,单线程模式机制分析。
  • 5 种数据结构的特点、命令使用、应用场景示例。
  • 键遍历、数据库管理。
    redis客户端有很多种形态:
  1. 命令行客户端。redis-cli
  2. 图形化界面客户端。桌面程序,web程序(有可能连不上redis,因为中间会经历很多跳板机,壁垒机,权限校验)
  3. 基于redis的api自行开发客户端(工作中最主要的形态)类似于MySQL的C语言API和JDBC。

2.1 基础知识

在正式介绍 5 种数据结构之前,了解一下 Redis 的一些全局命令、数据结构和内部编码、单线程命令处理机制是十分必要的,它们能为后面内容的学习打下一个良好的基础。

主要体现在两个方面:

  1. Redis 的命令有上百个,如果纯靠死记硬背比较困难,但是如果理解 Redis 的一些机制,会发现这些命令有很强的通用性。
  2. Redis 不是万金油,有些数据结构和命令必须在特定场景下使用,一旦使用不当可能对 Redis 本身或者应用本身造成致命伤害。

2.1.1 基本命令

2.1.1.1 SET

设置key以保存字符串值。如果key已经保存了一个值,则不管其类型如何,它都将被覆盖。

在SET操作成功时,先前与该键关联的任何存活时间都将被丢弃。

语法:

mysql 复制代码
SET key value [NX | XX | IFEQ ifeq-value | IFNE ifne-value |
  IFDEQ ifdeq-digest | IFDNE ifdne-digest] [GET] [EX seconds |
  PX milliseconds | EXAT unix-time-seconds |
  PXAT unix-time-milliseconds | KEEPTTL]

命令有效版本:1.0.0 之后

时间复杂度:O(N)

返回值:无

实例:

mysql 复制代码
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> set key2 value2
OK
127.0.0.1:6379> set "key3" value2
OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> get value2
(nil)
127.0.0.1:6379> get key2
"value2"
127.0.0.1:6379> get key3
"value2"
127.0.0.1:6379> 

2.1.1.2 GET

获取key的值。如果键不存在,则返回nil。和null/NULL是一个意思。

如果存储在key处的值不是字符串,则返回错误,因为GET只处理字符串值。

语法:

mysql 复制代码
GET key

命令有效版本:1.0.0 之后

时间复杂度:O(N)

返回值:key的值。

实例:

mysql 复制代码
127.0.0.1:6379> set key1 value1
OK
127.0.0.1:6379> set key2 value2
OK
127.0.0.1:6379> set "key3" value2
OK
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> get value2
(nil)
127.0.0.1:6379> get key2
"value2"
127.0.0.1:6379> get key3
"value2"
127.0.0.1:6379> 

2.1.2 基本全局命令

Redis 有 5 种数据结构,但它们都是键值对种的值,对于键来说有一些通用的命令。

redis中的命令不区分大小写。


2.1.2.1 KEYS

返回所有满足样式(pattern)的 key。支持如下统配样式:

  1. h?llo 匹配 hello , hallo 和 hxllo
  2. h*llo 匹配 hllo 和 heeeello
  3. h[ae]llo 匹配 hello 和 hallo 但不匹配 hillo
  4. h[^e]llo 匹配 hallo , hbllo , ... 但不匹配 hello
  5. h[a-b]llo 匹配 hallo 和 hbllo(匹配a到b范围内的字符)

上述规则这些不用刻意去背,有个印象就行了。要用的时候去查一下就行。

注意:keys的时间复杂度是O(N),所以在一些生产环境上会禁止使用keys命令,尤其是keys *。

因为生产环境上的key可能会非常多,而redis是一个单线程服务器,执行keys *的时间非常长,就会使得redis服务器被阻塞,无法给其他客户提供服务。这样的后果可能是灾难性的。

因为redis经常会做缓存,挡在MySQL前面,替MySQL负重前行。万一redis被一个keys *阻塞住了,其他的查询redis操作就超时了。这些请求就会直接查数据库,突然一堆请求过来,MySQL措手不及就可能挂掉。整个系统基本就挂了,如果没有及时发现的话,年终奖就飞走啦。(银手镯应该不至于)

语法:

mysql 复制代码
 KEYS pattern

命令有效版本:1.0.0 之后

时间复杂度:O(N)

返回值:匹配 pattern 的所有 key。

示例:

mysql 复制代码
127.0.0.1:6379> MSET firstname Jack lastname Stuntman age 35keys *  
OK
127.0.0.1:6379> KEYS *name*
1) "firstname"
2) "lastname"
127.0.0.1:6379> KEYS a??
1) "age"
127.0.0.1:6379> KEYS *
1) "firstname"
2) "lastname"
3) "backup3"
4) "age"
127.0.0.1:6379> 

2.1.2.2 EXISTS

判断某个 key 是否存在。

语法:

mysql 复制代码
EXISTS key [key ...]

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:key 存在的个数。

实例:

mysql 复制代码
127.0.0.1:6379> SET key1 "Hello"
OK
127.0.0.1:6379> exists key1
(integer) 1
127.0.0.1:6379> exists nosuchkey
(integer) 0
127.0.0.1:6379> set key2 "World"
OK
127.0.0.1:6379> exists key1 key2 nosuchkey
(integer) 2
127.0.0.1:6379> exists key1 key2
(integer) 2
127.0.0.1:6379> 

2.1.2.3 DEL

删除指定的 key。

语法:

mysql 复制代码
DEL key [key ...]

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:删除掉的 key 的个数。

不过不小心删一两个倒是没事,一不小心全删掉或者删掉大半,就会有很大的影响了。请求都打给MySQL就可能把MySQL搞挂掉。

如果把redis作为数据库,误删的影响就大了。

如果把redis作为消息队列,误删的影响需要具体情况具体分析。

示例:

mysql 复制代码
127.0.0.1:6379> SET key1 "Hello"
"OK"
127.0.0.1:6379> SET key2 "World"
"OK"
127.0.0.1:6379> DEL key1 key2 key3
(integer) 2
127.0.0.1:6379>

2.1.2.4 EXPIRE

为指定的 key 添加秒级的过期时间(Time To Live TTL)

语法:

mysql 复制代码
EXPIRE key seconds

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:1 表示设置成功。0 表示设置失败。
其实很多业务都有时间限制。

例如:

  1. 手机验证码几分钟内有效

  2. 外卖优惠卷几号后过期

  3. 基于redis实现分布式锁,为了避免不能正确解锁的情况(比如解锁的数据丢了),通常都会在加锁的时候设置一下过期时间。

    其实所谓的redis实现分布式锁,就是给redis里面写一个特殊的key value。

示例:

mysql 复制代码
127.0.0.1:6379> SET mykey "Hello"
OK
127.0.0.1:6379> get mykey
"Hello"
127.0.0.1:6379> expire mykey 10
(integer) 1
127.0.0.1:6379> ttl mykey
(integer) 7
127.0.0.1:6379> get mykey
"Hello"
127.0.0.1:6379> ttl mykey
(integer) 1
127.0.0.1:6379> ttl mykey
(integer) -2
127.0.0.1:6379> get mykey
(nil)
127.0.0.1:6379> 

2.1.2.5 TTL

获取指定 key 的过期时间,秒级。

语法:

mysql 复制代码
TTL key

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值:剩余过期时间。-1 表示没有关联过期时间,-2 表示 key 不存在。

示例:

mysql 复制代码
127.0.0.1:6379> SET mykey "Hello"
OK
127.0.0.1:6379> get mykey
"Hello"
127.0.0.1:6379> expire mykey 10
(integer) 1
127.0.0.1:6379> ttl mykey
(integer) 7
127.0.0.1:6379> get mykey
"Hello"
127.0.0.1:6379> ttl mykey
(integer) 1
127.0.0.1:6379> ttl mykey
(integer) -2
127.0.0.1:6379> get mykey
(nil)
127.0.0.1:6379> 

EXPIRE 和 TTL 命令都有对应的支持毫秒为单位的版本:PEXPIRE 和 PTTL。
键的过期机制:

  1. 定期删除:每次抽取一部分,进行验证过期时间。保证这个抽取检查的过程,足够快。因为如果把所有的key都抽取一边就太耗时了。

    为啥这里对于定期删除的时间,有明确的要求呢?

    因为redis是单线程的程序。主要的任务是处理每个命令的任务,刚才扫描过期key,如果扫描过期key 消耗的时间太多了,就可能导致正常处理请求命令就被阻塞了。(产生了类似于执行keys *这样的效果,外界看起来就像redis挂了一样)

  2. 惰性删除:假设这个key已经到过期时间了,但是暂时还没删它,key还存在.紧接着,后面又一次访问,正好用到了这个key,于是这次访问就会让redis服务器触发删除key的操作,同时再返回一个nil。

    类似于:有一个杂货铺老板,店里有一堆东西,但是不知道哪些过期了。你买东西的时候,他发现过期了,就会丢掉商品不让你买。

下图是定期删除:

注意:

redis中没有采取定时器的方式来实现过期key删除。
虽然没有采取定时器,但是有两种高效的定时器方案可以看一下:(下面两个方案并不在redis中采用)

方案一:基于优先级队列/堆

正常的队列是先进先出,优先级队列则是按照指定的优先级,先出。

这个优先级高可以自定义。

例如:在redis过期key的场景中,就可以通过"过期时间越早,就是优先级越高"

现在假定有很多key 设置了过期时间,就可以把这些key加入到一个优先级队列中,指定优先级规则是过期时间早的,先出队列,队首元素,就是最早的要过期的key。

比如:

key1:12:00

key2:13:00

key3:14:00

key1先过期,先进队列,key2其次,key3最后

此时定时器中只要分配一个线程,让这个线程去检查队首元素,看是否过期即可。如果队首元素还没过期,后续元素一定没过期。此时扫描线程 不需要遍历所有key只盯住这一个队首元素即可。

另外在扫描线程检查队首元素过期时间的时候,也不能检查的太频繁,此时做法就是可以根据当前时刻和队首元素的过期时间,设置一个等待。当时间差不多到了,系统再唤醒这个线程。此时扫描线程不需要高频扫描队首元素,把cpu的开销也节省下来了。

万一在线程休眠的时候,来了一个新的任务,是11:30要执行,可以在新任务添加的时候,唤醒一下刚才的线程~~重新检查一下队首元素,再根据时间差距重新调整阻塞时间即可。
方案二:基于时间轮实现的定时器

把时间划分成很多小段。(划分的粒度,看实际需求)


2.1.2.6 TYPE

返回 key 对应的值的数据类型。

语法:

mysql 复制代码
TYPE key

命令有效版本:1.0.0 之后

时间复杂度:O(1)

返回值: none , string , list , set , zset , hash and stream ...

示例:

mysql 复制代码
127.0.0.1:6379> SET key1 "value"
OK
127.0.0.1:6379> LPUSH key2 "value"
(integer) 1
127.0.0.1:6379> SADD key3 "value"
(integer) 1
127.0.0.1:6379> TYPE key1
string
127.0.0.1:6379> TYPE key2
list
127.0.0.1:6379> TYPE key3
set
127.0.0.1:6379> 
相关推荐
CJi0NG1 小时前
【自用】MySQL-多表查询
数据库·mysql
雨落在了我的手上1 小时前
MySQL数据库基础(12):事务
数据库·mysql
D姐聊门店1 小时前
门店收银系统断网兜底怎么设计:本地账本、离线续传与日终对账的工程实现
数据库·saas
用户8662978480082 小时前
抓包看 KingbaseES 内部协议?官方给了个现成工具
数据库
wefg12 小时前
【Redis】安装与启动
数据库·redis·缓存
大圣编蚕2 小时前
常见的 Redis 面试题(高频整理)
数据库·redis·缓存
派小心.3 小时前
网站统计采样数据的误差核对
大数据·服务器·前端·数据库·人工智能·小程序·数据分析
龙腾AI白云3 小时前
数字孪生、具身智能、世界模型三者交叉
数据库·人工智能·机器学习·flask·pygame
小猿姐3 小时前
KubeBlocks Enterprise v2.3 发布!
redis·kubernetes·aiops
SelectDB技术团队3 小时前
Apache Doris 基于 Lance 的向量检索深度解析
数据库·数据仓库·数据分析·湖仓一体·apache doris·lakehouse·多模湖仓