首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Redis从入门到精通:一文搞定环境搭建、核心命令与高性能奥秘

Redis从入门到精通:一文搞定环境搭建、核心命令与高性能奥秘

作者头像
Undoom
发布2025-08-23 08:12:03
发布2025-08-23 08:12:03
5080
举报
文章被收录于专栏:学习学习

前言

在当今这个追求极致性能和高并发的互联网时代,Redis已然成为后端技术栈中不可或缺的“瑞士军刀”。无论是作为高速缓存、分布式锁,还是实现消息队列,它无处不在。但你是否曾好奇,小小的Redis为何能拥有如此惊人的性能?它赖以成名的单线程模型,究竟是如何“单枪匹马”地处理数以万计的并发请求的? 本文将带你开启一场Redis的深度探索之旅。我们不谈空洞的理论,而是从零开始,手把手教你如何在Linux上搭建环境、修改配置。随后,我们将深入浅出地讲解set、keys、expire等一系列核心命令,并重点剖析keys *命令在生产环境中的潜在风险。最后,我们将直面那个“百万美元”的面试题——Redis为什么这么快?通过对其底层数据结构、单线程模型和IO多路复用机制的层层剖析,让你不仅知其然,更知其所以然。准备好了吗?让我们一起揭开Redis高性能的神秘面纱!

环境搭建

我们安装的Redis5版本

在Linux中进行安装

Redis官方是不支持Windows版本的

打开Linux,先切换到root用户

然后使用apt命令来搜索Redis相关的软件包

代码语言:javascript
复制
apt search redis
image.png
image.png

这里我们已经安装好了

代码语言:javascript
复制
apt install redis
image.png
image.png

安装好了之后,我们可以输入命令进行查询Redis是否在运行中

代码语言:javascript
复制
netstat -app |grep redis
image.png
image.png

我们这里的ip是0.0.0.0说明别的主机也是可以进行访问的

但是你如果一开始是127.0.0.1的话,绑定这个127.0.0.1的ip意味着只能由当前主机上的客户端访问,跨主机就访问不了

那么我们就得进行配置文件的修改操作了,不然的话别的主机是访问不了你的redis的

我们输入命令进入到配置文件路径下

代码语言:javascript
复制
cd /etc/redis/

可以看到一个redis.conf文件,这个就是redis的配置文件

image.png
image.png

进入到文件中,找到这个bind

image.png
image.png

改成我这个样子0.0.0.0 然后我们还需要将保护模式给关闭了 将后面改成no

image.png
image.png

这里的端口号是6379

我们的redis是不需要配置密码的 虽然我们没有密码,但是非常安全

因为我们的数据不值钱

在修改配置文件之后,如果想生成改变的话,我们得重新启动redis服务器

代码语言:javascript
复制
srevice redis-server restart

输入命令之后,看到active running就说明你重启成功了

使用redis自带的客户端链接服务器

在终端输入命令

代码语言:javascript
复制
redis-cli
image.png
image.png

为了验证是否链接成功,我们这里发送一个ping命令,成功的话会显示一个PONG的

image.png
image.png

如果想退出redis的话,我们输入ctrl d即可进行退出操作了

Ctrl c也可以退出

Redis的客户端也有很多的形态 1、自带了命令行客户端

代码语言:javascript
复制
redis-cli

如果想要连接别的redis的话 -h就是ip地址,-p是端口号

代码语言:javascript
复制
redis-cli -h 127.0.0.1 -p 6379
image.png
image.png

2、图形化界面的客户端 (桌面程序,web程序)

我们谈到mysql这样的快,是相对于mysql这样的关系型数据库的

如果我们直接和内存中的操作变量相比的话,就没有优势了,甚至更慢了

使用hash mcp的话是直接操作内存 而我们使用redis是先通过网络,再操作内存

是否使用redis,要结合实际的需求来确定

未来要扩展分布式系统,使用redis是更佳的

redis核心命令

get和set

Redis中最核心的两个命令

