REmote DIctionary Server(Redis) 是一個由Salvatore Sanfilippo寫的key-value存儲系統(tǒng)脆侮。
Redis是一個開源的使用ANSI C語言編寫、遵守BSD協(xié)議勇劣、支持網(wǎng)絡靖避、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫比默,并提供多種語言的API幻捏。
它通常被稱為數(shù)據(jù)結構服務器,因為值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等類型命咐。
Redis的特點:
Redis 與其他 key - value 緩存產(chǎn)品有以下三個特點:
- Redis支持數(shù)據(jù)的持久化篡九,可以將內(nèi)存中的數(shù)據(jù)保存在磁盤中,重啟的時候可以再次加載進行使用醋奠。
- Redis不僅僅支持簡單的key-value類型的數(shù)據(jù)榛臼,同時還提供list,set窜司,zset讽坏,hash等數(shù)據(jù)結構的存儲。
- Redis支持數(shù)據(jù)的備份例证,即master-slave模式的數(shù)據(jù)備份路呜。
Redis的優(yōu)勢:
- 性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
- 豐富的數(shù)據(jù)類型 – Redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數(shù)據(jù)類型操作织咧。
- 原子 – Redis的所有操作都是原子性的胀葱,同時Redis還支持對幾個操作全并后的原子性執(zhí)行。
- 豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過期等等特性笙蒙。
Redis與其他key-value存儲有什么不同抵屿?
- Redis有著更為復雜的數(shù)據(jù)結構并且提供對他們的原子性操作,這是一個不同于其他數(shù)據(jù)庫的進化路徑捅位。Redis的數(shù)據(jù)類型都是基于基本數(shù)據(jù)結構的同時對程序員透明轧葛,無需進行額外的抽象。
- Redis運行在內(nèi)存中但是可以持久化到磁盤艇搀,所以在對不同數(shù)據(jù)集進行高速讀寫時需要權衡內(nèi)存尿扯,因為數(shù)據(jù)量不能大于硬件內(nèi)存。在內(nèi)存數(shù)據(jù)庫方面的另一個優(yōu)點是焰雕,相比在磁盤上相同的復雜的數(shù)據(jù)結構衷笋,在內(nèi)存中操作起來非常簡單,這樣Redis可以做很多內(nèi)部復雜性很強的事情矩屁。同時辟宗,在磁盤格式方面他們是緊湊的以追加的方式產(chǎn)生的爵赵,因為他們并不需要進行隨機訪問。
Redis應用場景
1. 顯示最新的項目列表
下面這個語句常用來顯示最新項目泊脐,隨著數(shù)據(jù)多了空幻,查詢毫無疑問會越來越慢。
SELECT * FROM foo WHERE ... ORDER BY time DESC LIMIT 10
在Web應用中容客,“列出最新的回復”之類的查詢非常普遍氛悬,這通常會帶來可擴展性問題。這令人沮喪耘柱,因為項目本來就是按這個順序被創(chuàng)建的,但要輸出這個順序卻不得不進行排序操作棍现。
類似的問題就可以用Redis來解決调煎。比如說,我們的一個Web應用想要列出用戶貼出的最新20條評論己肮。在最新的評論邊上我們有一個“顯示全部”的鏈接士袄,點擊后就可以獲得更多的評論。
我們假設數(shù)據(jù)庫中的每條評論都有一個唯一的遞增的ID字段谎僻。我們可以使用分頁來制作主頁和評論頁娄柳,使用Redis的模板:
- 每次新評論發(fā)表時,我們會將它的ID添加到一個Redis列表:
LPUSH latest.comments <ID>
- 我們將列表裁剪為指定長度艘绍,因此Redis只需要保存最新的5000條評論:
LTRIM latest.comments 0 5000
- 每次我們需要獲取最新評論的項目范圍時赤拒,我們調(diào)用一個函數(shù)來完成(使用偽代碼):
FUNCTION get_latest_comments(start,num_items): id_list = redis.lrange("latest.comments",start,start+num_items-1) IF id_list.length < num_items id_list = SQL_DB("SELECT ... ORDER BY time LIMIT ...") END RETURN id_list END
這里我們做的很簡單。在Redis中我們的最新ID使用了常駐緩存诱鞠,這是一直更新的挎挖。但是我們做了限制不能超過5000個ID,因此我們的獲取ID函數(shù)會一直詢問Redis航夺。只有在start/count參數(shù)超出了這個范圍的時候蕉朵,才需要去訪問數(shù)據(jù)庫。
我們的系統(tǒng)不會像傳統(tǒng)方式那樣“刷新”緩存阳掐,Redis實例中的信息永遠是一致的始衅。SQL數(shù)據(jù)庫(或是硬盤上的其他類型數(shù)據(jù)庫)只是在用戶需要獲取“很遠”的數(shù)據(jù)時才會被觸發(fā),而主頁或第一個評論頁是不會麻煩到硬盤上的數(shù)據(jù)庫了缭保。
2. 刪除與過濾
我們可以使用LREM來刪除評論汛闸。如果刪除操作非常少,另一個選擇是直接跳過評論條目的入口艺骂,報告說該評論已經(jīng)不存在蛉拙。
有些時候你想要給不同的列表附加上不同的過濾器。如果過濾器的數(shù)量受到限制彻亲,你可以簡單的為每個不同的過濾器使用不同的Redis列表孕锄。畢竟每個列表只有5000條項目吮廉,但Redis卻能夠使用非常少的內(nèi)存來處理幾百萬條項目。
3. 排行榜相關
另一個很普遍的需求是各種數(shù)據(jù)庫的數(shù)據(jù)并非存儲在內(nèi)存中畸肆,因此在按得分排序以及實時更新這些幾乎每秒鐘都需要更新的功能上數(shù)據(jù)庫的性能不夠理想宦芦。
典型的比如那些在線游戲的排行榜,比如一個Facebook的游戲轴脐,根據(jù)得分你通常想要:
列出前100名高分選手
列出某用戶當前的全球排名
這些操作對于Redis來說小菜一碟调卑,即使你有幾百萬個用戶,每分鐘都會有幾百萬個新的得分大咱。
模式是這樣的恬涧,每次獲得新得分時,我們用這樣的代碼:
ZADD leaderboard
你可能用userID來取代username碴巾,這取決于你是怎么設計的溯捆。
得到前100名高分用戶很簡單:
ZREVRANGE leaderboard 0 99
用戶的全球排名也相似,只需要:
ZRANK leaderboard
4. 按照用戶投票和時間排序
排行榜的一種常見變體模式就像Reddit或Hacker News用的那樣厦瓢,新聞按照類似下面的公式根據(jù)得分來排序:score = points / time^alpha
因此用戶的投票會相應的把新聞挖出來提揍,但時間會按照一定的指數(shù)將新聞埋下去。下面是我們的模式煮仇,當然算法由你決定劳跃。
模式是這樣的,開始時先觀察那些可能是最新的項目浙垫,例如首頁上的1000條新聞都是候選者刨仑,因此我們先忽視掉其他的,這實現(xiàn)起來很簡單夹姥。
每次新的新聞貼上來后贸人,我們將ID添加到列表中,使用LPUSH + LTRIM佃声,確保只取出最新的1000條項目艺智。
有一項后臺任務獲取這個列表,并且持續(xù)的計算這1000條新聞中每條新聞的最終得分圾亏。計算結果由ZADD命令按照新的順序填充生成列表十拣,老新聞則被清除。這里的關鍵思路是排序工作是由后臺任務來完成的志鹃。
5. 過期項目處理
另一種常用的項目排序是按照時間排序夭问。我們使用unix時間作為得分即可。
模式如下:
每次有新項目添加到我們的非Redis數(shù)據(jù)庫時曹铃,我們把它加入到排序集合中缰趋。這時我們用的是時間屬性,current_time和time_to_live。
另一項后臺任務使用ZRANGE…SCORES查詢排序集合秘血,取出最新的10個項目味抖。如果發(fā)現(xiàn)unix時間已經(jīng)過期,則在數(shù)據(jù)庫中刪除條目灰粮。
6. 計數(shù)
Redis是一個很好的計數(shù)器仔涩,這要感謝INCRBY和其他相似命令。
我相信你曾許多次想要給數(shù)據(jù)庫加上新的計數(shù)器粘舟,用來獲取統(tǒng)計或顯示新信息熔脂,但是最后卻由于寫入敏感而不得不放棄它們。
好了柑肴,現(xiàn)在使用Redis就不需要再擔心了霞揉。有了原子遞增(atomic increment),你可以放心的加上各種計數(shù)晰骑,用GETSET重置适秩,或者是讓它們過期。
例如這樣操作:
INCR user: EXPIRE
user: 60
你可以計算出最近用戶在頁面間停頓不超過60秒的頁面瀏覽量些侍,當計數(shù)達到比如20時,就可以顯示出某些條幅提示政模,或是其它你想顯示的東西岗宣。
7. 特定時間內(nèi)的特定項目
另一項對于其他數(shù)據(jù)庫很難,但Redis做起來卻輕而易舉的事就是統(tǒng)計在某段特點時間里有多少特定用戶訪問了某個特定資源淋样。比如我想要知道某些特定的注冊用戶或IP地址耗式,他們到底有多少訪問了某篇文章。
每次我獲得一次新的頁面瀏覽時我只需要這樣做:
SADD page:day1:<page_id> <user_id>
當然你可能想用unix時間替換day1趁猴,比如time()-(time()%3600*24)等等刊咳。
想知道特定用戶的數(shù)量嗎?只需要使用
SCARD page:day1:<page_id>
需要測試某個特定用戶是否訪問了這個頁面儡司?
8. 實時分析正在發(fā)生的情況娱挨,用于數(shù)據(jù)統(tǒng)計與防止垃圾郵件等
我們只做了幾個例子,但如果你研究Redis的命令集捕犬,并且組合一下跷坝,就能獲得大量的實時分析方法,有效而且非常省力碉碉。使用Redis原語命令柴钻,更容易實施垃圾郵件過濾系統(tǒng)或其他實時跟蹤系統(tǒng)。
9. Pub/Sub
Redis的Pub/Sub非常非常簡單垢粮,運行穩(wěn)定并且快速贴届。支持模式匹配,能夠實時訂閱與取消頻道。
10. 隊列
你應該已經(jīng)注意到像list push和list pop這樣的Redis命令能夠很方便的執(zhí)行隊列操作了毫蚓,但能做的可不止這些:比如Redis還有l(wèi)ist pop的變體命令占键,能夠在列表為空時阻塞隊列。
11. 緩存
Redis的緩存部分值得寫一篇新文章绍些,我這里只是簡單的說一下捞慌。Redis能夠替代memcached,讓你的緩存從只能存儲數(shù)據(jù)變得能夠更新數(shù)據(jù)柬批,因此你不再需要每次都重新生成數(shù)據(jù)了啸澡。