目录

一.在Ubuntu系统安装Redis

二. redis客户端介绍

 三. 全局命令

3.1.GET和SET命令

3.2.KEYS(生产环境禁止使用)

3.3.EXISTS

3.4.DEL

3.5.EXPIRE

3.6.TTL

3.6.1.Redis的过期策略

3.6.2.基于优先级队列/堆的实现去实现定时器

3.6.3.定时器:基于时间轮的实现 

3.7.TYPE  

四.常用数据结构

4.1.Redis 底层数据结构与编码优化

4.2.Redis数据结构和内部编码

4.3.有没有必要记住啥时候使用啥编码方式呢?

五.单线程模型

5.1. 引出单线程模型

5.2. 为什么单线程还能这么快 


一.在Ubuntu系统安装Redis

首先我们可以看看

apt search redis

我们找到下面这个来 

apt install redis

netstat -anp | grep redis

 Redis默认端口是6379。这里还是有一点问题。

大家注意到了吗?127.0.0.1可是本地回环IP啊,Redis绑定的IP地址可是本地回环IP啊,这怎么可以。

我们需要去修改一下Redis的配置文件,把这个绑定的IP地址修改为

cd /etc/redis

这个redis.conf就是redis的配置文件。

vim /etc/redis/redis.conf

我们往里面找到下面这一行

把上面那个给我修改成下面这个

除了上面这个绑定IP地址需要改之外,我们还需要修改下面这个

我们把yes给修改成no

有人就好奇,我们的redis有必要配置密码吗?

没有必要,虽然我们的Redis没有密码,但是因为我们的数据不值钱,就可以说是非常安全的,所以我们不配置密码的。

接下来我们需要重启redis服务器

service redis-server restart

 

接下来我们可以redis自带的客户端redis-cil登陆一下我们的redis。

redis-cli

到这一步,我们的redis就算是安装完成了。

那我们怎么退出呢?

直接ctl+d就能退出

二. redis客户端介绍

Redis 的核心架构:客户端-服务器模型

与 MySQL 等主流数据库系统类似,Redis 也严格遵循客户端-服务器 (Client-Server, C/S) 架构模型。这是其设计的基础,理解这一点至关重要:

  1. 服务端 (redis-server)

    • 这是 Redis 的核心,一个常驻运行的后台进程

    • 它负责数据存储、管理、执行命令、处理持久化、维护数据结构等核心功能。

    • 服务端通常运行在指定的服务器或主机上,监听配置的 TCP 端口(默认是著名的 6379 端口),等待客户端的连接请求。

    • 值得注意的是,Redis 服务端在处理命令时是单线程的(核心网络模型是 Reactor 模式),这使其命令执行具有原子性,但也意味着单个耗时操作会阻塞后续请求。

  2. 客户端

    • 任何需要与 Redis 服务进行交互的程序或工具都属于客户端范畴。

    • 客户端通过网络协议(RESP - REdis Serialization Protocol)与服务端建立连接、发送命令并接收结果。

    • 客户端的形态多样,如前所述,包括:

      • 命令行客户端 (redis-cli)

      • 图形用户界面 (GUI) 客户端

      • 各种编程语言的客户端库 (如 redis-py, Jedis, Lettuce, ioredisgo-redis 等),这些库被集成到应用程序代码中,是生产环境最主要的交互方式。

  3. 交互过程

    • 应用程序(客户端)发起连接请求到 redis-server

    • 连接建立后,客户端向服务端发送符合 RESP 格式的命令(例如 SET key valueGET keyLPUSH list item)。

    • 服务端接收命令,在其内存数据库中执行相应的操作。

    • 服务端将执行结果(数据、状态信息或错误)序列化为 RESP 格式,通过网络返回给客户端。

    • 客户端接收并解析结果,供应用程序使用。

Redis 客户端形态概览

Redis 提供了多种客户端连接方式,以适应不同的使用场景和需求:

  1. 命令行客户端 (redis-cli)

    • Redis 自带的标准命令行工具,功能强大且轻量。

    • 直接在终端启动即可连接本地默认端口的 Redis 服务:redis-cli

    • 支持指定连接远程或特定端口的 Redis 服务:redis-cli -h <hostname> -p <port>

    • 这是学习和基础管理最常用的方式,尤其在服务器环境或需要通过 SSH 连接时非常方便。

  2. 图形化界面客户端 (GUI)

    • 提供可视化的桌面程序或 Web 应用界面,操作更直观,方便查看数据结构和监控状态。

    • 市面上有多种选择(如 Another Redis Desktop Manager, RedisInsight, Redis Desktop Manager, web-redis 等)。

    • 重要提示: 在企业生产环境中,图形化客户端的使用可能受限:

      • 办公 Windows 电脑可能无法直接连接到部署在隔离网络(如生产服务器、内网)中的 Redis 实例。

      • 连接路径通常涉及复杂的网络架构,如跳板机、堡垒机,并需要严格的权限审批。

      • 其可用性不如命令行工具 (redis-cli) 或程序化 API 稳定可靠。

  3. 基于 Redis API 的程序化客户端

    • 这是实际开发工作中最主要的集成方式。非常类似于mysql的c语言api

    • 开发者使用 Redis 官方或社区提供的各种编程语言客户端库(如 Python 的 redis-py, Java 的 Jedis/Lettuce, Node.js 的 ioredis, Go 的 go-redis 等),在自己的应用程序代码中直接与 Redis 进行交互。

    • 这种方式提供了最大的灵活性,允许应用程序执行所有 Redis 命令、管理连接池、处理序列化/反序列化,并实现缓存、会话存储、消息队列等核心功能。

    • 其原理和重要性类似于关系型数据库中的 JDBC (Java) 或 ODBC/Python DB-API 等驱动接口,是实现应用与 Redis 深度集成的关键。

Redis 性能的辩证认知与应用场景分析

Redis 性能快不快,得看和谁比:MYSQL<Redis<内存变量

  • 相较于 MySQL 等磁盘存储的关系型数据库,Redis 基于内存操作,速度优势显著。

  • 但若与应用程序进程内直接操作内存变量(如 HashMap) 相比,Redis 需经过网络通信才能访问数据,反而会引入额外开销,性能可能更低


