關於Jedis連接池

使用普通jedis對象和jedis連接池之間的差別

關於Redis中比較耗時的命令
keys、sort,exists等命令
1.keys [pattern]模式查詢 O(n) :
keys hello* 以hello開頭的key值。
2.sort 主要對List,Set,Zset來進行排序。
同樣,既然需要使用keys這些耗時的操作,那么我們就將它們剝離出去,比如單開一個redis slave結點,專門用於keys、sort等耗時的操作,這些查詢一般不會是線上的實時業務,查詢慢點就慢點,主要是能完成任務,而對於線上的耗時快的任務沒有影響
3.exists key_name:查詢key是否存在
redis本身是key-value的形式,時間復雜度本來是O(1),但是為什么會超時呢?
我們發現在EXISTS命令處理函數中實現了清除過期key的主動策略,會先調用expireIfNeeded函數檢查要訪問的key是否過期,如果過期就delete掉這個key。del命令在刪除元素很多的復合數據類型(list、hash、zset、set)時是一個很耗時的操作。由於存在元素很多的zset,和ZADD一樣,在刪除zset時需要一個一個遍歷所有元素,時間復雜度是大O(n)。由於這個刪除操作在EXISTS命令的處理函數中執行,所以導致EXISTS耗時過長。
上面主要的原因還是我們處理過期key的方式的問題,我們處理過期key:
1.定期刪除:redis默認是每隔 100ms 就隨機抽取一些設置了過期時間的key,檢查其是否過期,如果過期就刪除。注意這里是隨機抽取的。為什么要隨機呢?你想一想假如 redis 存了幾十萬個 key ,每隔100ms就遍歷所有的設置過期時間的 key 的話,就會給 CPU 帶來很大的負載!
2.惰性刪除 :定期刪除可能會導致很多過期 key 到了時間並沒有被刪除掉。所以就有了惰性刪除。假如你的過期 key,靠定期刪除沒有被刪除掉,還停留在內存里,除非你的系統去查一下那個 key,才會被redis給刪除掉。然后我們在做每一個操作的時候,都會執行exists keyname 的操作,如果遇到了過期key,我們會在這里面執行刪除key的操作,所以非常花時間。
針對刪除大key這個問題:
1.我們可以選擇適當增加過期時間,但不能從根本上解決問題
2.redis作者提供了解決方案,具體就是使用異步線程對大key進行刪除操作,避免阻塞主線程。
4.smembers命令:用於獲取集合全集,如果一個集合中保存了千萬條數據量,一次取回會造成處理線程的長時間阻塞,時間復雜度O(n)
解決方案:
和sort,keys等命令不一樣,smembers可能是線上實時應用場景中使用頻率非常高的一個命令,這里分流一招並不適合,我們更多的需要從設計層面來考慮;
在設計時,我們可以控制集合的數量,將集合數一般保持在500個以內;
比如原來使用一個鍵來存儲一年的記錄,數據量大,我們可以使用12個鍵來分別保存12個月的記錄,或者365個鍵來保存每一天的記錄,將集合的規模控制在可接受的范圍;
如果不容易將集合划分為多個子集合,而堅持用一個大集合來存儲,那么在取集合的時候可以考慮使用SRANDMEMBER key [count];隨機返回集合中的指定數量,當然,如果要遍歷集合中的所有元素,這個命令就不適合了;
5.生成RDB快照文件時,save命令會帶來阻塞:
當然我們采用bgsave來fork()一個子進程來做數據持久化的bgsave,雖然在redis底層我們采用寫時復制策略copy-on-write(為子進程創建虛擬空間結構,復制父進程的虛擬空間結構,不分配物理內存,就有點像只復制地址,這樣可以極大的提高redis性能,但如果對父進程有寫入操作了,那么我們還是要對子進程復制父進程的物理內存,這是非常耗時的,所以在bgsave命令的時候不要對父進程寫入)。
在極端的情況下,父進程內存空間特別大,它的頁表大小也會有點大,即使不復制物理內存,也可能很耗時哦。
6.mset,mget也是O(n)
redis慢查詢
對於一個redis命令生命周期:

慢查詢的配置:
1.慢查詢的語句會被放在一個隊列里面
slowlog-max-len這個慢查詢隊列的長度,這個隊列放在內存中不會被持久化(需要定期持久化慢查詢)
2.慢查詢閾值
slowlog-log-slower-than(微秒)當大於這個時間的時候會被放在慢查詢隊列里面
慢查詢命令:
slowlog get [n]:獲取慢查詢隊列,獲取前n條慢查詢的數據。
slowlog len:獲取慢查詢隊列長度
slowlog reset:清空慢查詢隊列
對於AOF阻塞定位:
info persistence
有aof_delayed_fsync:100可以看到進行了多少個這樣的命令。
pipeline:解決耗時操作的最簡單的操作:(簡單來說就是減少了客戶端發送請求的數量,可以在一定程度上幫助提高性能)
Redis 管道技術可以在服務端未響應時,客戶端可以繼續向服務端發送請求,並最終一次性讀取所有服務端的響應,簡單描述就是一次性可以發送多個請求。
1.pipeline不是原子操作,其中的所有請求還是按原來的順序,但中間可能插入其他的請求。
2.pipeline每次只能作用在一個Redis節點上
沒有pipline
Jedis jedis = new Jedis("127.0.0.1",6379); for(int i=0;i<1000;i++) { jedis.hset("hashkey:"+i,"field"+i,"value"+i); }
使用pipline
Jedis jedis = new Jedis("127.0.0.1",6379); for(int i=0;i<100;i++) { Pipeline pipeline = jedis.pipelined(); for(int j=i*100;j<(i+1)*100;j++) { pipeline.hset("hashkey:"+i,"field"+i,"value"+i); } pipeline.syncAndReturnAll(); }
我們也可以通過pipline高效插入:方在2.6版本推出了一個新的功能-pipe mode,即將支持Redis協議的文本文件直接通過pipe導入到服務端。
1.新建文本文件,創建redis命令
SET Key0 Value0
SET Key1 Value1
...
SET KeyN ValueN
2. 將這些命令轉化成Redis Protocol。
因為Redis管道功能支持的是Redis Protocol,而不是直接的Redis命令。
3.利用管道插入:
cat data.txt | redis-cli --pipe
