深入淺出Redis(一)簡介,深入淺出redis簡介

來源:互聯網
上載者:User

深入淺出Redis(一)簡介,深入淺出redis簡介
Redis是什麼官方描述Redis是一個開源的,支援網路、基於記憶體亦可持久化的日誌型鍵-值倉儲。個人認為這樣描述過於簡單,不足以體現出Redis的強大。Redis比起傳統的鍵-值倉儲要強大許多,事實上Redis支援五種資料結構,而傳統的鍵-值儲存只是其中之一。要真正認識Redis的強大所在,就必須理解它所支援的五種資料結構,如何操作它們以及它們能夠解決哪些類型的問題。
靈活的資料結構只是Redis強大的一方面,另一方面則它的高效能,實在是太快了,有測評說比Memcached還快。Redis的出現,很大程度彌補了memcached這類鍵-值儲存的不足,在部分場合可以對關聯式資料庫起到很好的補充作用。另外,Redis支援大量的用戶端API,從java、C到Ruby等,可以從官網http://redis.io/clients查詢是否有你需要的API。


Redis基礎既然Redis是一個鍵-值倉儲,我們先來說說鍵、值。鍵(key)是一個標識符,就像程式語裡中我們定義一個變數的名字一樣,比如a:b:c這個鍵,其中冒號':'沒有特殊含義,跟其它字元一樣,但是在實際中可以用做分隔字元來更好地維護系統的所有鍵。值(value)是我們真在關心的內容,它與鍵(key)綁定在一起,你可以存放一個字串,一個數字,或者以json或xml格式序列化一個對象,因為Redis並不關心你存放什麼東西,它只是以位元組數組的形式來儲存它們。如:

set people:id001 '{"id":"id001","name":"ZhangSan","age":"21","sex":"male"}'

這裡使用set命令把一個值(對象以json格式序列化)存放到鍵people:zhangsan中去。之後,可以使用get命令查詢對應的值:
get people:id001

能不能像關聯式資料庫或文檔型資料庫那樣,基於對象的年齡或性別來查詢呢?對不起,不可以,Redis不支援基於對象的屬性來查詢,因為Redis只是簡單的以位元組資料的形式來儲存,並不關心存放的內容。這既是Redis的限制,同時也成就了它最大的優點:快。
雖然Redis本身不支援基於對象的屬性來查詢,我們還是可以自己來實現,比如要基於name來查詢,我們可以這麼做:
hset people:lookup:name ZhangSan id001

把name與id的對應關係儲存起來,之後便可以通過name查詢到id,再根據id查詢對象了。這裡我們提出了實現的方案,但在實際中是否值得這麼做卻需要更多的考慮,畢竟我們還有關聯式資料庫,而且人為的維護這樣的依賴關係可不是件容易的事,想想如果刪除了一個對象,我們還得更新people:lookup:name這個鍵。

Redis的五種資料結構到目前為止,我們已經看到了三個命令,set、get和hset,Redis是如何知道該使用哪一種資料結構的呢?事實上,每種資料結構都單獨提供了若干命令,當使用某個具體的命令時,就決定了具體的資料結構了。比如使用set或get命令時,操作的是String類型的值;而使用hset命令時,操作的是Hash類型的值。
StringsStrings是Redis中最基本的資料類型,但是別被這個名字迷惑了,因為Redis並不關心值是什麼,所以你可以存放字串、數字、十進位串等任何東西,甚至是對象的序列化。就像之前我們使用set命令把值存入鍵中,再通過get命令取出鍵對應的值一樣,Redis就是這麼簡單。當然,與Strings相關的命令還有很多,可以到http://redis.io/commands#string查看全部的命令,這裡我們就不一一介紹了。
值得強調的是Strings類型還支援位操作,如:
127.0.0.1:6379> setbit mykey 1 1(integer) 0127.0.0.1:6379> setbit mykey 2 1(integer) 0127.0.0.1:6379> setbit mykey 3 1(integer) 0127.0.0.1:6379> setbit mykey 10 1(integer) 0127.0.0.1:6379> getbit mykey 3(integer) 1127.0.0.1:6379> bitcount mykey(integer) 4

setbit命令可以設定某個key指定位置為1或0,getbit可以查詢key指定位置的值(0或1),bitcount命令可以查詢某個key一共有多少為1的十進位位。這裡有一篇文章提到有人使用Redis位操作來統計“一天中有多少活躍使用者訪問”,對於1.3億的訪問量在一台macbook上只用了50ms和16MB的記憶體就計算出來了,是不是太神奇了。
還有一點,雖然Redis對存放的值並不關心,但有些命令卻依賴於值的類型。比如incr、decr等命令,當鍵的值不是數值的時候就會報錯:
127.0.0.1:6379> set mykey 1OK127.0.0.1:6379> incr mykey(integer) 2127.0.0.1:6379> incr mykey(integer) 3127.0.0.1:6379> get mykey"3"127.0.0.1:6379> set akey abcOK127.0.0.1:6379> incr akey(error) ERR value is not an integer or out of range