单机系统下的选择困境
在单应用服务器场景中,是否引入 Redis 需具体问题具体分析

  • 进程内变量 (如 HashMap) 的优势

    • 极致速度:直接内存操作,无网络延迟。

  • 引入 Redis 的代价与收益

    • 代价 (可能更慢)网络 I/O 成为瓶颈。

    • 核心收益

      • 数据持久化与隔离:Redis 作为独立进程存储数据,应用服务器重启不会丢失数据

      • 为分布式演进铺路:未来扩展为多节点分布式系统时,Redis 可充当共享、中心化的数据存储,天然支持多应用服务器访问同一数据源。而进程内变量无法跨进程共享。


实例剖析:用户点赞数存储
以存储 <视频ID, 点赞数> 键值对为例:

  • 方案 A:进程内 HashMap

    • 优势:操作极快(纳秒级内存访问)。

    • 致命缺点:数据与应用生命周期绑定;服务器重启数据清零;无法支持分布式扩展。

  • 方案 B:Redis

    • 代价:每次读写需网络通信(毫秒级),速度慢于 HashMap。

    • 核心优势:数据独立持久化;天然支持多服务器节点访问同一数据集;便于后续扩展。

  1. 技术选型原则:理性决策,避免“锤子思维”

    • 深入分析需求:明确当前痛点(速度?持久性?)与未来规划(是否分布式?)。

    • 权衡利弊:清晰认知引入任何技术(如 Redis)解决什么问题,同时引入什么问题(如网络延迟、运维复杂度)。

    • 切忌“无脑”跟风:不能因为 Redis “快” 或 “流行” 就不加思考地使用。技术是工具,对症下药才是关键

 

 三. 全局命令

通过 redis-cli 客户端和 redis 服务器交互,涉及到很多的 redis 的命令

redis 的命令非常非常多!!!

  1. 掌握常用命令(多练多练习)

  2. 学会使用 redis 的文档

阅读文档,是程序域的基础!!

任何一个工具软件,去找相关资料,一定是官方网站!!!

虽然 redis 这种知名软件,都是有中文文档的。但是老温仍然建议大家看英文的。(英语这一关,必须要过,也一定能过!!)

后面工作中可能会用到一些不太知名的软件/库,很可能没有中文文档,但是一定有英文文档。


我们看看Redis的官方文档在哪里?

首先我们来到Redis官网:Redis - The Real-time Data Platform

例如说我们需要查询一下ping命令,我们可以像下面这样 

 

后续我们查询文档的时候,就是按照上面这样子去操作。


在 Redis 中,操作不同数据结构(如 String, Hash, List, Set, Sorted Set 等)时,通常需要使用其对应的专属命令。例如,操作 List 会用到 LPUSH/RPOP,操作 Hash 会用到 HSET/HGET

而 全局命令 (Global Commands) 则是一类特殊的命令,它们的核心特点是:不依赖于键(Key)所关联的具体数据结构类型。这意味着:

  1. 跨数据结构通用性: 无论一个键关联的是 String、List、Hash 还是其他任何 Redis 数据结构,全局命令都可以作用于这个键。

  2. 操作对象是键本身: 这些命令通常关注的是键的元数据生命周期管理,而非其内部存储的具体数据结构值(虽然有些命令如 TYPE 会返回数据类型信息)。

  3. 提供统一的操作接口: 它们在 Redis 的键空间 (Key Space) 层面工作,为管理和操作所有键提供了一组基础且一致的指令。

我们整个第三大节讲述的都是全局命令。

3.1.GET和SET命令

这两个命令是Redis的核心命令,

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

  • get就是根据key来获取value
  • set则是把key和value存储进去

注意:key和value会被解析为字符串

接下来我们来演示一下

我们先进入redis客户端

redis-cli

接下来我们将使用set来存储一下键值对

set key1 value1
set key2 value2

接下来我们使用get命令,就能获取我们上面使用set存储的键值对

如果我们查询的是一个不存在的key呢?

这里的nil和C语言的NULL是一样的,都是表示空。

现在学到这里,我们就能使用redis解决大量的问题了。这就是redis的优势之一——学习成本低

3.2.KEYS(生产环境禁止使用)

Redis 支持很多种数据结构~~

整体上来说,Redis 是键值对结构。key 固定就是字符串。value 实际上会有多种类型,就像下面这些

  • 字符串
  • 哈希表
  • 列表
  • 集合
  • 有序集合

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

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

而我们本文中讲解的所有命令都是属于全局命令。


现在我们先来讲解keys

KEYS用来查询当前服务器上符合条件的KEY

语法:

KEYS pattern
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(N)
  • 返回值:匹配 pattern 的所有 key。

模式(Pattern)

指包含特殊通配符的字符串表达式(英文术语需重点掌握:pattern)。
其核心作用是 描述目标字符串的特征规则,实现高效匹配检索。

通配符语义详解

符号

名称

匹配规则

示例

?

单字符占位

匹配任意1个字符

h?llo → hello/hallo

*

多字符通配

匹配0个或多个字符

h*llo → hllo/heeeello

[ ]

字符集

匹配括号内任意1个字符

h[ae]llo → hello/hallo

[^ ]

排除字符集

匹配不在括号内的字符

h[^e]llo → hallo/hbllo

[a-b]

字符范围

匹配指定范围内的字符

h[a-c]llo → hallo/hbllo/hcllo

 我们来举一下例子

set hello 1
set hallo 1
set hbllo 1
set hllo 1
set heeeeeeeello 1

?表示匹配任意1个字符

*匹配0个或多个字符

[ ] 匹配括号内任意1个字符

[^ ]匹配不在括号内的字符

[a-b]匹配指定范围内的字符

注意事项:

生产环境必须禁止使用!

核心问题

  1. 性能杀手
    KEYS * 命令需要遍历所有键(时间复杂度 O(N))。当 Redis 存储海量数据时,此操作可能耗时数秒甚至分钟级。

  2. 单线程阻塞灾难
    Redis 单线程架构导致:

    • 执行 KEYS * 期间整个服务被冻结

    • 所有客户端请求超时失败

    • 缓存失效引发数据库雪崩(如 MySQL 被突发流量击垮)

    • 最终导致系统级联瘫痪

  3. 职业风险
    若因此引发线上事故:

    • 轻则年终奖受损

    • 重则面临失业危机

3.3.EXISTS

判断某个key是否存在。

语法:

EXISTS key [key ...]
  1. 命令有效版本:1.0.0之后
  2. 时间复杂度:O(1),这个是因为redis就是按照哈希表的方式来组织key的。
  3. 返回值:key存在的个数。

