Redis一些简单通用命令&认识常用数据类型和编码方式&认识Redis单线程模型

通用命令

get() / set()

这是Redis中两个最为核心的命令。

set插入

这里的key 和 value都是字符串,我们可以加双引号 或者单引号,或者不加。

get查找

如果查询的key值不存在,那么会返回一个 nil ,也就是代表空

在Redis中命令是不区分大小写的。

这就是Redis最核心的命令和它俩的使用方法了,看起来就像是一个网络版的hash map一样。这也是Redis的一个优势,使用简单 学习成本低。

keys()

在这里补充一下 Redis全局命令的概念

Redis支持很多的数据结构,从整体上来说,Redis是键值对结构,key是固定的字符串,但是value实际上会有多种类型,比如:

字符串 哈希表 列表 集合 有序集合,一般来说操作不同的数据结构就会有不同的命令。

而全局命令就是能够搭配任意一个数据结构使用的命令。

keys():用来查询当前服务器上匹配的key。

语法:

bash 复制代码
KEYS pattern

其中 pattern(模式)是包含特殊符号的字符串。

这个pattern存在的意义是为了描述另外的字符串长什么样

pattern的使用规则:

一般生产环境中是禁止使用keys的,尤其是 keys *。

注意:keys 命令的时间复杂度 O(N)的,又因为redis是单线程的,所以当数据量很大时,用 keys * 就把redis阻塞了,无法给其他客户端提供服务。

这样带来的结果往往是灾难性的,因为redis作为缓存,它是挡在mysql前,帮mysql分担请求的,如果此时redis阻塞了,那么请求突然就全打往mysql了,mysql可能会突然承受不主压力而挂掉了,如果mysql也挂了,那整个服务差不多也瘫痪了。

exists()

exists():判断key是否存在。 这里的key可以是多个。

返回值: 有效key的个数

针对多个key来说,这是挺有用的。

Redis组织这些key就是按照哈希表的方式来组织的。

关于这个命令的时间复杂度 ,官方给出的是 O(N),但是注意,这里的O(N)中的N指的是我们传入的key的个数,不是传统意义上的O(N)。

有两种写法:

如上,第一种写法查询一次,第二种解法查询两次,那么这两种写法有什么区别呢?

我们始终要记住Redis是 客户端 - 服务器结构的程序,而客户端 与 服务器之间是通过网络来通信的,因此分开的写法会产生更多的轮次的网络通信,导致效率变低。

del()

删除指定的key

可以一次删除一个或者多个

时间复杂度 : (N)跟exists一样

返回值:删掉的key的个数

而Redis删除数据的严重性要看应用场景

不过归根结底还是不要乱删数据。

expire()

给指定的key设置过期时间

语法:

bash 复制代码
EXPIRE key seconds

成功时返回1,失败时返回0

其中seconds的单位是秒。

这个key得是之前就有的,不然这个命令就会执行失败。

时间复杂度 O(1)

这个命令在有时间限制的场景下应用很广泛,比如手机验证码有效时间。

另外还有关于基于redis实现的分布式锁

补充:

一秒对于计算机来说还是很漫长的,所以还有一个命令 : pexpire ,也就是多加了个p,此时seconds的单位就是毫秒了,用法跟 expire是一样的。

ttl()

查看指定的key的过期时间。单位:秒级

这里的ttl跟网络IP协议那里的TTL是不一样的。

返回值:

返回剩余的过期时间。如果该key没有关联过期时间返回-1,如果该key不存在返回-2。

同样还有一个命令 : pttl 。用法一样,不过单位是毫秒。

补充: Redis的key过期策略

Redis的过期策略是怎么实现的呢? 这也是一道经典的面试题。

如果直接遍历所有key的方式来检查哪些key过期了,效率未免太低了。

对此Redis整体的策略有:

1.定期删除:

周期性的每次抽取一部分key,进行验证过期时间。 Redis会保证这个抽查的速度足够快。

为什么对于定期删除有明确的速度要求,只抽取一部分验证来保证抽查速度快呢?

因为Redis是单线程的程序,它的主要任务都是在单线程中执行的,如果扫描过期的key消耗的时间太多了,就有可能导致正常处理请求命令被阻塞了(产生了类似执行 key * 的效果)。

2.惰性删除:

假设这个key已经过期了,但是暂时还没有删除它,当紧接着后面的一次访问正好用到了这个key,那么服务器就触发了删除key的操作,同时再返回一个nil。

在Redis中这两种删除策略是搭配在一起使用的,但是仍然会存在很多过期的key残留在服务器上,没有及时删除掉,为此Redis还提供了一系列内存淘汰策略。

扩展:

理解定时器的实现原理

1.基于优先级队列/堆

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

这个优先级是可以自定义的。

在Redis过期key销毁的场景中,距离过期时间越近,那么优先级就越高。

我们把设置了过期时间的key放入到这个堆中,那么堆顶元素就是最早会过期的key。

此时定时器只要分配一个线程,去检查这个堆顶,查看堆顶的元素是否过期即可。

也就是不需要遍历所有key,而是只需要检查堆顶元素即可。