HashHash的存在使的Redis變得更加強大,功能上它類似於Java語言中的Map。對於Hash來說,一個值可以有若干個欄位(field),或者叫屬性,比起Strings類型使用序列化的方式來儲存物件來說,使用Hash來儲存對話更加合理:
127.0.0.1:6379> hset people:id001 id id001(integer) 1127.0.0.1:6379> hset people:id001 name ZhangSan(integer) 1127.0.0.1:6379> hset people:id001 age 21(integer) 1127.0.0.1:6379> hset people:id001 sex male(integer) 1127.0.0.1:6379> hget people:id001 name"ZhangSan"

比起Strings,使用Hash來存放對象的一個好處是需要更新某個屬性時,不用更新整個對象的序列化字串。
ListList相當於程式設計語言中的數組,但比數組的操作要靈活很多。List類型在Redis中是有序的,使用lpush和rpush命令可以把元素分別加到資料的左邊和右邊,lindex命令可以擷取指定位置的元素,lrange命令可以獲得指定位置區間的多個元素,lpop(rpop)命令刪除並返回最左邊(最右邊)的元素等:
127.0.0.1:6379> lpush mylist b(integer) 1127.0.0.1:6379> lpush mylist a(integer) 2127.0.0.1:6379> rpush mylist c(integer) 3127.0.0.1:6379> rpush mylist d(integer) 4127.0.0.1:6379> lindex mylist 1"b"127.0.0.1:6379> lrange mylist 2 31) "c"2) "d"127.0.0.1:6379> lpop mylist"a"127.0.0.1:6379> rpop mylist"d"127.0.0.1:6379> lrange mylist 0 -11) "b"2) "c"

通過List,我們可以非常容易的實現各個鏈表、棧等資料結構,得益於Redis的優點,使用這些實現在效能上都非常的快。
SetsSets是一個集合,比起List,它不允許重複的值,而且也不是有序的,所以不能通過下標索引來擷取元素。可以使用sadd命令向集合中加入新的元素,smember命令返回集合中的所有元素,sismember命令判斷一個元素是否屬於某個集合(值得一提的是sismember命令的複雜度是O(1),不管集合中有多少個元素,它總是花費固定的時間完成執行)。
127.0.0.1:6379> sadd myset java(integer) 1127.0.0.1:6379> sadd myset c++(integer) 1127.0.0.1:6379> sadd myset objective-c(integer) 1127.0.0.1:6379> sadd myset java(integer) 0127.0.0.1:6379> smembers myset1) "objective-c"2) "c++"3) "java"127.0.0.1:6379> sismember myset java(integer) 1127.0.0.1:6379> sismember myset c(integer) 0

對於具體超大資料量的系統來說,使用Sets做來唯一性判斷未嘗不是一個好的方案。
Sorted SetsSorted Sets是一個非常強大的資料結構,它在Sets的基礎上,為集合中每個元素繫結了一個數值,這樣一來就可以對集合做一些排序相關的操作了。zadd命令向集合中新增一個元素,同時指定該元素對應的數值;zank返回元素在集合中根據數值排序後的下標索引,zrevrank與zank類似,只是排序方式由大到小;zrange與zrevrange命令可以根據下標擷取排序後的元素,非常有用的命令。比如我們使用Sorted Sets來存放一個成績表,可以非常容易處理諸如:多少人在90分以上,多少人不及格,80分以上的人是哪些,排名第一的是誰,多少分等等查詢:
127.0.0.1:6379> zadd math:score 58 person1 63 person2 78 person3 85 person4 90 person5 100 person6(integer) 6127.0.0.1:6379> zrevrangebyscore math:score 100 60 //及格的人是哪些1) "person6"2) "person5"3) "person4"4) "person3"5) "person2"127.0.0.1:6379> zrevrangebyscore math:score 100 90 //90分以上(包括90)的人是哪些1) "person6"2) "person5"127.0.0.1:6379> zrevrangebyscore math:score 100 (90 //90分以上(不包括90)的人是哪些1) "person6"127.0.0.1:6379> zcount math:score 90 +inf //90分以上有多少人(integer) 2127.0.0.1:6379> zcount math:score -inf 59 //多少人不及格(integer) 1127.0.0.1:6379> zrevrange math:score 0 0 withscores //排名第一的是誰,多少分1) "person6"2) "100"

現實生活中有很多類似的情境、業務都可以使用Sorted Sets來解決。電商系統可以使用Sorted Sets來維護每個使用者的月消費額度,對使用者分級並進行不同的促銷措施;門戶網站可以記錄使用者每周或每月的登入次數,並對活躍使用者加以積分獎勵等手段;博額系統可以對每篇部落格的閱讀量、好評率進行統計,從進評選精品文章等等,應用的情境實在是太多了,只有你想不到,沒有做不到,而且別忘了,Redis很快。