关键特性

  1. 键唯一性原则
    Redis 采用键值对存储模型(类哈希表结构),所有键必须是全局唯一的标识符。

  2. 底层高效实现

    • 时间复杂度:O(1)(恒定时间操作)

    • 实现原理:Redis 使用哈希表组织所有键,实现瞬时查找

  3. 数据结构分层理解

    层级组织结构示例说明
    键存储层全局哈希表所有键的存储容器
    值存储层多数据结构值可以是字符串/列表等

我们看一下例子 

 

3.4.DEL

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

语法: 

DEL key [key ...]
  •  命令有效版本:1.0.0之后
  • 时间复杂度:O(1),这个是因为redis就是按照哈希表的方式来组织key的。
  • 返回值:删除掉的key的个数。

我们来看看

核心认知:数据删除的破坏力完全取决于 Redis 在系统中的角色定位

之前我们在学习mysql的时候,总是强调删除数据的操作是十分危险的操作,因为一删除数据就没有了,那么现在redis呢?

这个其实需要分情况讨论

  1. Redis作为缓存(最常见)
  2. Redis作为主数据库
  3. Redis作为消息队列

一、Redis作为缓存场景下的相对可控性

作为 MySQL 的前置缓存(主流应用模式),Redis 存储的仅是热点数据副本。此时删除部分键值如同撤下商场的展示样品——虽然会导致短暂缓存击穿,但源数据仍安全存储在 MySQL 中。通过自动重建机制,通常能在秒级恢复服务。真正的风险在于大规模批量删除:当大量热数据突然消失,所有请求将如洪水般直冲后端数据库。某电商曾因误删 80% 缓存键,致使 12 万 QPS 的流量瞬间压垮 MySQL,引发 47 分钟服务瘫痪,直接损失 180 万美元。这种 "缓存雪崩→数据库崩溃→业务停摆" 的连锁反应,正是运维人员最警惕的噩梦。

二、Redis作为主数据库的毁灭性风险

当 Redis 承担唯一数据源角色时,任何删除操作都等同于焚毁仓库库存。不同于缓存场景的数据重建可能,此时误删将导致业务核心数据永久丢失。某社交平台曾因 FLUSHDB 命令清空用户关系库,致使 3000 万用户连接图谱消失,最终需花费 17 小时从冷备中恢复——期间市值蒸发 9%。这种场景下,即便删除单个键(如用户支付凭证)也可能引发资金对账灾难。

三、Redis作为消息队列角色的复杂影响

Redis 用作消息队列时,风险呈现灰度特征:

  • 低风险场景:删除非关键消息(如用户行为日志),最多影响数据分析完整性

  • 高风险场景:清除待处理任务(如支付订单),将直接导致资金损失。某金融系统曾误删 10 万条支付指令,引发 300 万美元资金流水断裂,整个技术团队季度奖金归零
    判断标准取决于消息的不可再生性——如同销毁物流清单,普通商品可补发,而限时药品缺货将酿成事故。

3.5.EXPIRE

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

语法:  

EXPIRE key seconds  
  • 注意单位是秒!!!
  • 命令有效版本:1.0.0 之后  
  • 时间复杂度:O(1)  ,
  • 返回值:1 表示设置成功。0 表示设置失败。  

示例:  

3.6.TTL

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

语法:

TTL key
  • 命令有效版本:1.0.0 之后
  • 时间复杂度:O(1)
  • 返回值:剩余过期时间。-1 表示没有关联过期时间,-2 表示 key 不存在。

示例:

EXPIRE 和 TTL 命令都有对应的支持毫秒为单位的版本:PEXPIRE 和 PTTL,详细用法就不再介绍了。

3.6.1.Redis的过期策略

 redis里面有很多key,这些key里面势必有很多key有过期时间,那么这个时候redis服务器是怎么知道当前有哪些key过期了需要去删除,哪一些key还没有过期呢?

想象一个超级忙碌的便利店,只有一个老板(Redis的单线程):

  1. 定期抽查(定期删除):

    • 老板很忙: 老板大部分时间都在收银台(处理顾客请求),不能一直去检查货架上的商品过期没。

    • 抽空检查: 每隔一小会儿(比如100毫秒),老板就快速离开收银台,去货架上随机抽查一小部分商品(比如20件)。

    • 限时检查: 这个检查动作有严格的时间限制(比如25毫秒)!不管查到多少,时间一到必须立刻回收银台。为啥?因为不能让顾客(请求)等太久,否则队伍排老长,店就瘫痪了(阻塞)。

    • 发现过期: 抽查时发现过期的商品(键),就当场扔掉(删除)。

    • 为啥不全查? 货太多(海量键),全查一遍太花时间,顾客等不起!

  2. 顾客结账时检查(惰性删除):

    • 即时发现: 当顾客(客户端)想买(访问)某个商品(键)时,老板在收银台拿到这个商品会先看一眼保质期。

    • 当场处理: 如果发现过期了,老板立刻说:“这个过期了,不能卖”,然后顺手把它扔进垃圾桶(删除),告诉顾客没有这个货了(返回空值)。

    • 你的方便面经历: 这就像小时候你去小卖部买方便面,老板拿出来才看到过期了,然后说“这个坏了,我给你换一个/没货了”。过期的东西不会自己消失,只有被人拿起来看的时候才会被发现并处理掉。

问题:总有“漏网之鱼”

  • 有些商品(键)可能一直没被老板抽检到,也一直没人来买(访问)。它们就默默地待在货架(内存)的角落里过期了,成了“幽灵商品”(幽灵数据),占着地方(内存)。

终极清仓大甩卖(内存淘汰机制):

  • 货架满了咋办? 当便利店的货架(内存)快被塞满时,光靠抽查和结账检查不够用了。这时老板会启动紧急清仓策略(内存淘汰机制)。

  • 按规则扔: 老板会根据定好的规则(比如“把最近最没人买的商品扔掉” - LRU策略),主动清理掉一批商品(键),腾出空间给新货,即使有些商品可能还没过期。这是防止内存彻底用光的最后防线。

灵魂拷问:为啥不用“定时炸弹”自动炸毁过期商品?

很多工程师想过:给每个商品装个小定时器(时间轮/优先级队列),到点就“砰”自动消失,多省事?

但老板(Redis作者)有顾虑:

  • 太复杂: 管理成千上万个定时器本身就很麻烦,需要额外的人手(多线程),违背了便利店“只雇一个老板”(单线程简单)的原则。

  • 干扰营业: 想象一下,每隔五分钟,店里就“砰”、“砰”、“砰”地自动炸毁过期商品。且不说吓跑顾客,爆炸声(定时器触发)本身就会打断老板收银(处理请求),让结账速度变慢(服务抖动)。老板宁愿自己抽空去检查,也不想被定时炸弹干扰。

