一拷恨、安裝Redis
體驗(yàn) Redis 需要使用 Linux 或者 Mac 環(huán)境张症,如果是 Windows 可以考慮使用虛擬機(jī)产还。主要方式有四種:
- 使用 Docker 安裝砖瞧。
- 通過(guò) Github 源碼編譯嘲恍。
- 直接安裝 apt-get install(Ubuntu)足画、yum install(RedHat) 或者 brew install(Mac)。
- 如果讀者懶于安裝操作佃牛,也可以使用網(wǎng)頁(yè)版的 Web Redis 直接體驗(yàn)淹辞。
1.1 Docker 方式
# 拉取 redis 鏡像
> docker pull redis
# 運(yùn)行 redis 容器
> docker run --name myredis -d -p6379:6379 redis
# 執(zhí)行容器中的 redis-cli,可以直接使用命令行操作 redis
> docker exec -it myredis redis-cli
1.2 Github 源碼編譯方式
# 下載源碼
> git clone --branch 2.8 --depth 1 git@github.com:antirez/redis.git
> cd redis
# 編譯
> make
> cd src
# 運(yùn)行服務(wù)器俘侠,daemonize表示在后臺(tái)運(yùn)行
> ./redis-server --daemonize yes
# 運(yùn)行命令行
> ./redis-cli
1.3 直接安裝方式
# mac
> brew install redis
# ubuntu
> apt-get install redis
# redhat
> yum install redis
# 運(yùn)行客戶(hù)端
> redis-cli
或參考菜鳥(niǎo)教程:Redis 安裝
二象缀、Redis簡(jiǎn)介
Remote Dictionary Server(Redis) 是一個(gè)由Salvatore Sanfilippo寫(xiě)的key-value存儲(chǔ)系統(tǒng)。Redis是一個(gè)開(kāi)源的使用ANSI C語(yǔ)言編寫(xiě)爷速、遵守BSD協(xié)議央星、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型惫东、Key-Value數(shù)據(jù)庫(kù)莉给,并提供多種語(yǔ)言的API。它通常被稱(chēng)為數(shù)據(jù)結(jié)構(gòu)服務(wù)器廉沮,因?yàn)橹担╲alue)可以是 字符串(String)颓遏、 哈希(Hash),、列表(list)滞时、 集合(sets) 和 有序集合(sorted sets)等類(lèi)型叁幢。
Redis 是完全開(kāi)源免費(fèi)的,是一個(gè)高性能的key-value數(shù)據(jù)庫(kù)漂洋。Redis 與其他 key - value 緩存產(chǎn)品有以下三個(gè)特點(diǎn):
1遥皂、Redis支持?jǐn)?shù)據(jù)的持久化力喷,可以將內(nèi)存中的數(shù)據(jù)保存在磁盤(pán)中,重啟的時(shí)候可以再次加載進(jìn)行使用演训。
2弟孟、Redis不僅僅支持簡(jiǎn)單的key-value類(lèi)型的數(shù)據(jù),同時(shí)還提供list样悟,set拂募,zset,hash等數(shù)據(jù)結(jié)構(gòu)的存儲(chǔ)窟她。
3陈症、Redis支持?jǐn)?shù)據(jù)的備份,即master-slave模式的數(shù)據(jù)備份震糖。
三录肯、Redis 優(yōu)勢(shì)
1、性能極高 – Redis能讀的速度是110000次/s,寫(xiě)的速度是81000次/s 吊说。
2论咏、豐富的數(shù)據(jù)類(lèi)型 – Redis支持二進(jìn)制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數(shù)據(jù)類(lèi)型操作。
3颁井、原子 – Redis的所有操作都是原子性的厅贪,意思就是要么成功執(zhí)行要么失敗完全不執(zhí)行。單個(gè)操作是原子性的雅宾。多個(gè)操作也支持事務(wù)养涮,即原子性,通過(guò)MULTI和EXEC指令包起來(lái)眉抬。
4贯吓、豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過(guò)期等等特性。
四吐辙、Redis與其他key-value存儲(chǔ)有什么不同宣决?
1、Redis有著更為復(fù)雜的數(shù)據(jù)結(jié)構(gòu)并且提供對(duì)他們的原子性操作昏苏,這是一個(gè)不同于其他數(shù)據(jù)庫(kù)的進(jìn)化路徑尊沸。Redis的數(shù)據(jù)類(lèi)型都是基于基本數(shù)據(jù)結(jié)構(gòu)的同時(shí)對(duì)程序員透明,無(wú)需進(jìn)行額外的抽象贤惯。
2洼专、Redis運(yùn)行在內(nèi)存中但是可以持久化到磁盤(pán),所以在對(duì)不同數(shù)據(jù)集進(jìn)行高速讀寫(xiě)時(shí)需要權(quán)衡內(nèi)存孵构,因?yàn)閿?shù)據(jù)量不能大于硬件內(nèi)存屁商。在內(nèi)存數(shù)據(jù)庫(kù)方面的另一個(gè)優(yōu)點(diǎn)是,相比在磁盤(pán)上相同的復(fù)雜的數(shù)據(jù)結(jié)構(gòu)颈墅,在內(nèi)存中操作起來(lái)非常簡(jiǎn)單蜡镶,這樣Redis可以做很多內(nèi)部復(fù)雜性很強(qiáng)的事情雾袱。同時(shí),在磁盤(pán)格式方面他們是緊湊的以追加的方式產(chǎn)生的官还,因?yàn)樗麄儾⒉恍枰M(jìn)行隨機(jī)訪(fǎng)問(wèn)芹橡。
五、Redis 數(shù)據(jù)類(lèi)型
Redis支持五種數(shù)據(jù)類(lèi)型:String(字符串)望伦,Hash(哈希)林说,List(列表),Set(集合)及zset(sorted set:有序集合)屯伞。
5.1 String(字符串)
String 是 Redis 最基本的類(lèi)型腿箩,你可以理解成與 Memcached 一模一樣的類(lèi)型,一個(gè) key 對(duì)應(yīng)一個(gè) value劣摇。String 類(lèi)型是二進(jìn)制安全的珠移。意思是 Redis 的 String 可以包含任何數(shù)據(jù)。比如jpg圖片或者序列化的對(duì)象末融。String 類(lèi)型是 Redis 最基本的數(shù)據(jù)類(lèi)型剑梳,String 類(lèi)型的值最大能存儲(chǔ) 512MB。
5.1.1鍵值對(duì)
實(shí)例
127.0.0.1:6379> set name "AlanChen"
OK
127.0.0.1:6379> get name
"AlanChen"
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
在以上實(shí)例中我們使用了 Redis 的 set 和 get 命令滑潘。鍵為 name,對(duì)應(yīng)的值為 AlanChen锨咙。
字符串結(jié)構(gòu)使用非常廣泛语卤,一個(gè)常見(jiàn)的用途就是緩存用戶(hù)信息。我們將用戶(hù)信息結(jié)構(gòu)體使用 JSON 序列化成字符串酪刀,然后將序列化后的字符串塞進(jìn) Redis 來(lái)緩存粹舵。同樣,取用戶(hù)信息會(huì)經(jīng)過(guò)一次反序列化的過(guò)程骂倘。
Redis 的字符串是動(dòng)態(tài)字符串眼滤,是可以修改的字符串,內(nèi)部結(jié)構(gòu)實(shí)現(xiàn)上類(lèi)似于 Java 的 ArrayList历涝,采用預(yù)分配冗余空間的方式來(lái)減少內(nèi)存的頻繁分配诅需,內(nèi)部為當(dāng)前字符串實(shí)際分配的空間 capacity 一般要高于實(shí)際字符串長(zhǎng)度 len。當(dāng)字符串長(zhǎng)度小于 1M 時(shí)荧库,擴(kuò)容都是加倍現(xiàn)有的空間堰塌,如果超過(guò) 1M,擴(kuò)容時(shí)一次只會(huì)多擴(kuò) 1M 的空間分衫。需要注意的是字符串最大長(zhǎng)度為 512M场刑。
5.1.2批量鍵值對(duì)
可以批量對(duì)多個(gè)字符串進(jìn)行讀寫(xiě),節(jié)省網(wǎng)絡(luò)耗時(shí)開(kāi)銷(xiāo)蚪战。
實(shí)例
127.0.0.1:6379> mset name1 AlanChen name2 Kyra
OK
127.0.0.1:6379> mget name1 name2
1) "AlanChen"
2) "Kyra"
5.1.3過(guò)期和 set 命令擴(kuò)展
可以對(duì) key 設(shè)置過(guò)期時(shí)間牵现,到點(diǎn)自動(dòng)刪除铐懊,這個(gè)功能常用來(lái)控制緩存的失效時(shí)間。不過(guò)這個(gè)「自動(dòng)刪除」的機(jī)制是比較復(fù)雜的瞎疼。
實(shí)例
127.0.0.1:6379> set name "AlanChen"
OK
127.0.0.1:6379> get name
"AlanChen"
127.0.0.1:6379> expire name 5 # 5s 后過(guò)期
(integer) 1
... #wait for 5s
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> setex name 5 "AlanChen" #5s 后過(guò)期科乎,等價(jià)于 set+expire
OK
127.0.0.1:6379> get name
"AlanChen"
... #wait for 5s
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> setnx name "AlanChen" #如果 name 不存在就執(zhí)行 set 創(chuàng)建
(integer) 1
127.0.0.1:6379> get name
"AlanChen"
127.0.0.1:6379> setnx name "AC" #因?yàn)?name 已經(jīng)存在,所以 set 創(chuàng)建不成功
(integer) 0
127.0.0.1:6379> get name #沒(méi)有改變
"AlanChen"
5.1.4 計(jì)數(shù)
如果 value 值是一個(gè)整數(shù)丑慎,還可以對(duì)它進(jìn)行自增操作喜喂。自增是有范圍的,它的范圍是 signed long 的最大最小值竿裂,超過(guò)了這個(gè)值玉吁,Redis 會(huì)報(bào)錯(cuò)。
實(shí)例
127.0.0.1:6379> set age 30
OK
127.0.0.1:6379> incr age
(integer) 31
127.0.0.1:6379> incrby age 5
(integer) 36
127.0.0.1:6379> incrby age -5
(integer) 31
5.2 Hash(哈希)
Redis Hash 是一個(gè)鍵值(key=>value)對(duì)集合腻异。Redis Hash 是一個(gè) String 類(lèi)型的 field 和 value 的映射表进副,Hash 特別適合用于存儲(chǔ)對(duì)象。
實(shí)例
127.0.0.1:6379> hmset Student name "AlanChen" age 18
OK
127.0.0.1:6379> hgetall Student
1) "name"
2) "AlanChen"
3) "age"
4) "18"
127.0.0.1:6379> hget Student name
"AlanChen"
127.0.0.1:6379> hlen Student
(integer) 2
實(shí)例中我們使用了 Redis hmset, hget 命令悔常,hmset 設(shè)置了三個(gè) field=>value 對(duì), hget 獲取對(duì)應(yīng) field 對(duì)應(yīng)的 value影斑。每個(gè) Hash 可以存儲(chǔ) 232 -1 鍵值對(duì)(40多億)。
Redis 的字典相當(dāng)于 Java 語(yǔ)言里面的 HashMap机打,它是無(wú)序字典矫户。內(nèi)部實(shí)現(xiàn)結(jié)構(gòu)上同 Java 的 HashMap 也是一致的,同樣的數(shù)組 + 鏈表二維結(jié)構(gòu)残邀。第一維 hash 的數(shù)組位置碰撞時(shí)皆辽,就會(huì)將碰撞的元素使用鏈表串接起來(lái)。
不同的是芥挣,Redis 的字典的值只能是字符串驱闷,另外它們 rehash 的方式不一樣,因?yàn)?Java 的 HashMap 在字典很大時(shí)空免,rehash 是個(gè)耗時(shí)的操作空另,需要一次性全部 rehash。Redis 為了高性能蹋砚,不能堵塞服務(wù)扼菠,所以采用了漸進(jìn)式 rehash 策略。漸進(jìn)式 rehash 會(huì)在 rehash 的同時(shí)坝咐,保留新舊兩個(gè) hash 結(jié)構(gòu)娇豫,查詢(xún)時(shí)會(huì)同時(shí)查詢(xún)兩個(gè) hash 結(jié)構(gòu),然后在后續(xù)的定時(shí)任務(wù)中以及 hash 操作指令中畅厢,循序漸進(jìn)地將舊 hash 的內(nèi)容一點(diǎn)點(diǎn)遷移到新的 hash 結(jié)構(gòu)中冯痢。當(dāng)搬遷完成了,就會(huì)使用新的hash結(jié)構(gòu)取而代之。當(dāng) hash 移除了最后一個(gè)元素之后浦楣,該數(shù)據(jù)結(jié)構(gòu)自動(dòng)被刪除袖肥,內(nèi)存被回收。
hash 結(jié)構(gòu)也可以用來(lái)存儲(chǔ)用戶(hù)信息振劳,不同于字符串一次性需要全部序列化整個(gè)對(duì)象椎组,hash 可以對(duì)用戶(hù)結(jié)構(gòu)中的每個(gè)字段單獨(dú)存儲(chǔ)。這樣當(dāng)我們需要獲取用戶(hù)信息時(shí)可以進(jìn)行部分獲取历恐。而以整個(gè)字符串的形式去保存用戶(hù)信息的話(huà)就只能一次性全部讀取寸癌,這樣就會(huì)比較浪費(fèi)網(wǎng)絡(luò)流量。
hash 也有缺點(diǎn)弱贼,hash 結(jié)構(gòu)的存儲(chǔ)消耗要高于單個(gè)字符串蒸苇,到底該使用 hash 還是字符串,需要根據(jù)實(shí)際情況再三權(quán)衡吮旅。
5.3 List(列表)
Redis 列表是簡(jiǎn)單的字符串列表溪烤,按照插入順序排序。你可以添加一個(gè)元素到列表的頭部(左邊)或者尾部(右邊)庇勃。
Redis 的列表相當(dāng)于 Java 語(yǔ)言里面的 LinkedList檬嘀,注意它是鏈表而不是數(shù)組。這意味著 list 的插入和刪除操作非吃鹑拢快鸳兽,時(shí)間復(fù)雜度為 O(1),但是索引定位很慢罕拂,時(shí)間復(fù)雜度為 O(n)贸铜。當(dāng)列表彈出了最后一個(gè)元素之后,該數(shù)據(jù)結(jié)構(gòu)自動(dòng)被刪除聂受,內(nèi)存被回收。
5.3.1 基本操作
實(shí)例
127.0.0.1:6379> rpush nameList AlanChen
(integer) 1
127.0.0.1:6379> rpush nameList Kyra
(integer) 2
127.0.0.1:6379> rpush nameList AC
(integer) 3
127.0.0.1:6379> lrange nameList 0 1
1) "AlanChen"
2) "Kyra"
列表最多可存儲(chǔ) 232 - 1 元素 (4294967295, 每個(gè)列表可存儲(chǔ)40多億)烤镐。
Redis 的列表結(jié)構(gòu)常用來(lái)做異步隊(duì)列使用蛋济。將需要延后處理的任務(wù)結(jié)構(gòu)體序列化成字符串塞進(jìn) Redis 的列表,另一個(gè)線(xiàn)程從這個(gè)列表中輪詢(xún)數(shù)據(jù)進(jìn)行處理炮叶。
5.3.2 先進(jìn)先出:隊(duì)列
127.0.0.1:6379> rpush userNames AlanChen Kyra AC
(integer) 3
127.0.0.1:6379> llen userNames
(integer) 3
127.0.0.1:6379> lpop userNames
"AlanChen"
127.0.0.1:6379> lpop userNames
"Kyra"
127.0.0.1:6379> lpop userNames
"AC"
127.0.0.1:6379> lpop userNames
(nil)
5.3.3 先進(jìn)后出:棧
127.0.0.1:6379> del userNames
(integer) 0
127.0.0.1:6379> rpush userNames AlanChen Kyra AC
(integer) 3
127.0.0.1:6379> rpop userNames
"AC"
127.0.0.1:6379> rpop userNames
"Kyra"
127.0.0.1:6379> rpop userNames
"AlanChen"
127.0.0.1:6379> rpop userNames
(nil)
5.3.4 lindex 碗旅、 trim
lindex 相當(dāng)于 Java 鏈表的get(int index)方法,它需要對(duì)鏈表進(jìn)行遍歷镜悉,性能隨著參數(shù)index增大而變差祟辟。
ltrim 跟的兩個(gè)參數(shù)start_index和end_index定義了一個(gè)區(qū)間,在這個(gè)區(qū)間內(nèi)的值侣肄,ltrim 要保留旧困,區(qū)間之外統(tǒng)統(tǒng)砍掉。我們可以通過(guò)ltrim來(lái)實(shí)現(xiàn)一個(gè)定長(zhǎng)的鏈表,這一點(diǎn)非常有用吼具。
index 可以為負(fù)數(shù)僚纷,index=-1表示倒數(shù)第一個(gè)元素,同樣index=-2表示倒數(shù)第二個(gè)元素拗盒。
> rpush books python java golang
(integer) 3
> lindex books 1 # O(n) 慎用
"java"
> lrange books 0 -1 # 獲取所有元素怖竭,O(n) 慎用
1) "python"
2) "java"
3) "golang"
> ltrim books 1 -1 # O(n) 慎用
OK
> lrange books 0 -1
1) "java"
2) "golang"
> ltrim books 1 0 # 這其實(shí)是清空了整個(gè)列表,因?yàn)閰^(qū)間范圍長(zhǎng)度為負(fù)
OK
> llen books
(integer) 0
5.3.5 linked list & quickest
如果再深入一點(diǎn)陡蝇,你會(huì)發(fā)現(xiàn) Redis 底層存儲(chǔ)的還不是一個(gè)簡(jiǎn)單的 linkedlist痊臭,而是稱(chēng)之為快速鏈表 quicklist 的一個(gè)結(jié)構(gòu)。
首先在列表元素較少的情況下會(huì)使用一塊連續(xù)的內(nèi)存存儲(chǔ)登夫,這個(gè)結(jié)構(gòu)是 ziplist广匙,也即是壓縮列表。它將所有的元素緊挨著一起存儲(chǔ)悼嫉,分配的是一塊連續(xù)的內(nèi)存艇潭。當(dāng)數(shù)據(jù)量比較多的時(shí)候才會(huì)改成 quicklist。因?yàn)槠胀ǖ逆湵硇枰母郊又羔樋臻g太大戏蔑,會(huì)比較浪費(fèi)空間蹋凝,而且會(huì)加重內(nèi)存的碎片化。比如這個(gè)列表里存的只是 int 類(lèi)型的數(shù)據(jù)总棵,結(jié)構(gòu)上還需要兩個(gè)額外的指針 prev 和 next 鳍寂。所以 Redis 將鏈表和 ziplist 結(jié)合起來(lái)組成了 quicklist。也就是將多個(gè) ziplist 使用雙向指針串起來(lái)使用情龄。這樣既滿(mǎn)足了快速的插入刪除性能迄汛,又不會(huì)出現(xiàn)太大的空間冗余。
5.4 Set(集合)
Redis 的 Set 是 String 類(lèi)型的無(wú)序集合骤视。集合是通過(guò)哈希表實(shí)現(xiàn)的鞍爱,所以添加,刪除专酗,查找的復(fù)雜度都是 O(1)睹逃。
集合中最大的成員數(shù)為 232 - 1(4294967295, 每個(gè)集合可存儲(chǔ)40多億個(gè)成員)。
Redis 的集合相當(dāng)于 Java 語(yǔ)言里面的 HashSet祷肯,它內(nèi)部的鍵值對(duì)是無(wú)序的唯一的沉填。它的內(nèi)部實(shí)現(xiàn)相當(dāng)于一個(gè)特殊的字典,字典中所有的 value 都是一個(gè)值NULL佑笋。當(dāng)集合中最后一個(gè)元素移除之后翼闹,數(shù)據(jù)結(jié)構(gòu)自動(dòng)刪除,內(nèi)存被回收蒋纬。
Set 結(jié)構(gòu)可以用來(lái)存儲(chǔ)活動(dòng)中獎(jiǎng)的用戶(hù) ID猎荠,因?yàn)橛腥ブ毓δ芗崛酰梢员WC同一個(gè)用戶(hù)不會(huì)中獎(jiǎng)兩次。
實(shí)例
127.0.0.1:6379> sadd nameSet AlanChen
(integer) 1
127.0.0.1:6379> sadd nameSet AlanChen #重復(fù)
(integer) 0
127.0.0.1:6379> sadd nameSet AC Kyra
(integer) 2
127.0.0.1:6379> members nameSet # 注意順序,和插入的并不一致法牲,因?yàn)?set 是無(wú)序的
1) "AC"
2) "AlanChen"
3) "Kyra"
127.0.0.1:6379> sismember nameSet AC # 查詢(xún)某個(gè) value 是否存在史汗,相當(dāng)于 contains(o)
(integer) 1
127.0.0.1:6379> sismember nameSet Tom
(integer) 0
127.0.0.1:6379> scare nameSet # 獲取長(zhǎng)度相當(dāng)于 count()
(integer) 3
127.0.0.1:6379> stop nameSet # 彈出一個(gè)
"Kyra"
5.5 zset(sorted set:有序集合)
Redis zset 和 Set 一樣也是String類(lèi)型元素的集合,且不允許重復(fù)的成員拒垃。不同的是每個(gè)元素都會(huì)關(guān)聯(lián)一個(gè)double類(lèi)型的分?jǐn)?shù)停撞。Redis正是通過(guò)分?jǐn)?shù)來(lái)為集合中的成員進(jìn)行從小到大的排序。zset的成員是唯一的,但分?jǐn)?shù)(score)卻可以重復(fù)悼瓮。
zset 可能是 Redis 提供的最為特色的數(shù)據(jù)結(jié)構(gòu)戈毒,它也是在面試中面試官最?lèi)?ài)問(wèn)的數(shù)據(jù)結(jié)構(gòu)。它類(lèi)似于 Java 的 SortedSet 和 HashMap 的結(jié)合體横堡,一方面它是一個(gè) set埋市,保證了內(nèi)部 value 的唯一性,另一方面它可以給每個(gè) value 賦予一個(gè) score命贴,代表這個(gè) value 的排序權(quán)重道宅。它的內(nèi)部實(shí)現(xiàn)用的是一種叫做「跳躍列表」的數(shù)據(jù)結(jié)構(gòu)。
zset 中最后一個(gè) value 被移除后胸蛛,數(shù)據(jù)結(jié)構(gòu)自動(dòng)刪除污茵,內(nèi)存被回收。
zset 可以用來(lái)存粉絲列表葬项,value 值是粉絲的用戶(hù) ID泞当,score 是關(guān)注時(shí)間。我們可以對(duì)粉絲列表按關(guān)注時(shí)間進(jìn)行排序民珍。
zset 還可以用來(lái)存儲(chǔ)學(xué)生的成績(jī)襟士,value 值是學(xué)生的 ID,score 是他的考試成績(jī)嚷量。我們可以對(duì)成績(jī)按分?jǐn)?shù)進(jìn)行排序就可以得到他的名次陋桂。
實(shí)例
> zadd books 9.0 "think in java"
(integer) 1
> zadd books 8.9 "java concurrency"
(integer) 1
> zadd books 8.6 "java cookbook"
(integer) 1
> zrange books 0 -1 # 按 score 排序列出,參數(shù)區(qū)間為排名范圍
1) "java cookbook"
2) "java concurrency"
3) "think in java"
> zrevrange books 0 -1 # 按 score 逆序列出蝶溶,參數(shù)區(qū)間為排名范圍
1) "think in java"
2) "java concurrency"
3) "java cookbook"
> zcard books # 相當(dāng)于 count()
(integer) 3
> zscore books "java concurrency" # 獲取指定 value 的 score
"8.9000000000000004" # 內(nèi)部 score 使用 double 類(lèi)型進(jìn)行存儲(chǔ)嗜历,所以存在小數(shù)點(diǎn)精度問(wèn)題
> zrank books "java concurrency" # 排名
(integer) 1
> zrangebyscore books 0 8.91 # 根據(jù)分值區(qū)間遍歷 zset
1) "java cookbook"
2) "java concurrency"
> zrangebyscore books -inf 8.91 withscores # 根據(jù)分值區(qū)間 (-∞, 8.91] 遍歷 zset,同時(shí)返回分值身坐。inf 代表 infinite,無(wú)窮大的意思落包。
1) "java cookbook"
2) "8.5999999999999996"
3) "java concurrency"
4) "8.9000000000000004"
> zrem books "java concurrency" # 刪除 value
(integer) 1
> zrange books 0 -1
1) "java cookbook"
2) "think in java"
六部蛇、容器型數(shù)據(jù)結(jié)構(gòu)的通用規(guī)則
list/set/hash/zset 這四種數(shù)據(jù)結(jié)構(gòu)是容器型數(shù)據(jù)結(jié)構(gòu),它們共享下面兩條通用規(guī)則:
1咐蝇、create if not exists
如果容器不存在涯鲁,那就創(chuàng)建一個(gè),再進(jìn)行操作。比如 rpush 操作剛開(kāi)始是沒(méi)有列表的抹腿,Redis 就會(huì)自動(dòng)創(chuàng)建一個(gè)岛请,然后再 rpush 進(jìn)去新元素。
2警绩、drop if no elements
如果容器里元素沒(méi)有了崇败,那么立即刪除元素,釋放內(nèi)存肩祥。這意味著 lpop 操作到最后一個(gè)元素后室,列表就消失了。
七混狠、過(guò)期時(shí)間
Redis 所有的數(shù)據(jù)結(jié)構(gòu)都可以設(shè)置過(guò)期時(shí)間岸霹,時(shí)間到了,Redis 會(huì)自動(dòng)刪除相應(yīng)的對(duì)象将饺。需要注意的是過(guò)期是以對(duì)象為單位贡避,比如一個(gè) hash 結(jié)構(gòu)的過(guò)期是整個(gè) hash 對(duì)象的過(guò)期,而不是其中的某個(gè)子 key予弧。
還有一個(gè)需要特別注意的地方是如果一個(gè)字符串已經(jīng)設(shè)置了過(guò)期時(shí)間刮吧,然后你調(diào)用了 set 方法修改了它,它的過(guò)期時(shí)間會(huì)消失桌肴。
127.0.0.1:6379> set name alanchen
OK
127.0.0.1:6379> tel name #查看過(guò)期的剩余時(shí)間 -1 表示不過(guò)期
(integer) -1
127.0.0.1:6379> expire name 20 # 設(shè)置過(guò)期時(shí)間為20s
(integer) 1
127.0.0.1:6379> tel name #查看過(guò)期的剩余時(shí)間
(integer) 16
127.0.0.1:6379> set name AC # 重新設(shè)置name
OK
127.0.0.1:6379> tel name # name的過(guò)期時(shí)間消失
(integer) -1
八皇筛、各個(gè)數(shù)據(jù)類(lèi)型應(yīng)用場(chǎng)景
類(lèi)型 | 簡(jiǎn)介 | 特性 | 場(chǎng)景 |
---|---|---|---|
String(字符串) | 二進(jìn)制安全 | 可以包含任何數(shù)據(jù),比如jpg圖片或者序列化的對(duì)象,一個(gè)鍵最大能存儲(chǔ)512M | - |
Hash(字典) | 鍵值對(duì)集合,即編程語(yǔ)言中的Map類(lèi)型 | 適合存儲(chǔ)對(duì)象,并且可以像數(shù)據(jù)庫(kù)中update一個(gè)屬性一樣只修改某一項(xiàng)屬性值(Memcached中需要取出整個(gè)字符串反序列化成對(duì)象修改完再序列化存回去) | 存儲(chǔ)、讀取坠七、修改用戶(hù)屬性 |
List(列表) | 鏈表(雙向鏈表) | 增刪快,提供了操作某一段元素的API | 1水醋、最新消息排行等功能(比如朋友圈的時(shí)間線(xiàn)) 2、消息隊(duì)列 |
Set(集合) | 哈希表實(shí)現(xiàn),元素不重復(fù) | 1彪置、添加拄踪、刪除,查找的復(fù)雜度都是O(1) 2、為集合提供了求交集拳魁、并集惶桐、差集等操作 | 1、共同好友 2潘懊、利用唯一性,統(tǒng)計(jì)訪(fǎng)問(wèn)網(wǎng)站的所有獨(dú)立ip 3姚糊、好友推薦時(shí),根據(jù)tag求交集,大于某個(gè)閾值就可以推薦 |
Sorted Set(有序集合) | 將Set中的元素增加一個(gè)權(quán)重參數(shù)score,元素按score有序排列 | 數(shù)據(jù)插入集合時(shí),已經(jīng)進(jìn)行天然排序 | 1、排行榜 2授舟、帶權(quán)重的消息隊(duì)列 |
九救恨、Redis & 多個(gè)數(shù)據(jù)庫(kù)
Redis支持多個(gè)數(shù)據(jù)庫(kù),并且每個(gè)數(shù)據(jù)庫(kù)的數(shù)據(jù)是隔離的不能共享释树,并且基于單機(jī)才有肠槽,如果是集群就沒(méi)有數(shù)據(jù)庫(kù)的概念擎淤。
Redis是一個(gè)字典結(jié)構(gòu)的存儲(chǔ)服務(wù)器,而實(shí)際上一個(gè)Redis實(shí)例提供了多個(gè)用來(lái)存儲(chǔ)數(shù)據(jù)的字典秸仙,客戶(hù)端可以指定將數(shù)據(jù)存儲(chǔ)在哪個(gè)字典中嘴拢。這與我們熟知的在一個(gè)關(guān)系數(shù)據(jù)庫(kù)實(shí)例中可以創(chuàng)建多個(gè)數(shù)據(jù)庫(kù)類(lèi)似,所以可以將其中的每個(gè)字典都理解成一個(gè)獨(dú)立的數(shù)據(jù)庫(kù)寂纪。
每個(gè)數(shù)據(jù)庫(kù)對(duì)外都是一個(gè)從0開(kāi)始的遞增數(shù)字命名席吴,Redis默認(rèn)支持16個(gè)數(shù)據(jù)庫(kù)(可以通過(guò)配置文件支持更多,無(wú)上限)弊攘,可以通過(guò)配置databases來(lái)修改這一數(shù)字抢腐。客戶(hù)端與Redis建立連接后會(huì)自動(dòng)選擇0號(hào)數(shù)據(jù)庫(kù)襟交,不過(guò)可以隨時(shí)使用SELECT命令更換數(shù)據(jù)庫(kù)迈倍,如要選擇1號(hào)數(shù)據(jù)庫(kù):
redis> SELECT 1
OK
redis [1] > GET foo
(nil)
然而這些以數(shù)字命名的數(shù)據(jù)庫(kù)又與我們理解的數(shù)據(jù)庫(kù)有所區(qū)別。首先Redis不支持自定義數(shù)據(jù)庫(kù)的名字捣域,每個(gè)數(shù)據(jù)庫(kù)都以編號(hào)命名啼染,開(kāi)發(fā)者必須自己記錄哪些數(shù)據(jù)庫(kù)存儲(chǔ)了哪些數(shù)據(jù)。另外Redis也不支持為每個(gè)數(shù)據(jù)庫(kù)設(shè)置不同的訪(fǎng)問(wèn)密碼焕梅,所以一個(gè)客戶(hù)端要么可以訪(fǎng)問(wèn)全部數(shù)據(jù)庫(kù)迹鹅,要么連一個(gè)數(shù)據(jù)庫(kù)也沒(méi)有權(quán)限訪(fǎng)問(wèn)。最重要的一點(diǎn)是多個(gè)數(shù)據(jù)庫(kù)之間并不是完全隔離的贞言,比如FLUSHALL命令可以清空一個(gè)Redis實(shí)例中所有數(shù)據(jù)庫(kù)中的數(shù)據(jù)斜棚。
綜上所述,這些數(shù)據(jù)庫(kù)更像是一種命名空間该窗,而不適宜存儲(chǔ)不同應(yīng)用程序的數(shù)據(jù)弟蚀。比如可以使用0號(hào)數(shù)據(jù)庫(kù)存儲(chǔ)某個(gè)應(yīng)用生產(chǎn)環(huán)境中的數(shù)據(jù),使用1號(hào)數(shù)據(jù)庫(kù)存儲(chǔ)測(cè)試環(huán)境中的數(shù)據(jù)酗失,但不適宜使用0號(hào)數(shù)據(jù)庫(kù)存儲(chǔ)A應(yīng)用的數(shù)據(jù)而使用1號(hào)數(shù)據(jù)庫(kù)B應(yīng)用的數(shù)據(jù)义钉,不同的應(yīng)用應(yīng)該使用不同的Redis實(shí)例存儲(chǔ)數(shù)據(jù)。由于Redis非常輕量級(jí)规肴,一個(gè)空Redis實(shí)例占用的內(nèi)在只有1M左右捶闸,所以不用擔(dān)心多個(gè)Redis實(shí)例會(huì)額外占用很多內(nèi)存。
十拖刃、Redis 發(fā)布訂閱
十一删壮、Redis 事務(wù)
Redis 事務(wù)可以一次執(zhí)行多個(gè)命令, 并且?guī)в幸韵氯齻€(gè)重要的保證:
1兑牡、批量操作在發(fā)送 EXEC 命令前被放入隊(duì)列緩存央碟。
2、收到 EXEC 命令后進(jìn)入事務(wù)執(zhí)行发绢,事務(wù)中任意命令執(zhí)行失敗硬耍,其余的命令依然被執(zhí)行。
3边酒、在事務(wù)執(zhí)行過(guò)程经柴,其他客戶(hù)端提交的命令請(qǐng)求不會(huì)插入到事務(wù)執(zhí)行命令序列中。
一個(gè)事務(wù)從開(kāi)始到執(zhí)行會(huì)經(jīng)歷以下三個(gè)階段:開(kāi)始事務(wù)墩朦、命令入隊(duì)坯认、執(zhí)行事務(wù)。
實(shí)例
以下是一個(gè)事務(wù)的例子氓涣, 它先以 multi 開(kāi)始一個(gè)事務(wù)牛哺, 然后將多個(gè)命令入隊(duì)到事務(wù)中, 最后由 EXEC 命令觸發(fā)事務(wù)劳吠, 一并執(zhí)行事務(wù)中的所有命令:
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set user-name "AlanChen"
QUEUED
127.0.0.1:6379> get user-name
QUEUED
127.0.0.1:6379> hmset User name "AC" age 10
QUEUED
127.0.0.1:6379> sadd names "Tom"
QUEUED
127.0.0.1:6379> sadd names "Kyra"
QUEUED
127.0.0.1:6379> exec
1) OK
2) "AlanChen"
3) OK
4) (integer) 1
5) (integer) 1
127.0.0.1:6379> get user-name
"AlanChen"
127.0.0.1:6379> smembers names
1) "Tom"
2) "Kyra"
資料來(lái)源:
《Redis深度歷險(xiǎn):核心原理與應(yīng)用實(shí)踐》