代码语言:javascript
复制
get    #根据key来取value

set    #把key和value存储进去

redis是按照键值对的方式存储数据的

代码语言:javascript
复制
set key value

这里的key和value必须都是字符串

image.png
image.png

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

get命令直接输入key就能得到value

image.png
image.png

如果key不存在的话,会返回一个nil,和null/NULL是一个意思

image.png
image.png

使用简单,学习成本很低 将redis当成一个网络版本的哈希表

keys(全局命令)

redis支持很多种数据结构

整体来说,redis是键值对结构,key固定就是字符串,valude实际上会有很多种类型(字符串、哈希表、列表、集合、有序集合)

操作不同的数据结构,就会有不同的命令

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

keys 用来查询当前服务器上匹配的key

通过一些特殊符号(通配符)来描述key的摸样,匹配上述摸样的key就能被查询出来

语法结构

代码语言:javascript
复制
keys pattren

pattren存在的意义,是去描述另外的字符串长啥样

  • ?匹配任意一个字符 我们这里插入了三个键值对,然后使用?通配符进行查找
image.png
image.png
    • 匹配一个或者n个相同字符
    image.png
    image.png
  • [ae]只能匹配到a或者e,别的不行,相当于给出固定的选项
image.png
image.png
  • [^e]排除e,只有e匹配不了,其他的都能匹配
image.png
image.png
  • [a-c]a到c中间的字母都是可以的,包含a和c
image.png
image.png

keys命令的时间复杂度是O(N) 因为在执行这个命令的时候会遍历我们所有的key 所以在生产环境上,一般都会禁止使用keys命令 尤其是大杀器keys *

因为生产环境上的key可能非常多,而redis是一个单线程的服务器

执行Keys * 的时间非常长,就使redis服务器被阻塞了,无法给其他客户端提供服务

image.png
image.png

整个系统基本瘫痪

exists(判断Key是否存在)

判断某个key是否存在

返回的是key存在的个数

针对多个key来说是很有用的

时间复杂度是O(1)

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

redis支持很多数据结构=》指的是一个value可以是一些复杂的数据结构

redis自身的键值对值通过哈希表来组织的

redis具体的某个值,又可以是一些数据结构

image.png
image.png

返回对应key的个数 上面我们是分开的写法,会产生更多轮次的网络通信,成本高,效率低

下面是合并在一起的,直接返回两个Key存在的总数值

image.png
image.png

redis的很多命令都是支持一次就能操作多个key的

image.png
image.png

del(删除key)

删除指定的key 可以一次删除一个或者多个Key

返回值是删除掉的Key的个数

image.png
image.png
image.png
image.png
image.png
image.png

如果redis存储的是全量数据的话,那么误删数据问题就很大了

如果是热点数据的话误删一两个还是无所谓的

expire(设置过期时间)

给指定的key设置过期时间 key存活的值超过这个指定的时间就会被自动删除了

很多的业务场景都是有时间限制的,比如说手机验证码

基于redis实现分布式锁,为了避免出现不能正常解锁的情况,通常都会在加锁的时候设置一下过期时间(所谓的使用redis作为分布式锁,就是给redis里写一个特殊的额key value)我们将这个Key value删除掉就相当于自动解锁

使用格式如下:

代码语言:javascript
复制
expire key seconds

如果给不存在的key进行过期时间的设置的话是会设置失败的,那么就会返回0

如果返回0就说明你设置失败了,如果是1的话就说明成功了

image.png
image.png

ttl(查询存活时间)

查看当前key的过期时间还剩多少

代码语言:javascript
复制
TTL key

返回的是过期时间

返回值是剩余过期时间

-1表示没有关联过期时间 ,-2表示key不存在

image.png
image.png

key的过期策略的实现

redis怎么知道那些Key要过期,那些key要被删除,哪些key还没过期?

如果直接遍历所有的key,他的效率是很低的