血泪教训:

  • 有个大超市(电商平台)曾经就是“抽检”动作慢了一点点(超时7毫秒),结果在打折日顾客爆满时,收银台(主线程)被拖住了,顾客请求堵成长龙,最后整个系统都崩溃了(数据库雪崩)。这证明了那个“25毫秒必须回收银台”的红线是多么重要!

3.6.2.基于优先级队列/堆的实现去实现定时器

定时器:基于优先级队列/堆的实现

定时器的核心功能是在指定时间点到达后,执行预设的任务。

1. 优先级队列基础

  • 普通队列遵循“先进先出”(FIFO)原则。

  • 优先级队列则根据自定义的优先级决定元素的出队顺序优先级高的元素先出队。

  • 优先级定义示例: 在Redis处理过期Key的场景中,可以将“过期时间越早”定义为优先级越高。

2. 过期Key管理的应用
假设有大量Key设置了过期时间:

  • 将这些Key放入一个优先级队列,并指定排序规则:过期时间最早的Key优先级最高,位于队首

  • 队首元素的意义: 它始终是最早要过期的Key!

    • 示例:

      • key1: 过期时间 12:00

      • key2: 过期时间 13:00

      • key3: 过期时间 14:00

      • 此时队首元素是 key1 (12:00)。

3. 高效的扫描机制

  • 定时器只需分配一个专用扫描线程

  • 该线程的核心工作是:持续检查队首元素是否已过期

  • 关键原理: 如果队首元素尚未过期,那么队列中所有后续元素必然也未过期!因此,扫描线程无需遍历整个队列,只需聚焦于队首元素即可,大大减少检查开销。

4. 优化:避免忙等待与节省CPU

  • 频繁检查队首元素(例如循环空转)会浪费CPU资源。

  • 优化策略:

    1. 扫描线程计算当前时间队首元素过期时间的差值。

    2. 根据这个时间差,让线程主动休眠(阻塞) 相应时长。

    3. 当休眠时间结束(或接近队首元素过期时间)时,系统自动唤醒线程进行检查。

  • 效果: 线程在等待期间不占用CPU,显著节省资源。

5. 处理新增的高优先级任务

  • 场景: 当扫描线程处于休眠状态时,如果添加了一个新的任务,其过期时间(例如 11:30)早于当前队首元素(例如 12:00)

  • 解决方案:

    1. 在添加新任务的逻辑中,立即唤醒处于休眠状态的扫描线程。

    2. 被唤醒的线程重新检查新的队首元素(此时是刚加入的11:30任务)。

    3. 根据新的队首元素的过期时间重新计算并调整休眠时间

  • 意义: 确保了即使在高优先级新任务插入时,定时器也能及时响应并调整调度计划,保证准确性。

总结: 这种基于优先级队列(通常使用最小堆实现)的定时器方案,通过聚焦队首元素和智能休眠机制,实现了对大量定时任务的高效、低开销管理,特别适用于像Redis过期Key处理这类需要精确管理最早到期事件的场景。其效率高度依赖于优先级队列(堆)操作的性能(O(log n) 的插入和删除)。

3.6.3.定时器:基于时间轮的实现 

时间轮(Timing Wheel)是一种将时间离散化处理的高效定时器设计方案,尤其适合管理大量短周期定时任务。

1. 核心结构:离散化时间片

  • 将连续的时间流划分为固定长度的小时间单元,称为 “槽”(Slot) 或 “格子”

  • 时间粒度(Tick Duration): 每个槽代表的时间长度(例如:100ms)。该粒度是核心参数,需根据实际应用场景(如对精度的要求、任务密度)进行配置。

  • 时间轮结构: 一个环形数组(或循环队列),数组的每个元素对应一个时间槽。

2. 任务组织:槽与链表

  • 每个时间槽上关联一个链表

  • 每个链表节点代表一个需要在该槽对应的时间点(或之后) 执行的定时任务

  • 任务表示: 包含任务执行所需的逻辑。在底层实现中,这通常是一个函数指针(或回调函数) 及其参数。在面向对象语言(如 Java)中,也可以通过实现特定接口(如 Runnable)的任务对象来封装执行逻辑。

3. 运作机制:指针扫描

  • 时间轮由一个指针(Current Pointer) 驱动,该指针按照固定的时间间隔(等于时间粒度,如每 100ms)向前移动一个槽位。

  • 指针移动: 通常由一个独立的时钟线程或集成在事件循环中的计时器驱动。

  • 任务触发: 每当指针移动到一个新的槽位时:

    1. 取出该槽位上链表中的所有任务节点。

    2. 执行这些任务节点关联的任务逻辑。

  • 关键点: 指针移动到某个槽位,意味着该槽位所代表的“到期时间窗口”(例如第 N 个槽代表 [N * tick_duration, (N+1) * tick_duration))已到达或刚过,挂在该槽上的任务应被触发。

4. 添加定时任务

  • 场景: 添加一个需要在 delay 毫秒后执行的任务(例如:delay = 300ms)。

  • 计算槽位:

    1. 确定任务需要经过多少个完整的时间片(槽):slots = delay / tick_duration (取整或向上取整,取决于具体实现策略)。

    2. 计算任务应放入的目标槽索引target_slot_index = (current_pointer_index + slots) % total_slots

  • 挂载任务: 将代表该任务的新节点插入到 target_slot_index 对应槽的链表中。

5. 处理长周期任务

  • 挑战: 如果任务的延迟时间(例如 3000ms)远大于时间轮的总跨度total_span = tick_duration * total_slots),直接按上述方法计算 target_slot_index 会失效(因为 slots 可能远大于 total_slots)。

  • 解决方案:多级时间轮 (Hierarchical Timing Wheels)

    • 构建多个层级的时间轮,每一层覆盖更大的时间范围。

    • 例如:第一级时间轮(细轮):粒度 100ms,共 10 个槽 -> 覆盖 1000ms。

    • 第二级时间轮(粗轮):粒度 1000ms (1s),共 10 个槽 -> 覆盖 10000ms (10s)。

    • 当一个任务在细轮上到期时间超过其覆盖范围时,会被“降级”添加到粗轮中更远的槽位。

    • 粗轮指针移动更慢(每 1s 移动一次)。当粗轮上的任务即将进入细轮覆盖范围时,会被“升级”重新分配到细轮的合适槽位。

    • 通过层级结构,可以用有限的空间(槽数)高效管理极长延时的任务。

