【Redis】string字符串

目錄

一.常見命令?

1.1.SET

1.2.GET

1.3.MGET ?

1.4.MSET ?

1.5.SETNX

二.計數命令

2.1.INCR ?

2.2.INCRBY

2.3.DECR

2.4.DECYBY

2.5.INCRBYFLOAT

三 . 其他命令

3.1.APPEND

3.2.GETRANGE

3.3.SETRANGE

3.4.STRLEN ?

四. 字符串類型內部編碼

五. 典型使用場景

5.1.緩存(Cache)功能

5.2.計數(Counter)功能

5.3.共享會話(Session)

5.4.?機驗證碼


字符串(String)是?Redis 最基礎、最核心的數據類型,理解其特性至關重要:

  1. 基礎構建塊:

    • 鍵的類型:?Redis 中所有的鍵 (Key) 本質上都是字符串類型

    • 值的基石:?其他復雜數據結構(列表 List、集合 Set、有序集合 Sorted Set、哈希 Hash)的元素值 (Value)?也必須是字符串類型。這使得字符串成為學習和理解 Redis 整個數據模型的基礎。

  2. 靈活的值類型 (二進制安全):

    • Redis 字符串類型存儲的值 (Value)?具有極高的靈活性,可以是:

      • 文本數據:?普通字符串、JSON、XML、CSV 等格式的文本。

      • 數值數據:?整數或浮點數(Redis 提供專門的命令如?INCR,?DECR,?INCRBYFLOAT?來操作數值)。

      • 二進制數據:?如圖片、音頻、視頻文件或任何序列化的字節流。

    • 關鍵特性:二進制安全 (Binary Safe)

      • Redis?內部存儲和操作字符串值完全基于原始字節序列(二進制流)

      • 這意味著?Redis 本身不感知、也不處理任何字符集編碼(如 UTF-8, GBK)問題

      • 客戶端存入什么編碼的字節流,Redis 就原樣存儲;客戶端讀取時,也將得到完全相同的字節流。字符集的解析完全由客戶端負責。

    • 容量限制:?單個字符串值的最大容量為?512MB

核心價值與應用場景:

  • 緩存 (Cache):?存儲 HTML 片段、用戶會話信息、序列化對象等。

  • 計數器 (Counter):?利用?INCR/DECR?命令實現點擊計數、庫存計數等。

  • 分布式鎖 (Distributed Lock):?配合?SET?命令的?NX/PX?等選項實現簡單鎖機制。

  • 配置存儲:?存儲應用配置項。

  • 二進制數據存儲:?存儲小型圖片、驗證碼、序列化數據等(需注意 512MB 限制)。

總結:?Redis 字符串類型不僅是鍵的載體,更是所有復雜數據結構元素的基石。其?二進制安全?的特性賦予了它存儲任意數據的強大能力,而?512MB 的上限?和?豐富的操作命令?使其成為 Redis 最常用、最靈活的數據類型之一。

一.常見命令?

1.1.SET

功能:?將字符串類型的值設置到指定的鍵中。

  • 覆蓋規則:?如果鍵已存在,無論其原先存儲的數據類型是什么(字符串、列表、哈希等),都會被覆蓋為新的字符串值。

  • TTL 處理:?原先與該鍵關聯的任何生存時間(TTL)都會被清除失效

語法:

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

版本要求:?1.0.0 起可用

時間復雜度:?O(1)

選項:

SET?命令提供多種選項來控制其行為:

  • EX seconds: 設置鍵的過期時間,單位是

  • PX milliseconds: 設置鍵的過期時間,單位是毫秒

  • NX:?僅當鍵不存在 (Not eXists)?時才執行設置操作。如果鍵已存在,則設置操作不會執行

  • XX:?僅當鍵存在 (eXists)?時才執行設置操作。如果鍵不存在,則設置操作不會執行

功能整合:?上面這些新選項使得單個?SET?命令能夠完全覆蓋?SETNXSETEXPSETEX?的功能:

  • SET key value NX?等價于?SETNX key value

  • SET key value EX seconds?等價于?SETEX key seconds value

  • SET key value PX milliseconds?等價于?PSETEX key milliseconds value

重要說明:

  1. 互斥性:?NX?和?XX?選項是互斥的,同一命令中只能指定其中一個。

  2. 命令演變:?由于?SET?命令結合選項 (NX,?XX,?EX,?PX) 可以完全替代?SETNXSETEXPSETEX?等獨立命令的功能,在 Redis 的后續版本中,這些獨立命令的實現已被整合到?SET?命令中。雖然這些獨立命令目前通常仍保留可用(以兼容舊腳本或習慣),但推薦優先使用帶選項的?SET?命令,因為它功能更統一、強大。

返回值:

  • 如果設置操作成功執行,返回字符串?"OK"

  • 如果因為指定了?NX?或?XX?選項但條件不滿足(即?NX?時鍵已存在,或?XX?時鍵不存在),導致設置操作未執行,則返回?(nil)

話不多說,我們看看例子

NX選項

可以看到啊!!上面這個SET mykey "World" NX并沒有生效啊!!!這個是因為

NX:?僅當鍵不存在 (Not eXists)?時才執行設置操作。如果鍵已存在,則設置操作不會執行

?我們看看它生效的樣子

XX選項

?

我們看到:上面這個SET mykey "World" XX并沒有生效啊!!!這個是因為

