成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Redis 字符串用起來簡單,但是原理可是真不簡單

存儲 存儲軟件 Redis
無論你現在使用什么編程語言,每天最高頻使用的應該就是字符串。可以說字符串對象很基礎,也很重要。

[[357941]]

本文轉載自微信公眾號「Java極客技術」,作者鴨血粉絲 。轉載本文請聯系Java極客技術公眾號。   

Hello,大家好,我是阿粉~

無論你現在使用什么編程語言,每天最高頻使用的應該就是字符串。可以說字符串對象很基礎,也很重要。

那么今天想跟大家聊聊 Redis 字符串相關實現,來看下這個看起來簡單的字符串,為什么實現起來確實不簡單?

看完這篇文章你可以學到:

  • Redis 字符串對象多種數據結構
  • 底層數據結構轉換關系
  • SDS 動態字符串
  • Redis 采用 C 語言實現,那么字符串為什么不直接使用 C 語言的字符串?

Redis 對象

每當我們在 Redis 中保存一對新的鍵值對時,Redis 至少會創建兩個對象,一個對象用作保存鍵,而另一個對象用作保存值。

Redis 中每個對象都是一個 redisObject 結構,這個結構中有三個屬性:

  • type,表示對象的類型
  • encoding,表示編碼
  • pstr,指向底層數據結構的指針

 

type 代表對象類型,目前可以使用類型為:

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

 

我們可以使用 Redis 的 TYPE 指令,查看當前鍵對應值對象類型。

 

Redis 鍵對象說起來很簡單,它總是是一個字符串對象,而值對象就相對復雜了,它可以是字符串,也可以是集合,也可以是字典等對象。

每個對象類型底層實現的時候將會采用了多種數據結構,而 encoding 代表底層數據結構類型:

 

我們可以使用 Redis 的 OBJECT ENCODING 指令查看一個當前鍵值對象底層的編碼:

 

每個對象類型,可能支持多種數據結構,關系如下:

 

Redis 字符串對象Redis 字符串對象底層支持三種數據結構,分別是(下面使用 OBJECT ENCODING 輸出):

  • int
  • embstr
  • raw

int 代表 long 類型的整數,只要字符串對象中保存的是一個整數值,就將會使用 int 。

 

 


 

 

這里需要注意,只有整數才會使用 int,如果是浮點數, Redis 內部其實先將浮點數轉化為字符串值,然后再保存。

embstr 與 raw 類型底層的數據結構其實都是 SDS (簡單動態字符串),Redis 內部定義 sdshdr 一種結構。

那這兩者的區別其實在于,embstr類型將會調用內存分配函數,分配一塊連續的內存空間,空間中依次包含 redisObject 與 sdshdr 兩個數據結構。

而 raw 類型將會調用兩次內存分配函數,分配兩塊內存空間,一塊用于包含 redisObject結構,而另一塊用于包含 sdshdr 結構。

 

SDS 簡單動態字符串

接下來我們來看下簡單動態字符串。

sdshdrsds

底層的結構包含三個屬性:

  • len,用于記錄下面 buf[] 數組已使用的長度,即等于 SDS 所保存的字符串長度
  • free,用于記錄下面buf[] 數組未使用長度
  • buf[],用戶保存字符串

 

這里我們需要注意了,buf[]數組中最后一個字節將會保存一個空字符 \0,代表字符串結束。sdshdr 結構中的 len 長度是沒有包含這個這一個空字符。

這一點設計上與 C 語言的字符串相同。

SDS 擴容

當我們對 SDS 字符串進行增長操作,如果 SDS 空間不足,SDS 將會先進行擴容,然后再執行修改操作。

假設當前 SDS 值如下所示:

 

現在執行一次字符串拼接指令,

  1. APPEND sds " Cluster" 

由于拼接完成之后字符串總長度為 13,SDS 剩余空間不足,所以 SDS 將進行擴容,重新執行一次內存重分配。

由于我們上面的例子,SDS 修改之后的長度(即 len 屬性值)小于 1MB,那么程序將會分配和 len 屬性同樣大小的未使用空間,此時 SDS 中 len 屬性與 free 屬性值相同。

此時 SDS buf 數組的實際長度為:

  1. 13+13+1=27 

 

如果 SDS 修改之后長度大于 1MB,那么 Redis 將會分配 1MB的未使用空間。

假設進行修改之后,SDS len 屬性變為 20MB,那么程序將會分配 1 MB 的未使用空間,此時 SDS buf 數組的實際長度為:

  1. 20MB+1MB+1byte 

接著上面的例子,如果我們再次執行字符串拼接指令:

  1. APPEND sds " Guide" 

這次 SDS 未使用空間足夠保存拼接字符串,所以這次不需要重新分配內存。

 


 

 

SDS 惰性空間釋放

當我們對字符串進行縮減操作, SDS 字符串值將會被縮短,這樣 SDS 剩余空間將會變多。

此時程序不會立即就使用內存分配函數回收多余空間,而是使用 free 屬性記錄多余空間,等待后面使用。

通過這種惰性空間釋放策略,SDS 避免字符串縮短所需內存重分配操作,后續 SDS 字符串增長,可以直接使用多余的空間。

當然 SDS 也有相應的 API,可以真正釋放 SDS 未使用的空間,所以不用擔心惰性釋放策略帶來的內存浪費。

為什么 Redis 不直接使用 C 語言字符串?Redis 底層使用 C 語言編程,那么其實 C 語言也有字符串,Redis 完全可以復用 C 語言字符串~

那么為什么 Redis 還要閉門造車,重新設計一個 SDS 數據結構呢?

