一、Redis 协议

1.1 RESP

RESP 是 Redis 客户端与服务器之间的通信协议,采用文本格式(基于 ASCII 字符),支持多种数据类型的序列化和反序列化

RESP 通过首字符区分数据类型,主要支持 5 种类型:

类型首字符格式示例说明
简单字符串++OK\r\n\r\n 结尾,用于返回状态信息(如 OK)
错误信息--ERR wrong type\r\n格式同简单字符串,但表示错误
整数::10086\r\n用于返回计数、自增结果等整数
批量字符串$$5\r\nhello\r\n用于存储二进制安全的字符串(长度 + 内容)
数组(列表)**2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n用于表示多个元素的集合(如命令参数)

示例解析

客户端发送命令 SET name redis 时,协议格式为:

`*3\r\n$3\r\nSET\r\n$4\r\nname\r\n$5\r\nredis\r\n`
  • *3 表示数组包含 3 个元素(命令 + 2 个参数)
  • $3\r\nSET 表示第一个元素是长度为 3 的字符串 “SET”
  • $4\r\nname表示第二个元素是长度为4的字符串name
  • $5\r\nredis表示第三个元素是长度为5的字符串redis
  • \r\n是最后的结束分隔符

1.2 Redis Pipeline

Redis Pipeline(管道)是 Redis 客户端提供的一种优化网络通信的机制,允许客户端一次性发送多个命令到服务器,再批量接收所有命令的响应,从而大幅减少网络往返次数,提升通信效率

在这里插入图片描述

  • 传统模式:客户端发送一个命令 → 等待服务器响应 → 再发送下一个命令(每命令 1 次网络往返)。
  • Pipeline 模式:客户端一次性发送多个命令 → 服务器批量执行 → 一次性返回所有结果(N 个命令仅 1 次网络往返)。

Pipeline 特点

  1. 非原子性:Pipeline 不保证事务性,命令按顺序执行,但中间若某命令失败,后续命令仍会继续执行(与 MULTI/EXEC 事务不同)。

  2. 顺序性:服务器按接收顺序执行 Pipeline 中的命令,响应结果也与命令顺序一一对应。

  3. 适用场景

    • 批量读写操作(如批量设置多个键值对)。
    • 非依赖型命令(命令之间无因果关系,不需要前一个命令的结果作为后一个的参数)。

1.3 Redis 事务

Redis 事务是一组命令的集合,通过 MULTIEXEC 等命令将多个操作封装为一个不可分割的工作单元,要么全部执行,要么全部不执行(特殊情况除外,见后文说明)。它主要用于保证一系列操作的原子性,避免中间被其他命令干扰

Redis 事务通过以下命令实现完整流程:

命令作用
MULTI开启事务,后续命令进入 “队列” 等待执行,而非立即执行
EXEC执行事务队列中的所有命令,返回各命令的结果(按入队顺序)
DISCARD取消事务,清空队列,放弃执行
WATCH监控一个或多个键,若事务执行前被监控的键发生变动,则事务被打断(乐观锁)

Redis 事务的特点

  1. 原子性限制

    • 若事务中命令存在语法错误(如命令不存在),EXEC 会直接放弃所有命令(全部不执行)。
    • 若命令语法正确但运行时错误(如对字符串执行 LPOP),错误命令会失败,其他命令仍会执行,不回滚 ,这与传统数据库事务的 “完全回滚” 不同,Redis 不支持部分失败后的回滚,需业务层处理。
  2. 顺序性:事务中的命令按入队顺序执行,不会被其他客户端的命令插入。

  3. 乐观锁机制:通过 WATCH 实现,适用于 “读 - 改 - 写” 场景,防止并发修改导致的数据不一致。

基础命令

MULTI + EXEC 执行事务
# 开启事务
127.0.0.1:6379> MULTI
OK# 命令入队(此时仅排队,不执行)
127.0.0.1:6379(TX)> SET name "redis"
QUEUED
127.0.0.1:6379(TX)> GET name
QUEUED
127.0.0.1:6379(TX)> INCR counter
QUEUED# 执行事务(返回所有命令结果,按入队顺序)
127.0.0.1:6379(TX)> EXEC
1) OK
2) "redis"
3) (integer) 1

在这里插入图片描述

