優化的一些建議
1、盡量使用短的key
當然在精簡的同時,不要完了key的“見名知意”。對於value有些也可精簡,比如性別使用0、1。
**2、避免使用keys ***
keys *, 這個命令是阻塞的,即操作執行期間,其它任何命令在你的實例中都無法執行。當redis中key數據量小時到無所謂,數據量大就很糟糕了。所以我們應該避免去使用這個命令。可以去使用SCAN,來代替。
3、在存到Redis之前先把你的數據壓縮下
redis為每種數據類型都提供了兩種內部編碼方式,在不同的情況下redis會自動調整合適的編碼方式。
4、設置 key 有效期
我們應該盡可能的利用key有效期。比如一些臨時數據(短信校驗碼),過了有效期Redis就會自動為你清除!
5、選擇回收策略(maxmemory-policy)
當 Redis 的實例空間被填滿了之后,將會嘗試回收一部分key。根據你的使用方式,強烈建議使用 volatile-lru(默認) 策略——前提是你對key已經設置了超時。但如果你運行的是一些類似於 cache 的東西,並且沒有對 key 設置超時機制,可以考慮使用 allkeys-lru 回收機制,具體講解查看 。maxmemory-samples 3 是說每次進行淘汰的時候 會隨機抽取3個key 從里面淘汰最不經常使用的(默認選項)
maxmemory-policy 六種方式 :
- volatile-lru:只對設置了過期時間的key進行LRU(默認值)
- allkeys-lru : 是從所有key里 刪除 不經常使用的key
- volatile-random:隨機刪除即將過期key
- allkeys-random:隨機刪除
- volatile-ttl : 刪除即將過期的
- noeviction : 永不過期,返回錯誤
6、使用bit位級別操作和byte字節級別操作來減少不必要的內存使用。
- bit位級別操作:GETRANGE, SETRANGE, GETBIT and SETBIT
- byte字節級別操作:GETRANGE and SETRANGE
7、盡可能地使用hashes哈希存儲。
8、當業務場景不需要數據持久化時,關閉所有的持久化方式可以獲得最佳的性能。
9、想要一次添加多條數據的時候可以使用管道。
10、限制redis的內存大小(64位系統不限制內存,32位系統默認最多使用3GB內存)
數據量不可預估,並且內存也有限的話,盡量限制下redis使用的內存大小,這樣可以避免redis使用swap分區或者出現OOM錯誤。(使用swap分區,性能較低,如果限制了內存,當到達指定內存之后就不能添加數據了,否則會報OOM錯誤。可以設置maxmemory-policy,內存不足時刪除數據。)
11、SLOWLOG [get/reset/len]
- slowlog-log-slower-than 它決定要對執行時間大於多少微秒(microsecond,1秒 = 1,000,000 微秒)的命令進行記錄。
- slowlog-max-len 它決定 slowlog 最多能保存多少條日志,當發現redis性能下降的時候可以查看下是哪些命令導致的。
優化實例分析
管道性能測試
redis的管道功能在命令行中沒有,但是redis是支持管道的,在java的客戶端(jedis)中是可以使用的:
示例代碼
//注:具體耗時,和自身電腦有關(博主是在虛擬機中運行的數據)
/**
* 不使用管道初始化1W條數據
* 耗時:3079毫秒
* @throws Exception
*/
@Test
public void NOTUsePipeline() throws Exception {
Jedis jedis = JedisUtil.getJedis();
long start_time = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
jedis.set("aa_"+i, i+"");
}
System.out.println(System.currentTimeMillis()-start_time);
}
/**
* 使用管道初始化1W條數據
* 耗時:255毫秒
* @throws Exception
*/
@Test
public void usePipeline() throws Exception {
Jedis jedis = JedisUtil.getJedis();
long start_time = System.currentTimeMillis();
Pipeline pipelined = jedis.pipelined();
for (int i = 0; i < 10000; i++) {
pipelined.set("cc_"+i, i+"");
}
pipelined.sync();//執行管道中的命令
System.out.println(System.currentTimeMillis()-start_time);
}
hash的應用
示例:我們要存儲一個用戶信息對象數據,包含以下信息:
key為用戶ID,value為用戶對象(姓名,年齡,生日等)如果用普通的key/value結構來存儲,主要有以下2種存儲方式:
- 將用戶ID作為查找key,把其他信息封裝成一個對象以序列化的方式存儲
缺點:增加了序列化/反序列化的開銷,引入復雜適應系統(Complex adaptive system,簡稱CAS)修改其中一項信息時,需要把整個對象取回,並且修改操作需要對並發進行保護。
- 用戶信息對象有多少成員就存成多少個key-value對
雖然省去了序列化開銷和並發問題,但是用戶ID為重復存儲。
- Redis提供的Hash很好的解決了這個問題,提供了直接存取這個Map成員的接口。Key仍然是用戶ID, value是一個Map,這個Map的key是成員的屬性名,value是屬性值。( 內部實現:Redis Hashd的Value內部有2種不同實現,Hash的成員比較少時Redis為了節省內存會采用類似一維數組的方式來緊湊存儲,而不會采用真正的HashMap結構,對應的value redisObject的encoding為zipmap,當成員數量增大時會自動轉成真正的HashMap,此時encoding為ht )。
Instagram內存優化
Instagram可能大家都已熟悉,當前火熱的拍照App,月活躍用戶3億。四年前Instagram所存圖片3億多時需要解決一個問題:想知道每一張照片的作者是誰(通過圖片ID反查用戶UID),並且要求查詢速度要相當的塊,如果把它放到內存中使用String結構做key-value:
HSET "mediabucket:1155" "1155315" "939"
HGET "mediabucket:1155" "1155315"
"939"
測試:1百萬數據會用掉70MB內存,3億張照片就會用掉21GB的內存。當時(四年前)最好是一台EC2的 high-memory 機型就能存儲(17GB或者34GB的,68GB的太浪費了),想把它放到16G機型中還是不行的。
Instagram的開發者向Redis的開發者之一Pieter Noordhuis詢問優化方案,得到的回復是使用Hash結構。具體的做法就是將數據分段,每一段使用一個Hash結構存儲.
由於Hash結構會在單個Hash元素在不足一定數量時進行壓縮存儲,所以可以大量節約內存。這一點在上面的String結構里是不存在的。而這個一定數量是由配置文件中的hash-zipmap-max-entries參數來控制的。經過實驗,將hash-zipmap-max-entries設置為1000時,性能比較好,超過1000后HSET命令就會導致CPU消耗變得非常大。
HSET "mediabucket:1155" "1155315" "939"
HGET "mediabucket:1155" "1155315"
"939"
測試:1百萬消耗16MB的內存。總內存使用也降到了5GB。當然我們還可以優化,去掉mediabucket:key長度減少了12個字節。
HSET "1155" "315" "939"
HGET "1155" "315"
"939"
啟動時WARNING優化
在我們啟動redis時,默認會出現如下三個警告:
- 一、修改linux中TCP監聽的最大容納數量
WARNING: The TCP backlog setting of 511 cannot be enforced because
/proc/sys/net/core/somaxconn is set to the lower value of 128.
在高並發環境下你需要一個高backlog值來避免慢客戶端連接問題。注意Linux內核默默地將這個值減小到/proc/sys/net/core/somaxconn的值,所以需要確認增大somaxconn和tcp_max_syn_backlog兩個值來達到想要的效果。
echo 511 > /proc/sys/net/core/somaxconn
注意:這個參數並不是限制redis的最大鏈接數。如果想限制redis的最大連接數需要修改maxclients,默認最大連接數為10000
- 二、修改linux內核內存分配策略
錯誤日志:WARNING overcommit_memory is set to 0! Background save may fail under low memory condition.
To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or
run the command 'sysctl vm.overcommit_memory=1'
原因:
redis在備份數據的時候,會fork出一個子進程,理論上child進程所占用的內存和parent是一樣的,比如parent占用的內存為8G,這個時候也要同樣分配8G的內存給child,如果內存無法負擔,往往會造成redis服務器的down機或者IO負載過高,效率下降。所以內存分配策略應該設置為 1(表示內核允許分配所有的物理內存,而不管當前的內存狀態如何)。
內存分配策略有三種
可選值:0、1、2。
-
0, 表示內核將檢查是否有足夠的可用內存供應用進程使用;如果有足夠的可用內存,內存申請允許;否則,內存申請失敗,並把錯誤返回給應用進程。
-
1, 不管需要多少內存,都允許申請。
-
2, 只允許分配物理內存和交換內存的大小(交換內存一般是物理內存的一半)。
-
三、關閉Transparent Huge Pages(THP)
THP會造成內存鎖影響redis性能,建議關閉
Transparent HugePages :用來提高內存管理的性能
Transparent Huge Pages在32位的RHEL 6中是不支持的
執行命令 echo never > /sys/kernel/mm/transparent_hugepage/enabled
把這條命令添加到這個文件中/etc/rc.local
原文出處
xiaoxiaomo -> http://blog.xiaoxiaomo.com/2016/05/02/Redis-%E4%BC%98%E5%8C%96%E8%AF%A6%E8%A7%A3/