XX:?僅當鍵存在 (eXists)?時才執行設置操作。如果鍵不存在,則設置操作不會執行

我們來看看它執行的樣子

EX選項

至于這個PX選項,由于時間太短了,我們就不演示了。

1.2.GET

獲取 key 對應的 value。

如果 key 不存在,返回 nil。

如果 value 的數據類型不是 string,會報錯。

語法:

GET key
  • 命令有效版本:1.0.0 之后
  • 時間復雜度:O(1)
  • 返回值:key 對應的 value,或者 nil 當 key 不存在。

1.3.MGET ?

一次性獲取多個 key 的值。

如果對應的 key 不存在或者對應的數據類型不是 string,返回 nil。

語法:

MGET key [key ...]
  • 命令有效版本:1.0.0 之后 ?
  • 時間復雜度:O(N) ,N 是我們這個命令里要獲取的?key 數量? ,不是redis里面所有key的數量
  • 返回值:對應 value 的列表

我們來對比一下這個get命令和mget命令啊!!!

很明顯啊,聰明人都會選項mget來獲取value。

我們看看怎么使用

1.4.MSET ?

一次性設置多個 key 的值。 ?

覆蓋規則:

  • 如果指定的?key?已存在,無論其原先存儲的數據類型是什么(字符串、列表、哈希、集合、有序集合等),MSET?都會將其覆蓋為新的字符串值

  • 這是由?MSET?的設計目標決定的:它是一個原子性的批量設置字符串值的命令。

TTL 處理:

  • 對于?MSET?命令中指定的、已經存在的?key,原先與該鍵關聯的任何生存時間(TTL)都會被清除失效。執行?MSET?后,這些鍵將變成永久的(沒有過期時間),除非后續顯式地為它們設置新的 TTL(例如使用?EXPIRE?或?SET?的?EX/PX?選項)。

  • 對于?MSET?命令中指定的、之前不存在的?key,設置后會成為永久鍵(沒有 TTL),除非后續顯式設置。

語法: ?

MSET key value [key value ...] ?
  • 命令有效版本:1.0.1 之后 ?
  • 時間復雜度:O(N)
  • N 是 key 數量 ?
  • 返回值:永遠是 OK ?

多說無益,我們直接上手看例子

這就很好的說明了:如果指定的?key?已存在,無論其原先存儲的數據類型是什么(字符串、列表、哈希、集合、有序集合等),MSET?都會將其覆蓋為新的字符串值

接下來我們接著看

對于?MSET?命令中指定的、之前不存在的?key,設置后會成為永久鍵(沒有 TTL),除非后續顯式設置。

1.5.SETNX

設置key-value但只允許在key之前不存在的情況下。

語法:

SETNX key value

命令有效版本:1.0.0之后

時間復雜度:O(1)

返回值:1表?設置成功。0表?沒有設置。

話不多說,我們直接看例子

這個就是沒有設置成功的情況,我們接下來看看設置成功的情況

SET、SETNX、SETXX執?流程

二.計數命令

2.1.INCR ?

功能:

將鍵?key?中存儲的整數值增加 1。

關鍵行為:

  • 如果鍵?key?不存在,則在執行遞增操作前,先將該鍵的值設置為 0

  • 如果鍵?key?存儲的值不是整數類型(無法解析為整數),則命令返回錯誤。

  • 如果鍵?key?存儲的值是整數,但遞增后超出 64 位有符號整數的表示范圍(即 C/C++ 中的?long long?類型,范圍從?-9223372036854775808?到?9223372036854775807),則命令返回錯誤。

原子性:
此命令是原子操作。在分布式環境下,多個客戶端同時對同一個鍵執行?INCR?時,Redis 的單線程執行模型確保該命令不會出現競態條件,每個操作都會順序執行,因此非常適合實現高并發場景下的計數器。

與浮點數的區別:
INCR?命令僅適用于整數值。若需操作浮點數,請使用?INCRBYFLOAT?命令。

語法:

INCR key
  • 命令有效版本:1.0.0 之后 ?
  • 時間復雜度:O(1) ?
  • 返回值:integer 類型的加完后的數值。

錯誤情況:

  • 當?key?存儲的不是字符串類型時,返回類型錯誤。

  • 當?key?存儲的字符串不能表示為整數,或操作后導致整數溢出時,返回錯誤。

話不多說,我們直接看例子

將鍵?key?中存儲的整數值增加 1。

如果鍵?key?不存在,則在執行遞增操作前,先將該鍵的值設置為 0

當?key?存儲的字符串不能表示為整數,返回錯誤

當?key?存儲的字符串操作后導致整數溢出時,返回錯誤

2.2.INCRBY

將 key 對應的 string 表示的數字加上對應的值。

如果 key 不存在,則視為 key 對應的 value 是 0。

如果 key 對應的 string 不是一個整型或者范圍超過了 64 位有符號整型,則報錯。

語法:

INCRBY key decrement
  • 命令有效版本:1.0.0 之后
  • 時間復雜度:O(1)
  • 返回值:integer 類型的加完后的數值。

錯誤情況:

  1. decrement不是整數時

  2. 當?key?存儲的不是字符串類型時

  3. 當?key?存儲的字符串不能表示為整數時

  4. 當操作結果超出 64 位有符號整數范圍時

話不多說,我們直接看例子

2.3.DECR