redis整体的策略是: 定期删除 比喻理解:超时老板会定期检查过期的产品,每次抽取一部分进行验证过期时间,保证这个抽取检查的过程,足够快 惰性删除 假设这个Key已经到了过期时间了,但是还没删除他,key还存在,紧接着,后面又一次访问用到这个key,于是这次访问就让redis服务器触发删除key的操作,同时再返回一个nil 比喻理解:当我买东西的时候,结账的时候老板发现过期了,然后不卖了

为什么这里对于定期删除的时间,有明确的要求呢? 因为redis是单线程的程序

如果扫描过期key消耗的时间太多了,就可能导致正常请求命令就被阻塞了(产生了类似执行与keys * 的效果)

redis为了对上述进行补充,还提供了一系列的内存淘汰策略

redis中并没有采取定时器的方式来实现过期key删除 如果有多个key过期,也可以通过一个定时器来高效/节省CPU的前提下来处理多个key

我们目前还不知道为啥redis没有采取这种定时器的方式

定时器的实现,势必就得引入多线程了

redis的早起版本就奠定了单线程的基调,引入多线程就打破了作者的初衷了

定时器的实现原理

在某个时间到达之后,执行指定的任务

基于优先级队列/堆 正常的队列是先进先出 优先级队列是啊按照指定的优先级,先出 啥叫优先级高,这个是我们自定义的

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

image.png
image.png

针对队首元素即可,不用遍历所有的key

image.png
image.png

第二种基于时间轮实现的定时器

把时间划分为很多的小块儿

image.png
image.png

这两种方法都是比较高效的定时器实现方案

type(查看value的类型)

返回key对应的value的类型

key都是string类型的,但是value可能存在多种类型

image.png
image.png
image.png
image.png

总结

上面我们已经学习了几个简单的命令

image.png
image.png

下面就围绕每个数据结构来介绍相关的命令了

image.png
image.png

常用数据结构

redis底层在实现上述数据结构的时候,会在源码层面,针对上述实现 进行特定的优化,来达到节省时间/空间的效果

内部的具体实现的具体实现结构(编码方式),会有变数

数据结构:redis承诺给你的,也可以理解成数据类型 编码方式:redis内部底层的实现

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

image.png
image.png
image.png
image.png

raw是最基本的字符串,底层就是持有一个char数组

int类型:redis通常也可以实现一些计数这样的功能 当value就是一个整数的时候,此时可能吧redis会直接使用int来保存

embstr针对短字符串进行的特殊优化

hashtable是最基本的哈希表

ziplist叫做压缩列表:在哈希表里面元素较少的时候,可能就优化成ziplist了 压缩列表,节省空间

那么为啥要压缩呢? redis上可能有很多的key 可能某些key的value是key

linkedlist:链表,一个节点指向另一个节点

ziplist就是压缩列表,如果列表中元素个数少,就使用ziplist 如果元素多的话就使用linkedlist

在redis3.2开始,引入了新的实现方式quicklist同时兼顾了linkedlist和ziplist的优点 quicklist就是一个链表,每个元素又是一个ziplist 这样把空间和效率都兼顾到了 类似于C++中的std::deque

intset :集合中存的都是整数的话,那么就会优化会intset

skiplist:跳表也是链表,不同于普通的链表,每个节点上有多个指针域,巧妙的搭配这些指针域的指向,就可以做到,从跳表啥啊昂查询元素的时间复杂度是O(logn)

我们可以使用object encoding key查看key对应的value对应的编码类型

image.png
image.png

redis会自动根据当前情况选择内部的编码方式

redis单线程模型

单线程模型的工作过程

redis只使用一个线程,处理所有的命令请求

不是说一个redis服务器进程内部真的就只有一个线程 其实也有多个线程,多个线程是在处理网络IO

image.png
image.png

这里我们的两个客户端访问服务器中的Key对应的value ,让value进行自增,

在多线程中,针对于类似的场景,两个线程尝试同时对一个变量进行自增,表面上是自增两次,实际上可能是一次 当前这两个客户端,也相当于并发的发起上述的请求 此时就意味着服务器这边也会存在类似的线程安全问题呢?