DISCARD 取消事务
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET a 10
QUEUED
127.0.0.1:6379(TX)> SET b 20
QUEUED# 取消事务,队列清空
127.0.0.1:6379(TX)> DISCARD
OK# 验证命令未执行
127.0.0.1:6379> GET a
(nil)

在这里插入图片描述

WATCH 监控键

两个客户端同时更新同一个键,确保只有先获取到原始值的客户端能成功更新

客户端A:

# 监控键 balance
127.0.0.1:6379> WATCH balance
OK# 读取当前值
127.0.0.1:6379> GET balance
"100"# 开启事务,准备更新(此时客户端 B 还未操作)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET balance 200
QUEUED

在这里插入图片描述

客户端B:

# 修改被监控的键 balance
127.0.0.1:6379> SET balance 150
OK

在这里插入图片描述

客户端A继续执行:

# 由于 balance 被 B 修改,事务被打断(返回 nil)
127.0.0.1:6379(TX)> EXEC
(nil)# 验证结果(A 的修改未生效)
127.0.0.1:6379> GET balance
"150"

在这里插入图片描述

WATCH 会在 EXEC 前检查监控的键是否被修改,若被修改则事务失败(返回 nil),需业务层重试

应用场景

1. 实现 ZPOP(原子性移除有序集合首个元素)

Redis 没有内置 ZPOP 命令,可用事务实现 “获取首个元素并删除” 的原子操作:

# 监控有序集合 zset,防止被其他客户端修改
127.0.0.1:6379> WATCH zset
OK# 获取首个元素(分数最低的)
127.0.0.1:6379> ZRANGE zset 0 0
1) "member1"# 开启事务,删除该元素
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> ZREM zset "member1"
QUEUED# 执行事务(若 zset 未被修改,返回 1 表示删除成功)
127.0.0.1:6379(TX)> EXEC
1) (integer) 1
2. 实现值的原子加倍操作

对一个键的值进行加倍,确保操作过程中不被其他客户端干扰:

# 监控键 score:10001
127.0.0.1:6379> WATCH score:10001
OK# 读取当前值
127.0.0.1:6379> GET score:10001
"5"# 开启事务,设置新值(5*2=10)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> SET score:10001 10
QUEUED# 执行事务(成功返回 OK)
127.0.0.1:6379(TX)> EXEC
1) OK

Lua脚本

Redis 中,Lua 脚本的执行也是原子性的(执行期间不会被其他命令打断),且功能更强大,两者的区别如下:

特性事务(MULTI/EXECLua 脚本(EVAL/EVALSHA
原子性保证命令按顺序执行,部分错误不回滚脚本内所有操作作为整体原子执行,支持复杂逻辑
灵活性仅支持简单命令队列,不支持条件判断支持分支、循环等逻辑,可实现复杂原子操作
网络开销需多次交互(MULTI→入队→EXEC一次请求即可,减少网络往返
适用场景简单批量操作,依赖乐观锁(WATCH)的场景复杂原子操作(如带条件的更新、多键联动)
示例:用 Lua 脚本实现原子加倍操作
EVAL "local val = tonumber(redis.call('GET', KEYS[1])); redis.call('SET', KEYS[1], val*2); return val*2" 1 score:10001

事务与Pipeline 对比

特性PipelineMULTI/EXEC 事务
网络优化减少网络往返(核心目的)无(仍需多次往返)
原子性无(命令逐个执行)有(所有命令要么全执行,要么全不执行)
命令依赖不支持(命令无因果关系)支持(可基于前序命令结果)
适用场景批量非依赖型命令需保证原子性的操作

1.4 Redis ACID

ACID 是数据库事务的四大特性(原子性、一致性、隔离性、持久性),Redis 作为内存数据库,对这些特性的支持与传统关系型数据库有显著差异

1. 原子性(Atomicity)

定义:事务中的操作要么全部成功,要么全部失败,不允许部分执行。

Redis 的支持情况

  • 不完整支持:Redis 事务通过 MULTI/EXEC 将命令入队,EXEC 时批量执行,但不支持回滚。
    • 若事务中存在语法错误(如命令不存在),EXEC 会直接放弃所有命令(全部不执行)。
    • 若命令语法正确但运行时错误(如对字符串执行 LPOP),错误命令会失败,其他命令仍会继续执行(不会回滚)

示例

key1 被成功设置为 “hello”,错误命令不影响其他命令执行,违反原子性。

# 开启事务
127.0.0.1:6379> MULTI
OK# 正确命令:设置 key1
127.0.0.1:6379(TX)> SET key1 "hello"
QUEUED# 运行时错误:对字符串执行 LPOP(列表操作)
127.0.0.1:6379(TX)> LPOP key1
QUEUED# 执行事务
127.0.0.1:6379(TX)> EXEC
1) OK  # 第一个命令成功
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value  # 第二个命令失败

在这里插入图片描述

2. 一致性(Consistency)

定义:事务执行前后,数据需满足预设的约束(如业务规则),保持逻辑一致。

Redis 的支持情况

  • 有限支持:仅保证数据结构层面的一致性(如字符串不会被改造成列表),但不保证业务逻辑一致性。
  • 若事务中部分命令失败,可能导致业务数据不一致(如转账时 “扣钱失败但加钱成功”)。

示例:模拟转账场景(A 向 B 转 100 元)

# 初始状态
127.0.0.1:6379> SET A 500
OK
127.0.0.1:6379> SET B 300
OK# 开启事务(假设 A 扣钱命令出错)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY A 100  # 正确命令:A 扣 100
QUEUED
127.0.0.1:6379(TX)> INCRBY B 100  # 正确命令:B 加 100
QUEUED
127.0.0.1:6379(TX)> INCRBY A abc  # 运行时错误:参数不是数字
QUEUED# 执行事务
127.0.0.1:6379(TX)> EXEC
1) (integer) 400  # A 扣钱成功
2) (integer) 400  # B 加钱成功
3) (error) ERR value is not an integer or out of range  # 第三个命令失败# 最终状态:A=400,B=400(总金额 800,初始总金额 800,数据结构一致)
# 但业务上:A 扣了 100,B 加了 100,看似正确?若第一个命令是错误(如 DECRBY A abc):
# 则 A 不变,B 加 100,总金额增加 100,业务逻辑不一致。

在这里插入图片描述

3. 隔离性(Isolation)

定义:多个事务并发执行时,彼此的操作互不干扰,结果等同于串行执行。

Redis 的支持情况

  • 完全支持:Redis 是单线程模型,所有命令(包括事务)按顺序执行,不存在并发冲突,天然满足隔离性。

示例:两个客户端并发执行事务

  • 客户端 1 执行事务:SET x 10; INCR x

  • 客户端 2 执行事务:SET x 20; INCR x

  • 结果:无论执行顺序如何,最终 x 要么是 11(客户端 1 先执行),要么是 21(客户端 2 先执行),不会出现中间状态。

4. 持久性(Durability)

定义:事务一旦提交,结果需永久保存(即使服务器崩溃)。

Redis 的支持情况

  • 条件支持:依赖持久化配置,默认不保证持久性。
    • 若使用 AOF 持久化 且配置 appendfsync=always,事务执行后会立即写入磁盘,保证持久性(但性能极差)。
    • 若使用 RDB 或默认 AOF 配置(everysecno),事务结果可能因崩溃丢失。

实际场景:生产环境极少使用 appendfsync=always,因此 Redis 事务通常不满足持久性。

总结:Redis 事务与 ACID

特性支持情况
原子性不支持(无回滚,部分命令失败不影响其他命令)
一致性仅保证数据结构一致,不保证业务逻辑一致
隔离性完全支持(单线程执行)
持久性仅在特定 AOF 配置下支持,实际场景中几乎不满足

补充:Lua 脚本的 ACID 表现

  • Lua 脚本执行是原子性的(全程无中断),且满足隔离性(单线程),但一致性和持久性仍与上述相同。

1.5 Redis 发布订阅

Redis 发布订阅是一种消息通信模式,支持 “一对多” 消息分发(多播),适用于简单的消息通知场景。其核心是 “频道(Channel)”:发布者向频道发送消息,订阅者从频道接收消息。

基础命令