將 key 對應的 string 表示的數字減一。

如果 key 不存在,則視為 key 對應的 value 是 0。

如果 key 對應的 string 不是一個整型或者范圍超過了 64 位有符號整型,則報錯。

語法:

DECR key
  • 命令有效版本:1.0.0 之后
  • 時間復雜度:O(1)
  • 返回值:integer 類型的減完后的數值。

話不多說,我們直接看例子

2.4.DECYBY

將 key 對應的 string 表示的數字減去對應的值。

如果 key 不存在,則視為 key 對應的 value 是 0。

如果 key 對應的 string 不是一個整型或者范圍超過了 64 位有符號整型,則報錯。

語法:

DECBBY key decrement
  • 命令有效版本:1.0.0 之后
  • 時間復雜度:O(1)
  • 返回值:integer 類型的減完后的數值。

2.5.INCRBYFLOAT

功能:

對鍵?key?存儲的浮點數值執行加減操作。

核心行為:

  1. 數值操作

    • 將鍵對應的字符串值解析為浮點數

    • 加上指定的增量值

    • 若增量為負數,則執行減法操作

  2. 鍵不存在時

    • 自動視為該鍵的值為?0.0

  3. 科學計數法

    • 支持使用科學計數法表示浮點數(如?1.23e4

  4. 錯誤條件

    • 鍵存在但非字符串類型?→ 報錯

    • 鍵值無法解析為浮點數?→ 報錯

語法:

INCRBYFLOAT key increment
  1. 命令有效版本:2.6.0 之后
  2. 時間復雜度:O(1)
  3. 返回值:加/減完后的數值。

話不多說,我們直接看例子

很多存儲系統和編程語言內部使用 CAS 機制實現計數功能,會有一定的 CPU 開銷,但在 Redis 中完全不存在這個問題,因為 Redis 是單線程架構,任何命令到了 Redis 服務端都要順序執行。

Redis里面整數和小數的存儲方式

Redis 在存儲數值類型時,對整數和小數采取了不同的底層處理方式。

  • 整數:?直接使用高效的整型數據結構(在 Redis 內部通常表示為?long?類型,對應 C 語言的?long long?或 Java 的?long,即 64 位有符號整數)。這使得整數的算術運算(如?INCR,?DECR,?INCRBY)非常高效,因為它們直接在內存中的二進制數值上進行操作。

  • 小數:?則是以序列化后的字符串形式存儲的(例如,"3.14")。這意味著每次需要對小數進行任何算術運算時(無論是 Redis 內置命令如?INCRBYFLOAT,還是客戶端應用邏輯),都必須:

    1. 將存儲的字符串解析為編程語言中的浮點數類型(如?double)。

    2. 在浮點數上執行運算。

    3. 將運算結果重新序列化為字符串。

    4. 最后再將字符串寫回?Redis。

這種“字符串-數值-字符串”的轉換過程,相比于整數的直接二進制運算,會帶來顯著的額外開銷(CPU 用于解析/序列化,內存用于存儲字符串)。選擇這種設計主要是因為:

  • 保持精度和可移植性:?字符串表示可以精確地記錄用戶輸入的小數值(尤其是涉及特定精度時),避免了二進制浮點數固有的精度損失問題(如 0.1 無法精確表示)。它也確保了不同客戶端或系統之間傳輸時值的一致性和可讀性。

  • 簡化實現:?統一用字符串存儲簡化了 Redis 內部對值類型的處理邏輯。

因此,雖然 Redis 提供了?INCRBYFLOAT?等命令支持小數運算,但其底層實現機制決定了其性能開銷遠大于整數的等效操作。在設計需要頻繁進行數值計算的數據結構時,應充分考慮這種差異。

三 . 其他命令

3.1.APPEND

如果 key 已經存在并且是一個 string,命令會將 value 追加到原有 string 的后邊。

如果 key 不存在,則效果等同于 SET 命令。

語法:

APPEND KEY VALUE
  • 命令有效版本:2.0.0 之后
  • 時間復雜度:O(1). 追加的字符串一般長度較短, 可以視為 O(1).
  • 返回值:追加完成之后 string 的長度。返回的是字節數!!!

注意事項

append 返回值, 長度的單位是 字節!!

redis 的字符串, 不會對字符編碼做任何處理. (redis 不認識字符, 只認識字節)

當前咱們的 xshell 終端, 默認的字符編碼是 utf8.

在終端中輸入漢字之后, 也就是按照 utf8 編碼的~~

一個漢字在 utf8 字符集中, 通常是 3 個字節的~

3.2.GETRANGE

功能:

返回鍵?key?對應字符串值的子串,子串范圍由?start?和?end?下標確定(包含兩端字符)。

索引規則:

  1. 正數索引:從左向右計數(0 表示第一個字符)

  2. 負數索引:從右向左計數

    • -1:倒數第一個字符

    • -2:倒數第二個字符

    • 其他負數依此類推

  3. 范圍處理

    • 若?start?超出字符串左邊界,視為 0

    • 若?end?超出字符串右邊界,視為最后一個字符

    • 若?start > end,返回空字符串

語法:

GETRANGE key start end
  • 命令有效版本:2.4.0 之后
  • 時間復雜度:O(N). N 為 [start, end] 區間的長度. 由于 string 通常比較短, 可以視為是 O(1)
  • 返回值:string 類型的子串、

閉區間包含

負數索引

越界處理

空鍵處理

完整實例

3.3.SETRANGE

功能:

從指定偏移量?offset?開始,覆蓋鍵?key?存儲的字符串值的部分內容。

注意這里的偏移量可是以字節為單位的。

核心行為:

  1. 覆蓋規則

    • 從?offset?位置開始,用?value?覆蓋原字符串

    • 若?offset?大于原字符串長度,自動用空字節(\x00)填充中間空缺

  2. 鍵不存在時

    • 視為空字符串?""?處理

    • 等同于在指定位置創建新字符串

  3. 長度變化

    • 操作后字符串長度 = max(原長度, offset + value長度)

    • 可能擴展字符串長度

語法:

SETRANGE key offset value
  • 命令有效版本:2.2.0 之后
  • 時間復雜度:O(N),N 為 value 的長度。由于一般給的 value 比較短,通常視為 O(1)。
  • 返回值:替換后的 string 的長度。

話不多說,直接看例子:

基礎覆蓋:從指定偏移量?offset?開始,覆蓋鍵?key?存儲的字符串值的部分內容。

超出長度處理:若?offset?大于原字符串長度,自動用空字節(\x00)填充中間空缺

空鍵操作:鍵不存在時,視為空字符串?""?處理,偏移量前面的全部自動用空字節(\x00)填充

注意這里的偏移量可是以字節為單位的。

如果我們當前的value是中文字符串,進行?SETRANGE操作的話可能會出現問題的。因為一個中文字符通常不止一個字節。

我們看看

3.4.STRLEN ?

獲取 key 對應的 string 的長度。也就是對應string的字節數。

當 key 存放的類似不是 string 時,報錯。

語法:

STRLEN key
  • 命令有效版本:2.2.0 之后 ?
  • 時間復雜度:O(1) ?
  • 返回值:string 的長度。或者當 key 不存在時,返回 0。

注意事項:

1.字節長度

無論字符串使用何種編碼(如 ASCII、UTF-8、GBK),STRLEN?始終返回字符串占用的?字節數
例如:

  • 字符串?"hello"(ASCII)→ 5 字節 →?返回 5
  • 字符串?"你好"(UTF-8 編碼)→ 每個漢字占 3 字節 → 總 6 字節 →?返回 6

2.非字符串類型報錯

若 Key 存儲的不是字符串類型(如 List/Hash/Set),Redis 會返回錯誤:(error) WRONGTYPE Operation against a key holding the wrong kind of value

3.Key 不存在:若 Key 不存在,返回 0(視為空字符串)。


話不多說,我們直接看例子

字符串?"你好"(注意我們現在是使用了UTF-8 編碼)→ 每個漢字占 3 字節 → 總 6 字節 →?返回 6

在編程和數據庫設計中,理解字符串長度和字符編碼至關重要,不同環境下的處理方式存在顯著差異:

長度單位:

  • C++:?std::string?的?length()?或?size()?方法返回的是字節數

  • Java:?String?的?length()?方法返回的是?字符數

  • MySQL:?VARCHAR(N)?定義中的?N?指定的是字符數。一個字符可以是一個英文字母、一個漢字或其他語言符號。

四. 字符串類型內部編碼

Redis 為了優化內存使用和性能,會根據字符串值的類型和長度動態選擇三種不同的內部編碼表示:

  • int:8個字節的?整型。
  • embstr:?于等于39個字節的字符串。
  • raw:?于39個字節的字符串。

Redis 會根據當前值的類型和?度動態決定使?哪種內部編碼實現。

int?(整型):

當字符串值可以表示為?8 字節長整型 (long)?時使用。

優勢:?內存占用小,操作效率高(支持數值計算)。

示例:

值 '6379' 被識別為整數,使用 int 編碼

embstr?(嵌入式字符串):

用于存儲長度?小于或等于 44 字節?的字符串值?(注意:Redis 版本演進中,這個閾值從早期的 39 字節提高到了 5.0 及以后版本的 44 字節)

特點:?RedisObject 對象頭和數據本身存儲在一塊連續的內存中。

優勢:?內存分配和釋放只需一次操作,緩存局部性好,訪問效率高。

示例:



?短字符串 "hello" 使用 embstr 編碼

raw?(原始字符串):

用于存儲長度?大于 44 字節?的字符串值。

特點:?RedisObject 對象頭和數據本身存儲在兩塊獨立分配的內存中。

原因:?對于長字符串,分配連續的大塊內存代價更高且不靈活。

示例:

"raw"? # 長字符串使用 raw 編碼

忠告

?不建議大家去記, 長度大于 39 這樣的數字~~

給大家講個情景吧:

對于某個業務場景, 有很多很多的 key , 類型都是 string

但是每個 value 的 string 長度都是 100 左右~~ 更關注與整體的內存空間.

因此的話, 這樣的字符串使用 embstr 來存儲也不是 不能考慮~~

上述效果具體怎么實現?

  1. 先看 redis 是否提供了對應的配置項, 可以修改 39 這個數字~~
  2. 如果沒有提供配置型, 就需要針對 redis 源碼進行魔 改~

為啥很多大廠, 往往是自己造輪子, 而不是直接使用業 界成熟的呢?

開源的組件, 往往考慮的是通用性~~ 但是大廠往往會遇到一些極端的業務場景~~ 往往就需要根據當前的極端業務, 針對上述的開源組件 進行定制化~~

五. 典型使用場景

5.1.緩存(Cache)功能

????????緩存(Cache)功能是?較典型的緩存使?場景,其中Redis作為緩沖層,MySQL作為存儲層,絕?部分請 求的數據都是從Redis中獲取。

緩存讀取的核心流程:

  1. 優先查詢 Redis:?當應用服務器需要數據時,首先嘗試從 Redis 緩存中讀取。

  2. 緩存命中 (Cache Hit):?如果所需數據存在于 Redis 中,則直接將其返回給應用服務器。此時不會訪問后端數據庫,顯著提升響應速度和降低數據庫負載。

  3. 緩存未命中 (Cache Miss):

    • 如果 Redis 中不存在所需數據,則應用服務器轉而查詢后端數據庫 (如 MySQL)。

    • 從數據庫獲取到結果后,將其返回給應用服務器。

    • 同時,將這個查詢結果寫入 Redis 緩存,以備后續相同的請求使用。

Redis 與“熱點數據”:

  • Redis 緩存最典型的應用場景就是存儲高頻訪問的數據,即“熱點數據”。

  • 上述緩存策略(最近查詢到的數據寫入緩存)本質上是將最近被訪問的數據視為潛在的熱點數據。

  • 這隱含了一個假設:某個數據一旦被訪問,它在近期內很可能被再次訪問(時間局部性原理)。這種策略(如 LRU - Least Recently Used 的變體)能有效捕捉并加速訪問這類熱點數據。

潛在問題:緩存膨脹與解決方案

上述策略存在一個明顯的問題:隨著時間推移,越來越多的 Key 會因為首次訪問而從數據庫加載并寫入 Redis。如果不加控制,Redis 中存儲的數據量會持續增長,最終可能導致:

  • 內存耗盡:?Redis 主要依賴內存存儲,無限增長的數據必然耗盡內存資源。

  • 性能下降:?內存不足可能導致 Redis 自身操作變慢,甚至觸發更嚴重的問題。

核心解決方案:

  1. 設置過期時間 (TTL - Time To Live):

    • 在將數據寫入 Redis 時,必須為其設置一個合理的過期時間。例如:SET key value EX 3600?(設置 key 在 3600 秒后過期)。

    • 這確保了即使數據不再被訪問,也會在過期后被自動刪除,釋放內存空間。

    • 過期時間的選擇需要結合業務場景(數據更新頻率、重要性、容忍的陳舊度等)。

  2. 配置內存淘汰策略 (Eviction Policies):

    • 當 Redis 內存使用達到配置的最大限制 (maxmemory) 時,它會根據設定的淘汰策略自動刪除部分 Key 以釋放空間,保證新數據可以寫入。

    • 常見的淘汰策略包括:

      • volatile-lru?/?allkeys-lru: 淘汰最近最少使用的 Key(有過期時間的 / 所有Key)。

      • volatile-lfu?/?allkeys-lfu: 淘汰最不經常使用的 Key(有過期時間的 / 所有Key)。

      • volatile-ttl: 淘汰即將過期的 Key。

      • volatile-random?/?allkeys-random: 隨機淘汰 Key(有過期時間的 / 所有Key)。

      • noeviction: 不淘汰,新寫入操作會報錯(通常不推薦用于緩存場景)。

    • 選擇合適的淘汰策略對于平衡內存使用和緩存命中率至關重要。


由于Redis具有?撐?并發的特性,所以緩存通常能起到加速讀寫和降低后端壓?的作?。

下?的偽代碼模擬了業務數據訪問過程:

1)假設業務是根據??uid獲取??信息