幸运的是,并不会,redis服务器实际上是单线程模型,保证了当前收到的这多个请求是串行执行的

这两个请求在排队,依次进行执行,顺序执行

多个请求同时到达redis服务器,也是在队列中进行排队

redis能够使用单线程模型很好的工作,原因主要在于redis的核心业务逻辑都是短平快的,不太消耗CPU资源也不太吃多核了

redis要特别小心,某个操作占用时间长,就会阻塞其他命令的执行,比如说keys *

redis为什么这么快(重要面试题)

为什么效率这么高,速度为什么这么快

谈到快以及效率高,都是我们和其他的关系型数据库进行对比的

我们参照物是数据库

1、redis访问内存,数据库则是访问硬盘,

2、redis核心功能,比数据库的核心功能更简单 数据库对于数据的插入删除查询,都有更复杂的功能支持,这样的功能势必要花费更多的开销,针对插入删除,数据库中的各种约束,都会使数据库做额外的工作,Redis干的活少,提供的功能相较于redis也是少了不少了 3、redis采取单线程模型,避免了一些不必要的线程竞争开销 redis每个操作都是短平快的,就是简单操作一下内存数,不是特别小号cpu的操作,就算是搞多个线程,也提升不大 4、处理网络IO的时候,使用了epoll这样的IO多路复用机制 一个线程就可以管理多个socket 针对tcp来说,服务器这边每次要服务一个客户端,都需要给这个客户安排一个socket

一个服务器服务很多客户端,同时就有很多的socket 这些socket上都是无时无刻的在传输数据么?

很多情况下,每个客户端和服务器之前的通信也没有那么频繁 此时这么socket大部分时间都是静默的,上面是没有数据需要传输的

同一时刻,只有少数socket是活跃的

最开始介绍TCP服务器的时候,有一个版本就是每个客户端给分配一个线程 客户端多了,线程就多了,系统开销就大了

通过IO多路复用,一个线程来处理多个socket

image.png
image.png

总结

总的来说,本文为我们提供了一份从实践到理论的Redis核心知识图谱。 我们从最基础的环境搭建入手,详细走过了在Linux上安装、配置Redis的全过程,确保你能顺利启动并连接到自己的Redis服务器。 接着,我们深入学习了Redis的核心全局命令,包括最基本的GET/SET,用于模式匹配的KEYS(及其性能警告),判断存在的EXISTS,以及实现业务场景必不可少的EXPIRE和TTL。这些命令是日常开发中使用频率最高的利器。 随后,文章进一步揭示了Redis高效的秘密——其丰富的数据结构与巧妙的内部编码。我们了解到,Redis对外提供统一的数据类型(如List, Hash),但在底层会根据数据规模和特点,智能地选择ziplist、skiplist等最优的编码方式,以此在空间和时间效率上达到极致平衡。 最后,我们集中探讨了Redis架构的精髓——单线程模型,并完美解答了“Redis为什么这么快”这一经典面试题。其核心优势可归结为四点: 纯内存操作:所有操作均在内存中完成,速度远超磁盘I/O。 功能相对简单:核心功能聚焦于数据读写,避免了关系型数据库的复杂逻辑。 单线程模型:避免了多线程带来的上下文切换和锁竞争的开销。 I/O多路复用:采用epoll等机制,单个线程即可高效处理大量网络连接。 通过这篇文章,你不仅掌握了Redis的实用操作技巧,更深入理解了其背后的设计哲学与高性能原理,为你在实际项目应用和技术面试中增添了充足的信心。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 环境搭建
  • redis核心命令
    • get和set
    • keys(全局命令)
    • exists(判断Key是否存在)
    • del(删除key)
    • expire(设置过期时间)
    • ttl(查询存活时间)
    • key的过期策略的实现
    • 定时器的实现原理
    • type(查看value的类型)
    • 总结
  • 常用数据结构
  • redis单线程模型
    • 单线程模型的工作过程
    • redis为什么这么快(重要面试题)
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档