6. 配置要点

  • 时间粒度 (tick_duration): 决定了定时器的最小精度和扫描频率。粒度越小,精度越高,但指针移动和任务检查开销也越大。

  • 槽数量 (total_slots): 决定了时间轮单层的最大覆盖时间 (tick_duration * total_slots)。

  • 权衡与优化: 需要根据应用的典型任务延时分布、任务数量、精度要求、CPU 负载容忍度等因素,灵活调整粒度和槽数,或在需要时采用多级时间轮。

总结: 时间轮通过将时间离散化为槽并使用指针周期性扫描,实现了对大量定时任务的批量触发,其时间复杂度(添加任务 O(1),触发任务 O(n) 其中 n 是槽上任务数)在任务密集时通常优于基于堆的方案。结合多级时间轮,它能有效处理各种时间跨度的定时任务,是高性能网络框架(如 Netty)、消息队列(如 Kafka)、操作系统内核中管理定时器的常用高效机制。 

注意事项

此处大家一定要注意!Redis 并没有采取上述的方案!

但是要了解这两种方案,都是属于高效的定时器的实现方式。很多场景可能都会用到。

在 Redis 源码中,有一个比较核心的机制,是 事件循环。

3.7.TYPE  

返回 key 对应的数据类型。  

语法:  

TYPE key  
  • 命令有效版本:1.0.0 之后  
  • 时间复杂度:O(1)  
  • 返回值:none, string, list, set, zset, hash and stream...  

示例:

DEL key1
DEL key2
DEL key3 
SET key1 "value"  
LPUSH key2 "value"  
SADD key3 "value"  
TYPE key1  
TYPE key2 
TYPE key3  

四.常用数据结构

我们知道在 Redis 中,操作不同数据结构(如 String, Hash, List, Set, Sorted Set 等)时,通常需要使用其对应的专属命令。例如,操作 List 会用到 LPUSH/RPOP,操作 Hash 会用到 HSET/HGET

而 全局命令 (Global Commands) 则是一类特殊的命令,它们的核心特点是:不依赖于键(Key)所关联的具体数据结构类型

那么我们在上面这一个大节讲的就是全局命令,我们接下来需要讲解各个数据结构对应的专属命令。但是在介绍各个数据对应的专属命令之前,我们需要先看看我们在redis里面常用的数据结构有哪些?


type 命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串)、list(列 表)、hash(哈希)、set(集合)、zset(有序集合),但这些只是Redis对外的数据结构,如图所⽰。 

后续的文章我们将通过围绕各个数据结构来介绍相关命令。

通用命令是 操作不同的数据结构,就会有不同的命令。

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

4.1.Redis 底层数据结构与编码优化

Redis 在实现其提供的数据结构(如 String, Hash, List, Set, Sorted Set)时,采用了高度优化的策略。

关键在于其底层编码(Encoding)机制

  1. 数据结构与编码分离

    • 数据结构(Data Structure):这是 Redis 对外暴露的抽象接口(如 HSETHGET 操作的 Hash 结构),Redis 对这类操作提供明确的时间复杂度承诺(例如,Hash 的查询、插入、删除通常承诺为 O(1))。

    • 编码实现(Encoding):这是 Redis 在内存中实际存储和表示该数据的内部实现方式。同一个 Redis 数据结构(如 Hash),根据其包含数据的当前状态(如元素数量、元素大小、元素类型),可能会使用多种不同的底层数据结构来实现。

  2. 动态优化与透明性

    • Redis 的核心优化在于:它会根据数据的具体特征,在运行时自动选择最节省内存(空间优化)或最高效(时间优化)的底层编码方式。

    • 例如,一个较小的 Hash 对象可能使用内存紧凑的 ziplist(压缩列表) 编码,其查询操作可能接近 O(N),但由于 N 非常小,实际效率很高且极其节省内存。随着该 Hash 对象的元素数量增长或元素值变大,Redis 会在内部自动将其转换为标准的 hashtable(哈希表) 编码,此时查询、插入、删除操作就能严格保证 O(1) 的时间复杂度。

    • 这种转换对用户是完全透明的。用户始终使用 HGETHSET 等命令操作 Hash,无需关心底层是 ziplist 还是 hashtable。Redis 确保了无论底层编码如何变化,其操作都符合对外承诺的时间复杂度特性(在转换发生后)。

  3. 优化的目标与效果

    • 节省内存(Space Efficiency):在数据量小或结构简单时,使用如 ziplist、intset(整数集合) 等编码能显著减少内存占用。例如,ziplist 通过连续内存存储、省略指针、特殊编码小整数等方式压缩空间。

    • 提升速度(Time Efficiency):在数据量大或操作复杂时,使用如 hashtable、skiplist(跳表) 等编码能提供高效的随机访问(O(1))或范围查询(O(logN))。

    • 平衡取舍(Trade-off):Redis 的编码机制本质是在时间复杂度和空间复杂度之间做智能的、动态的权衡,针对不同的数据规模和应用场景选择最优解。

总结:

Redis 并非简单地为每种数据结构固定使用一种底层实现。它通过精妙的编码机制,在运行时根据数据的实时状态(大小、类型),动态切换其内部实际使用的底层数据结构。这使得 Redis 能够在保证对外接口一致性和时间复杂度承诺的前提下,在源码层面进行极致的优化,从而达到节省宝贵内存空间或提升操作执行速度的效果。用户看到的是稳定的“数据结构”接口和性能承诺,而 Redis 内部则灵活地运用着多种“编码方式”来达成这一目标。

4.2.Redis数据结构和内部编码

数据结构内部编码备注
stringraw

