1、降低redis內存占用的優點
1、有助於減少創建快照和加載快照所用的時間
2、提升載入AOF文件和重寫AOF文件時的效率
3、縮短從服務器進行同步所需的時間
4、無需添加額外的硬件就可以讓redis存貯更多的數據
2、短結構
Redis為列表、集合、散列、有序集合提供了一組配置選項,這些選項可以讓redis以更節約的方式存儲較短的結構。
2.1、ziplist壓縮列表(列表、散列、有續集和)
通常情況下使用的存儲方式
當列表、散列、有序集合的長度較短或者體積較小的時候,redis將會采用一種名為ziplist的緊湊存儲方式來存儲這些結構。
ziplist是列表、散列、有序集合這三種不同類型的對象的一種非結構化表示,它會以序列化的方式存儲數據,這些序列化的數據每次被讀取的時候都需要進行解碼,每次寫入的時候也要進行編碼。
雙向列表與壓縮列表的區別:
為了了解壓縮列表比其他數據結構更加節約內存,我們以列表結構為例進行深入研究。
典型的雙向列表
1、在典型雙向列表里面,每個值都都會有一個節點表示。每個節點都會帶有指向鏈表前一個節點和后一個節點的指針,以及一個指向節點包含的字符串值的指針。
2、每個節點包含的字符串值都會分為三部分進行存儲。包括字符串長度、字符串值中剩余可用字節數量、以空字符結尾的字符串本身。
例子:
假若一個某個節點存儲了’abc’字符串,在32位的平台下保守估計需要21個字節的額外開銷(三個指針+兩個int+空字符即:3*4+2*4+1=21)
由例子可知存儲一個3字節字符串就需要付出至少21個字節的額外開銷。
ziplist
壓縮列表是由節點組成的序列,每個節點包含兩個長度和一個字符串。第一個長度記錄前一個節點的長度(用於對壓縮列表從后向前遍歷);第二個長度是記錄本當前點的長度;被存儲的字符串。
例子:
存儲字符串’abc’,兩個長度都可以用1字節來存儲,因此所帶來的額外開銷為2字節(兩個長度即1+1=2)
結論:
壓縮列表是通過避免存儲額外的指針和元數據,從而達到降低額外的開銷。
配置:
1 #list2 list-max-ziplist-entries 51 2 #表示允許包含的最大元素數量 3 list-max-ziplist-value 64 #表示壓縮節點允許存儲的最大體積 4 #hash #當超過任一限制后,將不會使用ziplist方式進行存儲 5 hash-max-ziplist-entries 512 6 hash-max-ziplist-value 64 7 #zset 8 zset-max-ziplist-entries 128 9 zset-max-ziplist-value 64
測試list:
1、建立test.php文件
1 #test.php 2 <?php 3 $redis=new Redis(); 4 $redis->connect('192.168.95.11','6379'); 5 for ($i=0; $i<512 ; $i++) 6 { 7 $redis->lpush('test-list',$i.'-test-list'); #往test-list推入512條數據 8 } 9 ?>
此時的test-list中含有512條數據,沒有超除配置文件中的限制
2、往test-list中再推入一條數據
此時test-list含有513條數據,大於配置文件中限制的512條,索引將放棄ziplist存儲方式,采用其原來的linkedlist存儲方式,散列與有序集合同理。
2.2、intset整數集合(集合)
前提條件,集合中包含的所有member都可以被解析為十進制整數。
以有序數組的方式存儲集合不僅可以降低內存消耗,還可以提升集合操作的執行速度。
配置:
1 set-max-intset-entries 512 #限制集合中member個數,超出則不采取i那個tset存儲
測試:
建立test.php文件
1 #test.php 2 <?php 3 $redis=new Redis(); 4 $redis->connect('192.168.95.11','6379'); 5 for ($i=0; $i<512 ; $i++) 6 { 7 $redis->sadd('test-set',$i); #給集合test-set插入512個member 8 } 9 ?> 10
2.3、性能問題
不管列表、散列、有序集合、集合,當超出限制的條件后,就會轉換為更為典型的底層結構類型。因為隨着緊湊結構的體積不斷變大,操作這些結構的速度將會變得越來越慢。
測試:
#將采用list進行代表性測試
測試思路:
1、在默認配置下往test-list推入50000條數據,查看所需時間;接着在使用rpoplpush將test-list數據全部推入到新列表list-new中,查看所需時間
2、修改配置,list-max-ziplist-entries 100000,再執行上面的同樣操作
3、對比時間,得出結論
默認配置下測試:
1、插入數據,查看時間
1 #test1.php 2 <?php 3 header("content-type: text/html;charset=utf8;"); 4 $redis=new Redis(); 5 $redis->connect('192.168.95.11','6379'); 6 $start=time(); 7 for ($i=0; $i<50000 ; $i++) 8 { 9 $redis->lpush('test-list',$i.'-aaaassssssddddddkkk'); 10 } 11 $end=time(); 12 echo "插入耗時為:".($end-$start).'s'; 13 ?>
結果耗時4秒
2、執行相應命令,查看耗時
1 #test2.php 2 <?php 3 header("content-type: text/html;charset=utf8;"); 4 $redis=new Redis(); 5 $redis->connect('192.168.95.11','6379'); 6 $start=time(); 7 $num=0; 8 while($redis->rpoplpush('test-list','test-new')) 9 { 10 $num+=1; 11 } 12 echo '執行次數為:'.$num."<br/>"; 13 $end=time(); 14 echo "耗時為:".($end-$start).'s'; 15 ?>
更改配置文件下測試
1、先修改配置文件
list-max-ziplist-entries 100000 #將這個值修改大一點,可以更好的凸顯對性能的影響
list-max-ziplist-value 64 #此值可不做修改
2、插入數據
執行test1.php
結果為:耗時12s
3、執行相應命令,查看耗時
執行test2.php
結果為:執行次數:50000,耗時12s
結論:
在本機中執行測試50000條數據就相差8s,若在高並發下,長壓縮列表和大整數集合將起不到任何的優化,反而使得性能降低。
3、片結構
分片的本質就是基於簡單的規則將數據划分為更小的部分,然后根據數據所屬的部分來決定將數據發送到哪個位置上。很多數據庫使用這種技術來擴展存儲空間,並提高自己所能處理的負載量。
結合前面講到的,我們不難發現分片結構對於redis的重要意義。因此我們需要在配置文件中關於ziplist以及intset的相關配置做出適當的調整。
3.1、分片式散列
#ShardHash.class.php
散列分片主要是根據基礎鍵以及散列包含的鍵計算出分片鍵ID,然后再與基礎鍵拼接成一個完整的分片鍵。在執行hset與hget以及大部分hash命令時,都需要先將key(field)通過shardKey方法處理,得到分片鍵才能夠進行下一步操作。
回到頂部
3.2、分片式集合
如何構造分片式集合才能夠讓它更節省內存,性能更加強大呢?主要的思路就是,將集合里面的存儲的數據盡量在不改變其原有功能的情況下轉換成可以被解析為十進制的數據。根據前面所講到的,當集合中的所有成員都能夠被解析為十進制數據時,將會采用intset存儲方式,這不僅能夠節省內存,而且還可以提高響應的性能。
4、將信息打包轉換成存儲字節
結合前面所講的分片技術,采用string分片結構為大量連續的ID用戶存儲信息。
使用定長字符串,為每一個ID分配n個字節進行存儲相應的信息。