Redis命令前面我們已經看到,Redis的每種資料類型都提供了一組相關的命令,掌握並靈活地使用這些命令是用好Redis最基本的要求,官方文檔裡面有每個命令的詳細說明,這裡我們就不再重重了。值得注意的是每個命令的時間複雜度,一般有O(1)、O(logN)和O(N)等等幾種,在生產環境一定要檢查每個命令的複雜度,如果重雜複是類似O(logN)這種與資料量相關的命令,在資料量巨大的時候就可能成為效能瓶頸,這裡需要查閱文檔看看其它命令或者若干其它命令的組合是否可以完成相同的功能,以此來提升效能。
Redis所有命令都是原子性的,這是因為Redis內部是使用單線程實現的,就算不同的用戶端同一時刻發送多條命令到Redis Server,這些命令也是串列執行的,因為在Redis Server中只有一個線程依次地處理它們。Redis能否像資料庫事務一樣,支援多條命令的原子性呢?答案是肯定的,Redis支援事務,相關的內容我們後續再介紹。
除了與資料類型相關的命令,還有一些一類型無關的命令,這裡我們簡單介紹一些。
Redis中也有資料庫的概念,就像在資料庫系統中我們可以建立多個資料庫一樣。在Redis中資料庫是通過一個數字來標識的,預設情況下是串連到0這個庫,可以使用select選擇當前的資料庫。
127.0.0.1:6379> select 1 //改變當前庫為1OK127.0.0.1:6379[1]> select 0 //改變當前庫為0,預設庫OK127.0.0.1:6379> 

exists命令可以檢查某個key是否存在;del命令刪除key;keys命令可以根據正則查詢有哪些key,但這個命令最好不要在生產環境使用;rename可以修改key的名字。
127.0.0.1:6379> set key1 1OK127.0.0.1:6379> set key2 2OK127.0.0.1:6379> exists key1(integer) 1127.0.0.1:6379> exists key0(integer) 0127.0.0.1:6379> del key1(integer) 1127.0.0.1:6379> keys *1) "key2"127.0.0.1:6379> rename key2 key2-newOK127.0.0.1:6379> keys *1) "key2-new"127.0.0.1:6379> 

Redis還支援為key指定一個到期時間,當某個key達到到期時間後,Redis將會刪除這個key。這個特性使得Redis非常適合用來做緩衝。expire命令可以設定某個key多長時間後到期(單位:秒),ttl命令可以查詢key多久後到期,persist命令刪除key的到期時間(永不到期)。
127.0.0.1:6379> set key1 1OK127.0.0.1:6379> set key2 2OK127.0.0.1:6379> expire key1 30(integer) 1127.0.0.1:6379> ttl key1 //key1 26秒後到期(integer) 26127.0.0.1:6379> ttl key2 //key2 永不到期(integer) -1127.0.0.1:6379> keys *1) "key1"2) "key2"127.0.0.1:6379> keys * //key1到期後被刪除1) "key2"127.0.0.1:6379> expire key2 10(integer) 1127.0.0.1:6379> ttl key2(integer) 5127.0.0.1:6379> persist key2 //使key2永不到期(integer) 1127.0.0.1:6379> ttl key2(integer) -1127.0.0.1:6379> keys *1) "key2"127.0.0.1:6379>


配置Redis從Redis官網下載下來的是一個.tar.gz檔案,解壓進入目錄執行make命令編譯後,就可以直接使用了。
$ cd redis-2.8.19$ make<pre name="code" class="plain">$ ./src/redis-server
編譯後在src目錄下會產生一些可執行檔,執行redis-server命令就可以啟動Redis Server了,之後可以使用redis-cli(用戶端串連工具)串連。
$ ./src/redis-cli 127.0.0.1:6379> 

上面我們啟動Redis Server時,沒有指定任何的參數,Redis會以預設參數來配置。Redis是通過一個設定檔來配置其行為的,可以從解壓目錄中看到一個redis.conf檔案,裡面對每個配置都有詳細的說明,在啟動redis時指定設定檔,可以在redis-server命令後面指定設定檔的路徑。
$ ./src/redis-server ./redis.conf

預設情況下,所有的用戶端都可以串連並操作Redis Server,可以在redis.conf檔案中設定requirepass設定密碼。Redis不支援對許可權的管理,用戶端一旦驗證通過,就可以執行任何命令,這點是非常不安全的,尤其是在生產環境。可以redis.conf檔案中通過rename-command重新命名一些安全敏感的命令。
關於Redis的其它進階功能與相關配置,盡請期待後續章節...

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.