UserInfo getUserInfo(long uid) {...}

2)?先從Redis獲取??信息,我們假設??信息保存在"user:info:"對應的鍵中:

// 根據 uid 得到 Redis 的鍵
String key = "user:info:" + uid;// 嘗試從 Redis 中獲取對應的值
String value = Redis 執行命令: get key;// 如果緩存命中 (hit)
if (value != null) {// 假設我們的用戶信息按照 JSON 格式存儲UserInfo userInfo = JSON 反序列化(value);return userInfo;
}

3)如果沒有從 Redis 中得到用戶信息,及緩存 miss,則進一步從 MySQL 中獲取對應的信息,隨后寫入緩存并返回:

// 如果緩存未命中(miss)
if (value == null) {// 從數據庫中,根據 uid 獲取用戶信息UserInfo userInfo = MySQL 執行 SQL: select * from user_info where uid = <uid>}// 如果表中沒有 uid 對應的用戶信息
if (userInfo == null) {響應 404return null;
}// 將用戶信息序列化成 JSON 格式
String value = JSON 序列化(userInfo);// 寫入緩存,為了防止數據腐爛(rot),設置過期時間為 1 小時(3600 秒)
Redis 執行命令: set key value ex 3600// 返回用戶信息
return userInfo;
}

通過增加緩存功能,在理想情況下,每個用戶信息,一個小時期間只會有一次 MySQL 查詢,極大地提升了查詢效率,也降低了 MySQL 的訪問數。