命令作用示例
SUBSCRIBE订阅一个或多个频道SUBSCRIBE news sport
PSUBSCRIBE订阅符合模式的频道(支持 * 通配符)PSUBSCRIBE news.*(匹配 news.tech 等)
PUBLISH向频道发布消息PUBLISH news "Redis 发布订阅示例"
UNSUBSCRIBE取消订阅频道UNSUBSCRIBE news
PUNSUBSCRIBE取消订阅模式频道PUNSUBSCRIBE news.*

应用场景

发布订阅

场景:客户端 A 订阅 news 频道,客户端 B 向 news 发布消息。

客户端 A(订阅者)

# 订阅 news 频道
127.0.0.1:6379> SUBSCRIBE news
Reading messages... (press Ctrl-C to quit)
1) "subscribe"  # 订阅成功的反馈
2) "news"
3) (integer) 1# 收到客户端 B 发布的消息
1) "message"    # 消息类型
2) "news"       # 频道
3) "Redis 发布订阅示例"  # 消息内容

在这里插入图片描述

客户端 B(发布者)

# 向 news 频道发布消息
127.0.0.1:6379> PUBLISH news "Redis 发布订阅示例"
(integer) 1  # 表示有 1 个订阅者接收成功s

在这里插入图片描述

模式订阅

场景:客户端 C 订阅 news.* 模式(匹配 news.technews.sport 等频道)。

客户端 A(订阅者)

127.0.0.1:6379> PSUBSCRIBE news.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "news.*"
3) (integer) 1# 收到向 news.tech 发布的消息
1) "pmessage"   # 模式消息类型
2) "news.*"     # 订阅的模式
3) "news.tech"  # 实际频道
4) "AI 技术新突破"  # 消息内容

在这里插入图片描述

客户端 B(发布者)

127.0.0.1:6379> PUBLISH news.tech "AI 技术新突破"
(integer) 1  # 客户端 A 收到

在这里插入图片描述

总结

缺点与局限性
  • 无消息持久化:Redis 不会存储发布的消息,若订阅者离线,期间的消息会永久丢失。
  • 无确认机制:发布者无法知道消息是否被订阅者接收。
  • 服务器重启丢失:Redis 重启后,所有订阅关系和未传递的消息会被清空。
  • 单独连接:订阅操作会阻塞连接(等待消息推送),需与普通命令连接分离(单独开连接处理订阅)。
适用场景

适用于实时通知、日志广播等对消息可靠性要求不高的场景(如聊天室、实时监控告警)。若需保证消息可达性,建议使用 Redis Stream 或专业消息队列(如 Kafka、RabbitMQ)。

1.6 Redis IO多线程

Redis 在 6.0 版本中引入了 IO 多线程 特性,主要用于优化网络 IO 操作的性能,解决传统单线程模型在高并发场景下的网络瓶颈。但需要注意的是,Redis 的核心命令执行仍然是单线程的,IO 多线程仅负责 网络数据的读写(接收客户端请求和发送响应结果)。

可以修改配置文件开启IO多线程

# 开启 IO 多线程(默认 no)
io-threads-do-reads yes# 设置 IO 线程数量(建议为 CPU 核心数的 1/2 或 1/4,避免线程切换开销)
# 注意:总线程数 = 配置数 + 1(主线程),如配置 4 则共 5 个线程
io-threads 4

为什么要使用IO多线程

Redis IO 多线程的核心设计约束是:仅将 “网络数据读写” 和 “协议解析” 拆分到多线程,而命令的执行、内存操作等核心逻辑仍由主线程单线程处理。这一原则确保了:

  • 避免多线程竞争数据(无需复杂锁机制),保留 Redis 单线程的简单性和安全性;
  • 仅优化最耗时的网络 IO 环节(在高并发场景下,网络读写可能占总耗时的 60% 以上)。

IO多线程流程概述

Redis 的 IO 多线程采用 “主线程 + 多 IO 线程” 的混合模型,核心流程如下:

  1. 接收请求阶段

    • 主线程监听客户端连接,当有新请求到达时,将连接分配给 IO 线程。
    • 多个 IO 线程并行读取客户端发送的命令数据(解析成 Redis 协议格式),并暂存到队列中。
  2. 命令执行阶段

    • 主线程从队列中取出所有解析好的命令,按顺序执行(保持单线程特性,保证命令的原子性和隔离性)。
  3. 发送响应阶段

    • 主线程将命令执行结果分发给 IO 线程。
    • 多个 IO 线程并行将结果发送回客户端。

