本文開始會講解一下redis的基本優化,而後會舉一些優化示例代碼或實例。最後講解一下,默認啓動redis時,所報的一些警示錯誤。java
1、優化的一些建議linux
一、儘可能使用短的keyredis
固然在精簡的同時,不要爲了key的「見名知意」。對於value有些也可精簡,好比性別使用0、1。數組
二、避免使用keys *服務器
keys *, 這個命令是阻塞的,即操做執行期間,其它任何命令在你的實例中都沒法執行。當redis中key數據量小時到無所謂,數據量大就很糟糕了。因此咱們應該避免去使用這個命令。能夠去使用SCAN,來代替。併發
三、在存到Redis以前先把你的數據壓縮下dom
redis爲每種數據類型都提供了兩種內部編碼方式,在不一樣的狀況下redis會自動調整合適的編碼方式。tcp
四、設置key有效期高併發
咱們應該儘量的利用key有效期。好比一些臨時數據(短信校驗碼),過了有效期Redis就會自動爲你清除!性能
五、選擇回收策略(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 : 永不過時,返回錯誤
六、使用bit位級別操做和byte字節級別操做來減小沒必要要的內存使用
bit位級別操做:GETRANGE, SETRANGE, GETBIT and SETBIT byte字節級別操做:GETRANGE and SETRANGE
七、儘量地使用hashes哈希存儲
八、當業務場景不須要數據持久化時,關閉全部的持久化方式能夠得到最佳的性能
數據持久化時須要在持久化和延遲/性能之間作相應的權衡.
九、想要一次添加多條數據的時候可使用管道
十、限制redis的內存大小(64位系統不限制內存,32位系統默認最多使用3GB內存)
數據量不可預估,而且內存也有限的話,儘可能限制下redis使用的內存大小,這樣能夠避免redis使用swap分區或者出現OOM錯誤。(使用swap分區,性能較低,若是限制了內存,當到達指定內存以後就不能添加數據了,不然會報OOM錯誤。能夠設置maxmemory-policy,內存不足時刪除數據)
十一、SLOWLOG [get/reset/len]
slowlog-log-slower-than 它決定要對執行時間大於多少微秒(microsecond,1秒 = 1,000,000 微秒)的命令進行記錄。 slowlog-max-len 它決定 slowlog 最多能保存多少條日誌,當發現redis性能降低的時候能夠查看下是哪些命令致使的。
2、管道測試
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)修改其中一項信息時,須要把整個對象取回,而且修改操做須要對併發進行保護。
二、用戶信息對象有多少成員就存成多少個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"
3、優化案例
一、修改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、一、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