與 MySQL 等關系型數據庫不同的是,Redis 沒有表、字段這種命名空間,而且也沒有對鍵名有強制要求(除了不能使用一些特殊字符)。

但設計合理的鍵名,有利于防止鍵沖突和項目的可維護性,比較推薦的方式是使用 "業務名:對象名:唯一標識:屬性" 作為鍵名。

例如 MySQL 的數據庫名為 vs,用戶表名為 user_info,那么對應的鍵可以使用 "vs:user_info:6379"、"vs:user_info:6379:name" 來表示,如果當前 Redis 只會被一個業務

使用,可以省略業務名 "vs:"。

如果鍵名過程,則可以使用團隊內部都認同的縮寫替代,例如 "user:6379:friends:messages:5217" 可以被 "u:6379:fr:m:5217" 代替。

畢竟鍵名過長,還是會導致 Redis 的性能明顯下降的。

5.2.計數(Counter)功能

許多應用都會使用 Redis 作為計數的基礎工具,它可以實現快速計數、查詢緩存的功能,同時數據可以異步處理或者落地到其他數據源。

如圖所示,例如視頻網站的視頻播放次數可以使用 Redis 來完成:用戶每播放一次視頻,相應的視頻播放數就會自增 1。

// 在 Redis 中統計某視頻的播放次數
long incrVideoCounter(long vid) {key = "video:" + vid;long count = Redis 執行命令:incr keyreturn counter;
}