这个是最基本的字符串(底层就是持有一个char数组(c++)或者byte数组(java)。

注意c++里的char是一个字节,和java里面的byte是一样的,java里面的char是两个字节

intredis通常也可以用来实现一些”计数“这样的功能,当value就是一个整数的时候,redis就会使用int来保存
embstr针对短字符串进行的特殊优化
hashhashtable最基本的哈希表,这个redis内部的哈希表和java里面的hashtable可是有一点不太一样
ziplist

哈希表里面元素比较少的时候,就会被优化为ziplist。

为啥要压缩??

redis 上有很多很多 key. 可能是某些 key 的 value 是 hash . 此时, 如果 key 特别多, 对应的 hash 也特别多, 但是每个 hash 又不大的情况下, 就尽量去压缩. 压缩之后就可以让整体占用的内存更小了.

listlinkedlist链表
ziplist压缩链表
quicklist

从 redis 3.2 开始, 引入了新的实现方式 quicklist 同时兼顾了 linkedlist 和 ziplist 的优点~~ quicklist 就是一个链表, 每个元素又是一个 ziplist 把空间和效率都折衷的兼顾到~~ quicklist

比较类似于 C++ 中的 std::deque

至于在Java 标准库中, 据我所知, 好像没有对应的结构~~

sethashtable哈希表
intset集合中保存的都是整数
zsetskiplist

跳表~~

跳表是一种多层有序链表结构,通过在基础链表上建立多级“快速通道”(索引层),使数据查询时能像地铁换乘一样逐级跳跃前进,将查找时间复杂度从普通链表的 O(n) 优化到 O(log n)。它通过随机算法决定每个节点的索引高度(类似抛硬币),在保证高效查询的同时,支持 O(log n) 复杂度的插入和删除操作,成为平衡树的高效替代方案。

ziplist压缩

可以看到每种数据结构都有⾄少两种以上的内部编码实现,例如list数据结构包含了linkedlist和 ziplist 两种内部编码。

同时有些内部编码,例如ziplist,可以作为多种数据结构的内部实现,可以通 过object encoding命令查询内部编码:

set hello world
lpush mylist a b c
object encoding hello
object encoding mylist

可以看到hello对应值的内部编码是embstr,键mylist对应值的内部编码是quicklist。

Redis 这样设计有两个好处:

  • 1)可以改进内部编码,⽽对外的数据结构和命令没有任何影响,这样⼀旦开发出更优秀的内部编码, ⽆需改动外部数据结构和命令,例如Redis3.2提供了quicklist,结合了ziplist和linkedlist两者的优 势,为列表类型提供了⼀种更为优秀的内部编码实现,⽽对⽤⼾来说基本⽆感知。
  • 2)多种内部编码实现可以在不同场景下发挥各⾃的优势,例如ziplist⽐较节省内存,但是在列表元素 ⽐较多的情况下,性能会下降,这时候Redis会根据配置选项将列表类型的内部实现转换为 linkedlist,整个过程⽤⼾同样⽆感知。

4.3.有没有必要记住啥时候使用啥编码方式呢?

我们都知道redis 会自动根据当前的实际情况选择内部的编码方式,自动适应的。

那么是否要记住,啥时候使用啥编码方式呢?  

只记思想,不记数字!!!  

网上的说法:如果字符串长度小于39字节,使用 embstr,超过 39 字节,使用raw  

大家注意了啊记数字,没有任何意义!!  

  • 1. 数字都是可配置的。  
  • 2. 数字是咋来的?考证清楚~~  

相比于知道数字,更重要的是知道数字是怎么得到的。  

就可以根据所处的实际场景,重新得到这样的数字。  

很多同学会陷入“记忆数字”的误区~~  
学校的考试就乐意考这种问题~~  

考试和面试,和工作,都差别很大~~  

  1. 考试的评价标准:有标准答案,看你做的对不对。  
  2. 面试的评价标准:有部分标准答案,也有一部分没有标准答案。看你解决问题的过程和思路 = > 面试官是否认可你这个人。  
  3. 实际工作的评价标准:如何低成本的解决问题~~

HashMap 的相关数字:

  1. 链表长度达到 xxxx => 变成红黑树
  2. 负载因子达到 xxxx => 触发扩容

实际工作中遇到的场景,上述数字就不一定合适了~~

正确的做法,是根据实际的测试结果,测试出一个更合适的数值~~

同理,类似的还有,一个线程池,线程数目设置成多少....类似于这样的一些 "参数" 非常常见的,都是 "可调" 的~~

五.单线程模型

需要特别澄清的是,当我们说 Redis 采用单线程模型时,指的是其核心的 命令请求处理 和 命令执行 逻辑是由 一个主线程 串行完成的。这并不意味着整个 Redis 服务器进程内部只有一个线程。

实际上,现代 Redis 版本(尤其是 Redis 6.0 及以后)在内部使用了多个线程,但这些线程主要服务于以下目的:

  1. 网络 I/O 多线程 (Redis 6.0+ 优化):

    • 作用: 利用多核并行处理网络读(接收请求)和写(发送响应)。

    • 关键: I/O 线程只负责数据搬运。收到的命令交给主线程执行,主线程生成的响应再交给 I/O 线程发送。命令执行本身仍是单线程。

  2. 后台线程 (避免阻塞):

    • 目的: 将可能长时间阻塞主线程的慢操作移出主线程,异步执行。

    • 主要场景 (举例):

      • 惰性删除: 异步释放超大 Key 的内存。

      • AOF 刷盘 (fsync): 异步执行磁盘同步操作(取决于配置)。

    • 其他: 还处理一些如 RDB 生成协调、集群维护等后台任务。

5.1. 引出单线程模型

现在开启了三个redis-cli客⼾端同时往同一个redis服务器里面执⾏命令

客⼾端1设置⼀个字符串键值对:

127.0.0.1:6379> set hello world

客⼾端2对counter做⾃增操作:

127.0.0.1:6379> incr counter

客⼾端3对counter做⾃增操作:

 127.0.0.1:6379> incr counter

这三个客户端几乎是“并发”地发起了各自的命令请求。此时,Redis 服务器端是否会出现类似多线程环境下的并发安全问题(如数据竞争)呢?

答案是:不会

这得益于 Redis 核心处理模型的单线程特性。Redis 使用单线程(主要指其核心网络 I/O 和命令执行线程)来处理所有客户端请求。当多个请求同时到达服务器时,它们首先会被放入一个队列中进行排队。随后,Redis 服务器严格地按照先进先出(FIFO) 的顺序,从队列中逐个取出命令并串行执行

从微观角度看,Redis 服务器对这些命令的处理是完全顺序化、串行化的。一个命令必须完整执行完毕后,才会开始处理队列中的下一个命令。这种机制从根本上避免了多线程并发执行可能导致的竞态条件(Race Condition)和数据不一致性问题。

Redis 能够高效地采用单线程模型,关键在于其核心操作都是轻量级的、内存访问密集型的

  1. 内存操作:数据主要存储在内存中,访问速度极快。

  2. 高效数据结构:使用精心设计的数据结构(如哈希表、跳表、压缩列表等),保障了基础操作(如 GET, SET, INCR, LPUSH 等)的时间复杂度通常是 O(1) 或 O(log N)。

  3. 非 CPU 密集型:主要瓶颈通常在内存带宽或网络 I/O,而非 CPU 计算能力本身,因此对多核 CPU 的依赖度相对较低。单线程避免了多线程上下文切换和锁竞争的开销。

