redis session java获取attribute_redis里的数据结构
Redis作為當前使用非常廣泛的內存數據庫,在代碼層面做了很多極致的優化,已獲取更好的性能。其中重要的一部分,就是對于底層數據結構的使用。Redis會根據數據量、數據大小等來優化對于不同結構的使用,從而獲得更佳的運行效率和內存占用。Redis的核心數據結構包括簡單動態字符串、列表、字典、跳躍表、整數集合、壓縮列表。
接下來,我們就依次講講這些數據結構。
簡單動態字符串(SDS)
Redis是用C語言實現的。先復習一下C,C里的字符串中不記錄字符串長度,以空字符標記結尾。這樣會顯而易見的帶來三個問題:1.獲取字符串長度需要O(n)的復雜度;2.操作不慎會導致緩沖區溢出,例如內存中緊鄰的兩個字符串,如果對前一個調用strcat拼接其他字符串,就會造成溢出;3. 一些特殊內容,如圖像、音頻等轉成二進制時,難免其中夾雜空字符等特殊字符,這樣就無法被C字符串存儲了,即C字符串不具備二進制安全性。
而這幾點,對于Redis的應用場景來說,影響其實都是非常大的。因此,在redis中定義了一個新的結構,用來保存字符串,即SDS。
SDS的核心思想就是額外使用一個字段記錄字符串的長度,這樣,上面三個問題就都迎刃而解了。
此外,redis從4.0開始對SDS做了一個代碼層面的優化,優化了內存占用,不過不影響其底層邏輯。
這是redis 3.0里SDS的源碼:
struct sdshdr {unsigned int len;unsigned int free;char buf[]; };而這是redis 4.0之后SDS的源碼:
struct __attribute__ ((__packed__)) sdshdr5 {unsigned char flags; /* 3 lsb of type, and 5 msb of string length */char buf[]; }; struct __attribute__ ((__packed__)) sdshdr8 {uint8_t len; /* used */uint8_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[]; }; struct __attribute__ ((__packed__)) sdshdr16 {uint16_t len; /* used */uint16_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[]; }; struct __attribute__ ((__packed__)) sdshdr32 {uint32_t len; /* used */uint32_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[]; }; struct __attribute__ ((__packed__)) sdshdr64 {uint64_t len; /* used */uint64_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[]; };可以看到,在新版的源碼里,數據存儲會根據情況使用uint8,uint16等不同類型。在C里,一個int占用4個字節,因此,對于原版的SDS來說,即使存儲的信息非常少,也會固定占到8個字節。而uint8只占一個字節,uint16只占2個字節,對于小數據來說,redis的內存占用會有明顯優化。
此外,redis會有空間預分配、惰性釋放等機制,減少內存分配的次數。SDS的實現方式也保證了大部分方法可以兼容C字符串,減少了大量實現成本。
鏈表
Redis里的鏈表是一個普通的雙向無環鏈表,相信大家都很熟悉了,就不細說了,結構如下。
typedef struct listNode {struct listNode *prev;struct listNode *next;void *value;} listNode;Redis中的列表對象,底層就是鏈表。
字典
字典也就是我們常說的map。
typedef struct dictht {dictEntry **table;unsigned long size; //hash表長度unsigned long sizemask;unsigned long used; //已有的長度} dictht;Redis中的字典是hash表,使用鏈地址法解決hash地址沖突。
類似于java等語言中的hashMap, redis的字典也會有rehash的機制,保證其負載因子維持在合理的范圍內。
跳躍表 (skiplist)
Skiplist是一種應用非常廣的數據結構,通常是作為AVL樹的一種替代選擇,和AVL樹一樣,skiplist的查找復雜度也是O(logn), 但是實現會簡單的多,下邊我們用短短的幾行字就能把SkipList的所有內容講的非常清楚。此外,在并發環境下,SkipList也會有很大優勢,因為AVL數在平衡過程中,可能會涉及到很多節點,也就需要鎖住很多節點,SkipList則完全不存在這種問題。
從網上找了一張示意圖,可以很清楚的展示出SkipList的結構。跳躍表說白了就是一個多層的列表,每一個元素會隨機的出現在某一層上,然后某一層的鏈表中會包含所有高于或等于本層的元素。
跳躍表的查找就是從高層查起,逐步降層,定位到具體元素。比如要查詢7, 其順序就是9->6->7.
跳躍表的插入也是先做一次查找,然后直接給元素設置一個隨機的層數,再調整指針。
刪除則是刪除節點,然后調整指針。
Redis中的有序集合,就是基于跳躍表實現的。
整數集合(intset)和壓縮列表(ziplist)
這兩個結構非常像,因此就放在一起講了。它們都是針對特定條件下的小數據集做的特定優化。
整數集合是一個有序集合,使用的條件是集合中只包含整數,且元素個數不多。
壓縮列表同樣是針對列表項非常少的情況,且要求元素只能是小整數值或短字符串。它可以提供類似雙向鏈表的功能。
因為整數集合和壓縮列表都是針對小數據集的,所以可以使用連續的內存空間去保存,實現也就簡單了很多,這里就不細說了。
在實際應用中,zipList可以作為鏈表或者字典的替代品,應用在redis的列表、哈希、有序集合中。整數集合則作為字典的替代品,用在集合對象中。
以上就是redis中主要的數據結構,在這些結構的基礎上,redis實現了大量功能完善的對象,供我們使用。理解了redis這些底層結構的原理,也可以幫助我們更好的發揮redis的價值。
總結
以上是生活随笔為你收集整理的redis session java获取attribute_redis里的数据结构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开线程插数据_python笔记7-多线程
- 下一篇: 欧几里得范数_从范数到正则化