實際上要開發一個成熟、穩定的真實計數系統,要面臨的挑戰遠不止如此簡單:防作弊、按照不同維度計數、避免單點問題、數據持久化到底層數據源等。

Redis 并不擅長數據統計!

為什么呢??Redis 的核心優勢在于快速的鍵值存取和豐富的數據結構操作,但它缺乏內置的復雜聚合計算能力,特別是像排序、分組、多鍵關聯統計這類操作。

典型例子:?比如,你想在上面提到的 Redis 緩存(或存儲)中,統計出播放量排名前 100 的視頻有哪些。要實現這個需求:

  • 基于 Redis 搞就很麻煩!?你可能需要:

    1. 遍歷所有相關的視頻 Key(如果數據量大,KEYS?命令是災難性的,SCAN?也慢)。

    2. 逐個獲取它們的播放量(多次?GET/HGET?命令,網絡開銷大)。

    3. 在客戶端(應用服務器)內存中對所有視頻的播放量進行排序。

    4. 最后取出前 100 名。
      這個過程不僅性能開銷巨大(O(N) 復雜度),容易阻塞 Redis(如果遍歷不當),而且實現邏輯復雜,代碼量多。

相比之下,MySQL 的優勢就顯現了:

  • 如果上述播放量數據是存儲在 MySQL 數據庫中的(比如存在一個?videos?表里,有?video_id?和?play_count?字段)。

  • 那么,一個簡單的 SQL 查詢就搞定了!?例如:

    SELECT video_id, play_count FROM videos ORDER BY play_count DESC LIMIT 100;
  • MySQL 強大的查詢優化引擎會高效地處理排序 (ORDER BY) 和限制結果集 (LIMIT),通常利用索引就能快速完成,復雜度遠低于 Redis 的 O(N) 遍歷,而且代碼極其簡潔。

5.3.共享會話(Session)

如圖所示,一個分布式 Web 服務將用戶的 Session 信息(例如用戶登錄信息)保存在各自的服務器中,但這樣會造成一個問題:出于負載均衡的考慮,分布式服務會將用戶的訪問請求均衡到不同的服務器上,并且通常無法保證用戶每次請求都會被均衡到同一臺服務器上,這樣當用戶刷新一次訪問是可能會發現需要重新登錄,這個問題是用戶無法容忍的。?

為了解決這個問題,可以使?Redis將??的Session信息進?集中管理,如圖2-13所?,在這種模 式下,只要保證Redis是?可?和可擴展性的,?論??被均衡到哪臺Web服務器上,都集中從 Redis 中查詢、更新Session信息。?

總結一下,就是下面這樣子:

分布式 Session 問題
在分布式 Web 服務中,若用戶 Session 信息(如登錄狀態)存儲在各自服務器內存中,當負載均衡將用戶請求分配到不同服務器時,用戶可能因訪問不同服務器而丟失 Session 狀態(例如刷新頁面后需重新登錄)。這種體驗是用戶無法容忍的。

解決方案:Redis 集中管理

  1. 統一存儲:所有服務器將 Session 數據(鍵值對結構)集中存儲到 Redis。

  2. 高可用保障:通過 Redis 集群(主從復制+哨兵)或 Redis Cluster 確保服務連續性。

  3. 全局訪問:用戶請求攜帶?Session ID(通常存于瀏覽器 Cookie),無論分配到哪臺服務器,均從 Redis 查詢/更新狀態。
    →?實現效果:用戶登錄狀態、購物車等會話數據在分布式環境下保持全局一致


醫療場景類比

原始案例

"我前段時間生病,聲帶發炎到完全說不出話~~
掛號看專家,做喉鏡、霧化理療,開了一周藥量。醫生囑托:'先搞一周看看,一周后復查~~'
一周后復查,發現首診醫生不在!!! 新醫生未接觸過我的病例,不了解情況~~
硬著頭皮就診,新醫生刷我的就診卡,電腦上立即顯示完整病史~~"

技術映射

  • 病人=客戶端:需要連續醫療服務

  • 醫生=Web服務器:多實例并行服務

  • 醫生憑個人記憶=服務器本地存儲Session:導致切換服務器時狀態丟失

  • 就診卡=Session ID:唯一標識患者身份(通過Cookie傳遞)

  • 醫院病例系統=Redis:集中存儲病史(會話狀態),實現醫生(服務器)間數據共享