需要特别注意的弊端:

单线程模型带来的最大挑战是阻塞风险。如果某个命令执行时间过长(例如执行了一个复杂度为 O(N) 且 N 很大的命令,如 KEYS *,或者处理一个非常大的值),它会阻塞后续所有在队列中等待的命令的执行!这会导致客户端请求的延迟显著增加,甚至出现超时。

因此,在使用 Redis 时,必须格外谨慎

  1. 避免执行耗时命令:尤其是时间复杂度为 O(N) 的命令,需严格控制 N 的大小。例如,避免在生产环境使用 KEYS,改用 SCAN;避免操作过大的 List、Set、Hash 或 String。

  2. 优化数据结构与操作:选择合适的数据结构和命令,确保核心操作高效。

  3. 监控慢查询:利用 Redis 的慢查询日志 (SLOWLOG) 功能监控和优化执行时间过长的命令。

也就是说


我们所谓的Redis是采⽤单线程模型执⾏命令的是指:

虽然三个客⼾端看起来是同时要求Redis去执⾏命令的,但微观⻆度,这些命令还是采⽤线性⽅式去执⾏的,只是原则上命令的执⾏顺序是不确定的,但⼀定不会有两条命令被同步执⾏,

如下面这些图所⽰,可以想象Redis内部只有⼀个服务窗⼝,多个客⼾端按照它们达到的先后顺序被排队在窗⼝前,依次接受Redis的服务,所以两条incr命令⽆论执⾏顺序,结果⼀定是2,不会发⽣并发问题,这个就是Redis的单线程执⾏模型。

5.2. 为什么单线程还能这么快 

通常的理解是,单线程处理能力在多核时代会落后于多线程。例如,搬运 10000 公斤货物:

  • 如果只有一辆车(单线程),运力 200 公斤/次,需要往返 50 次

  • 如果有 50 辆车(多线程),理论上只需 1 次 就能完成。

那么,为什么 Redis 采用单线程模型,却能轻松达到每秒数万甚至十万级的操作处理能力 (QPS) 呢? 这看似违反常理,实则有其深刻原因,可以归结为以下几点:

1.极致的内存访问速度:

  • 核心基础: Redis 将所有数据存储在内存 (RAM) 中。内存访问的延迟通常在 100 纳秒 (ns) 级别,比访问磁盘(毫秒级,ms)或网络快几个数量级。

  • 性能基石: 这个数量级的差异是 Redis 超高吞吐量的根本物理基础。CPU 执行指令的速度极快(纳秒级),瓶颈往往在于获取数据。内存访问速度使得 CPU 能够被高效喂饱数据。

2.高效的 I/O 多路复用与非阻塞模型:

  • 技术核心: Redis 使用 epoll (Linux) / kqueue (BSD) / select (跨平台,效率较低) 等 I/O 多路复用 (I/O Multiplexing) 技术。

  • 工作原理: 单个线程可以同时监控成千上万个网络套接字 (Socket) 的状态(可读、可写、异常)。当某个套接字有数据可读(客户端请求到达)或可写(可以发送响应)时,事件驱动模型会通知 Redis 主线程进行处理。

  • 避免阻塞: 配合非阻塞 Socket,Redis 主线程在等待网络 I/O(如读请求未完全到达、写缓冲区满)时不会被挂起,而是立即转去处理其他已就绪的套接字事件,最大化利用 CPU 时间片,避免在空等上浪费资源。图 2-6 应展示了这个事件循环过程。

3.单线程模型的固有优势:

  • 零锁开销: 没有线程切换 (Context Switching) 和锁竞争 (Lock Contention) 的开销。多线程在竞争共享资源(如内存数据结构)时,需要加锁解锁,这本身有开销,且线程切换涉及寄存器保存恢复、缓存失效等,消耗 CPU 周期。

  • 简化设计: 单线程模型极大地简化了内部实现。数据结构和算法无需考虑线程安全(如加锁),设计更简单、高效、不易出错。避免了死锁等复杂并发问题。

  • 天然原子性: 每个命令的执行在单线程中都是天然原子性的,客户端无需担心命令执行过程中的并发干扰。

总结与关键洞察:

Redis 的高性能是上述因素协同作用的结果:

  • 内存速度提供了物理可能性。

  • I/O 多路复用 + 非阻塞 解决了海量网络连接的管理问题,让单线程能够高效处理高并发网络 I/O,将 CPU 时间几乎全部用在刀刃上(命令执行)

  • 单线程模型 消除了多线程固有的并发控制开销(锁、切换),在 CPU 层面达到了极高的效率,尤其是在核心操作本身非常轻量快速(O(1) 或低 O(N))的情况下。

虽然单线程给Redis带来很多好处,但还是有⼀个致命的问题:对于单个命令的执⾏时间都是有 要求的。

如果某个命令执⾏过⻓,会导致其他命令全部处于等待队列中,迟迟等不到响应,造成客⼾ 端的阻塞,对于Redis这种⾼性能的服务来说是⾮常严重的,所以Redis是⾯向快速执⾏场景的数据 库。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/91872.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/91872.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/91872.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu22.04系统实践 linux基础入门命令(三) 用户管理命令

以下有免费的4090云主机提供ubuntu22.04系统的其他入门实践操作 地址&#xff1a;星宇科技 | GPU服务器 高性能云主机 云服务器-登录 相关兑换码星宇社区---4090算力卡免费体验、共享开发社区-CSDN博客 之所以推荐给大家使用&#xff0c;是因为上面的云主机目前是免费使用的…

DPDK中的TCP头部处理

1. TCP头部结构 TCP头部通常为20字节&#xff08;不含可选字段&#xff09;&#xff0c;每个字段占据固定的字节位置。以下是TCP头部的结构&#xff0c;按字节位置逐一说明&#xff1a;0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 …

开源在线客服系统Chatwoot配置文件

参考&#xff1a; https://developers.chatwoot.com/self-hosted/deployment/dockerhttps://developers.chatwoot.com/self-hosted/deployment/docker 1、.env 配置文件 # Learn about the various environment variables at # https://www.chatwoot.com/docs/self-hosted/co…

PHP进阶语法详解:命名空间、类型转换与文件操作

掌握了PHP面向对象编程的基础后&#xff0c;就可以深入学习命名空间、类型转换、文档注释、序列化以及文件操作等重要概念。 1、命名空间&#xff08;Namespace&#xff09; 命名空间是PHP 5.3引入的重要特性&#xff0c;它解决了类名、函数名和常量名冲突的问题&#xff0c;使…

