日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

startindex 不能大于字符串长度_玩转云端丨redis的5种对象与8种数据结构之字符串对象(下)...

發布時間:2023/12/4 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 startindex 不能大于字符串长度_玩转云端丨redis的5种对象与8种数据结构之字符串对象(下)... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引言

本文是對《redis設計與實現(第二版)》中數據結構與對象相關內容的整理與說明。本篇文章只對對象結構,1種對象——字符串對象。以及字符串對象所對應的兩種編碼——raw和embstr,進行了詳細介紹。表達一些本人的想法與看法,也希望更多朋友一起來討論,分享交流。

作者:太陽

云掣科技-數據庫團隊數據庫工程師

字符串對象

字符串對象可以存儲整數、浮點數、字符串,具體策略是:

當存儲整數時,用到的編碼是int,底層的數據結構可以用來存儲long類型的整數;當存儲字符串時,如果字符串的長度小于等于32字節,那么將用編碼為embstr的格式來存儲;如果字符串的長度大于32字節,將用編碼為raw的SDS格式來存儲;當存儲浮點數時會先將浮點數轉換為字符串,如果轉換后的字符串長度小于32字節就用編碼為embstr的格式來存儲,否則用編碼為raw的SDS格式來存儲。

下圖是一個字符串對象的結構圖,最左側是對象結構,中間跟右側合起來是raw編碼的SDS數據結構(sdshdr),示例圖:

raw編碼,簡單動態字符串(simple dynamic string-SDS)

redis用的并不是C語言傳統的字符串,而是自己構建了簡單動態字符串(simpledynamic string,SDS)。

當redis打印日志信息或輸出報錯信息,這些輸出的字符串是不會被修改的字符串字面量(sting literal),此時用的是C語言傳統的字符串來存儲這些信息的。當redis需要存儲的是可以被修改的字符串時,就會使用SDS結構。


除了用來保存數據庫中的字符串值之外,SDS還被用作緩沖區(buffer):AOF模塊中的AOF緩沖區,以及客戶端狀態中的輸入緩沖區,都是由SDS實現的。

SDS的結構

SDS結構示意圖如下所示:

sdshdr是該數據結構的名稱即SDS,其中:

buf屬性,是一個字節數組,用來保存字符串,后面箭頭對應的就是實際保存的字符串內容,最后以’0’空字符串結尾;

len屬性,記錄的是buf數組中實際已使用的字節數量,等于SDS所保存字符串的長度;

free屬性,記錄的是buf數組中未使用字節的數量。

SDS優點

一、可以用O(1)的復雜度獲取到字符串長度

SDS的len屬性記錄了字符串的長度,而傳統C字符串要想知道長度需要遍歷整個字符串。相比于傳統C字符串,redis獲取字符串長度所需的復雜度從O(N)降低到了O(1)。

即使對非常長的字符串反復執行STRLEN命令(獲取字符串長度),也不會造成過多的性能消耗。

二、杜絕緩沖區溢出

在傳統的C字符串中,如果要修改字符串的內容,但修改后字符串的長度超過原先的長度就會發生溢出現象。詳見下圖:

在SDS中,當需要對buf字節數組中存儲的內容進行修改(增添或刪除)時,API會先通過free和len屬性檢查SDS的空間是否足夠,如果不夠的話,SDS會自動擴展空間再對內容進行修改。關于自動擴展空間的策略見下方“空間預分配”的內容。三、減少修改字符串長度時所需的內存重分配次數

對于傳統C字符串:

如果執行的是增長字符串的操作,如拼接操作(append),那么在執行命令之前,程序需要先通過內存重分配來擴展底層數據的空間大小——否則會產生緩沖區溢出。


如果執行的是縮短字符串的操作,如截斷操作(trim),那么在執行這個操作之后,程序需要通過內存重分配來釋放字符串不再使用的空間——否則會產生內存泄漏。

對于redis中的SDS結構:

內存重分配設計復雜的算法,是一個比較耗時的操作,redis作為速度要求嚴苛、數據會被頻繁執行的數據庫,如果每次修改字符串都需要進行一次內存重分配,會嚴重影響性能。

使用SDS,buf數組里可以包含未使用的字節,這些字節的數量由free屬性記錄,可以減少修改字符串長度時所需的內存重分配次數。

空間預分配和惰性空間釋放

通過SDS中free屬性定義的未使用空間,SDS可以實現空間預分配和惰性空間釋放兩種優化策略:

1、空間預分配策略——可以降低字符串增長操作引起的內存重分配

當需要修改SDS的內容,且需要進行空間擴展的時候,程序不僅會為SDS分配修改所需的必須空間,還會為SDS分配額外的未使用空間。

其中,額外分配的未使用空間數量由以下公式決定:

如果對SDS進行修改之后,SDS的長度(即len屬性的值)將小于1MB,那么程序將分配和len屬性同樣大小的未使用空間,這時SDS len屬性的值將和free屬性的值相同。

如果對SDS進行修改后,SDS的長度將大于等于1MB,那么程序會分配1MB的未使用空間。

說明

如果對一個字符串的末尾持續追加內容,當字符串整體大小大于1MB時,即使只追加一字節的字符,程序也會額外分配1MB的空間,當再次追加一字節的字符時,程序不會再額外分配1MB的空間,而是使用已有的空閑空間。

即在擴展空間之前,會先檢查未使用的空間是否足夠,如果足夠,是不會額外再擴展的。

通過空間預分配策略,SDS將連續增長N次字符串所需的內存重分配次數從必定N次降低為最多N次。

2、惰性空間釋放策略——可以降低字符串縮短操作引起的內存重分配

