『壹』 redis是幹嘛的
Redis(Remote Dictionary Server ),即遠程字典服務,是一個開源的使用ANSIC語言編寫、支持網路、可基於內存亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。
Redis是一個高性能的key-value資料庫。redis的出現,很大程度補償了memcached這類key/value存儲的不足,在部分場合可以對關系資料庫起到很好的補充作用。它提供了Java,C/C+,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,使用很方便。
相關內容:
下面是官方的bench-mark數據:
測試完成了50個並發執行100000個請求。
設置和獲取的值是一個256位元組字元串。
Linux box是運行Linux 2.6,這是X3320 Xeon 2.5 ghz。
文本執行使用loopback介面(127.0.0.1)。
結果:讀的速度是110000次/s,寫的速度是81000次/s。
『貳』 redis中的數據佔用內存大小分析
如今越來越來的系統中使用 redis 作為緩存系統,但是隨著用戶量的增長,業務數據不斷增多,redis伺服器的內存空間有可能會到瓶頸了,及時觀察redis中的各種鍵內存佔用多少,會方便我們評估何時升級redis伺服器規格,以及對於是否需要進行程序優化來設計合理的存儲結構都會有很大幫助,下面給大家介紹兩款工具 rdr 和 redis-rdb-tools ,可以很好的滿足我們的需要
詳細使用參考官方倉庫:https://github.com/xueqiu/rdr
rdr提供了linux/OSX/Windows下的可執行文件,直接點擊下載,我這里演示Windows下的使用方式
下載下來後存儲到d:/dev路徑下
1.首先要去redis伺服器端將rdb文件復制到本地,為了方便,我將rdb文件放到了rdr工具所在目錄
這里再說下redis rdb文件該怎麼找到,通過查看redis伺服器配置文件 redis.conf ,搜索 dbfilename 可以快速定位到該配置,按照路徑就可以找到redis rdb備份文件了
2.在rdr工具所在的路徑下打開命令行窗口,執行指令
可以看到指令執行成功後,在本地啟動了server,監聽埠8080
3.打開瀏覽器,訪問http://localhost:8080/,能看到詳細的內存佔用數據報告,包括鍵數量、不同的數據類型、元素計數等
不過通過網頁版的數據報告中有個小問題,對於redis中的不同資料庫沒有明顯的區分展示~,混在一起,不是太清晰
詳細使用參考官方倉庫:https://github.com/sripathikrishnan/redis-rdb-tools/
1.安裝python環境,我這里安裝了python2.7.15
2.通過pip安裝redis-rdb-tools
我這里python-lzf庫沒有安裝成功,不過不影響實際使用,這個庫是為了加速rdb文件解析速度~
3.安裝完成後就可以在命令行中使用了,輸入指令生成內存報告文件
等待一段時間,命令阻塞執行完成後,就會在-f參數指定的路徑下生成對應的文件
用excel打開生成的csv文件,可以看到詳細的統計結果,包含了所有資料庫下所有key的內存佔用情況~
redis-rdb-tools中還帶了一個很有用的命令,能幫助我們直接查詢單個key的內存佔用情況,命令格式如下
執行測試下效果,可以看到命令執行完成後,直接回顯出指定key對應的內存佔用情況了
注意該操作在生產環境下慎用,視key大小情況再行決定是否執行,有可能會阻塞執行很長時間才能計算出結果~
『叄』 Redis --- 八種數據類型(基本命令)
String、Hash、List、Set和Zset。
等同於java中的, Map<String,String> string 是redis裡面的最基本的數據類型,一個key對應一個value。
應用場景 :String是最常用的一種數據類型,普通的key/value存儲都可以歸為此類,如用戶信息,登錄信息和配置信息等;
實現方式 :String在redis內部存儲默認就是一個字元串,被redisObject所引用,當遇到incr、decr等操作(自增自減等原子操作)時會轉成數值型進行計算,此時redisObject的encoding欄位為int。
Redis雖然是用C語言寫的,但卻沒有直接用C語言的字元串,而是自己實現了一套字元串。目的就是為了提升速度,提升性能。 Redis構建了一個叫做簡單動態字元串(Simple Dynamic String),簡稱SDS。
Redis的字元串也會遵守C語言的字元串的實現規則,即 最後一個字元為空字元。然而這個空字元不會被計算在len里頭。
Redis動態擴展步驟:
Redis字元串的性能優勢
常用命令 :set/get/decr/incr/mget等,具體如下;
ps:計數器(字元串的內容為整數的時候可以使用),如 set number 1。
補充:
等同於java中的: Map<String,Map<String,String>> ,redis的hash是一個string類型的field和value的映射表, 特別適合存儲對象。 在redis中,hash因為是一個集合,所以有兩層。第一層是key:hash集合value,第二層是hashkey:string value。所以判斷是否採用hash的時候可以參照有兩層key的設計來做參考。並且注意的是, 設置過期時間只能在第一層的key上面設置。
應用場景 :我們要存儲一個用戶信息對象數據,其中包括用戶ID、用戶姓名、年齡和生日,通過用戶ID我們希望獲取該用戶的姓名或者年齡或者生日;
實現方式 :Redis的Hash實際是內部存儲的Value為一個HashMap,並提供了直接存取這個Map成員的介面。如,Key是用戶ID, value是一個Map。 這個Map的key是成員的屬性名,value是屬性值 。這樣對數據的修改和存取都可以直接通過其內部Map的Key(Redis里稱內部Map的key為field), 也就是通過 key(用戶ID) + field(屬性標簽) 就可以操作對應屬性數據。 當前HashMap的實現有兩種方式 :當HashMap的成員比較少時Redis為了節省內存會採用類似一維數組的方式來緊湊存儲,而不會採用真正的HashMap結構,這時對應的value的redisObject的encoding為zipmap,當成員數量增大時會自動轉成真正的HashMap,此時redisObject的encoding欄位為int。
常用命令 :hget/hset/hgetall等,具體如下:
等同於java中的 Map<String,List<String>> ,list 底層是一個鏈表,在redis中,插入list中的值,只需要找到list的key即可,而不需要像hash一樣插入兩層的key。 list是一種有序的、可重復的集合。
應用場景 :Redis list的應用場景非常多,也是Redis最重要的數據結構之一,比如twitter的關注列表,粉絲列表等都可以用Redis的list結構來實現;
實現方式 :Redis list的實現為一個 雙向鏈表 ,即可以支持反向查找和遍歷,更方便操作,不過帶來了部分額外的內存開銷,Redis內部的很多實現,包括 發送緩沖隊列 等也都是用的這個數據結構。
常用命令 :lpush/rpush/lpop/rpop/lrange等,具體如下:
性能總結 :
它是一個字元串鏈表,left、right都可以插入添加。
等同於java中的 Map<String,Set<String>> ,Set 是一種無序的,不能重復的集合。並且在redis中,只有一個key它的底層由hashTable實現的,天生去重。
應用場景 :Redis set對外提供的功能與list類似是一個列表的功能,特殊之處在於set是可以自動去重的,當你需要存儲一個列表數據,又不希望出現重復數據時,set是一個很好的選擇,並且 set提供了判斷某個成員是否在一個set集合內的重要介面 ,這個也是list所不能提供的;如保存一些標簽的名字。標簽的名字不可以重復,順序是可以無序的。
實現方式 :set 的內部實現是一個 value永遠為null的HashMap,實際就是通過計算hash的方式來快速排重的,這也是set能提供判斷一個成員是否在集合內的原因。
常用命令 :sadd/spop/smembers/sunion等,具體如下:
ZSet(Sorted Set:有序集合) 每個元素都會關聯一個double類型的分數score,分數允許重復,集合元素按照score排序( 當score相同的時候,會按照被插入的鍵的字典順序進行排序 ),還可以通過 score 的范圍來獲取元素的列表。
應用場景 :Redis sorted set的使用場景與set類似,區別是set不是自動有序的,而sorted set可以 通過用戶額外提供一個優先順序(score)的參數來為成員排序,並且是插入有序的,即自動排序。 當你需要一個有序的並且不重復的集合列表,那麼可以選擇sorted set數據結構,比如twitter 的public timeline可以以發表時間作為score來存儲,這樣獲取時就是自動按時間排好序的。
底層實現 : zset 是 Redis 提供的一個非常特別的數據結構,常用作排行榜等功能,以用戶 id 為 value ,關注時間或者分數作為 score 進行排序。實現機制分別是 zipList 和 skipList 。規則如下:
zipList:滿足以下兩個條件
skipList:不滿足以上兩個條件時使用跳錶、組合了hash和skipList
為什麼用skiplist不用平衡樹?
主要從內存佔用、對范圍查找的支持和實現難易程度這三方面總結的原因。
拓展:mysql為什麼不用跳錶?
常用命令 :zadd/zrange/zrem/zcard等;
官網地址: https://redis.io/commands/geoadd
可以用來推算兩地之間的距離,方圓半徑內的人。
關於經度緯度的限制: https://www.redis.net.cn/order/3685.html
一般我們使用Hyperloglog做基數統計。
什麼是基數?就是一個集合中不重復的數的個數。
集合A:{1,3,5,7,9,7}
集合B:{1,3,5,7,9}
AB集合的基數都是5
應用:統計網站的訪問量(一個人訪問網站很多次仍然算作一次)。
優點:佔用的內存是固定的,找2^64次方個數的基數,只需要12KB內存。
缺點:有0.81%的錯誤率,可以忽略不計
概述: bitmap 存儲的是連續的二進制數字(0 和 1),通過 bitmap, 只需要一個 bit 位來表示某個元素對應的值或者狀態,key 就是對應元素本身 。 我們知道 8 個 bit 可以組成一個 byte,所以 bitmap 本身會極大的節省儲存空間。
應用場景: 適合需要保存狀態信息(比如是否簽到、是否登錄...)並需要進一步對這些信息進行分析的場景。比如用戶簽到情況、活躍用戶情況、用戶行為統計(比如是否點贊過某個視頻)。
針對上面提到的一些場景,這里進行進一步說明。
使用場景一:用戶行為分析 很多網站為了分析你的喜好,需要研究你點贊過的內容。
使用場景二:統計活躍用戶
使用時間作為 key,然後用戶 ID 為 offset,如果當日活躍過就設置為 1
那麼我該如果計算某幾天/月/年的活躍用戶呢(暫且約定,統計時間內只有有一天在線就稱為活躍),有請下一個 redis 的命令
使用場景三:用戶在線狀態
對於獲取或者統計用戶在線狀態,使用 bitmap 是一個節約空間效率又高的一種方法。
只需要一個 key,然後用戶 ID 為 offset,如果在線就設置為 1,不在線就設置為 0。
補充 :
巨人的肩膀:
https://www.cnblogs.com/Small-sunshine/p/11687809.html
https://mp.weixin.qq.com/s/CMu7oXVIKp2s-PXTdMlimA
『肆』 redis的數據是存在內存里嗎
Redis就是基於內存可持久化的key-value資料庫。
1、性能問題,Hashmap存儲大量數知據時需要不斷擴容,Redis支持2的32次方個key,每個key或者value大小最大512M。
2、Hashmap是線程不安道全的,redis因為操作原子性不需要考慮這個。
3、Redis可持久化,Hashmap雖然也可以序列專化,但是Java的序列化因為安全問題說是要廢除了,效率也沒有Redis高,而且Redis有多屬種持久化策略。
4、Redis可擴展可分布式部署。
(4)內存存儲redis擴展閱讀:
redis的存儲分為內存存儲、磁碟存儲和log文件三部分,配置文件中有三個參數對其進行配置。
save seconds updates,save配置,指出在多長時間內,有多少次更新操作,就將數據同步到數據文件。這個可以多個條件配合,比如默認配置文件中的設置,就設置了三個條件。
appendonly yes/no ,appendonly配置,指出是否在每次更新操作後進行日誌記錄,如果不開啟,可能會在斷電時導致一段時間內的數據丟失。因為redis本身同步數據文件是按上面的save條件來同步的,所以有的數據會在一段時間內只存在於內存中。
『伍』 redis怎麼進行內存管理
當mem_fragmentation_ratio>1時,說明used_memory_rss-used_memory多出的部分內存並沒有用於數據存儲,而是被內存碎片所消耗,如果兩者相差很大,說明碎片率嚴重。
當mem_fragmentation_ratio<1時,這種情況一般出現在操作系統把Redis內存交換(Swap)到硬碟導致,出現這種情況時要格外關注,由於硬碟速度遠遠慢於內存,Redis性能會變得很差,甚至僵死。Redis進程內消耗主要包括:自身內存+對象內存+緩沖內存+內存碎片,其中Redis空進程自身內存消耗非常少,通常used_memory_rss在3MB左右,used_memory在800KB左右,一個空的Redis進程消耗內存可以忽略不計。
◆ 緩沖內存主要包括: 客戶端緩沖、復制積壓緩沖區、AOF緩沖區。客戶端緩沖指的是所有接入到Redis伺服器TCP連接的輸入輸出緩沖。輸入緩沖無法控制,最大空間為1G,如果超過將斷開連接。
◆ 復制積壓緩沖區: Redis在2.8版本之後提供了一個可重用的固定大小緩沖區用於實現部分復制功能,根據repl-backlog-size參數控制,默認1MB。對於復制積壓緩沖區整個主節點只有一個,所有的從節點共享此緩沖區,因此可以設置較大的緩沖區空間,如100MB,這部分內存投入是有價值的,可以有效避免全量復制AOF緩沖區:這部分空間用於在Redis重寫期間保存最近的寫入命令,AOF緩沖區空間消耗用戶無法控制,消耗的內存取決於AOF重寫時間和寫入命令量,這部分空間佔用通常很小。
Redis默認的內存分配器採用jemalloc, 可選的分配器還有:glibc、tcmalloc 。內存分配器為了更好地管理和重復利用內存,分配內存策略一般採用固定范圍的內存塊進行分配。內存碎片問題雖然是所有內存服務的通病,但是jemalloc針對碎片化問題專門做了優化,一般不會存在過度碎片化的問題,正常的碎片率(mem_fragmentation_ratio)在1.03左右。
一般以下場景容易出現高內存碎片問題:
● 頻繁做更新操作,例如頻繁對已存在的鍵執行append、setrange等更新操作。大量過期鍵刪除,鍵對象過期刪除後,釋放的空間無法得到充分利用,導致碎片率上升。
出現高內存碎片問題時常見的解決方式如下:
● 數據對齊:在條件允許的情況下盡量做數據對齊,比如數據盡量採用數字類型或者固定長度字元串等,但是這要視具體的業務而定,有些場景無法做到。
● 安全重啟:重啟節點可以做到內存碎片重新整理,因此可以利用高可用架構,如Sentinel或Cluster,將碎片率過高的主節點轉換為從節點,進行安全重啟子進程內存消耗主要指執行AOF/RDB重寫時Redis創建的子進程內存消耗。Redis執行fork操作產生的子進程內存佔用量對外表現為與父進程相同,理論上需要一倍的物理內存來完成重寫操作。
內存大頁機制(Transport Huge Pages,THP),是linux2.6.38後支持的功能,該功能支持2MB的大爺內存分配,默認開啟。在redis.conf中增加了一個新的配置項「disable-thp」來控制THP是否開啟。
子進程內存消耗總結如下:
1、Redis產生的子進程並不需要消耗1倍的父進程內存,實際消耗根據期間寫入命令量決定,但是依然要預留出一些內存防止溢出。
2、需要設置sysctl vm.overcommit_memory=1允許內核可以分配所有的物理內存,防止Redis進程執行fork時因系統剩餘內存不足而失敗。
3、排查當前系統是否支持並開啟THP,如果開啟建議關閉,防止-onwrite期間內存過度消耗。
在日誌信息中可以查看到關於THP的日誌內容, 如下:
Redis使用maxmemory參數限制最大可用內存。限制內存的目的主要有:
1、用於緩存場景,當超出內存上限maxmemory時使用LRU等刪除策略釋放空間。
2、防止所用內存超過伺服器物理內存。
需要注意 ,maxmemory限制的是Redis實際使用的內存量,也就是used_memory統計項對應的內存。由於內存碎片率的存在,實際消耗的內存可能會比maxmemory設置的更大,實際使用時要小心這部分內存溢出。
Redis的內存回收機制主要體現在以下兩個方面:
1、刪除到達過期時間的鍵對象。
2、內存使用達到maxmemory上限時觸發內存溢出控制策略。
刪除過期鍵對象:
Redis所有的鍵都可以設置過期屬性,內部保存在過期字典中。由於進程內保存大量的鍵,維護每個鍵精準的過期刪除機制會導致消耗大量的CPU,對於單線程的Redis來說成本過高,因此Redis採用惰性刪除和定時任務刪除機制實現過期鍵的內存回收。
● 惰性刪除:惰性刪除用於當客戶端讀取帶有超時屬性的鍵時,如果已經超過鍵設置的過期時間,會執行刪除操作並返回空,這種策略是出於節省CPU成本考慮,不需要單獨維護TTL鏈表來處理過期鍵的刪除。但是單獨用這種方式存在內存泄露的問題,當過期鍵一直沒有訪問將無法得到及時刪除,從而導致內存不能及時釋放。正因為如此,Redis還提供另一種定時任務刪除機製作為惰性刪除的補充。
● 定時任務刪除:Redis內部維護一個定時任務,默認每秒運行10次(通過配置hz控制)。定時任務中刪除過期鍵邏輯採用了自適應演算法,根據鍵的過期比例、使用快慢兩種速率模式回收鍵, 流程如圖所示。
當Redis所用內存達到maxmemory上限時會觸發相應的溢出控制策略。具體策略受maxmemory-policy參數控制,Redis支持6種策略, 如下所示:
1)noeviction: 默認策略,不會刪除任何數據,拒絕所有寫入操作並返回客戶端錯誤信息(error)OOM command not allowed when used memory,此時Redis只響應讀操作。
2)volatile-lru: 根據LRU演算法刪除設置了超時屬性(expire)的鍵,直到騰出足夠空間為止。如果沒有可刪除的鍵對象,回退到noeviction策略。
3)allkeys-lru: 根據LRU演算法刪除鍵,不管數據有沒有設置超時屬性,直到騰出足夠空間為止。
4)allkeys-random: 隨機刪除所有鍵,直到騰出足夠空間為止。
5)volatile-random: 隨機刪除過期鍵,直到騰出足夠空間為止。
6)volatile-ttl: 根據鍵值對象的ttl屬性,刪除最近將要過期數據。如果沒有,回退到noeviction策略。
『陸』 Redis是將數據儲存進內存嗎
Redis就是基於內存可持久化的key-value資料庫。
性能問題,Hashmap存儲大量數據時需要不斷擴容,Redis支持2的32次方個key,每個key或者value大小最大512M;
Hashmap是線程不安全的,redis因為操作原子性不需要考慮這個;
Redis可持久化,Hashmap雖然也可以序列化,但是Java的序列化因為安全問題說是要廢除了,效率也沒有Redis高,而且Redis有多種持久化策略;
Redis可擴展可分布式部署
『柒』 Redis的內存優化
一. redisObject對象
二. 縮減鍵值對象
三. 共享對象池
四. 字元串優化
五. 編碼優化
六. 控制key的數量
Redis存儲的所有值對象在內部定義為redisObject結構體,內部結構如下圖所示。
表示當前對象使用的數據類型,Redis主要支持5種數據類型:string,hash,list,set,zset。可以使用type {key}命令查看對象所屬類型,type命令返回的是值對象類型,鍵都是string類型。
表示Redis內部編碼類型,encoding在Redis內部使用,代表當前對象內部採用哪種數據結構實現。理解Redis內部編碼方式對於優化內存非常重要 ,同一個對象採用不同的編碼實現內存佔用存在明顯差異,具體細節見之後編碼優化部分。
記錄對象最後一次被訪問的時間,當配置了 maxmemory和maxmemory-policy=volatile-lru | allkeys-lru 時, 用於輔助LRU演算法刪除鍵數據。可以使用object idletime {key}命令在不更新lru欄位情況下查看當前鍵的空閑時間。
記錄當前對象被引用的次數,用於通過引用次數回收內存,當refcount=0時,可以安全回收當前對象空間。使用object refcount {key}獲取當前對象引用。當對象為整數且范圍在[0-9999]時,Redis可以使用共享對象的方式來節省內存。具體細節見之後共享對象池部分。
與對象的數據內容相關,如果是整數直接存儲數據,否則表示指向數據的指針。Redis在3.0之後對值對象是字元串且長度<=39位元組的數據,內部編碼為embstr類型,字元串sds和redisObject一起分配,從而只要一次內存操作。
降低Redis內存使用最直接的方式就是縮減鍵(key)和值(value)的長度。
其中java-built-in-serializer表示JAVA內置序列化方式,更多數據見jvm-serializers項目: https://github.com/eishay/jvm-serializers/wiki,其它語言也有各自對應的高效序列化工具。
值對象除了存儲二進制數據之外,通常還會使用通用格式存儲數據比如:json,xml等作為字元串存儲在Redis中。這種方式優點是方便調試和跨語言,但是同樣的數據相比位元組數組所需的空間更大,在內存緊張的情況下,可以使用通用壓縮演算法壓縮json,xml後再存入Redis,從而降低內存佔用,例如使用GZIP壓縮後的json可降低約60%的空間。
對象共享池指Redis內部維護[0-9999]的整數對象池。創建大量的整數類型redisObject存在內存開銷,每個redisObject內部結構至少佔16位元組,甚至超過了整數自身空間消耗。所以Redis內存維護一個[0-9999]的整數對象池,用於節約內存。 除了整數值對象,其他類型如list,hash,set,zset內部元素也可以使用整數對象池。因此開發中在滿足需求的前提下,盡量使用整數對象以節省內存。
整數對象池在Redis中通過變數REDIS_SHARED_INTEGERS定義,不能通過配置修改。可以通過object refcount 命令查看對象引用數驗證是否啟用整數對象池技術,如下:
設置鍵foo等於100時,直接使用共享池內整數對象,因此引用數是2,再設置鍵bar等於100時,引用數又變為3,如下圖所示。
使用整數對象池究竟能降低多少內存?讓我們通過測試來對比對象池的內存優化效果,如下表所示。
使用共享對象池後,相同的數據內存使用降低30%以上。可見當數據大量使用[0-9999]的整數時,共享對象池可以節約大量內存。需要注意的是對象池並不是只要存儲[0-9999]的整數就可以工作。當設置maxmemory並啟用LRU相關淘汰策略如:volatile-lru,allkeys-lru時,Redis禁止使用共享對象池,測試命令如下:
LRU演算法需要獲取對象最後被訪問時間,以便淘汰最長未訪問數據,每個對象最後訪問時間存儲在redisObject對象的lru欄位。對象共享意味著多個引用共享同一個redisObject,這時lru欄位也會被共享,導致無法獲取每個對象的最後訪問時間。如果沒有設置maxmemory,直到內存被用盡Redis也不會觸發內存回收,所以共享對象池可以正常工作。
綜上所述,共享對象池與maxmemory+LRU策略沖突,使用時需要注意。 對於ziplist編碼的值對象,即使內部數據為整數也無法使用共享對象池,因為ziplist使用壓縮且內存連續的結構,對象共享判斷成本過高,ziplist編碼細節後面內容詳細說明。
首先整數對象池復用的幾率最大,其次對象共享的一個關鍵操作就是判斷相等性,Redis之所以只有整數對象池,是因為整數比較演算法時間復雜度為O(1),只保留一萬個整數為了防止對象池浪費。如果是字元串判斷相等性,時間復雜度變為O(n),特別是長字元串更消耗性能(浮點數在Redis內部使用字元串存儲)。對於更復雜的數據結構如hash,list等,相等性判斷需要O(n2)。對於單線程的Redis來說,這樣的開銷顯然不合理,因此Redis只保留整數共享對象池。
字元串對象是Redis內部最常用的數據類型。所有的鍵都是字元串類型, 值對象數據除了整數之外都使用字元串存儲。比如執行命令:lpush cache:type 「redis」 「memcache」 「tair」 「levelDB」 ,Redis首先創建」cache:type」鍵字元串,然後創建鏈表對象,鏈表對象內再包含四個字元串對象,排除Redis內部用到的字元串對象之外至少創建5個字元串對象。可見字元串對象在Redis內部使用非常廣泛,因此深刻理解Redis字元串對於內存優化非常有幫助:
Redis沒有採用原生C語言的字元串類型而是自己實現了字元串結構,內部簡單動態字元串(simple dynamic string),簡稱SDS。結構下圖所示。
Redis自身實現的字元串結構有如下特點:
因為字元串(SDS)存在預分配機制,日常開發中要小心預分配帶來的內存浪費,例如下表的測試用例。
從測試數據可以看出,同樣的數據追加後內存消耗非常嚴重,下面我們結合圖來分析這一現象。階段1每個字元串對象空間佔用如下圖所示。
階段1插入新的字元串後,free欄位保留空間為0,總佔用空間=實際佔用空間+1位元組,最後1位元組保存『\0』標示結尾,這里忽略int類型len和free欄位消耗的8位元組。在階段1原有字元串上追加60位元組數據空間佔用如下圖所示。
追加操作後字元串對象預分配了一倍容量作為預留空間,而且大量追加操作需要內存重新分配,造成內存碎片率(mem_fragmentation_ratio)上升。直接插入與階段2相同數據的空間佔用,如下圖所示。
階段3直接插入同等數據後,相比階段2節省了每個字元串對象預分配的空間,同時降低了碎片率。
字元串之所以採用預分配的方式是防止修改操作需要不斷重分配內存和位元組數據拷貝。但同樣也會造成內存的浪費。字元串預分配每次並不都是翻倍擴容,空間預分配規則如下:
字元串重構:指不一定把每份數據作為字元串整體存儲,像json這樣的數據可以使用hash結構,使用二級結構存儲也能幫我們節省內存。同時可以使用hmget,hmset命令支持欄位的部分讀取修改,而不用每次整體存取。例如下面的json數據:
分別使用字元串和hash結構測試內存表現,如下表所示。
根據測試結構,第一次默認配置下使用hash類型,內存消耗不但沒有降低反而比字元串存儲多出2倍,而調整hash-max-ziplist-value=66之後內存降低為535.60M。因為json的videoAlbumPic屬性長度是65,而hash-max-ziplist-value默認值是64,Redis採用hashtable編碼方式,反而消耗了大量內存。調整配置後hash類型內部編碼方式變為ziplist,相比字元串更省內存且支持屬性的部分操作。下一節將具體介紹ziplist編碼優化細節。
Redis對外提供了string,list,hash,set,zet等類型,但是Redis內部針對不同類型存在編碼的概念,所謂編碼就是具體使用哪種底層數據結構來實現。編碼不同將直接影響數據的內存佔用和讀寫效率。使用object encoding {key}命令獲取編碼類型。如下:
Redis針對每種數據類型(type)可以採用至少兩種編碼方式來實現,下表表示type和encoding的對應關系。
了解編碼和類型對應關系之後,我們不禁疑惑Redis為什麼需要對一種數據結構實現多種編碼方式?
主要原因是Redis作者想通過不同編碼實現效率和空間的平衡。比如當我們的存儲只有10個元素的列表,當使用雙向鏈表數據結構時,必然需要維護大量的內部欄位如每個元素需要:前置指針,後置指針,數據指針等,造成空間浪費,如果採用連續內存結構的壓縮列表(ziplist),將會節省大量內存,而由於數據長度較小,存取操作時間復雜度即使為O(n2)性能也可滿足需求。
Redis內存優化
編碼類型轉換在Redis寫入數據時自動完成,這個轉換過程是不可逆的,轉換規則只能從小內存編碼向大內存編碼轉換。例如:
以上命令體現了list類型編碼的轉換過程,其中Redis之所以不支持編碼回退,主要是數據增刪頻繁時,數據向壓縮編碼轉換非常消耗CPU,得不償失。以上示例用到了list-max-ziplist-entries參數,這個參數用來決定列表長度在多少范圍內使用ziplist編碼。當然還有其它參數控制各種數據類型的編碼,如下表所示:
掌握編碼轉換機制,對我們通過編碼來優化內存使用非常有幫助。下面以hash類型為例,介紹編碼轉換的運行流程,如下圖所示。
理解編碼轉換流程和相關配置之後,可以使用config set命令設置編碼相關參數來滿足使用壓縮編碼的條件。對於已經採用非壓縮編碼類型的數據如hashtable,linkedlist等,設置參數後即使數據滿足壓縮編碼條件,Redis也不會做轉換,需要重啟Redis重新載入數據才能完成轉換。
ziplist編碼主要目的是為了節約內存,因此所有數據都是採用線性連續的內存結構。ziplist編碼是應用范圍最廣的一種,可以分別作為hash、list、zset類型的底層數據結構實現。首先從ziplist編碼結構開始分析,它的內部結構類似這樣:<….>。一個ziplist可以包含多個entry(元素),每個entry保存具體的數據(整數或者位元組數組),內部結構如下圖所示。
ziplist結構欄位含義:
根據以上對ziplist欄位說明,可以分析出該數據結構特點如下:
下面通過測試展示ziplist編碼在不同類型中內存和速度的表現,如下表所示。
測試數據採用100W個36位元組數據,劃分為1000個鍵,每個類型長度統一為1000。從測試結果可以看出:
intset編碼是集合(set)類型編碼的一種,內部表現為存儲有序,不重復的整數集。當集合只包含整數且長度不超過set-max-intset-entries配置時被啟用。執行以下命令查看intset表現:
以上命令可以看出intset對寫入整數進行排序,通過O(log(n))時間復雜度實現查找和去重操作,intset編碼結構如下圖所示。
intset的欄位結構含義:
根據以上測試結果發現intset表現非常好,同樣的數據內存佔用只有不到hashtable編碼的十分之一。intset數據結構插入命令復雜度為O(n),查詢命令為O(log(n)),由於整數佔用空間非常小,所以在集合長度可控的基礎上,寫入命令執行速度也會非常快,因此當使用整數集合時盡量使用intset編碼。上表測試第三行把ziplist-hash類型也放入其中,主要因為intset編碼必須存儲整數,當集合內保存非整數數據時,無法使用intset實現內存優化。這時可以使用ziplist-hash類型對象模擬集合類型,hash的field當作集合中的元素,value設置為1位元組佔位符即可。使用ziplist編碼的hash類型依然比使用hashtable編碼的集合節省大量內存。
當使用Redis存儲大量數據時,通常會存在大量鍵,過多的鍵同樣會消耗大量內存。Redis本質是一個數據結構伺服器,它為我們提供多種數據結構,如hash,list,set,zset 等結構。使用Redis時不要進入一個誤區,大量使用get/set這樣的API,把Redis當成Memcached使用。對於存儲相同的數據內容利用Redis的數據結構降低外層鍵的數量,也可以節省大量內存。如下圖所示,通過在客戶端預估鍵規模,把大量鍵分組映射到多個hash結構中降低鍵的數量。
hash結構降低鍵數量分析:
通過這個測試數據,可以說明:
關於hash鍵和field鍵的設計:
使用hash結構控制鍵的規模雖然可以大幅降低內存,但同樣會帶來問題,需要提前做好規避處理。如下:
本文主要講解Redis內存優化技巧,Redis的數據特性是」ALL IN MEMORY」,優化內存將變得非常重要。對於內存優化建議讀者先要掌握Redis內存存儲的特性比如字元串,壓縮編碼,整數集合等,再根據數據規模和所用命令需求去調整,從而達到空間和效率的最佳平衡。建議使用Redis存儲大量數據時,把內存優化環節加入到前期設計階段,否則數據大幅增長後,開發人員需要面對重新優化內存所帶來開發和數據遷移的雙重成本。當Redis內存不足時,首先考慮的問題不是加機器做水平擴展,應該先嘗試做內存優化。當遇到瓶頸時,再去考慮水平擴展。即使對於集群化方案,垂直層面優化也同樣重要,避免不必要的資源浪費和集群化後的管理成本。
『捌』 redis內存滿了怎麼辦
redis內存滿了解決方法:
1,增加內存。
2,使用內存淘汰策略。
3,Redis集群。
重點介紹下2、3:
第二點:
我們知道,redis設置配置文件的maxmemory參數,可以控制其最大可用內存大小(位元組)。
那麼當所需內存,超過maxmemory怎麼辦?
這個時候就該配置文件中的maxmemory-policy出場了。
其默認值是noeviction。
下面我將列出當可用內存不足時,刪除redis鍵具有的淘汰規則。
規則說明:
1、volatile-lru
使用LRU演算法刪除一個鍵(只對設置了生存時間的鍵)
2、allkeys-lru
使用LRU演算法刪除一個鍵
3、volatile-random
隨機刪除一個鍵(只對設置了生存時間的鍵)
4、allkeys-random
隨機刪除一個鍵
5、volatile-ttl
刪除生存時間最近的一個鍵
6、noeviction
不刪除鍵,只返回錯誤
LRU演算法,least RecentlyUsed,最近最少使用演算法。也就是說默認刪除最近最少使用的鍵。
但是一定要注意一點!redis中並不會准確的刪除所有鍵中最近最少使用的鍵,而是隨機抽取3個鍵,刪除這三個鍵中最近最少使用的鍵。
那麼3這個數字也是可以設置的,對應位置是配置文件中的maxmeory-samples.
三、集群怎麼做
Redis僅支持單實例,內存一般最多10~20GB。對於內存動輒100~200GB的系統,就需要通過集群來支持了。
Redis集群有三種方式:客戶端分片、代理分片、RedisCluster(在之後一篇文章詳細說一下。)
1、客戶端分片
通過業務代碼自己實現路由
優勢:可以自己控制分片演算法、性能比代理的好
劣勢:維護成本高、擴容/縮容等運維操作都需要自己研發
2、代理分片
代理程序接收到來自業務程序的數據請求,根據路由規則,將這些請求分發給正確的Redis實例並返回給業務程序。使用類似Twemproxy、Codis等中間件實現。
優勢:運維方便、程序不用關心如何鏈接Redis實例
劣勢:會帶來性能消耗(大概20%)、無法平滑擴容/縮容,需要執行腳本遷移數據,不方便(Codis在Twemproxy基礎上優化並實現了預分片來達到Auto Rebalance)。
3、Redis Cluster
優勢:官方集群解決方案、無中心節點,和客戶端直連,性能較好
劣勢:方案太重、無法平滑擴容/縮容,需要執行相應的腳本,不方便、太新,沒有相應成熟的解決案例
『玖』 Redis的主要功能
緩存:這應該是 Redis 最主要的功能了,也是大型網站必備機制,合理地使用緩存不僅可以加 快數據的訪問速度,而且能夠有效地降低後端數據源的壓力。共享Session:對於一些依賴 session 功能的服務來說,如果需要從單機變成集群的話,可以選擇 redis 來統一管理 session。消息隊列系統:消息隊列系統可以說是一個大型網站的必備基礎組件,因為其具有業務 解耦、非實時業務削峰等特性。Redis提供了發布訂閱功能和阻塞隊列的功 能,雖然和專業的消息隊列比還不夠足夠強大,但是對於一般的消息隊列功 能基本可以滿足。比如在分布式爬蟲系統中,使用 redis 來統一管理 url隊列。分布式鎖:在分布式服務中。可以利用Redis的setnx功能來編寫分布式的鎖,雖然這個可能不是太常用。 當然還有諸如排行榜、點贊功能都可以使用 Redis 來實現,但是 Redis 也不是什麼都可以做,比如數據量特別大時,不適合 Redis,我們知道 Redis 是基於內存的,雖然內存很便宜,但是如果你每天的數據量特別大,比如幾億條的用戶行為日誌數據,用 Redis 來存儲的話,成本相當的高。
『拾』 Redis內存滿了會怎麼樣
1、通過配置文件配置
通過在Redis安裝目錄下面的redis.conf配置文件中添加以下配置設置內存大小
redis的配置文件不一定使用的是安裝目錄下面的redis.conf文件,啟動redis服務的時候是可以傳一個參數指定redis的配置文件的
2、通過命令修改
Redis支持運行時通過命令動態修改內存大小
既然可以設置Redis最大佔用內存大小,那麼配置的內存就有用完的時候。那在內存用完的時候,還繼續往Redis裡面添加數據不就沒內存可用了嗎?
實際上Redis定義了幾種策略用來處理這種情況:
當使用volatile-lru、volatile-random、volatile-ttl這三種策略時,如果沒有key可以被淘汰,則和noeviction一樣返回錯誤
獲取當前內存淘汰策略:
通過配置文件設置淘汰策略(修改redis.conf文件):
通過命令修改淘汰策略:
近似LRU演算法
Redis使用的是近似LRU演算法,它跟常規的LRU演算法還不太一樣。近似LRU演算法通過隨機采樣法淘汰數據,每次隨機出5(默認)個key,從裡面淘汰掉最近最少使用的key。
可以通過maxmemory-samples參數修改采樣數量:
例:maxmemory-samples 10
maxmenory-samples配置的越大,淘汰的結果越接近於嚴格的LRU演算法
Redis為了實現近似LRU演算法,給每個key增加了一個額外增加了一個24bit的欄位,用來存儲該key最後一次被訪問的時間。
Redis3.0對近似LRU的優化
Redis3.0對近似LRU演算法進行了一些優化。新演算法會維護一個候選池(大小為16),池中的數據根據訪問時間進行排序,第一次隨機選取的key都會放入池中,隨後每次隨機選取的key只有在訪問時間小於池中最小的時間才會放入池中,直到候選池被放滿。當放滿後,如果有新的key需要放入,則將池中最後訪問時間最大(最近被訪問)的移除。
當需要淘汰的時候,則直接從池中選取最近訪問時間最小(最久沒被訪問)的key淘汰掉就行。