Webpack 搭建 Vue3 脚手架详细步骤

创建一个新的 Vue 项目 1&#xff09;初始化项目目录 新建一个文件夹&#xff0c;或者使用以下指令 mkdir webpack-vue_demo cd webpack-vue_demo2&#xff09;初始化 npm 项目 npm init -y3&#xff09;安装 vue 和 webpack 相关依赖 npm install vue vue-loader vue-template…

【Git 误操作恢复指南】

Git 误操作恢复指南 适用场景&#xff1a;git reset --hard 误操作后的紧急恢复 风险等级&#xff1a;&#x1f534; 高风险 - 可能导致代码丢失 恢复成功率&#xff1a;95%&#xff08;CI/CD 环境下&#xff09; &#x1f6a8; 紧急情况概述 问题描述 在项目开发过程中&am…

Go语言 逃 逸 分 析

逃逸分析是什么 逃逸分析是编译器用于决定变量分配到堆上还是栈上的一种行为。一个变量是在堆上分配&#xff0c;还是在栈上分配&#xff0c;是经过编译器的逃逸分析之后得出的“结论”。Go 语言里编译器的逃逸分析&#xff1a;它是编译器执行静态代码分析后&#xff0c…

LeetCode算法日记 - Day 1: 移动零、复写零

目录 1. 移动零 1.1 思路解析 1.2 代码实现 2. 复写零 2.1 思路解析 2.2 代码实现 1. 移动零 283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请…

Odoo:免费开源的医疗器械行业解决方案

开源智造Odoo专家团队深知&#xff0c;作为医疗器械制造商&#xff0c;您的成功取决于制造卓越产品的能力。您必须遵循严密控制的流程&#xff0c;开发和制造出达到最严格质量标准的产品。“开源智造Odoo医疗器械行业解决方案”是为医疗器械制造商设计的全球企业资源规划(ERP)软…

Redis键值对中值的数据结构

前言 前面我们已经介绍了Redis的键值对存储管理的底层数据结构。如果不清楚的同志可以看我前面的博客 Redis数据库存储键值对的底层原理-CSDN博客 下面,我们来看一下Redis键值对中值的数据结构有那些叭 Redis常见的5种数据类型 string …

MySQL自动化安装工具-mysqldeploy

功能 可在linux系统上安装 mysql5.5/5.6/5.7/8.0/8.4 版本的 MySQL&#xff0c;可以初始化多实例 MySQL。 码云: https://gitee.com/hh688/mysqldeploy guithub: https://github.com/hhkens/mysqldeploy 限制 仅在 centos7 环境进行测试&#xff0c;后期可能支持更多系统。 此程…

简要探讨大型语言模型(LLMs)的发展历史

关注大型语言模型(LLMs) 简要探讨语言模型的发展历史 理解Transformer架构的基本元素和注意力机制 了解不同类型的微调方法 语言模型的大小之分 在语言模型领域,“小”和“大”是相对概念。几年前还被视为“巨大”的模型,如今已被认为相当小。该领域发展迅猛,从参数规模为…

Java试题-选择题(2)

Java试题-选择题(2) 题目 下列语句创建对象的总个数是: String s=“a”+“b”+"c”+“d”+"e” A.4 B.2 C.3 D.1 关于下面的程序段的说法正确的是()? File file1=new File(“e:\xxx\yyy\zzz");file1.mkdir(); A.如目录e:\xxx\yyy\不存在,程序会抛出FileN…

揭秘动态测试:软件质量的实战防线

动态测试概述&#xff08;扩展版&#xff09; 目录 动态测试概述&#xff08;扩展版&#xff09; 一、动态测试的定义与重要性 ⚡ 二、动态测试类型 &#x1f50d; &#xff08;一&#xff09;功能测试 &#x1f9e9; &#xff08;二&#xff09;非功能测试 &#x1f4ca…

机器学习①【机器学习的定义以及核心思想、数据集:机器学习的“燃料”(组成和获取)】

文章目录先言一、什么是机器学习1.机器学习的定义以及核心思想2.机器学习的四大类型2.1监督学习&#xff08;Supervised Learning&#xff09;2.2半监督学习&#xff08;Midsupervised Learning&#xff09;2.3无监督学习&#xff08;Unsupervised Learning&#xff09;2.4强化…

GaussDB 数据库架构师(十二) 资源规划

1 硬件和软件要求 1&#xff09;硬件配置示例 硬件配置示例设备类型 设备型号 数量 备注 计算节点 CPU&#xff1a; 2*64 Cores&#xff0c;Kunpeng 920 内存&#xff1a;32*32GB 系统盘&#xff1a;2*960GB SATA SSD 数据盘&#xff1a;24*960GB SATA SSD RAID卡&#x…

Linux系统文件与目录内容检索(Day.2)

一、文件和目录内容检索处理命令1、uniq去重语法uniq [options] [input_file [output_file]]选项选项作用-c进行计数&#xff0c;并删除文件中重复出现的行-d仅显示连续的重复行-u仅显示出现一次的行-i忽略大小写案例1、删除输入文件中的重复行sort input.txt | uniq2、仅显示重…

如何选择一个容易被搜索引擎发现的域名?

在这个数字化时代&#xff0c;域名不仅是企业线上身份的标识&#xff0c;更是影响网站搜索曝光率的关键因素。一个精心挑选的域名能为品牌带来更多自然流量&#xff0c;下面我们就来探讨几个实用技巧。一、简洁易记是王道好域名首先要让人过目不忘。想象一下&#xff0c;当用户…

树形DP进阶:结合dfn序的线性化树问题求解技巧

树形DP进阶&#xff1a;结合dfn序的线性化树问题求解技巧一、dfn序与树的线性化1.1 dfn序的基本概念1.2 树形DP结合dfn序的优势二、核心应用&#xff1a;子树区间的DP优化2.1 子树权值和的快速查询与更新问题描述结合dfn序的解法代码实现&#xff08;前缀和版本&#xff09;优化…

九、Maven入门学习记录

Maven介绍Maven作用统一项目结构Maven安装&#xff08;注意配置阿里云私服时url要跟换成最新的&#xff09;IDEA创建Meavn项目Maven坐标介绍IDEA导入Maven项目依赖配置依赖传递依赖传递-排除依赖依赖范围生命周期生命周期-执行特定生命周期生命周期-总结