核心結論
會話(Session)本質是客戶端與服務器交互中產生的專屬中間狀態,分布式系統必須通過集中存儲(如Redis)解決狀態一致性問題,如同醫院依賴共享病例系統保障連續診療。

5.4.?機驗證碼

????????很多應?出于安全考慮,會在每次進?登錄時,讓??輸??機號并且配合給?機發送驗證碼, 然后讓??再次輸?收到的驗證碼并進?驗證,從?確定是否是??本?。

為了短信接?不會頻繁訪 問,會限制??每分鐘獲取驗證碼的頻率,例如?分鐘不能超過5次,如圖所?。

此功能可以用以下偽代碼說明基本實現思路:

String 發送驗證碼(phoneNumber) {key = "shortMsg:limit:" + phoneNumber;// 設置過期時間為 1 分鐘 (60 秒)// 使用 NX,只在不存在 key 時才能設置成功bool r = Redis 執行命令: set key 1 ex 60 nxif (r == false) {// 說明之前設置過該手機的驗證碼了long c = Redis 執行命令: incr keyif (c > 5) {// 說明超過了一分鐘 5 次的限制了// 限制發送return null;}}// 說明要么之前沒有設置過手機的驗證碼;要么次數沒有超過 5 次String validationCode = 生成隨機的 6 位數的驗證碼();validationKey = "validation:" + phoneNumber;// 驗證碼 5 分鐘 (300 秒) 內有效Redis 執行命令: set validationKey validationCode ex 300;// 返回驗證碼,隨后通過手機短信發送給用戶return validationCode ;
}// 驗證用戶輸入的驗證碼是否正確
bool 驗證驗證碼(phoneNumber, validationCode) {validationKey = "validation:" + phoneNumber;String value = Redis 執行命令: get validationKey;if (value == null) {// 說明沒有這個手機的驗證碼記錄,驗證失敗return false;}if (value == validationCode) {return true;} else {return false;}
}

以上介紹了使用 Redis 的字符串數據類型可以使用的幾個場景,但其適用場景遠不止于此,開發人員可以結合字符串類型的特點以及提供的命令,充分發揮自己的想象力,在自己的業務中去找到合適的場景去使用 Redis 的字符串類型。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/94485.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/94485.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/94485.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Nginx 學習

通過網盤分享的文件&#xff1a;Nginx 鏈接: https://pan.baidu.com/s/1dCc7FoND90H_x7rvRUXJqg 提取碼: yyds 通過網盤分享的文件&#xff1a;Tomcat 鏈接: https://pan.baidu.com/s/1nj_5j_66gS_YHUAX1C25jg 提取碼: yyds Nginx安裝、啟動 安裝依賴庫 #安裝C編譯器 yum insta…

Java、Android及計算機基礎面試題總結

1. String、StringBuffer、StringBuilder區別特性StringStringBufferStringBuilder可變性不可變可變可變線程安全是是(synchronized)否性能低(頻繁操作時)中等高場景字符串常量多線程字符串操作單線程字符串操作2. 接口和抽象類的區別特性接口(Interface)抽象類(Abstract Class…

數據集相關類代碼回顧理解 | sns.distplot\%matplotlib inline\sns.scatterplot

【PyTorch】單目標檢測項目 目錄 os.path.join sns.distplot adjust_brightness os.path.join fullPath2imgos.path.join(path2data,"Training400",prefix,imgName[id_]) 使用os.path.join函數&#xff0c;智能地處理不同操作系統中的路徑分隔符問題&#xff0…

JavaScript:鏈式調用

概念 鏈式調用&#xff08;Method Chaining&#xff09;是 JavaScript 中一種常見的編程模式&#xff0c;允許通過連續調用對象的方法來簡化代碼。這種模式的核心在于每個方法返回調用對象本身&#xff08;通常是 this&#xff09;&#xff0c;從而可以繼續調用其他方法。 鏈式…

龍芯(loongson) ls2k1000 openwrt

PC環境&#xff1a;Linux Mint 21.3安裝依賴sudo apt install build-essential clang flex bison g gawk gcc-multilib g-multilib gettext git libncurses-dev libssl-dev python3-distutils rsync unzip zlib1g-dev file wget下載源碼&#xff1a;git clone https://gitee.co…

算法438. 找到字符串中所有字母異位詞

給定兩個字符串 s 和 p&#xff0c;找到 s 中所有 p 的 異位詞 的子串&#xff0c;返回這些子串的起始索引。不考慮答案輸出的順序。示例 1:輸入: s "cbaebabacd", p "abc" 輸出: [0,6] 解釋: 起始索引等于 0 的子串是 "cba", 它是 "abc&…

Go語言中的閉包詳解

閉包在Go語言中是一個能夠訪問并操作其外部作用域變量的函數&#xff0c;即使外部函數已經執行完畢。閉包由函數體和其引用的環境&#xff08;外部變量&#xff09;組成&#xff0c;及&#xff1a;閉包 函數 環境。閉包的特性&#xff1a;捕獲外部變量&#xff1a;內部函數可…

【DL學習筆記】Dataset類功能以及自定義

文章目錄一、Dataset 與 DataLoader 功能介紹抽象類Dataset的作用DataLoader 作用兩者關系二、自定義Dataset類Dataset的三個重要方法__len__()方法_getitem__()方法__init__ 方法三、現成的torchvision.datasets模塊MNIST舉例COCODetection舉例torchvision.datasets.MNIST使用…

Python爬蟲實戰:研究python_reference庫,構建技術研究數據系統

1. 引言 1.1 研究背景與意義 在大數據時代,數據已成為重要的生產要素。互聯網作為全球最大的信息庫,蘊含著海量有價值的數據。如何從紛繁復雜的網絡信息中快速、準確地提取所需數據,成為各行各業面臨的重要課題。網絡爬蟲技術作為數據獲取的關鍵手段,能夠模擬人類瀏覽網頁…

Web開發系列-第15章 項目部署-Docker

第15章 項目部署-Docker Docker技術能夠避免部署對服務器環境的依賴&#xff0c;減少復雜的部署流程。 輕松部署各種常見軟件、Java項目 參考文檔&#xff1a;?&#xfeff;??&#xfeff;??????&#xfeff;??&#xfeff;????????第十五章&#xff1a;…

微軟無界鼠標(Mouse without Borders)安裝及使用:多臺電腦共用鼠標鍵盤

文章目錄一、寫在前面二、下載安裝1、兩臺電腦都下載安裝2、被控端3、控制端主機三、使用一、寫在前面 在辦公中&#xff0c;我們經常會遇到這種場景&#xff0c;自己帶著筆記本電腦外加公司配置的臺式機。由于兩臺電腦&#xff0c;所以就需要搭配兩套鍵盤鼠標。對于有限的辦公…

nodejs 編程基礎01-NPM包管理

1:npm 包管理介紹 npm 是nodejs 的包管理工具&#xff0c;類似于java 的maven 和 gradle 等&#xff0c;用來解決nodejs 的依賴包問題 使用場景&#xff1a;1. 從NPM 服務騎上下載或拉去別人編寫好的第三方包到本地進行使用2. 將自己編寫代碼或軟件包發布到npm 服務器供他人使用…

基于Mediapipe_Unity_Plugin實現手勢識別

GitHub - homuler/MediaPipeUnityPlugin: Unity plugin to run MediaPipehttps://github.com/homuler/MediaPipeUnityPlugin 實現了以下&#xff1a; public enum HandGesture { None, Stop, ThumbsUp, Victory, OK, OpenHand } 核心腳本&#xff1a…

Android 項目構建編譯概述

主要內容是Android AOSP源碼的管理方式&#xff0c;項目源碼的構建和編譯&#xff0c;用到比如git、repo、gerrit一些命令工具&#xff0c;以及使用Soong編譯系統&#xff0c;編寫Android.bp文件的格式樣式。 1. Android操作系統堆棧概述 Android 是一個針對多種不同設備類型打…

Python爬蟲08_Requests聚焦批量爬取圖片

一、Requests聚焦批量爬取圖片 import re import requests import os import timeurl https://www.douban.com/ userAgent {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:122.0) Gecko/20100101 Firefox/122.0}#獲取整個瀏覽頁面 page_text requests.get(urlur…

Spring Cloud系列—簡介

目錄 1 單體架構 2 集群與分布式 3 微服務架構 4 Spring Cloud 5 Spring Cloud環境和工程搭建 5.1 服務拆分 5.2 示例 5.2.1 數據庫配置 5.2.2 父子項目創建 5.2.3 order_service子項目結構配置 5.2.4 product_service子項目結構配置 5.2.5 服務之間的遠程調用 5.…

【普中STM32精靈開發攻略】--第 1 章 如何使用本攻略

學習本開發攻略主要參考的文檔有《STM32F1xx 中文參考手冊》和《Cortex M3權威指南(中文)》&#xff0c;這兩本都是 ST 官方手冊&#xff0c;尤其是《STM32F1xx 中文參考手冊》&#xff0c;里面包含了 STM32F1 內部所有外設介紹&#xff0c;非常詳細。大家在學習 STM32F103的時…

【Docker】RK3576-Debian上使用Docker安裝Ubuntu22.04+ROS2

1、簡述 RK3576自帶Debian12系統,如果要使用ROS2,可以在Debian上直接安裝ROS2,缺點是有的ROS包需要源碼編譯;當然最好是使用Ubuntu系統,可以使用Docker安裝,或者構建Ubuntu系統,替換Debian系統。 推薦使用Docker來安裝Ubuntu22.04,這里會有個疑問,是否可以直接使用Do…

解決docker load加載tar鏡像報json no such file or directory的錯誤

在使用docker加載離線鏡像文件時&#xff0c;出現了json no such file or directory的錯誤&#xff0c;剛開始以為是壓縮包拷貝壞了&#xff0c;重新拷貝了以后還是出現了問題。經過網上查找方案&#xff0c;并且自己實踐&#xff0c;采用下面的簡單方法就可以搞定。 歸結為一句…

《協作畫布的深層架構:React與TypeScript構建多人實時繪圖應用的核心邏輯》

多人在線協作繪圖應用的構建不僅是技術棧的簡單組合,更是對實時性、一致性與用戶體驗的多維挑戰。基于React與TypeScript開發這類應用,需要在圖形繪制的基礎功能之外,解決多用戶并發操作的同步難題、狀態回溯的邏輯沖突以及大規模協作的性能瓶頸。每一層架構的設計,都需兼顧…