另外,检查堆顶元素的周期也不能太短, 对于下一次检查的时间可以设置为:堆顶元素距离过期的时间。在此期间就让这个线程阻塞挂起就可以了。

另外如果新来了一个元素,那么也会唤醒这个线程,会重新设置下一次的检查时间。

2.基于时间轮实现的定时器

如图,就是一个循环数组,把时间划分成很多小段,划分的粒度要看实际需求。

时间轮运行的时候有一个指针,这个指针每次会按照固定的时间间隔向前移动(这个时间间隔就是我们划分的时间粒度),每走到一个格子上,就会尝试执行这个格子上链表的所有任务,为什么是尝试呢?假设某个key的过期时间超过了这个时间轮能表示的最大时间,那么就会让这个key多转几圈,然后放到对应格子的链表上。并且在执行销毁任务时,还是会检查一下过期时间的,如果发现还没有到就不会销毁的。

type()

返回这个key对应的value的数据类型。

注意:Redis中key的类型只有string。

返回类型:
返回值为none就是没有这个key。

使用示例:

对于操作链表,插入的命令是 lpush 后面跟元素值;对于集合 插入命令是sadd;对于哈希表,插入命令是hset,可见命令都是不一样的。

常用数据结构

redis常用的数据结构有 字符串,哈希,列表,集合,有序集合。

另外Redis在底层实现这些数据结构的时候,会在源码层面针对上面的数据结构的实现进行特定的优化,来达到节省时间/空间的效果,也就是内部具体的数据结构(编码方式)还会有变数。

总结:同一个数据类型,背后的编码实现方式可能是不同的,会根据特定的场景进行优化

比如string类型,当value就是一个整数的时候,此时Redis可能直接会使用int来保存。

再比如hash表,当数据元素比较少时,会用一个ziplist来存储,以此来压缩空间,因为当元素比较少时,它遍历的速度也很快。

关于为什么要压缩?

再看看list set zset

并且在有序集合那里,底层的实现有跳表,因为有序集合里的元素是带有权值的,当需要在某个权值范围内遍历的时候,用跳表就比较合适。

并且在list和zset的ziplist那里,从Redis3.2开始,就引入了新的实现方式:quicklist。

可以使用命令

bash 复制代码
object encoding key

来查询这个key底层的编码方式

关于Redis单线程模型

Redis单线程工作过程

Redis只用一个线程来处理所有的命令请求。但是不是说Redis服务器内部真的只有一个线程,其他的多线程都是在网络部分处理IO的。

因为Redis实际处理请求是单线程,所以它保证了对收到来自不同客户端的请求的处理是串行化的,也就是不会有线程安全问题。

关于Redis能够使用单线程模型的主要原因:Redis的核心业务逻辑都是 短平快 的,这样的话就不吃CPU资源,也就不吃多核了,所以Redis使用单线程也能很好的工作。

弊端就是:Redis必须特别小心某个操作的时间过长,这样会阻塞其他命令的执行。

Redis单线程快在哪里?

Redis虽然是单线程,但是为什么效率高,速度快?(经典面试题)

首先 Redis的效率高,速度快 是参照于 数据库:MySQL, Oracle,SQL Server的。

原因:

1.Redis访问的是内存,而MySQL这样的数据库访问的是硬盘。

2.Redis的核心逻辑比那些数据库的核心功能要简单。

3.单线程模型,避免了一些不必要的线程竞争开销。

因为Redis的基本操作都是 短平快的,也就是简单操作一下内存,不是什么特别消耗CPU的操作,就算是多线程也提升不大。

4.处理网络IO的时候,用了epoll这样的多路复用机制。

IO多路复用适合交互不是很频繁,大部分时间都在等的场景,如果交互十分频繁(比如下载文件或者直播),此时还是创建一个线程来处理比较好。

相关推荐
Li zlun28 分钟前
MySQL 性能监控与安全管理完全指南
数据库·mysql·安全
养生技术人1 小时前
Oracle OCP认证考试题目详解082系列第48题
运维·数据库·sql·oracle·database·开闭原则·ocp
海阳宜家电脑1 小时前
Lazarus使用TSQLQuery更新的一点技巧
数据库·lazarus·tsqlquery
沐浴露z1 小时前
分布式场景下防止【缓存击穿】的不同方案
redis·分布式·缓存·redission
丨我是张先生丨2 小时前
SQLSERVER 查找存储过程中某个变量
数据库
Lisonseekpan3 小时前
Spring Boot 中使用 Caffeine 缓存详解与案例
java·spring boot·后端·spring·缓存
感谢地心引力3 小时前
【Python】基于 PyQt6 和 Conda 的 PyInstaller 打包工具
数据库·python·conda·pyqt·pyinstaller
lypzcgf4 小时前
Coze源码分析-资源库-编辑数据库-后端源码-数据存储层
数据库·coze·coze源码分析·智能体平台·ai应用平台
jackaroo20204 小时前
后端_Redis 分布式锁实现指南
数据库·redis·分布式
liuy96154 小时前
迷你论坛项目
数据库