【Redis】Redis 協議與連接

一、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…