當SDS中的字符串長度被縮短時,程序并不會立即使用內存重分配來回收縮短后多出來的字節空間,而是使用free屬性將這些字節的數量記錄起來,以備將來使用。


當然,redis提供了相應的命令來真正釋放這些未使用空間,避免不必要的內存浪費。

四、二進制安全

C字符串中的字符必須符合某種編碼(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,如果字符串除末尾外還有其它空字符,那么最先被程序讀入的空字符將被誤認為是字符串結尾,這些限制使得C字符串只能保存文本數據,而不能保存圖片、音頻、視頻、壓縮文件這樣的二進制數據。

為了確保redis可以適用于各種不同的使用場景,SDS的API都是二進制安全的(binary-safe),所有SDS API都會以處理二進制的方式來處理SDS存放buf數組里的數據,程序不會對其中的數據做任何限制、過濾或者假設,數據在寫入時是什么樣的,它被讀取時就是什么樣。

這也是RDS的buf屬性被稱為字節數組的原因——redis不是用這個數組來保存字符,而是用它來保存一系列二進制數據。五、兼容部分C字符串函數

SDS遵循空字符串結尾這一慣例,好處是可以直接重用C字符串<string.h>函數庫里的函數,從而避免了不必要的代碼重復。

embstr編碼

如果字符串對象保存的是長度小于等于32字節的字符串,那么將會使用embstr編碼,embstr編碼是專門用來保存短字符串的一種優化編碼方式。embstr編碼與raw編碼對應的字符串對象,都是由對象結構(redisObject)和數據結構(sdshdr)組成的。

區別在于用raw編碼的字符串對象會調用兩次內存分配函數來分別創建redisObject結構和sdshdr結構,而embstr編碼則通過調用一次內存分配函數來分配一塊連續的空間,空間中一次包含redisObject和sdshr兩個結構,embstr編碼的字符串對象結構圖如下所示:

兩者的區別

embstr編碼的字符串對象在執行命令時,產生的效果和raw編碼的字符串對象執行命令時產生的效果是相同的的,但使用embstr編碼的字符串對象來保存短字符串值有以下好處:

1、embstr編碼將創建字符串對象所需的內存分配次數從raw編碼的兩次降低為一次;

2、釋放embstr編碼的字符串對象只需要調用一次內存釋放函數,而釋放raw編碼的字符串對象需要調用兩次內存釋放函數;

3、embstr編碼的字符串對象的所有數據都保存在一塊連續的內存里,結構更加緊湊,而raw編碼是分散開的,redisObject對象結構和sdshdr數據結構彼此間是用指針相關聯的,embstr編碼的對象比raw編碼的對象能夠更好的利用緩存帶來的優勢。

編碼的轉換

int編碼的字符串對象和embstr編碼的字符串對象在條件滿足的情況下,會被轉換成raw編碼的字符串對象。encoding命令可以查看鍵對應的值,底層用的是什么編碼。

int轉換為raw

對于int編碼的字符串對象來說,如果我們向對象執行了一些命令,使得這個對象保存的不再是整數值,而是一個字符串值,那么字符串對象的編碼將從int變為raw。

27.0.0.1:6379> set a 100 //設置a=100 OK 127.0.0.1:6379> object encoding a //查看鍵a存儲的值用的是什么編碼 "int" 127.0.0.1:6379> append a 'a' //向鍵a的值中追加內容’a’,此時鍵a存儲的值將變為字符串類型 (integer) 4 127.0.0.1:6379> get a //查詢鍵a的值 "100a" 127.0.0.1:6379> object encoding a //查看鍵a存儲的值現在對應的編碼,發現已經變為raw格式的編碼,表示里面現在存儲的是字符串 "raw"

int編碼的字符串,存儲的是long類型的整數,范圍是2^63-1(2的63次方減一) ~ -2^63(2的63次方),當存儲的整數在該范圍內時,編碼為int,當值超過該范圍,編碼將轉換為embstr。

27.0.0.1:6379> set number1 9223372036854775807 OK 127.0.0.1:6379> object encoding number1 "int" 127.0.0.1:6379> set number2 9223372036854775808 OK 127.0.0.1:6379> object encoding number2 "embstr" 127.0.0.1:6379> set number3 -9223372036854775808 OK 127.0.0.1:6379> object encoding number3 "int" 127.0.0.1:6379> set number4 -9223372036854775809 OK 127.0.0.1:6379> object encoding number4 "embstr"

embstr轉換為raw

embstr編碼的字符串對象無法被修改(redis沒有為embstr編碼的字符串對象編寫任何響應的修改程序),只有int、raw編碼的字符串對象可以被修改,所以embstr編碼的字符串實際上是只讀的。


當對embstr編碼的字符串對象執行任何修改命令時,程序都會先將對象的編碼從embstr轉換為raw,然后再執行修改命令。所以一旦embstr編碼的字符串被修改,它的數據結構就會變成raw編碼的格式。

127.0.0.1:6379> set a 'ab' OK 127.0.0.1:6379> object encoding a "embstr" 127.0.0.1:6379> append a 'c' (integer) 3127.0.0.1:6379> get a "abc" 127.0.0.1:6379> object encoding a "raw"


碎碎念

以上就是根據《redis設計與實現(第二版)》中數據結構與對象相關內容進行的部分整理與分享,歡迎各位共同參與討論一起交流溝通。

Redis系列會在未來與大家見面!

總結

以上是生活随笔為你收集整理的startindex 不能大于字符串长度_玩转云端丨redis的5种对象与8种数据结构之字符串对象(下)...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。