redis學習總結css
參考:html
https://blog.csdn.net/richard_jason/article/details/53130369java
https://www.cnblogs.com/mrhgw/p/6278619.htmlpython
https://www.cnblogs.com/xuliangxing/p/7151812.html linux
https://blog.csdn.net/i10630226/article/details/79058513nginx
https://blog.csdn.net/happy_wu/article/details/78736641 web
https://blog.csdn.net/black_ox/article/details/48972085redis
Redis支持五種數據類型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。算法
參考:http://www.redis.net.cn/tutorial/3501.htmlsql
String數據結構是簡單的key-value類型,value其實不只能夠是String,也能夠是數字。
常規key-value緩存應用;
常規計數:微博數,粉絲數等。
Redis hash是一個string類型的field和value的映射表,hash特別適合用於存儲對象。
存儲部分變動的數據,如用戶信息等。
list就是鏈表,略有數據結構知識的人都應該能理解其結構。使用Lists結構,咱們能夠輕鬆地實現最新消息排行等功能。List的另外一個應用就是消息隊列,能夠利用List的PUSH操做,將任務存在List中,而後工做線程再用POP操做將任務取出進行執行。Redis還提供了操做List中某一段的api,你能夠直接查詢,刪除List中某一段的元素。
Redis的list是每一個子元素都是String類型的雙向鏈表,能夠經過push和pop操做從列表的頭部或者尾部添加或者刪除元素,這樣List便可以做爲棧,也能夠做爲隊列。
消息隊列系統
使用list能夠構建隊列系統,使用sorted set甚至能夠構建有優先級的隊列系統。
好比:將Redis用做日誌收集器
實際上仍是一個隊列,多個端點將日誌信息寫入Redis,而後一個worker統一將全部日誌寫到磁盤。
取最新N個數據的操做
//把當前登陸人添加到鏈表裏
ret = r.lpush("login:last_login_times", uid) //保持鏈表只有N位 ret = redis.ltrim("login:last_login_times", 0, N-1) //得到前N個最新登錄的用戶Id列表 last_login_list = r.lrange("login:last_login_times", 0, N-1)
好比sina微博:
在Redis中咱們的最新微博ID使用了常駐緩存,這是一直更新的。可是作了限制不能超過5000個ID,所以獲取ID的函數會一直詢問Redis。只有在start/count參數超出了這個範圍的時候,才須要去訪問數據庫。
系統不會像傳統方式那樣「刷新」緩存,Redis實例中的信息永遠是一致的。SQL數據庫(或是硬盤上的其餘類型數據庫)只是在用戶須要獲取「很遠」的數據時纔會被觸發,而主頁或第一個評論頁是不會麻煩到硬盤上的數據庫了。
set就是一個集合,集合的概念就是一堆不重複值的組合。利用Redis提供的set數據結構,能夠存儲一些集合性的數據。set中的元素是沒有順序的。
案例:
在微博應用中,能夠將一個用戶全部的關注人存在一個集合中,將其全部粉絲存在一個集合。Redis還爲集合提供了求交集、並集、差集等操做,能夠很是方便的實現如共同關注、共同喜愛、二度好友等功能,對上面的全部集合操做,你還能夠使用不一樣的命令選擇將結果返回給客戶端仍是存集到一個新的集合中。
交集,並集,差集
//book表存儲book名稱 set book:1:name "The Ruby Programming Language" set book:2:name "Ruby on rail" set book:3:name "Programming Erlang" //tag表使用集合來存儲數據,由於集合擅長求交集、並集 sadd tag:ruby 1 sadd tag:ruby 2 sadd tag:web 2 sadd tag:erlang 3 //即屬於ruby又屬於web的書? inter_list = redis.sinter("tag:web", "tag:ruby") //即屬於ruby,但不屬於web的書? diff_list = redis.sdiff("tag:ruby", "tag:web") //屬於ruby和屬於web的書的合集? union_list = redis.sunion("tag:ruby", "tag:web")
獲取某段時間全部數據去重值
這個使用Redis的set數據結構最合適了,只須要不斷地將數據往set中扔就好了,set意爲集合,因此會自動排重。
和set相比,sorted set增長了一個權重參數score,使得集合中的元素可以按score進行有序排列,好比一個存儲全班同窗成績的sorted set,其集合value能夠是同窗的學號,而score就能夠是其考試得分,這樣在數據插入集合的時候,就已經進行了自然的排序。能夠用sorted set來作帶權重的隊列,好比普通消息的score爲1,重要消息的score爲2,而後工做線程能夠選擇按score的倒序來獲取工做任務。讓重要的任務優先執行。
排行榜應用,取TOP N操做
這個需求與上面需求的不一樣之處在於,前面操做以時間爲權重,這個是以某個條件爲權重,好比按頂的次數排序,這時候就須要咱們的sorted set出馬了,將你要排序的值設置成sorted set的score,將具體的數據設置成相應的value,每次只須要執行一條ZADD命令便可。
//將登陸次數和用戶統一存儲在一個sorted set裏 zadd login:login_times 5 1 zadd login:login_times 1 2 zadd login:login_times 2 3 //當用戶登陸時,對該用戶的登陸次數自增1 ret = r.zincrby("login:login_times", 1, uid) //那麼如何得到登陸次數最多的用戶呢,逆序排列取得排名前N的用戶 ret = r.zrevrange("login:login_times", 0, N-1)
好比在線遊戲的排行榜,根據得分你一般想要:
- 列出前100名高分選手 - 列出某用戶當前的全球排名
這些操做對於Redis來講小菜一碟,即便你有幾百萬個用戶,每分鐘都會有幾百萬個新的得分。
模式是這樣的,每次得到新得分時,咱們用這樣的代碼:
ZADD leaderboard <score> <username>
你可能用userID來取代username,這取決於你是怎麼設計的。
獲得前100名高分用戶很簡單:
ZREVRANGE leaderboard 0 99
用戶的全球排名也類似,只須要:
ZRANK leaderboard <username>
須要精準設定過時時間的應用
好比你能夠把上面說到的sorted set的score值設置成過時時間的時間戳,那麼就能夠簡單地經過過時時間排序,定時清除過時數據了,不只是清除Redis中的過時數據,你徹底能夠把Redis裏這個過時時間當成是對數據庫中數據的索引,用Redis來找出哪些數據須要過時刪除,而後再精準地從數據庫中刪除相應的記錄。
範圍查找
來自Redis在Google Group上的一個問題,有一位同窗發貼求助,說要解決以下的一個問題:他有一個IP範圍對應地址的列表,如今須要給出一個IP的狀況下,迅速的查找到這個IP在哪一個範圍,也就是要判斷此IP的全部地。這個問題引來了Redis做者Salvatore Sanfilippo(@antirez)的回答。解答以下:
例若有下面兩個範圍,10-20和30-40
- A_start 10, A_end 20
- B_start 30, B_end 40
咱們將這兩個範圍的起始位置存在Redis的sorted set數據結構中,基本範圍起始值做爲score,範圍名加start和end爲其value值:
redis 127.0.0.1:6379> zadd ranges 10 A_start 1 redis 127.0.0.1:6379> zadd ranges 20 A_end 1 redis 127.0.0.1:6379> zadd ranges 30 B_start 1 redis 127.0.0.1:6379> zadd ranges 40 B_end 1
這樣數據在插入sorted set後,至關因而將這些起始位置按順序排列好了。
如今我須要查找15這個值在哪個範圍中,只須要進行以下的zrangbyscore查找:
redis 127.0.0.1:6379> zrangebyscore ranges (15 +inf LIMIT 0 1 1) "A_end"
這個命令的意思是在Sorted Sets中查找大於15的第一個值。(+inf在Redis中表示正無窮大,15前面的括號表示>15而非>=15)
查找的結果是A_end,因爲全部值是按順序排列的,因此能夠斷定15是在A_start到A_end區間上,也就是說15是在A這個範圍裏。至此大功告成。
固然,若是你查找到的是一個start,好比我們用25,執行下面的命令:
redis 127.0.0.1:6379> zrangebyscore ranges (25 +inf LIMIT 0 1 1) "B_start"
返回結果代表其下一個節點是一個start節點,也就是說25這個值不處在任何start和end之間,不屬於任何範圍。
固然,這個例子僅適用於相似上面的IP範圍查找的案例,由於這些值範圍之間沒有重合。若是是有重合的狀況,這個問題自己也就變成了一個一對多的問題。
Pub/Sub 從字面上理解就是發佈(Publish)與訂閱(Subscribe),在Redis中,你能夠設定對某一個key值進行消息發佈及消息訂閱,當一個key值上進行了消息發佈後,全部訂閱它的客戶端都會收到相應的消息。這一功能最明顯的用法就是用做實時消息系統,好比普通的即時聊天,羣聊等功能。
使用場景
Pub/Sub構建實時消息系統
Redis的Pub/Sub系統能夠構建實時的消息系統
好比不少用Pub/Sub構建的實時聊天系統的例子。
參考:
http://www.redis.net.cn/tutorial/3501.html
http://www.cnblogs.com/markhe/p/5689356.html
http://www.cnblogs.com/ggjucheng/p/3349102.html
Redis經常使用數據類型
Redis最爲經常使用的數據類型主要有如下:
在具體描述這幾種數據類型以前,咱們先經過一張圖瞭解下Redis內部內存管理中是如何描述這些不一樣數據類型的:
首先Redis內部使用一個redisObject對象來表示全部的key和value,redisObject最主要的信息如上圖所示:
type表明一個value對象具體是何種數據類型,
encoding是不一樣數據類型在redis內部的存儲方式,
好比:type=string表明value存儲的是一個普通字符串,那麼對應的encoding能夠是raw或者是int,若是是int則表明實際redis內部是按數值型類存儲和表示這個字符串的,固然前提是這個字符串自己能夠用數值表示,好比:"123" "456"這樣的字符串。
這裏須要特殊說明一下vm字段,只有打開了Redis的虛擬內存功能,此字段纔會真正的分配內存,該功能默認是關閉狀態的,該功能會在後面具體描述。經過上圖咱們能夠發現Redis使用redisObject來表示全部的key/value數據是比較浪費內存的,固然這些內存管理成本的付出主要也是爲了給Redis不一樣數據類型提供一個統一的管理接口,實際做者也提供了多種方法幫助咱們儘可能節省內存使用,咱們隨後會具體討論。
3. 各類數據類型應用和實現方式
下面咱們先來逐一的分析下這7種數據類型的使用和內部實現方式:
Strings 數據結構是簡單的key-value類型,value其實不只是String,也能夠是數字.經常使用命令: set,get,decr,incr,mget 等。
應用場景:String是最經常使用的一種數據類型,普通的key/ value 存儲均可以歸爲此類.便可以徹底實現目前 Memcached 的功能,而且效率更高。還能夠享受Redis的定時持久化,操做日誌及 Replication等功能。除了提供與 Memcached 同樣的get、set、incr、decr 等操做外,Redis還提供了下面一些操做:
實現方式:String在redis內部存儲默認就是一個字符串,被redisObject所引用,當遇到incr,decr等操做時會轉成數值型進行計算,此時redisObject的encoding字段爲int。
經常使用命令:hget,hset,hgetall 等。
應用場景:在Memcached中,咱們常常將一些結構化的信息打包成HashMap,在客戶端序列化後存儲爲一個字符串的值,好比用戶的暱稱、年齡、性別、積分等,這時候在須要修改其中某一項時,一般須要將全部值取出反序列化後,修改某一項的值,再序列化存儲回去。這樣不只增大了開銷,也不適用於一些可能併發操做的場合(好比兩個併發的操做都須要修改積分)。而Redis的Hash結構能夠使你像在數據庫中Update一個屬性同樣只修改某一項屬性值。
咱們簡單舉個實例來描述下Hash的應用場景,好比咱們要存儲一個用戶信息對象數據,包含如下信息:
用戶ID爲查找的key,存儲的value用戶對象包含姓名,年齡,生日等信息,若是用普通的key/value結構來存儲,主要有如下2種存儲方式:
第一種方式將用戶ID做爲查找key,把其餘信息封裝成一個對象以序列化的方式存儲,這種方式的缺點是,增長了序列化/反序列化的開銷,而且在須要修改其中一項信息時,須要把整個對象取回,而且修改操做須要對併發進行保護,引入CAS等複雜問題。
第二種方法是這個用戶信息對象有多少成員就存成多少個key-value對兒,用用戶ID+對應屬性的名稱做爲惟一標識來取得對應屬性的值,雖然省去了序列化開銷和併發問題,可是用戶ID爲重複存儲,若是存在大量這樣的數據,內存浪費仍是很是可觀的。
那麼Redis提供的Hash很好的解決了這個問題,Redis的Hash實際是內部存儲的Value爲一個HashMap,並提供了直接存取這個Map成員的接口,以下圖:
也就是說,Key仍然是用戶ID, value是一個Map,這個Map的key是成員的屬性名,value是屬性值,這樣對數據的修改和存取均可以直接經過其內部Map的Key(Redis裏稱內部Map的key爲field), 也就是經過 key(用戶ID) + field(屬性標籤) 就能夠操做對應屬性數據了,既不須要重複存儲數據,也不會帶來序列化和併發修改控制的問題。很好的解決了問題。
這裏同時須要注意,Redis提供了接口(hgetall)能夠直接取到所有的屬性數據,可是若是內部Map的成員不少,那麼涉及到遍歷整個內部Map的操做,因爲Redis單線程模型的緣故,這個遍歷操做可能會比較耗時,而另其它客戶端的請求徹底不響應,這點須要格外注意。
實現方式:
上面已經說到Redis Hash對應Value內部實際就是一個HashMap,實際這裏會有2種不一樣實現,這個Hash的成員比較少時Redis爲了節省內存會採用相似一維數組的方式來緊湊存儲,而不會採用真正的HashMap結構,對應的value redisObject的encoding爲zipmap,當成員數量增大時會自動轉成真正的HashMap,此時encoding爲ht。
經常使用命令:lpush,rpush,lpop,rpop,lrange等。
應用場景:
Redis list的應用場景很是多,也是Redis最重要的數據結構之一,好比twitter的關注列表,粉絲列表等均可以用Redis的list結構來實現。
Lists 就是鏈表,相信略有數據結構知識的人都應該能理解其結構。使用Lists結構,咱們能夠輕鬆地實現最新消息排行等功能。Lists的另外一個應用就是消息隊列,
能夠利用Lists的PUSH操做,將任務存在Lists中,而後工做線程再用POP操做將任務取出進行執行。Redis還提供了操做Lists中某一段的api,你能夠直接查詢,刪除Lists中某一段的元素。實現方式:
Redis list的實現爲一個雙向鏈表,便可以支持反向查找和遍歷,更方便操做,不過帶來了部分額外的內存開銷,Redis內部的不少實現,包括髮送緩衝隊列等也都是用的這個數據結構。
經常使用命令:
sadd,spop,smembers,sunion 等。
應用場景:
Redis set對外提供的功能與list相似是一個列表的功能,特殊之處在於set是能夠自動排重的,當你須要存儲一個列表數據,又不但願出現重複數據時,set是一個很好的選擇,而且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。
Sets 集合的概念就是一堆不重複值的組合。利用Redis提供的Sets數據結構,能夠存儲一些集合性的數據,好比在微博應用中,能夠將一個用戶全部的關注人存在一個集合中,將其全部粉絲存在一個集合。Redis還爲集合提供了求交集、並集、差集等操做,能夠很是方便的實現如共同關注、共同喜愛、二度好友等功能,對上面的全部集合操做,你還能夠使用不一樣的命令選擇將結果返回給客戶端仍是存集到一個新的集合中。
實現方式:
set 的內部實現是一個 value永遠爲null的HashMap,實際就是經過計算hash的方式來快速排重的,這也是set能提供判斷一個成員是否在集合內的緣由。
經常使用命令:
zadd,zrange,zrem,zcard等
使用場景:
Redis sorted set的使用場景與set相似,區別是set不是自動有序的,而sorted set能夠經過用戶額外提供一個優先級(score)的參數來爲成員排序,而且是插入有序的,即自動排序。當你須要一個有序的而且不重複的集合列表,那麼能夠選擇sorted set數據結構,好比twitter 的public timeline能夠以發表時間做爲score來存儲,這樣獲取時就是自動按時間排好序的。
另外還能夠用Sorted Sets來作帶權重的隊列,好比普通消息的score爲1,重要消息的score爲2,而後工做線程能夠選擇按score的倒序來獲取工做任務。讓重要的任務優先執行。
實現方式:
Redis sorted set的內部使用HashMap和跳躍表(SkipList)來保證數據的存儲和有序,HashMap裏放的是成員到score的映射,而跳躍表裏存放的是全部的成員,排序依據是HashMap裏存的score,使用跳躍表的結構能夠得到比較高的查找效率,而且在實現上比較簡單。
Pub/Sub 從字面上理解就是發佈(Publish)與訂閱(Subscribe),在Redis中,你能夠設定對某一個key值進行消息發佈及消息訂閱,當一個key值上進行了消息發佈後,全部訂閱它的客戶端都會收到相應的消息。這一功能最明顯的用法就是用做實時消息系統,好比普通的即時聊天,羣聊等功能。
誰說NoSQL都不支持事務,雖然Redis的Transactions提供的並非嚴格的ACID的事務(好比一串用EXEC提交執行的命令,在執行中服務器宕機,那麼會有一部分命令執行了,剩下的沒執行),可是這個Transactions仍是提供了基本的命令打包執行的功能(在服務器不出問題的狀況下,能夠保證一連串的命令是順序在一塊兒執行的,中間有會有其它客戶端命令插進來執行)。Redis還提供了一個Watch功能,你能夠對一個key進行Watch,而後再執行Transactions,在這過程當中,若是這個Watched的值進行了修改,那麼這個Transactions會發現並拒絕執行。
4. Redis實際應用場景
Redis在不少方面與其餘數據庫解決方案不一樣:它使用內存提供主存儲支持,而僅使用硬盤作持久性的存儲;它的數據模型很是獨特,用的是單線程。另外一個大區別在於,你能夠在開發環境中使用Redis的功能,但卻不須要轉到Redis。
轉向Redis固然也是可取的,許多開發者從一開始就把Redis做爲首選數據庫;但設想若是你的開發環境已經搭建好,應用已經在上面運行了,那麼更換數據庫框架顯然不那麼容易。另外在一些須要大容量數據集的應用,Redis也並不適合,由於它的數據集不會超過系統可用的內存。因此若是你有大數據應用,並且主要是讀取訪問模式,那麼Redis並非正確的選擇。
然而我喜歡Redis的一點就是你能夠把它融入到你的系統中來,這就可以解決不少問題,好比那些你現有的數據庫處理起來感到緩慢的任務。這些你就能夠經過Redis來進行優化,或者爲應用建立些新的功能。在本文中,我就想探討一些怎樣將Redis加入到現有的環境中,並利用它的原語命令等功能來解決 傳統環境中碰到的一些常見問題。在這些例子中,Redis都不是做爲首選數據庫。
下面這個語句經常使用來顯示最新項目,隨着數據多了,查詢毫無疑問會愈來愈慢。
在Web應用中,「列出最新的回覆」之類的查詢很是廣泛,這一般會帶來可擴展性問題。這使人沮喪,由於項目原本就是按這個順序被建立的,但要輸出這個順序卻不得不進行排序操做。
相似的問題就能夠用Redis來解決。好比說,咱們的一個Web應用想要列出用戶貼出的最新20條評論。在最新的評論邊上咱們有一個「顯示所有」的連接,點擊後就能夠得到更多的評論。
咱們假設數據庫中的每條評論都有一個惟一的遞增的ID字段。
咱們能夠使用分頁來製做主頁和評論頁,使用Redis的模板,每次新評論發表時,咱們會將它的ID添加到一個Redis列表:
咱們將列表裁剪爲指定長度,所以Redis只須要保存最新的5000條評論:
LTRIM latest.comments 0 5000
每次咱們須要獲取最新評論的項目範圍時,咱們調用一個函數來完成(使用僞代碼):
這裏咱們作的很簡單。在Redis中咱們的最新ID使用了常駐緩存,這是一直更新的。可是咱們作了限制不能超過5000個ID,所以咱們的獲取ID函數會一直詢問Redis。只有在start/count參數超出了這個範圍的時候,才須要去訪問數據庫。
咱們的系統不會像傳統方式那樣「刷新」緩存,Redis實例中的信息永遠是一致的。SQL數據庫(或是硬盤上的其餘類型數據庫)只是在用戶須要獲取「很遠」的數據時纔會被觸發,而主頁或第一個評論頁是不會麻煩到硬盤上的數據庫了。
咱們能夠使用LREM來刪除評論。若是刪除操做很是少,另外一個選擇是直接跳過評論條目的入口,報告說該評論已經不存在。
有些時候你想要給不一樣的列表附加上不一樣的過濾器。若是過濾器的數量受到限制,你能夠簡單的爲每一個不一樣的過濾器使用不一樣的Redis列表。畢竟每一個列表只有5000條項目,但Redis卻可以使用很是少的內存來處理幾百萬條項目。
另外一個很廣泛的需求是各類數據庫的數據並不是存儲在內存中,所以在按得分排序以及實時更新這些幾乎每秒鐘都須要更新的功能上數據庫的性能不夠理想。
典型的好比那些在線遊戲的排行榜,好比一個Facebook的遊戲,根據得分你一般想要:
- 列出前100名高分選手
- 列出某用戶當前的全球排名
這些操做對於Redis來講小菜一碟,即便你有幾百萬個用戶,每分鐘都會有幾百萬個新的得分。
模式是這樣的,每次得到新得分時,咱們用這樣的代碼:
ZADD leaderboard <score> <username>
你可能用userID來取代username,這取決於你是怎麼設計的。
獲得前100名高分用戶很簡單:ZREVRANGE leaderboard 0 99。
用戶的全球排名也類似,只須要:ZRANK leaderboard <username>。
排行榜的一種常見變體模式就像Reddit或Hacker News用的那樣,新聞按照相似下面的公式根據得分來排序:
score = points / time^alpha
所以用戶的投票會相應的把新聞挖出來,但時間會按照必定的指數將新聞埋下去。下面是咱們的模式,固然算法由你決定。
模式是這樣的,開始時先觀察那些多是最新的項目,例如首頁上的1000條新聞都是候選者,所以咱們先忽視掉其餘的,這實現起來很簡單。
每次新的新聞貼上來後,咱們將ID添加到列表中,使用LPUSH + LTRIM,確保只取出最新的1000條項目。
有一項後臺任務獲取這個列表,而且持續的計算這1000條新聞中每條新聞的最終得分。計算結果由ZADD命令按照新的順序填充生成列表,老新聞則被清除。這裏的關鍵思路是排序工做是由後臺任務來完成的。
另外一種經常使用的項目排序是按照時間排序。咱們使用unix時間做爲得分便可。
模式以下:
- 每次有新項目添加到咱們的非Redis數據庫時,咱們把它加入到排序集合中。這時咱們用的是時間屬性,current_time和time_to_live。
- 另外一項後臺任務使用ZRANGE…SCORES查詢排序集合,取出最新的10個項目。若是發現unix時間已通過期,則在數據庫中刪除條目。
Redis是一個很好的計數器,這要感謝INCRBY和其餘類似命令。
我相信你曾許屢次想要給數據庫加上新的計數器,用來獲取統計或顯示新信息,可是最後卻因爲寫入敏感而不得不放棄它們。
好了,如今使用Redis就不須要再擔憂了。有了原子遞增(atomic increment),你能夠放心的加上各類計數,用GETSET重置,或者是讓它們過時。
例如這樣操做:
INCR user:<id> EXPIRE
user:<id> 60
你能夠計算出最近用戶在頁面間停頓不超過60秒的頁面瀏覽量,當計數達到好比20時,就能夠顯示出某些條幅提示,或是其它你想顯示的東西。
另外一項對於其餘數據庫很難,但Redis作起來卻垂手可得的事就是統計在某段特色時間裏有多少特定用戶訪問了某個特定資源。好比我想要知道某些特定的註冊用戶或IP地址,他們到底有多少訪問了某篇文章。
每次我得到一次新的頁面瀏覽時我只須要這樣作:
SADD page:day1:<page_id> <user_id>
固然你可能想用unix時間替換day1,好比time()-(time()%3600*24)等等。
想知道特定用戶的數量嗎?只須要使用SCARD page:day1:<page_id>。
須要測試某個特定用戶是否訪問了這個頁面?SISMEMBER page:day1:<page_id>。
咱們只作了幾個例子,但若是你研究Redis的命令集,而且組合一下,就能得到大量的實時分析方法,有效並且很是省力。使用Redis原語命令,更容易實施垃圾郵件過濾系統或其餘實時跟蹤系統。
Redis的Pub/Sub很是很是簡單,運行穩定而且快速。支持模式匹配,可以實時訂閱與取消頻道。
你應該已經注意到像list push和list pop這樣的Redis命令可以很方便的執行隊列操做了,但能作的可不止這些:好比Redis還有list pop的變體命令,可以在列表爲空時阻塞隊列。
現代的互聯網應用大量地使用了消息隊列(Messaging)。消息隊列不只被用於系統內部組件之間的通訊,同時也被用於系統跟其它服務之間的交互。消息隊列的使用能夠增長系統的可擴展性、靈活性和用戶體驗。非基於消息隊列的系統,其運行速度取決於系統中最慢的組件的速度(注:短板效應)。而基於消息隊列能夠將系統中各組件解除耦合,這樣系統就再也不受最慢組件的束縛,各組件能夠異步運行從而得以更快的速度完成各自的工做。
此外,當服務器處在高併發操做的時候,好比頻繁地寫入日誌文件。能夠利用消息隊列實現異步處理。從而實現高性能的併發操做。
Redis的緩存部分值得寫一篇新文章,我這裏只是簡單的說一下。Redis可以替代memcached,讓你的緩存從只能存儲數據變得可以更新數據,所以你再也不須要每次都從新生成數據了。
redis中能夠使用expire命令設置一個鍵的生存時間,到時間後redis會自動刪除它。
expire 設置生存時間(單位/秒)
ttl 查看鍵的剩餘生存時間
persist 取消生存時間
expireat [key] unix時間戳1351858600
示例:
操做圖示:
應用場景:
redis中的事務是一組命令的集合。事務同命令同樣都是redis的最小執行單元。一組事務中的命令要麼都執行,要麼都不執行。(例如:轉帳)
原理:
先將屬於一個事務的命令發送給redis進行緩存,最後再讓redis依次執行這些命令。
應用場景:
命令:
錯誤處理
1:語法錯誤:致命的錯誤,事務中的全部命令都不會執行
2:運行錯誤:不會影響事務中其餘命令的執行
Redis 不支持回滾(roll back)
正由於redis不支持回滾功能,才使得redis在事務上能夠保持簡潔和快速。
watch命令
做用:監控一個或者多個鍵,當被監控的鍵值被修改後阻止以後的一個事務的執行。
可是不能保證其它客戶端不修改這一鍵值,因此咱們須要在事務執行失敗後從新執行事務中的命令。
注意:執行完事務的exec命令以後,watch就會取消對全部鍵值的監控
unwatch:取消監控
操做圖示:
sort命令能夠對列表類型,集合類型和有序集合類型進行排序。
by 參考鍵(參考鍵能夠是字符串類型或者是hash類型的某個字段,hash類型的格式爲:鍵名->字段名)
擴展 get參數
擴展 store參數
使用store 參數能夠把sort的排序結果保存到指定的列表中
性能優化
1:儘量減小待排序鍵中元素的數量
2:使用limit參數只獲取須要的數據
3:若是要排序的數據數量很大,儘量使用store參數將結果緩存。
操做圖示:
發佈:publish
訂閱:subscribe
取消訂閱:unsubscribe
按照規則訂閱:psubscribe
按照規則取消訂閱:punsubscribe
注意:使用punsubscribe命令只能退訂經過psubscribe 訂閱的頻道。
操做圖示:(訂閱頻道後,頻道每發佈一條消息,都能動態顯示出來)
訂閱:
發佈:
任務隊列:使用lpush和rpop(brpop)能夠實現普通的任務隊列。
brpop是列表的阻塞式(blocking)彈出原語。
它是 RPOP命令的阻塞版本,當給定列表內沒有任何元素可供彈出的時候,鏈接將被 BRPOP命令阻塞,直到等待超時或發現可彈出元素爲止。
當給定多個 key 參數時,按參數 key 的前後順序依次檢查各個列表,彈出第一個非空列表的尾部元素。
優先級隊列:
操做圖示:
redis的pipeline(管道)功能在命令行中沒有,可是redis是支持管道的,在java的客戶端(jedis)中是能夠使用的。
測試發現:
1:不使用管道方式,插入1000條數據耗時328毫秒
2:使用管道方式,插入1000條數據耗時37毫秒
在插入更多數據的時候,管道的優點更加明顯:測試10萬條數據的時候,不使用管道要40秒,實用管道378毫秒。
redis支持兩種方式的持久化,能夠單獨使用或者結合起來使用。
第一種:RDB方式(redis默認的持久化方式)
第二種:AOF方式
rdb方式的持久化是經過快照完成的,當符合必定條件時redis會自動將內存中的全部數據執行快照操做並存儲到硬盤上。默認存儲在dump.rdb文件中。(文件名在配置文件中dbfilename)
redis進行快照的時機(在配置文件redis.conf中)
Redis自動實現快照的過程
一、redis使用fork函數複製一份當前進程的副本(子進程)
二、父進程繼續接收並處理客戶端發來的命令,而子進程開始將內存中的數據寫入硬盤中的臨時文件
三、當子進程寫入完全部數據後會用該臨時文件替換舊的RDB文件,至此,一次快照操做完成。
注意:redis在進行快照的過程當中不會修改RDB文件,只有快照結束後纔會將舊的文件替換成新的,也就是說任什麼時候候RDB文件都是完整的。
這就使得咱們能夠經過定時備份RDB文件來實現redis數據庫的備份
RDB文件是通過壓縮的二進制文件,佔用的空間會小於內存中的數據,更加利於傳輸。
手動執行save或者bgsave命令讓redis執行快照。
兩個命令的區別在於,save是由主進程進行快照操做,會阻塞其它請求。bgsave是由redis執行fork函數複製出一個子進程來進行快照操做。
文件修復:
rdb的優缺點
優勢:因爲存儲的有數據快照文件,恢復數據很方便。
缺點:會丟失最後一次快照之後更改的全部數據。
aof方式的持久化是經過日誌文件的方式。默認狀況下redis沒有開啓aof,能夠經過參數appendonly參數開啓。
aof文件的保存位置和rdb文件的位置相同,都是dir參數設置的,默認的文件名是appendonly.aof,能夠經過appendfilename參數修改
redis寫命令同步的時機
aof日誌文件重寫
手動執行bgrewriteaof進行重寫
重寫的過程只和內存中的數據有關,和以前的aof文件無關。
所謂的「重寫」實際上是一個有歧義的詞語, 實際上, AOF 重寫並不須要對原有的 AOF 文件進行任何寫入和讀取, 它針對的是數據庫中鍵的當前值。
文件修復:
動態切換redis持久方式,從 RDB 切換到 AOF(支持Redis 2.2及以上)
注意:
一、當redis啓動時,若是rdb持久化和aof持久化都打開了,那麼程序會優先使用aof方式來恢復數據集,由於aof方式所保存的數據一般是最完整的。若是aof文件丟失了,則啓動以後數據庫內容爲空。
二、若是想把正在運行的redis數據庫,從RDB切換到AOF,建議先使用動態切換方式,再修改配置文件,重啓數據庫。(不能本身修改配置文件,重啓數據庫,不然數據庫中數據就爲空了。)
使用config set能夠動態設置參數信息,服務器重啓以後就失效了。
使用config get能夠查看全部能夠使用config set命令設置的參數
使用config rewrite命令對啓動 Redis 服務器時所指定的 redis.conf 文件進行改寫(Redis 2.8 及以上版本才能夠使用),主要是把使用config set動態指定的命令保存到配置文件中。
注意:config rewrite命令對 redis.conf 文件的重寫是原子性的, 而且是一致的: 若是重寫出錯或重寫期間服務器崩潰, 那麼重寫失敗, 原有 redis.conf 文件不會被修改。 若是重寫成功, 那麼 redis.conf 文件爲重寫後的新文件。
設置數據庫密碼
修改配置
驗證密碼
bind參數(可讓數據庫只能在指定IP下訪問)
命令重命名
修改命令的名稱
禁用命令
redis-cli 命令行
Redisclient(redis數據庫可視化工具,不怎麼實用)
http://www.oschina.net/news/53391/redisclient-1-0
http://www.oschina.net/news/55634/redisclient-2-0
以一種易於解釋(parse)且易於閱讀的格式,返回關於 Redis 服務器的各類信息和統計數值。
經過給定可選的參數 section ,可讓命令只返回某一部分的信息:
內容過多,詳細參考
http://redisdoc.com/server/info.html
測試狀況:
100萬個鍵值對(鍵是0到999999值是字符串「hello world」)在32位操做系統的筆記本上 用了100MB
使用64位的操做系統的話,相對來講佔用的內存會多一點,這是由於64位的系統裏指針佔用了8個字節,可是64位系統也能支持更大的內存,因此運行大型的redis服務仍是建議使用64位服務器
理論上Redis能夠處理多達2的32次方的keys,而且在實際中進行了測試,每一個實例至少存放了2億5千萬的keys
也能夠說Redis的存儲極限是系統中的可用內存值。
精簡鍵名和鍵值
鍵名:儘可能精簡,可是也不能單純爲了節約空間而使用不易理解的鍵名。
鍵值:對於鍵值的數量固定的話能夠使用0和1這樣的數字來表示,(例如:male/female、right/wrong)
當業務場景不須要數據持久化時,關閉全部的持久化方式能夠得到最佳的性能
內部編碼優化(你們能夠本身瞭解)
redis爲每種數據類型都提供了兩種內部編碼方式,在不一樣的狀況下redis會自動調整合適的編碼方式。(如圖所示)
SLOWLOG [get/reset/len]
當發現redis性能降低的時候能夠查看下是哪些命令致使的
修改linux內核內存分配策略
緣由:
redis在運行過程當中可能會出現下面問題
錯誤日誌:
redis在備份數據的時候,會fork出一個子進程,理論上child進程所佔用的內存和parent是同樣的,好比parent佔用的內存爲8G,這個時候也要一樣分配8G的內存給child,若是內存沒法負擔,每每會形成redis服務器的down機或者IO負載太高,效率降低。因此內存分配策略應該設置爲 1(表示內核容許分配全部的物理內存,而無論當前的內存狀態如何)。
內存分配策略有三種
可選值:0、一、2。
0, 表示內核將檢查是否有足夠的可用內存供應用進程使用;若是有足夠的可用內存,內存申請容許;不然,內存申請失敗,並把錯誤返回給應用進程。
1, 無論須要多少內存,都容許申請。
2, 只容許分配物理內存和交換內存的大小。(交換內存通常是物理內存的一半)
向/etc/sysctl.conf添加
或者執行
問題圖示:
關閉Transparent Huge Pages(THP)
THP會形成內存鎖影響redis性能,建議關閉
使用root用戶執行下面命令
把這條命令添加到這個文件中/etc/rc.local
修改linux中TCP 監聽的最大容納數量
在高併發環境下你須要一個高backlog值來避免慢客戶端鏈接問題。注意Linux內核默默地將這個值減少到/proc/sys/net/core/somaxconn的值,因此須要確認增大somaxconn和tcp_max_syn_backlog兩個值來達到想要的效果。
注意:這個參數並非限制redis的最大連接數。若是想限制redis的最大鏈接數須要修改maxclients,默認最大鏈接數爲10000。
限制redis的內存大小
經過redis的info命令查看內存使用狀況
若是不設置maxmemory或者設置爲0,64位系統不限制內存,32位系統最多使用3GB內存。
修改配置文件中的maxmemory和maxmemory-policy
若是能夠肯定數據總量不大,而且內存足夠的狀況下不須要限制redis使用的內存大小。若是數據量不可預估,而且內存也有限的話,儘可能限制下redis使用的內存大小,這樣能夠避免redis使用swap分區或者出現OOM錯誤。
注意:若是不限制內存,當物理內存使用完以後,會使用swap分區,這樣性能較低,若是限制了內存,當到達指定內存以後就不能添加數據了,不然會報OOM錯誤。能夠設置maxmemory-policy,內存不足時刪除數據。
拓展
used_memory是Redis使用的內存總量,它包含了實際緩存佔用的內存和Redis自身運行所佔用的內存(以字節(byte)爲單位,其中used_memory_human上的數據和used_memory是同樣的值,它以M爲單位顯示,僅爲了方便閱讀)。
若是一個Redis實例的內存使用率超過可用最大內存(used_memory >可用最大內存),那麼操做系統開始進行內存與swap空間交換,把內存中舊的或再也不使用的內容寫入硬盤上(硬盤上的這塊空間叫Swap分區),以便騰出新的物理內存給新頁或活動頁(page)使用。
在硬盤上進行讀寫操做要比在內存上進行讀寫操做,時間上慢了近5個數量級,內存是0.1us(微秒)、而硬盤是10ms(毫秒)。若是Redis進程上發生內存交換,那麼Redis和依賴Redis上數據的應用會受到嚴重的性能影響。 經過查看used_memory指標可知道Redis正在使用的內存狀況,若是used_memory>可用最大內存,那就說明Redis實例正在進行內存交換或者已經內存交換完畢。管理員根據這個狀況,執行相對應的應急措施。
排查方案:
如果在使用Redis期間沒有開啓rdb快照或aof持久化策略,那麼緩存數據在Redis崩潰時就有丟失的危險。由於當Redis內存使用率超過可用內存的95%時,部分數據開始在內存與swap空間來回交換,這時就可能有丟失數據的危險。
當開啓並觸發快照功能時,Redis會fork一個子進程把當前內存中的數據徹底複製一份寫入到硬盤上。所以如果當前使用內存超過可用內存的45%時觸發快照功能,那麼此時進行的內存交換會變的很是危險(可能會丟失數據)。 假若在這個時候實例上有大量頻繁的更新操做,問題會變得更加嚴重。
經過減小Redis的內存佔用率,來避免這樣的問題,或者使用下面的技巧來避免內存交換髮生:
一、儘量的使用Hash數據結構。由於Redis在儲存小於100個字段的Hash結構上,其存儲效率是很是高的。因此在不須要集合(set)操做或list的push/pop操做的時候,儘量的使用Hash結構。好比,在一個web應用程序中,須要存儲一個對象表示用戶信息,使用單個key表示一個用戶,其每一個屬性存儲在Hash的字段裏,這樣要比給每一個屬性單獨設置一個key-value要高效的多。 一般狀況下假若有數據使用string結構,用多個key存儲時,那麼應該轉換成單key多字段的Hash結構。 如上述例子中介紹的Hash結構應包含,單個對象的屬性或者單個用戶各類各樣的資料。Hash結構的操做命令是HSET(key, fields, value)和HGET(key, field),使用它能夠存儲或從Hash中取出指定的字段。
二、設置key的過時時間。一個減小內存使用率的簡單方法就是,每當存儲對象時確保設置key的過時時間。假若key在明確的時間週期內使用或者舊key不大可能被使用時,就能夠用Redis過時時間命令(expire,expireat, pexpire, pexpireat)去設置過時時間,這樣Redis會在key過時時自動刪除key。 假如你知道每秒鐘有多少個新key-value被建立,那能夠調整key的存活時間,並指定閥值去限制Redis使用的最大內存。
三、回收key。在Redis配置文件中(通常叫Redis.conf),經過設置「maxmemory」屬性的值能夠限制Redis最大使用的內存,修改後重啓實例生效。也能夠使用客戶端命令config set maxmemory 去修改值,這個命令是當即生效的,但會在重啓後會失效,須要使用config rewrite命令去刷新配置文件。 如果啓用了Redis快照功能,應該設置「maxmemory」值爲系統可以使用內存的45%,由於快照時須要一倍的內存來複制整個數據集,也就是說若是當前已使用45%,在快照期間會變成95%(45%+45%+5%),其中5%是預留給其餘的開銷。 若是沒開啓快照功能,maxmemory最高能設置爲系統可用內存的95%。
當內存使用達到設置的最大閥值時,須要選擇一種key的回收策略,可在Redis.conf配置文件中修改「maxmemory-policy」屬性值。 如果Redis數據集中的key都設置了過時時間,那麼「volatile-ttl」策略是比較好的選擇。但若是key在達到最大內存限制時沒可以迅速過時,或者根本沒有設置過時時間。那麼設置爲「allkeys-lru」值比較合適,它容許Redis從整個數據集中挑選最近最少使用的key進行刪除(LRU淘汰算法)。Redis還提供了一些其餘淘汰策略,以下:
經過設置maxmemory爲系統可用內存的45%或95%(取決於持久化策略)和設置「maxmemory-policy」爲「volatile-ttl」或「allkeys-lru」(取決於過時設置),能夠比較準確的限制Redis最大內存使用率,在絕大多數場景下使用這2種方式可確保Redis不會進行內存交換。假若你擔憂因爲限制了內存使用率致使丟失數據的話,能夠設置noneviction值禁止淘汰數據。
Redis是個單線程模型,客戶端過來的命令是按照順序執行的,因此想要一次添加多條數據的時候能夠使用管道,或者使用一次能夠添加多條數據的命令,例如:
在更新中保持用戶對數據的映射是系統中的一個廣泛任務。Redis的pub/sub功能使用了SUBSCRIBE、UNSUBSCRIBE和PUBLISH命令,讓這個變得更加容易。
代碼示例:
打印結果:
進行各類數據統計的用途是很是普遍的,好比想知道何時封鎖一個IP地址。INCRBY命令讓這些變得很容易,經過原子遞增保持計數;GETSET用來重置計數器;過時屬性expire用來確認一個關鍵字何時應該刪除。
代碼示例:
打印結果:
代碼示例:
打印結果:
變量a沒有被修改時:
變量a被修改時:
商品維度計數(喜歡數,評論數,鑑定數,瀏覽數,etc)
採用Redis 的類型: Hash. 若是你對redis數據類型不太熟悉,能夠參考 http://redis.io/topics/data-types-intro
爲product定義個key product:,爲每種數值定義hashkey, 譬如喜歡數xihuan
用戶維度計數(動態數、關注數、粉絲數、喜歡商品數、發帖數 等)
用戶維度計數同商品維度計數都採用 Hash. 爲User定義個key user:,爲每種數值定義hashkey, 譬如關注數follow
譬如將用戶的好友/粉絲/關注,能夠存在一個sorted set中,score能夠是timestamp,這樣求兩我的的共同好友的操做,可能就只須要用求交集命令便可。
緩存內容示例:(商品列表,評論列表,@提示列表,etc)
相對memcached 簡單的key-value存儲來講,redis衆多的數據結構(list,set,sorted set,hash, etc)能夠更方便cache各類業務數據,性能也不亞於memcached。
例如:
例如:(評論,發佈商品,論壇發貼,etc)
做爲一個電商網站被各類spam攻擊是少難免(垃圾評論、發佈垃圾商品、廣告、刷自家商品排名等),針對這些spam制定一系列anti-spam規則,其中有些規則能夠利用redis作實時分析,譬如:1分鐘評論不得超過2次、5分鐘評論少於5次等(更多機制/規則須要結合drools )。 採用sorted set將最近一天用戶操做記錄起來(爲何不所有記錄?節省memory,所有操做會記錄到log,後續利用hadoop進行更全面分析統計),經過
打印結果:
在逛有個相似微博的欄目我關注,裏面包括關注的人、主題、品牌的動態。redis在這邊主要看成cache使用。
這裏採用Redis的List數據結構或sorted set 結構, 方便實現最新列表or排行榜 等業務場景。
其實這業務場景也能夠算在計數上,也是採用Hash。以下:
當在集羣環境時候,java ConcurrentLinkedQueue 就沒法知足咱們需求,此時能夠採用Redis的List數據結構實現分佈式的消息隊列。
Redis使用的是常駐內存的緩存,速度很是快。LPUSH用來插入一個內容ID,做爲關鍵字存儲在列表頭部。LTRIM用來限制列表中的項目數最多爲5000。若是用戶須要的檢索的數據量超越這個緩存容量,這時才須要把請求發送到數據庫。
若是一篇文章被刪除,能夠使用LREM從緩存中完全清除掉。
排行榜(leader board)按照得分進行排序。ZADD命令能夠直接實現這個功能,而ZREVRANGE命令能夠用來按照得分來獲取前100名的用戶,ZRANK能夠用來獲取用戶排名,很是直接並且操做容易。
這就像Reddit的排行榜,得分會隨着時間變化。LPUSH和LTRIM命令結合運用,把文章添加到一個列表中。一項後臺任務用來獲取列表,並從新計算列表的排序,ZADD命令用來按照新的順序填充生成列表。列表能夠實現很是快速的檢索,即便是負載很重的站點。
使用unix時間做爲關鍵字,用來保持列表可以按時間排序。對current_time和time_to_live進行檢索,完成查找過時項目的艱鉅任務。另外一項後臺任務使用ZRANGE...WITHSCORES進行查詢,刪除過時的條目。
這是特定訪問者的問題,能夠經過給每次頁面瀏覽使用SADD命令來解決。SADD不會將已經存在的成員添加到一個集合。
使用Redis原語命令,更容易實施垃圾郵件過濾系統或其餘實時跟蹤系統。
在當前的編程中隊列隨處可見。除了push和pop類型的命令以外,Redis還有阻塞隊列的命令,可以讓一個程序在執行時被另外一個程序添加到隊列。你也能夠作些更有趣的事情,好比一個旋轉更新的RSS feed隊列。
Redis緩存使用的方式與memcache相同。
網絡應用不能無休止地進行模型的戰爭,看看這些Redis的原語命令,儘管簡單但功能強大,把它們加以組合,所能完成的就更沒法想象。固然,你能夠專門編寫代碼來完成全部這些操做,但Redis實現起來顯然更爲輕鬆。
使用expire設置驗證碼失效時間
redis既能夠做爲數據庫來用,也能夠做爲緩存系統來用
Redis過時策略
本文對Redis的過時機制簡單的講解一下
講解以前咱們先拋出一個問題,咱們知道不少時候服務器常常會用到redis做爲緩存,有不少數據都是臨時緩存一下,可能用過以後好久都不會再用到了(好比暫存session,又或者只存放日行情股票數據)那麼就會出現一下幾個問題了
以前一直接觸Redis不是很深刻,最近項目當中遇到一個需求場景,須要清空一些存放在Redis的數據,主要是經過一些時間進行過濾,刪除那些不知足的數據,可是這樣的工做天天都須要進行,那工做量就比較大了,並且天天都須要按時去手動清理,這樣作也不切實際,後面發現Redis中有個設置時間過時的功能,即對存儲在Redis數據庫中的值能夠設置一個過時時間。做爲一個緩存數據庫,這是很是實用的。這就是咱們本文要講到的Redis過時機制。其實這個機制運用的場景十分普遍,好比咱們通常項目中的token或者一些登陸信息,尤爲是短信驗證碼都是有時間限制的,或者是限制請求次數,若是按照傳統的數據庫處理方式,通常都是本身判斷過時,這樣無疑會嚴重影響項目性能。
Redis對存儲值的過時處理其實是針對該值的鍵(key)處理的,即時間的設置也是設置key的有效時間。Expires字典保存了全部鍵的過時時間,Expires也被稱爲過時字段。
注:
一、除了字符串本身獨有設置過時時間的方法外,其餘方法都須要依靠expire方法來設置時間
二、若是沒有設置時間,那緩存就是永不過時
三、若是設置了過時時間,以後又想讓緩存永不過時,使用persist key
通常主要包括4種處理過時方,其中expire都是以秒爲單位,pexpire都是以毫秒爲單位的。
1 EXPIRE key seconds //將key的生存時間設置爲ttl秒 2 PEXPIRE key milliseconds //將key的生成時間設置爲ttl毫秒 3 EXPIREAT key timestamp //將key的過時時間設置爲timestamp所表明的的秒數的時間戳 4 PEXPIREAT key milliseconds-timestamp //將key的過時時間設置爲timestamp所表明的的毫秒數的時間戳
備註:timestamp爲unix時間戳(例如:timestamp=1499788800 表示將在2017.07.12過時)
一、2兩種方式是設置一個過時的時間段,就是我們處理驗證碼最經常使用的策略,設置三分鐘或五分鐘後失效,把分鐘數轉換成秒或毫秒存儲到Redis中。
三、4兩種方式是指定一個過時的時間 ,好比優惠券的過時時間是某年某月某日,只是單位不同。
下面咱們就以EXPIREAT爲例子簡單講解下用法。
返回值
一個整數值1或0,以下:
語法
如下是以Redis的EXPIREAT命令的基本語法。
1 redis 127.0.0.1:6379> Expireat KEY_NAME TIME_IN_UNIX_TIMESTAMP
示例
首先,在Redis中建立一個鍵:akey
,並在akey
中設置一些值。
1 redis 127.0.0.1:6379> SET akey redis 2 OK
如今,爲設置建立的鍵設置超時時間爲60 秒。
1 127.0.0.1:6379> SET akey redis 2 OK 3 127.0.0.1:6379> EXPIREAT akey 1393840000 4 (integer) 1 5 127.0.0.1:6379> EXISTS akey 6 (integer) 0 7 127.0.0.1:6379> SET akey redis 8 OK 9 127.0.0.1:6379> EXPIREAT akey 1493840000 10 (integer) 1 11 127.0.0.1:6379> EXISTS akey 12 (integer) 1
其餘三個用法相似,這裏不逐一闡述
對字符串特殊處理的方式爲SETEX命令,SETEX命令爲指定的 key 設置值及其過時時間。若是 key 已經存在, SETEX 命令將會替換舊的值。
返回值
設置成功時返回 OK 。
語法
Redis Setex 命令基本語法以下:
redis 127.0.0.1:6379> SETEX KEY_NAME TIMEOUT VALUE
示例
1 redis 127.0.0.1:6379> SETEX mykey 60 redis 2 OK 3 redis 127.0.0.1:6379> TTL mykey 4 60 5 redis 127.0.0.1:6379> GET mykey 6 "redis
看完上面三種策略後能夠得出如下結論:
定時刪除和按期刪除爲主動刪除:Redis會按期主動淘汰一批已過去的key
惰性刪除爲被動刪除:用到的時候纔會去檢驗key是否是已過時,過時就刪除
惰性刪除爲redis服務器內置策略
按期刪除能夠經過:
注意:
# 設置數據庫數量。默認爲16個庫,默認使用DB 0,能夠使用"select 1"來選擇一號數據庫 # 注意:因爲默認使用0號數據庫,那麼咱們所作的全部的緩存操做都存在0號數據庫上, # 當你在1號數據庫上去查找的時候,就查不到以前set過得緩存 # 若想將0號數據庫上的緩存移動到1號數據庫,能夠使用"move key 1" databases 16
惰性刪除+按期刪除
過時key對RDB沒有任何影響
過時key對AOF沒有任何影響
重寫時,會先判斷key是否過時,已過時的key不會重寫到aof文件
單線程如何知足線上運行速度?
Redis服務器是一個事件驅動程序。處理如下兩類事件:
Redis 基於Reactor模式開發了本身的網絡事件處理器即文件事件處理器
文件事件處理器使用I/O多路複用程序來同時監聽多個套接字,並根據套接字目前執行的任務來爲套接字關聯不一樣的事務處理器。
當被監聽的套接字準備好執行鏈接應答、讀取、寫入、關閉等操做時,與操做相對應的文件事件就會產生,這時文件事件處理器就會調用套接字以前關聯好的事件處理器來處理這些事件。
文件事件處理器 由四個部分組成:套接字、I/O多路複用程序、文件事件分派器以及事件處理器 I/O多路複用程序老是會將全部產生事件的套接字放到一個隊列裏面,而後經過這個隊列以有序、同步、每次一個套接字的方式向文件事件分派器傳送套接字。當上一個套接字產生的事件被處理完畢以後,I/O多路複用程序纔會繼續向文件事件分派器傳送下一個套接字。
I/O多路複用程序 I/O多路複用程序的功能是經過包裝常見的select、epoll、evport和kqueue這些I/O多路複用函數庫來實現的。
目前Redis只使用週期性事件,而沒有使用定時事件。 一個事件時間主要由三個屬性組成:
1)id:服務器爲時間事件建立的全局惟一ID
2) when:毫秒精度的UNIX時間戳,記錄了時間事件的到達時間
3) timeProc:時間事件處理器,一個函數
Redis經過 MULTI 、 EXEC 、 WATCH 等命令來實現事務功能。事務提供了一種將多個命令請求打包,而後一次性、按順序地執行多個命令的機制,而且在事務執行期間,服務器不會中斷事務而改去執行其餘客戶端的命令請求,它會將事務中的全部命令都執行完畢,而後纔去處理其餘客戶端的命令請求。
下面給了一個事務的簡單例子:
127.0.0.1:6379> multi OK 127.0.0.1:6379> set "name" "wangjia06" QUEUED 127.0.0.1:6379> get "name" QUEUED 127.0.0.1:6379> set "company" "dianping" QUEUED 127.0.0.1:6379> get "company" QUEUED 127.0.0.1:6379> set age 28 QUEUED 127.0.0.1:6379> get age QUEUED 127.0.0.1:6379> exec 1) OK 2) "wangjia06" 3) OK 4) "dianping" 5) OK 6) "28"
退出一個事務能夠用 MULTI 命令, 此時再執行事務會報錯:
127.0.0.1:6379> multi OK 127.0.0.1:6379> get "company" QUEUED 127.0.0.1:6379> get "age" QUEUED 127.0.0.1:6379> discard OK 127.0.0.1:6379> exec (error) ERR EXEC without MULTI
一個事務包括三個步驟:
Redis事務內會遇到兩種錯誤:
127.0.0.1:6379> get name "wangjia08" 127.0.0.1:6379> multi OK 127.0.0.1:6379> set name "wangjia09" QUEUED 127.0.0.1:6379> get (error) ERR wrong number of arguments for 'get' command 127.0.0.1:6379> get name QUEUED 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379>
127.0.0.1:6379> multi OK 127.0.0.1:6379> set sex man QUEUED 127.0.0.1:6379> lpop sex QUEUED 127.0.0.1:6379> exec 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 127.0.0.1:6379> get sex "man" 127.0.0.1:6379>
Redis不支持事務回滾功能,緣由大概有兩點:
watch 命令給redis事務提供了一個 CAS 功能。它是一個樂觀鎖,檢查被監視的健是否至少有一個已經被修改過了,若是是的話拒絕執行,並返回nil表明執行失敗。
以下面例子,在一個客戶端開啓事務前對鍵「name」進行監視,若在這個事務執行前,另外一個客戶端修改了鍵「name」的值,以後事務執行後就會報nil。
127.0.0.1:6379> watch "name" OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set age 10 QUEUED 127.0.0.1:6379> get "name" QUEUED 127.0.0.1:6379> exec (nil)
127.0.0.1:6379> set "name" "wangjia08" OK
watch 命令監視多個鍵:
127.0.0.1:6379> watch "name" "age" OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> get "name" QUEUED 127.0.0.1:6379> get "age" QUEUED 127.0.0.1:6379> exec (nil) 127.0.0.1:6379>
127.0.0.1:6379> set "age" 11 OK
在 redis 中,容許用戶設置最大使用內存大小 server.maxmemory,在內存限定的狀況下是頗有用的。譬如,在一臺 8G 機子上部署了 4 個 redis 服務點,每個服務點分配 1.5G 的內存大小,減小內存緊張的狀況,由此獲取更爲穩健的服務。
redis中當內存超過限制時,按照配置的策略,淘汰掉相應的kv,使得內存能夠繼續留有足夠的空間保存新的數據。redis 肯定驅逐某個鍵值對後,會刪除這個數據,並將這個數據變動消息發佈到本地(AOF 持久化)和從機(主從鏈接)。
redis 提供 6種數據淘汰策略:
noeviction:不刪除任意數據(但redis還會根據引用計數器進行釋放),這時若是內存不夠時,會直接返回錯誤。
默認的內存策略是noeviction,在Redis中LRU算法是一個近似算法,默認狀況下,Redis隨機挑選5個鍵,而且從中選取一個最近最久未使用的key進行淘汰,在配置文件中能夠經過maxmemory-samples的值來設置redis須要檢查key的個數,可是栓查的越多,耗費的時間也就越久,可是結構越精確(也就是Redis從內存中淘汰的對象未使用的時間也就越久~),設置多少,綜合權衡。
LRU 數據淘汰機制是這樣的:
在數據集中隨機挑選幾個鍵值對,取出其中 lru 最小的鍵值對淘汰。因此,你會發現,redis
並非保證取得全部數據集中最近最少使用(LRU)的鍵值對,而只是隨機挑選的幾個鍵值對中的。
TTL 數據淘汰機制是這樣的:
從過時時間的表中隨機挑選幾個鍵值對,取出其中 ttl 最大的鍵值對淘汰。一樣你會發現,redis
並非保證取得全部過時時間的表中最快過時的鍵值對,而只是隨機挑選的幾個鍵值對中的。
Redis的併發競爭問題,主要是發生在併發寫競爭。
考慮到redis沒有像db中的sql語句,update val = val + 10 where ...,沒法使用這種方式進行對數據的更新。
假若有某個key = "price", value值爲10,如今想把value值進行+10操做。正常邏輯下,就是先把數據key爲price的值讀回來,加上10,再把值給設置回去。若是隻有一個鏈接的狀況下,這種方式沒有問題,能夠工做得很好,但若是有兩個鏈接時,兩個鏈接同時想對還price進行+10操做,就可能會出現問題了。
例如:兩個鏈接同時對price進行寫操做,同時加10,最終結果咱們知道,應該爲30纔是正確。
考慮到一種狀況:
T1時刻,鏈接1將price讀出,目標設置的數據爲10+10 = 20。
T2時刻,鏈接2也將數據讀出,也是爲10,目標設置爲20。
T3時刻,鏈接1將price設置爲20。
T4時刻,鏈接2也將price設置爲20,則最終結果是一個錯誤值20。
如何解決?
方案一:能夠使用獨佔鎖的方式,相似操做系統的mutex機制。(網上有例子,http://blog.csdn.net/black_ox/article/details/48972085 不過實現相對複雜,成本較高)
方案二:使用樂觀鎖的方式進行解決(成本較低,非阻塞,性能較高)
如何用樂觀鎖方式進行解決?
本質上是假設不會進行衝突,使用redis的命令watch進行構造條件。僞代碼以下:
watch price
get price $price
$price = $price + 10
multi
set price $price
exec
解釋一下:
watch這裏表示監控該key值,後面的事務是有條件的執行,若是從watch的exec語句執行時,watch的key對應的value值被修改了,則事務不會執行。
一樣考慮剛剛的場景,
T1時刻,鏈接1對price進行watch,讀出price值爲10,目標計算爲20;
T2時刻,鏈接2對price進行watch,讀出price值爲10,目標計算爲20;
T3時刻,鏈接2將目標值爲20寫到redis中,執行事務,事務返回成功。
T4時刻,鏈接1也對price進行寫操做,執行事務時,因爲以前已經watch了price,price在T1至T4之間已經被修改過了,因此事務執行失敗。
綜上,該樂觀鎖機制能夠簡單明瞭的解決了寫衝突的問題。
又問:若是多個寫操做同時過來,100個寫操做同時watch,則最終只會有一個成功,99個執行失敗,何解?
若是同時進行有多個請求進行寫操做,例如同一時刻有100個請求過來,那麼只會有一個最終成功,其他99個所有會失敗,效率不高。
並且從業務層面,有些是不可接受的場景。例如:你們同時去搶一個紅包,若是背後也是用樂觀鎖的機制去處理,那每一個請求後都只有一我的成功打開紅包,這對業務是不可忍受的。
在這種狀況下,若是想讓整體效率最大化,能夠採用排隊的機制進行。
將全部須要對同一個key的請求進行入隊操做,而後用一個消費者線程從隊頭依次讀出請求,並對相應的key進行操做。
這樣對於同一個key的全部請求就都是順序訪問,正常邏輯下則不會有寫失敗的狀況下產生 。從而最大化寫邏輯的整體效率。
Redis的內存存儲結構是個大的字典存儲,也就是咱們一般說的哈希表。Redis小到能夠存儲幾萬記錄的CACHE,大到能夠存儲幾千萬甚至上億的記錄(看內存而定),這充分說明Redis做爲緩衝的強大。Redis的核心數據結構就是字典(dict),dict在數據量不斷增大的過程當中,會遇到HASH(key)碰撞的問題,若是DICT不夠大,碰撞的機率增大,這樣單個hash 桶存儲的元素會越來愈多,查詢效率就會變慢。若是數據量從幾千萬變成幾萬,不斷減少的過程,DICT內存卻會形成沒必要要的浪費。Redis的dict在設計的過程當中充分考慮了dict自動擴大和收縮,實現了一個稱之爲rehash的過程。使dict出發rehash的條件有兩個:
1)總的元素個數 除 DICT桶的個數獲得每一個桶平均存儲的元素個數(pre_num),若是 pre_num > dict_force_resize_ratio,就會觸發dict 擴大操做。dict_force_resize_ratio = 5。
2)在總元素 * 10 < 桶的個數,也就是,填充率必須<10%, DICT便會進行收縮,讓total / bk_num 接近 1:1。
總結,Redis的rehash動做是一個內存管理和數據管理的一個核心操做,因爲Redis主要使用單線程作數據管理和消息效應,它的rehash數據遷移過程採用的是漸進式的數據遷移模式,這樣作是爲了防止rehash過程太長堵塞數據處理線程。並無採用memcached的多線程遷移模式。關於memcached的rehash過程,之後再作介紹。從redis的rehash過程設計的很巧,也很優雅。在這裏值得注意的是,redis在find數據的時候,是同時查找正在遷移的ht[0]和被遷移的ht[1]。防止遷移過程數據命不中的問題。