還記得第一次面試來也的時候,面試官問我,「會MongoDB嗎?」,「不會」;「知道redis嗎?」,「知道,可是沒用過」,「emm……」html
在面對高併發的數據讀取的時候,當連表查詢的需求不是那麼的強烈,此時,非關係型數據庫獲得了高速的發展。其中,非關係型數據庫中的鍵值數據庫中的redis即是咱們今天須要講的內容。其中redis爲了保證效率,會將數據保存在內存中(固然redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件)。python
Redis亦被稱爲數據機構服務器,由於它的值(Value)能夠是字符串(String),哈希(Hash),列表(List),集合(Sets)和有序集合(sorted sets)。程序員
Redis 在線測試:http://try.redis.io/面試
下面是一段來自於菜鳥教程的介紹redis
Redis 簡介
Redis 是徹底開源免費的,遵照BSD協議,是一個高性能的key-value數據庫。數據庫
Redis 與其餘 key - value 緩存產品有如下三個特色:編程
- Redis支持數據的持久化,能夠將內存中的數據保存在磁盤中,重啓的時候能夠再次加載進行使用。
- Redis不只僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲。
- Redis支持數據的備份,即master-slave模式的數據備份。
Redis 優點
- 性能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s 。
- 豐富的數據類型 – Redis支持二進制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數據類型操做。
- 原子 – Redis的全部操做都是原子性的,意思就是要麼成功執行要麼失敗徹底不執行。單個操做是原子性的。多個操做也支持事務,即原子性,經過MULTI和EXEC指令包起來。
- 豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過時等等特性。
Redis與其餘key-value存儲有什麼不一樣?
- Redis有着更爲複雜的數據結構而且提供對他們的原子性操做,這是一個不一樣於其餘數據庫的進化路徑。Redis的數據類型都是基於基本數據結構的同時對程序員透明,無需進行額外的抽象。
- Redis運行在內存中可是能夠持久化到磁盤,因此在對不一樣數據集進行高速讀寫時須要權衡內存,由於數據量不能大於硬件內存。在內存數據庫方面的另外一個優勢是,相比在磁盤上相同的複雜的數據結構,在內存中操做起來很是簡單,這樣Redis能夠作不少內部複雜性很強的事情。同時,在磁盤格式方面他們是緊湊的以追加的方式產生的,由於他們並不須要進行隨機訪問。
redis怎麼安裝,我就不作介紹了,由於不一樣的系統安裝方式不同。若是不想安裝話可使用在線的redis環境。不過仍是推薦一下,由於接下來會使用python去實現一些操做。其中redis數據庫的可視化可使用redis-desktop-manager。命令行操做使用redis-cli。緩存
接下來我將經過不一樣的數據結構來介紹redis的操做。bash
字符串(Strings)是Redis的基本數據結構之一,由key和value組成。咱們能夠這樣類比成編程語言的變量:keya表明變量名,value表明變量值。服務器
查看全部的key的命令
keys *
建立字符串
set key value
若是value中有空格,則須要使用**""**將value包起來。如:
set x "xxx xxx xxx"
讀取字符串
get key
若是獲取一個不存在的key,則會返回nil。
修改key裏面的值
進行修改
下面這個命令key存在則修改,不存在則建立
set key 新的值
若是咱們不但願set的命令覆蓋舊值怎麼辦?在使用"NX"參數便可。這樣,當key存在時,使用set key value NX
並不能覆蓋原來的值。
set key value NX
進行增添
若是咱們想在value的末尾加上一些字符串,使用append命令。(當key不存在的時候,則會建立key)固然,若是若是值有空格的話,和set的處理方法同樣。
append key value
數字進行修改
注意下面的命令只針對於value爲數字的狀況,不然就會報錯。
# 讓key裏面的數字加1
incr key
# 減1
decr key
# 加n
incrby key n
# 減n
decrby key n
刪除
若是key存在則放回1,不然返回0
del key
代碼實現
下面是使用python對以上的全部命令進行實現(任何編程語言都差很少)
import redis
client = redis.Redis()
# 得到全部的key
keys = client.keys()
# 建立字符串
client.set("thisKey","thatValue")
# 得到value
value = client.get("thisKey")
# 不覆蓋修改
client.set("thisKey","way",nx=True)
# 增添
client.append("thisKey","newMSg")
# 對數字就行修改
client.set("num",1)
client.incr("num")
client.incrby("num",10)
client.decr("num")
client.decrby("num",4)
# 進行刪除操做
client.delete("thisKey")
在前面咱們介紹了字符串類型的Redis儲存方案,這個時候咱們能夠想想若是我須要儲存10w我的的分數,右須要儲存10w我的的密碼(假如這樣作),那麼咱們須要多少多少個key?20w個key!!那麼咱們的key又該怎樣分配呢(注意:key不能重複)?咱們是否是得這樣:用戶名_score,用戶名_pwd,在用戶名後面加上不一樣的類型來表明不一樣的數據。那麼在redis如何解決這些問題呢?使用哈希表!!關於哈希表的數據結構咱們能夠看看這篇
Redis hash 是一個string類型的field和value的映射表(key任然爲key),hash特別適合用於存儲對象,每一個 hash 能夠存儲 232 - 1 鍵值對(43多億)。使用Hash表不只可以減小Redis中key的個數,還能優化存儲空間,佔用的內存要比字符串小不少。
下面是redis的存儲示意圖:
添加數據:
一次添加一個鍵值對數據:
hset key field value
一次添加多個鍵值對數據:
hmset key field1 value1 [field2 value2 ]
固然若是咱們不想對已經存在的field進行修改
hsetnx key field value
得到數據:
得到一個字段的值:
hget key field
得到多個字段的值:
hmget key field1 [field2]
得到全部的字段名和值
hgetall key
固然咱們也能夠分別得到全部的field得到value
hvals key
hkeys key
判斷是否存在某字段
HEXISTS key field
若是字段存在則返回1,若是不存在則返回0
得到哈希表中字段的數量
hlen key
接下來我就再也不使用python實現這些東西了,會更多的來介紹各類結構的特色。若是想了解更多的指令能夠看菜鳥教程
列表是一種很神奇的結構,能夠把列表成一根水管,數據從能夠從一邊進,而後從另一邊出來(固然,那一邊便可以進又能夠出,只不過順序不一樣而已)。那麼這種結構有什麼用處呢?咱們能夠以發消息爲例。發消息咱們須要保證消息到達的順序,那麼咱們是否是就可使用列表了呢?例如:發送消息從左邊進,接受消息從右邊獲得。下面介紹幾個簡單的指令:
插入數據
l表明left(左),r表明right(右)
從左邊插入數據
lpush key value
從右邊插入數據
rpush key value
得到列表的長度
注意下面的第一個l並非表明left,而是表明list
llen key
查看數據
lrang key 開始索引 結束索引
索引從最左邊開始編號,意思就是最後一個lpush的數據的索引是0(將列表想成一個小水管就行)。若是開始索引和結束索引同樣,就返回索引位置的值。那麼若是從右邊開始呢?使用「負索引」便可。其中**-1**表明最右邊的數據。-2表明最右邊的第二個數據。
彈出數據
彈出最左邊的數據
lpop key
彈出最右邊的數據
rpop key
彈出數據和查看數據的差異在於,彈出數據的同時也會將數據進行刪除。
這個集合和數學中的集合有着差很少的概念。怎麼說呢?在redis的集合中,數據是無序的,不能重複。
添加數據:
s表明集合(set)
sadd key value1 value2 value3……
得到集合中元素的數量
若是不存在這個集合則返回0
scard key
從集合中獲取數據
spop key count
前面咱們說道,集合是無序的,因此spop的獲取也是無序的。(得到數據後會將數據刪除)count表明獲取幾條數據。
這個是得到集合的全部數據(並不會刪除數據)。不過這個命令在生產環境中最好不要使用,由於數據量大的話你的服務器可能就炸了。
SMEMBERS key
判斷數據是否存在
sismember key value
存在返回1,不然返回0
刪除數據
srem key value1 value2……
下面即是數學上面的知識了
取交集
sinter key1 key2……
取並集
sunion key1 key2……
取差集
sdiff key1 key2……
有序集合,顧名思義就是集合裏面的數據是有序的。那麼它有什麼含義呢?咱們想象一下,在一個高併發的場景中,數據是一直更新的,若是咱們將數據存到數據庫中,若是須要實時獲取排名的話,那麼確定會對數據的性能形成很大的影響。畢竟數據量越大,排序時間也就越緩慢。和集合不一樣的是,有序集合的元素會關聯一個double類型的分數,其中元素不能重複,可是分數能夠重複。
添加數據
zadd key score1 member1 [score2 member2]
前面說過,score必須爲double類型,因此若是輸入非double則會報錯。
修改數據
修改數據可使用zadd進行修改數據的分數,同時能夠添加NX參數
zadd key NX sorce member
還可使用zincrby對數據對數據的分數進行加減操做
ZINCRBY key 改變量 member
其中改變量既能夠爲正數也能夠爲負數。若是member不存在則會建立,member的分數和改變量一致。
獲取數據
基於評分範圍內的排序
zrangebyscore 有序集合名 評分下限 評分上限 [withsores limit 切片開始位置 結果數量]
zrevrangebyscore 有序集合名 評分下限 評分上限 [withsores limit 切片開始位置 結果數量]
其中使用括號括起來的表明能夠省略,若是withsores省略表明只返回值,不返回評分。省略「limit 切片開始位置 結果數量」表明不對結果進行切片。
基於位置的排序
從小到大排序
位置是從零開始的
zrange 有序集合名 開始位置(含) 結束位置 (含)[withsores]
從大到小排序
zrevrange 有序集合名 開始位置(含) 結束位置 (含)[withsores]
得到排名
zrank 有序集合名 值
zrevrank 有序集合名 值
zrank 和 zrevrank的區別在於,zrank排名是從0開始的,評分越小則排名越靠近0,評分最小的值排名爲0。而zrevrank則是評分越大則越靠近0。相同點在於若是值不存在則返回None。
得到一個值的評分
zscore 有序集合名 值
若是一個值不存在則返回None。
查看某個評分範圍內的值有多少
zcount 有序集合名 評分下限 評分上限
咱們來講一個場景,仍是之前面的發消息場景爲例,若是服務端的消息在進行更新,那麼咱們如何更新客戶端的信息呢?按照前面的方法,咱們只有使用輪詢查詢的方式,按照必定的時間(好比說1s)檢查redis,看消息是否發生改變,若是消息發生了改變,則客戶端進行更新,那麼接下來就會有如下的問題:
這個時候咱們就是可使用redis的「發佈/訂閱」模式實現消息通訊。
發佈消息
publish 頻道名 信息
訂閱頻道
subscribe 頻道名1 頻道名2……
其中會返回3條消息:第一條是信息的類型,第二條是頻道名,第三條是被髮布的內容。其中須要注意,只能接受目前的消息,新加入的訂閱是沒辦法接受到之前的訂閱的。
以上即是redis的一個很簡單的入門教程。只介紹了redis的簡單使用和一些基本操做,至於redis命令中更復雜的指令,百度或者google就好了。對於redis,我以爲咱們(做爲一個學生)應該關注的是redis爲何可以如此優秀?裏面用了什麼數據結構,以及如何可以將redis應用到高併發的場景中,怎麼實現多服務器數據的保存以及備份問題。