在这里插入图片描述

IO多线程的实现

Redis 通过以下关键结构实现 IO 多线程的管理和协作:

1. IO 线程结构体(io_thread_data

每个 IO 线程对应一个结构体,存储线程状态、任务队列等信息

typedef struct {pthread_t thread;          // 线程 IDint fd;                    // 用于线程间通知的管道(pipe)写端redisAtomic size_t pending; // 待处理的任务数(原子变量,避免锁)list *clients;             // 分配给该线程的客户端连接列表redisAtomic int state;     // 线程状态:IO_THREAD_STATE_IDLE(空闲)/ RUNNING
} io_thread_data;
  • fd:主线程通过管道向 IO 线程发送 “有任务待处理” 的通知;
  • clients:该线程负责处理的客户端连接队列;
  • state:标记线程是否在工作,用于主线程判断是否可以分配新任务。
2. 全局 IO 线程管理器

Redis 用全局变量管理所有 IO 线程:

// 全局 IO 线程数组
static io_thread_data *io_threads;
// IO 线程数量(配置文件中的 io-threads 值)
static int io_threads_num;
// 是否开启 IO 多线程读(配置 io-threads-do-reads yes)
static int io_threads_do_reads = 0;

IO多线程详细过程

IO 多线程的工作流程可分为初始化、接收请求、命令执行、发送响应四个阶段,主线程与 IO 线程通过 “任务分配 - 通知 - 处理 - 同步” 的方式协作。

1. 初始化阶段(服务器启动时)
  • 步骤 1:读取配置文件的 io-threadsio-threads-do-reads 参数,确定是否开启 IO 多线程及线程数量(io_threads_num)。

  • 步骤 2:创建 io_threads_num 个 IO 线程,初始化每个线程的管道(用于主线程通知)和状态(IO_THREAD_STATE_IDLE)。

  • 步骤 3:为每个 IO 线程启动工作函数(IOThreadMain),线程进入循环等待状态(通过管道监听主线程的任务通知)。

IO 线程的主循环逻辑(IOThreadMain):

void *IOThreadMain(void *myid) {int id = *(int*)myid;while(1) {// 等待主线程通过管道发送通知(阻塞)if (aeWait(io_threads[id].fd, AE_READABLE, -1) <= 0)continue;// 读取管道数据(仅用于唤醒,数据无实际意义)char buf[1];read(io_threads[id].fd, buf, 1);// 处理分配给自己的客户端任务(读/写数据)if (io_threads_do_reads) {processPendingReads(id);  // 处理读任务(解析请求)} else {processPendingWrites(id); // 处理写任务(发送响应)}// 标记线程为空闲状态io_threads[id].state = IO_THREAD_STATE_IDLE;}
}
2. 接收请求阶段(客户端发送命令)

当客户端发送命令时,主线程与 IO 线程协作完成 “读取数据 + 解析协议”:

  • 步骤 1:主线程通过事件循环(aeMain)检测到客户端套接字可读,收集所有待读取的客户端连接。
  • 步骤 2:主线程将客户端连接平均分配给各个 IO 线程(避免某一线程负载过高),并将连接添加到对应线程的 clients 列表。
  • 步骤 3:主线程通过管道向每个 IO 线程发送一个字节的通知(唤醒线程),并标记线程状态为 “运行中”。
  • 步骤 4:IO 线程被唤醒后,执行 processPendingReads 函数:
    • 循环读取 clients 列表中每个客户端的网络数据;
    • 解析数据为 Redis 协议格式(如将 *3\r\n$3\r\nSET... 解析为命令和参数);
    • 解析完成后,将客户端标记为 “待执行” 状态,等待主线程处理。
  • 步骤 5:主线程等待所有 IO 线程完成读任务(通过轮询线程状态,直到所有线程回到 IDLE),然后进入命令执行阶段。
3. 命令执行阶段(主线程单线程处理)

IO 线程完成请求解析后,主线程接管后续流程:

  • 主线程遍历所有 “待执行” 的客户端,按顺序执行解析后的命令(如 GETSET 等);
  • 命令执行过程中,主线程独占数据访问权(无多线程竞争),保证原子性和隔离性;
  • 执行结果暂存在客户端的响应缓冲区中,等待发送。
4. 发送响应阶段(IO 线程并行发送)

命令执行完成后,主线程与 IO 线程协作将结果返回给客户端:

  • 步骤 1:主线程收集所有待发送响应的客户端连接,再次平均分配给各个 IO 线程。
  • 步骤 2:主线程通过管道通知 IO 线程处理写任务,标记线程状态为 “运行中”。
  • 步骤 3:IO 线程被唤醒后,执行 processPendingWrites 函数:
    • 循环将客户端响应缓冲区中的数据写入套接字(发送给客户端);
    • 若数据发送完毕,清理客户端状态;若未发送完毕(如数据量大),则下次继续发送。
  • 步骤 4:主线程等待所有 IO 线程完成写任务,然后进入下一轮事件循环。处理)

更多资料:https://github.com/0voice

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

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

相关文章

Android通知(Notification)全面解析:从基础到高级应用

一、Android通知概述通知(Notification)是Android系统中用于在应用之外向用户传递信息的重要机制。当应用需要告知用户某些事件或信息时&#xff0c;可以通过通知在状态栏显示图标&#xff0c;用户下拉通知栏即可查看详细信息。这种机制几乎被所有现代应用采用&#xff0c;用于…

VUE3(四)、组件通信

1、props作用&#xff1a;子组件之间的通信。父传子&#xff1a;属性值的非函数。子传父&#xff1a;属性值是函数。父组件&#xff1a;<template><div>{{ childeData }}</div>——————————————————————————————<child :pare…

【数据结构与算法】数据结构初阶:详解二叉树(六)——二叉树应用:二叉树选择题

&#x1f525;个人主页&#xff1a;艾莉丝努力练剑 ❄专栏传送门&#xff1a;《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题 &#x1f349;学习方向&#xff1a;C/C方向 ⭐️人生格言&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为…

Android广播实验

【实验目的】了解使用Intent进行组件通信的原理&#xff1b;了解Intent过滤器的原理和匹配机制&#xff1b;掌握发送和接收广播的方法【实验内容】任务1、普通广播&#xff1b;任务2、系统广播&#xff1b;任务3、有序广播&#xff1b;【实验要求】1、练习使用静态方法和动态方…

html转word下载

一、插件使用//转html为wordnpm i html-docx-js //保存文件到本地npm i file-saver 注&#xff1a;vite 项目使用esm模式会报错&#xff0c;with方法错误&#xff0c;修改如下&#xff1a;//直接安装修复版本npm i html-docx-fixed二、封装导出 exportWord.jsimport htmlDocx f…

北方公司面试记录

避免被开盒&#xff0c;先称之为“北方公司”&#xff0c;有确定结果后再更名。 先说流程&#xff0c;线下面试&#xff0c;时间非常急&#xff0c;下午两点钟面试&#xff0c;中午十二点打电话让我去&#xff0c;带两份纸质简历。 和一般的菌工单位一样&#xff0c;先在传达室…

linux——ps命令

PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND0 1 1 1 ? -1 Ss 0 0:01 /usr/lib/systemd/systemd1 123 123 123 ? -1 S 0 0:00 /usr/sbin/sshd -D123 456 456 456 pts/0 456 R 10…

C#.NET 依赖注入详解

一、是什么 在 C#.NET 中&#xff0c;依赖注入&#xff08;Dependency Injection&#xff0c;简称 DI&#xff09; 是一种设计模式&#xff0c;用于实现控制反转&#xff08;Inversion of Control&#xff0c;IoC&#xff09;&#xff0c;以降低代码耦合、提高可测试性和可维护…

Vue监视数据的原理和set()的使用

在 Vue 中&#xff0c;Vue.set()&#xff08;或 this.$set()&#xff09;是用于解决响应式数据更新检测的重要方法&#xff0c;其底层与 Vue 的数据监视原理紧密相关。以下从使用场景和实现原理两方面详细说明&#xff1a;一、Vue.set () 的使用场景与用法1. 为什么需要 Vue.se…

在 Vue 中,如何在回调函数中正确使用 this?

在 Vue 组件中&#xff0c;this 指向当前组件实例&#xff0c;但在回调函数&#xff08;如定时器、异步请求、事件监听等&#xff09;中&#xff0c;this 的指向可能会丢失或改变&#xff0c;导致无法正确访问组件的属性和方法。以下是在回调函数中正确使用 this 的几种常见方式…

第4章唯一ID生成器——4.4 基于数据库的自增主键的趋势递增的唯一ID

基于数据库的自增主键也可以生成趋势递增的唯一 ID&#xff0c;且由于唯一ID不与时间戳关联&#xff0c;所以不会受到时钟回拨问题的影响。 4.4.1 分库分表架构 数据库一般都支持设置自增主键的初始值和自增步长&#xff0c;以MySQL为例&#xff0c;自增主键的自增步长由auto_i…

设计模式:Memento 模式详解

Memento 模式详解Memento&#xff08;备忘录&#xff09;模式是一种行为型设计模式&#xff0c;用于在不破坏封装性的前提下&#xff0c;捕获并外部化一个对象的内部状态&#xff0c;以便在之后能够将该对象恢复到原先保存的状态。它广泛应用于需要实现撤销&#xff08;Undo&am…

数据结构(6)单链表算法题(下)

一、环形链表Ⅰ 1、题目描述 https://leetcode.cn/problems/linked-list-cycle 2、算法分析 思路&#xff1a;快慢指针 根据上图所示的流程&#xff0c;我们可以推测出这样一个结论&#xff1a;若链表带环&#xff0c;快慢指针一定会相遇。 那么&#xff0c;这个猜测是否正…

智能制造,从工厂建模,工艺建模,柔性制造,精益制造,生产管控,库存,质量等多方面讲述智能制造的落地方案。

智能制造&#xff0c;从工厂建模&#xff0c;工艺建模&#xff0c;柔性制造&#xff0c;精益制造&#xff0c;生产管控&#xff0c;库存&#xff0c;质量等多方面讲述智能制造的落地方案。

Qt 分裂布局:QSplitter 使用指南

在 GUI 开发中&#xff0c;高效管理窗口空间是提升用户体验的关键。QSplitter 作为 Qt 的核心布局组件&#xff0c;让动态分割窗口变得简单直观。一、QSplitter 核心功能解析 QSplitter 是 Qt 提供的布局管理器&#xff0c;专用于创建可调节的分割区域&#xff1a; 支持水平/垂…

R语言与作物模型(DSSAT模型)技术应用

R语言在DSSAT模型的气候、土壤、管理措施等数据准备&#xff0c;自动化模拟和结果分析上都发挥着重要的作用。一&#xff1a;DSSAT模型的高级应用 1.作物模型的概念 2.DSSAT模型发展现状 3.DSSAT与R语言的安装 4.DSSAT模型的高级应用案例 5.R语言在作物模型参数优化中的应用 6.…

JavaSE:学习输入输出编写简单的程序

一、打印输出到屏幕 Java提供了三种核心输出方法&#xff0c;适合不同场景&#xff1a; System.out.println() 打印内容后 自动换行 System.out.println("Welcome"); System.out.println("to ISS"); // 输出&#xff1a; // Welcome // to ISSSystem.out…

访问者模式感悟

访问者模式 首先有两个东西: 一个是访问者vistor (每一个访问者类都代表了一类操作) 一个是被访问者entity (model /info/pojo/node等等这些都行)也就是是说是一个实体类 其操作方法被抽离给了其他类。 访问者模式的核心思想就是**“把操作从数据结构中分离出来,每种操作…

从零到部署:基于Go和Docker的全栈短链接服务实战(含源码)

摘要&#xff1a;本文将手把手带你使用Go语言&#xff0c;并遵循依赖倒置、分层架构等最佳实践&#xff0c;构建一个高性能、高可用的全栈短链接生成器。项目采用Echo框架、GORM、Redis、MySQL&#xff0c;并通过Docker和Docker Compose实现一键式容器化部署到阿里云服务器。文…

MyBatis_3

上一篇文章&#xff0c;我们学习了使用XML实现MyBatis进行增、删、查、改等操作&#xff0c;本篇文章&#xff0c;我们将学习#{ }和${ }获取方法参数的区别和使用MyBatisXML实现动态SQL语句。 #{ }和${ }的区别 在之前的文章中我们都是使用#{ }进行赋值&#xff0c;但实际上M…