這是因為 C 原因這種簡單的字符串形式,在安全性,效率以及功能方面不滿足 Redis 需求。

首先如果我們需要獲取 C 語言字符串的長度,我們可以使用以下函數:

  1. strlen(str) 

strlen函數實際上使用遍歷的方式,對每個字符計數,直到遇到代表字符串結束的空字符。

這個操作時間復雜度 O(N)。

而 SDS 由于 len 記錄當前字符串的長度,所以直接讀取即可,時間復雜度僅為 O(1)。

這就確保獲取字符串長度的操作不會成為 Redis 性能瓶頸。

第二點每次增長或縮短 C 語言的字符串將會進行內存的重分配,否則可能導致緩沖區溢出,也有可能導致內存泄漏。

而 SDS 由于使用空間預分配的策略,如果 SDS 連續增長 N 次,內存重分配的次數從必定 N 次降低為最多 N 次。

第三點,C 語言字符串不能包含空字符,否則第一個空字符將會被誤認為字符串結尾,這就大大限制使用的場景。

而 SDS 中由于可以使用 len 屬性的值判斷字符串是否結束,所以沒有這種困擾。

所以 SDS 字符串是二進制安全的。

字符串對象底層數據結構轉換

SDS 結構由于需要額外的屬性記錄長度以及未使用長度,雖然這樣減少系統的復雜度,提高了性能,但是還是付出相應的代價,即存在一定內存空間浪費。

所以 Redis 字符串對象底層結構并不都是采用了 SDS。

如果字符串對象保存的是整數值,那么這個整數值將會直接保存在字符串對象結構的 ptr 屬性。

 

如果保存不是整數,但是字符串長度小于等于 39 字節,那么字符串將會使用 embstr編碼方式。最后上述兩個都不滿足才會使用 raw 編碼方式。

另外,int 編碼與 embstr,在一定條件也將會轉換為 raw 編碼格式。

如果對底層整數執行追加操作,使其不再是一個整數,則字符串對象的編碼就會從 int 變為 raw。

對于 embstr 編碼,實際其實只讀的,這是因為 Redis 底層并沒有提供任何程序修改 embstr 編碼對象。

所以一旦我們對embstr 編碼字符串進行修改,字符串對象的編碼就會從 embstr 變為 raw。

總結

Redis 字符串是我們平常操作最頻繁的數據結構,了解底層字符串對象,對于我們了解 Redis 非常重要的。

Redis 字符串對象底層結構可以分為整數與 SDS,當我們在操作 set 命令保存整數時,就將會直接使用整數。

而 SDS 是 Redis 內部定義另一種結構,相比于 C 語言字符串,它可以快速計算字符串長度,不需要頻繁的分配的內存,最終要一點 SDS 是一種二進制安全的字符串。

SDS 設計雖然帶來的很多優勢,但是這種結構相比于 C 語言,至少多占用 4(len)+4(free)=8 字節內存,如果 buf []數組還存在沒有使用的空間,空間浪費更加嚴重。

所以 Redis 字符串不是萬金油,某些場景下可能會占用大量內存。但是如果換一種數據結構,可能內存占用就會變少。

 

所以我們一定要基于合適場景使用合適的數據機構。

 

責任編輯:武曉燕 來源: Java極客技術
相關推薦

2021-10-19 08:20:47

單例模式設計模式面試

2014-02-24 14:45:23

XPath開發工具

2023-05-17 07:36:00

淺拷貝深拷貝對象

2017-12-25 15:35:36

iMac Pro芯片存儲

2022-03-16 22:24:50

ReactstateHooks

2014-12-19 10:07:10

C

2009-08-26 13:24:54

C#字符串

2012-06-26 09:40:14

部署開發管理

2015-05-28 10:35:07

前端gulpdemo

2019-03-07 15:43:22

Redis數據SDS

2020-11-27 14:28:13

數據分析工具數據庫

2019-02-21 10:06:49

2009-07-20 10:06:47

虛擬化思杰操作系統

2010-01-19 10:10:28

2020-05-13 12:17:33

RedisC字符C語言

2013-06-04 17:10:00

Linux命令

2011-12-28 15:11:09

iOS推薦

2020-11-11 15:36:51

服務器

2010-03-30 14:06:35

2021-06-30 09:20:18

NuShell工具Linux
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产伊人久久久 | 免费久久网站 | 成人在线电影在线观看 | 久久亚洲一区二区三 | 欧美视频在线播放 | 欧美jizzhd精品欧美巨大免费 | 亚洲97| 亚洲福利视频一区二区 | 亚洲一区精品在线 | 日本精品免费在线观看 | 午夜影院在线观看视频 | 欧美一区二区三区视频在线 | 亚洲一区 中文字幕 | 午夜免费在线电影 | 欧美一区二区三区 | 国产精品一区二区三区在线 | 一区二区久久电影 | 亚洲国产18 | 欧美一区二区三区在线视频 | 国产精品99久久久久久久vr | 久久网一区二区 | 欧美精品a∨在线观看不卡 国产精品久久国产精品 | 91精品久久| 国产中文字幕亚洲 | 中文字幕在线国产 | 亚洲精品久久久一区二区三区 | 91精品国产综合久久久动漫日韩 | 国产精品自拍视频网站 | 欧美精品一区在线 | 黄网在线观看 | 91精品无人区卡一卡二卡三 | 毛片黄| 亚洲成人毛片 | 黄色毛片免费看 | 天天搞天天操 | 日韩电影在线一区 | 欧美国产精品 | 国产日韩欧美一区二区 | 999re5这里只有精品 | 亚洲免费人成在线视频观看 | 久久91精品国产 |