這篇文章主要介紹了超強(qiáng)、超詳細(xì)Redis入門教程,本文詳細(xì)介紹了Redis數(shù)據(jù)庫各個(gè)方面的知識,需要的朋友可以參考下
【本教程目錄】
1.redis是什么
2.redis的作者何許人也
3.誰在使用redis
4.學(xué)會(huì)安裝redis
5.學(xué)會(huì)啟動(dòng)redis
6.使用redis客戶端
7.redis數(shù)據(jù)結(jié)構(gòu) – 簡介
8.redis數(shù)據(jù)結(jié)構(gòu) – strings
9.redis數(shù)據(jù)結(jié)構(gòu) – lists
10.redis數(shù)據(jù)結(jié)構(gòu) – 集合
11.redis數(shù)據(jù)結(jié)構(gòu) – 有序集合
12.redis數(shù)據(jù)結(jié)構(gòu) – 哈希
13.聊聊redis持久化 – 兩種方式
14.聊聊redis持久化 – RDB
15.聊聊redis持久化 – AOF
16.聊聊redis持久化 – AOF重寫
17.聊聊redis持久化 – 如何選擇RDB和AOF
18.聊聊主從 – 用法
19.聊聊主從 – 同步原理
20.聊聊redis的事務(wù)處理
21.教你看懂redis配置 – 簡介
22.教你看懂redis配置 -通用
23.教你看懂redis配置 – 快照
24.教你看懂redis配置 – 復(fù)制
25.教你看懂redis配置 – 安全
26.教你看懂redis配置 -限制
27.教你看懂redis配置 – 追加模式
28.教你看懂redis配置 – LUA腳本
29.教你看懂redis配置 – 慢日志
30.教你看懂redis配置 – 事件通知
31.教你看懂redis配置 – 高級配置
【redis是什么】
redis是一個(gè)開源的、使用C語言編寫的、支持網(wǎng)絡(luò)交互的、可基于內(nèi)存也可持久化的Key-Value數(shù)據(jù)庫。
redis的官網(wǎng)地址,非常好記,是redis.io。(特意查了一下,域名后綴io屬于國家域名,是british Indian Ocean territory,即英屬印度洋領(lǐng)地)
目前,Vmware在資助著redis項(xiàng)目的開發(fā)和維護(hù)。
【redis的作者何許人也】
開門見山,先看照片:
是不是出乎了你的意料,嗯,高手總會(huì)有些地方與眾不同的。
這位便是redis的作者,他叫Salvatore Sanfilippo,來自意大利的西西里島,現(xiàn)在居住在卡塔尼亞。目前供職于Pivotal公司。
他使用的網(wǎng)名是antirez,如果你有興趣,可以去他的博客逛逛,地址是antirez.com,當(dāng)然也可以去follow他的github,地址是http://github.com/antirez。
【誰在使用redis】
Blizzard、digg、stackoverflow、github、flickr …
【學(xué)會(huì)安裝redis】
從redis.io下載最新版redis-X.Y.Z.tar.gz后解壓,然后進(jìn)入redis-X.Y.Z文件夾后直接make即可,安裝非常簡單。
make成功后會(huì)在src文件夾下產(chǎn)生一些二進(jìn)制可執(zhí)行文件,包括redis-server、redis-cli等等:
復(fù)制代碼代碼如下:
$ find . -type f -executable ./redis-benchmark //用于進(jìn)行redis性能測試的工具 ./redis-check-dump //用于修復(fù)出問題的dump.rdb文件 ./redis-cli //redis的客戶端 ./redis-server //redis的服務(wù)端 ./redis-check-aof //用于修復(fù)出問題的AOF文件 ./redis-sentinel //用于集群管理
【學(xué)會(huì)啟動(dòng)redis】
啟動(dòng)redis非常簡單,直接./redis-server就可以啟動(dòng)服務(wù)端了,還可以用下面的方法指定要加載的配置文件:
復(fù)制代碼代碼如下:
./redis-server ../redis.conf
默認(rèn)情況下,redis-server會(huì)以非daemon的方式來運(yùn)行,且默認(rèn)服務(wù)端口為6379。
有關(guān)作者為什么選擇6379作為默認(rèn)端口,還有一段有趣的典故,英語好的同學(xué)可以看看作者這篇博文中的解釋。
【使用redis客戶端】
我們直接看一個(gè)例子:
復(fù)制代碼代碼如下:
//這樣來啟動(dòng)redis客戶端了 $ ./redis-cli //用set指令來設(shè)置key、value 127.0.0.1:6379> set name "roc" OK //來獲取name的值 127.0.0.1:6379> get name "roc" //通過客戶端來關(guān)閉redis服務(wù)端 127.0.0.1:6379> shutdown 127.0.0.1:6379>
【redis數(shù)據(jù)結(jié)構(gòu) – 簡介】
redis是一種高級的key:value存儲系統(tǒng),其中value支持五種數(shù)據(jù)類型:
1.字符串(strings)
2.字符串列表(lists)
3.字符串集合(sets)
4.有序字符串集合(sorted sets)
5.哈希(hashes)
而關(guān)于key,有幾個(gè)點(diǎn)要提醒大家:
1.key不要太長,盡量不要超過1024字節(jié),這不僅消耗內(nèi)存,而且會(huì)降低查找的效率;
2.key也不要太短,太短的話,key的可讀性會(huì)降低;
3.在一個(gè)項(xiàng)目中,key最好使用統(tǒng)一的命名模式,例如user:10000:passwd。
【redis數(shù)據(jù)結(jié)構(gòu) – strings】
有人說,如果只使用redis中的字符串類型,且不使用redis的持久化功能,那么,redis就和memcache非常非常的像了。這說明strings類型是一個(gè)很基礎(chǔ)的數(shù)據(jù)類型,也是任何存儲系統(tǒng)都必備的數(shù)據(jù)類型。
我們來看一個(gè)最簡單的例子:
復(fù)制代碼代碼如下:
set mystr "hello world!" //設(shè)置字符串類型 get mystr //讀取字符串類型
字符串類型的用法就是這么簡單,因?yàn)槭嵌M(jìn)制安全的,所以你完全可以把一個(gè)圖片文件的內(nèi)容作為字符串來存儲。
另外,我們還可以通過字符串類型進(jìn)行數(shù)值操作:
復(fù)制代碼代碼如下:
127.0.0.1:6379> set mynum "2" OK 127.0.0.1:6379> get mynum "2" 127.0.0.1:6379> incr mynum (integer) 3 127.0.0.1:6379> get mynum "3"
看,在遇到數(shù)值操作時(shí),redis會(huì)將字符串類型轉(zhuǎn)換成數(shù)值。
由于INCR等指令本身就具有原子操作的特性,所以我們完全可以利用redis的INCR、INCRBY、DECR、DECRBY等指令來實(shí)現(xiàn)原子計(jì)數(shù)的效果,假如,在某種場景下有3個(gè)客戶端同時(shí)讀取了mynum的值(值為2),然后對其同時(shí)進(jìn)行了加1的操作,那么,最后mynum的值一定是5。不少網(wǎng)站都利用redis的這個(gè)特性來實(shí)現(xiàn)業(yè)務(wù)上的統(tǒng)計(jì)計(jì)數(shù)需求。
【redis數(shù)據(jù)結(jié)構(gòu) – lists】
redis的另一個(gè)重要的數(shù)據(jù)結(jié)構(gòu)叫做lists,翻譯成中文叫做“列表”。
首先要明確一點(diǎn),redis中的lists在底層實(shí)現(xiàn)上并不是數(shù)組,而是鏈表,也就是說對于一個(gè)具有上百萬個(gè)元素的lists來說,在頭部和尾部插入一個(gè)新元素,其時(shí)間復(fù)雜度是常數(shù)級別的,比如用LPUSH在10個(gè)元素的lists頭部插入新元素,和在上千萬元素的lists頭部插入新元素的速度應(yīng)該是相同的。
雖然lists有這樣的優(yōu)勢,但同樣有其弊端,那就是,鏈表型lists的元素定位會(huì)比較慢,而數(shù)組型lists的元素定位就會(huì)快得多。
lists的常用操作包括LPUSH、RPUSH、LRANGE等。我們可以用LPUSH在lists的左側(cè)插入一個(gè)新元素,用RPUSH在lists的右側(cè)插入一個(gè)新元素,用LRANGE命令從lists中指定一個(gè)范圍來提取元素。我們來看幾個(gè)例子:
復(fù)制代碼代碼如下:
//新建一個(gè)list叫做mylist,并在列表頭部插入元素"1" 127.0.0.1:6379> lpush mylist "1" //返回當(dāng)前mylist中的元素個(gè)數(shù) (integer) 1 //在mylist右側(cè)插入元素"2" 127.0.0.1:6379> rpush mylist "2" (integer) 2 //在mylist左側(cè)插入元素"0" 127.0.0.1:6379> lpush mylist "0" (integer) 3 //列出mylist中從編號0到編號1的元素 127.0.0.1:6379> lrange mylist 0 1 1) "0" 2) "1" //列出mylist中從編號0到倒數(shù)第一個(gè)元素 127.0.0.1:6379> lrange mylist 0 -1 1) "0" 2) "1" 3) "2"
lists的應(yīng)用相當(dāng)廣泛,隨便舉幾個(gè)例子:
1.我們可以利用lists來實(shí)現(xiàn)一個(gè)消息隊(duì)列,而且可以確保先后順序,不必像mysql那樣還需要通過ORDER BY來進(jìn)行排序。
2.利用LRANGE還可以很方便的實(shí)現(xiàn)分頁的功能。
3.在博客系統(tǒng)中,每片博文的評論也可以存入一個(gè)單獨(dú)的list中。
【redis數(shù)據(jù)結(jié)構(gòu) – 集合】
redis的集合,是一種無序的集合,集合中的元素沒有先后順序。
集合相關(guān)的操作也很豐富,如添加新元素、刪除已有元素、取交集、取并集、取差集等。我們來看例子:
復(fù)制代碼代碼如下:
//向集合myset中加入一個(gè)新元素"one" 127.0.0.1:6379> sadd myset "one" (integer) 1 127.0.0.1:6379> sadd myset "two" (integer) 1 //列出集合myset中的所有元素 127.0.0.1:6379> smembers myset 1) "one" 2) "two" //判斷元素1是否在集合myset中,返回1表示存在 127.0.0.1:6379> sismember myset "one" (integer) 1 //判斷元素3是否在集合myset中,返回0表示不存在 127.0.0.1:6379> sismember myset "three" (integer) 0 //新建一個(gè)新的集合yourset 127.0.0.1:6379> sadd yourset "1" (integer) 1 127.0.0.1:6379> sadd yourset "2" (integer) 1 127.0.0.1:6379> smembers yourset 1) "1" 2) "2" //對兩個(gè)集合求并集 127.0.0.1:6379> sunion myset yourset 1) "1" 2) "one" 3) "2" 4) "two"
對于集合的使用,也有一些常見的方式,比如,QQ有一個(gè)社交功能叫做“好友標(biāo)簽”,大家可以給你的好友貼標(biāo)簽,比如“大美女”、“土豪”、“歐巴”等等,這時(shí)就可以使用redis的集合來實(shí)現(xiàn),把每一個(gè)用戶的標(biāo)簽都存儲在一個(gè)集合之中。
【redis數(shù)據(jù)結(jié)構(gòu) – 有序集合】
redis不但提供了無需集合(sets),還很體貼的提供了有序集合(sorted sets)。有序集合中的每個(gè)元素都關(guān)聯(lián)一個(gè)序號(score),這便是排序的依據(jù)。
很多時(shí)候,我們都將redis中的有序集合叫做zsets,這是因?yàn)樵趓edis中,有序集合相關(guān)的操作指令都是以z開頭的,比如zrange、zadd、zrevrange、zrangebyscore等等
老規(guī)矩,我們來看幾個(gè)生動(dòng)的例子:
//新增一個(gè)有序集合myzset,并加入一個(gè)元素baidu.com,給它賦予的序號是1:
復(fù)制代碼代碼如下:
127.0.0.1:6379> zadd myzset 1 baidu.com (integer) 1 //向myzset中新增一個(gè)元素360.com,賦予它的序號是3 127.0.0.1:6379> zadd myzset 3 360.com (integer) 1 //向myzset中新增一個(gè)元素google.com,賦予它的序號是2 127.0.0.1:6379> zadd myzset 2 google.com (integer) 1 //列出myzset的所有元素,同時(shí)列出其序號,可以看出myzset已經(jīng)是有序的了。 127.0.0.1:6379> zrange myzset 0 -1 with scores 1) "baidu.com" 2) "1" 3) "google.com" 4) "2" 5) "360.com" 6) "3" //只列出myzset的元素 127.0.0.1:6379> zrange myzset 0 -1 1) "baidu.com" 2) "google.com" 3) "360.com"
【redis數(shù)據(jù)結(jié)構(gòu) – 哈希】
最后要給大家介紹的是hashes,即哈希。哈希是從redis-2.0.0版本之后才有的數(shù)據(jù)結(jié)構(gòu)。
hashes存的是字符串和字符串值之間的映射,比如一個(gè)用戶要存儲其全名、姓氏、年齡等等,就很適合使用哈希。
我們來看一個(gè)例子:
復(fù)制代碼代碼如下:
//建立哈希,并賦值 127.0.0.1:6379> HMSET user:001 username antirez password P1pp0 age 34 OK //列出哈希的內(nèi)容 127.0.0.1:6379> HGETALL user:001 1) "username" 2) "antirez" 3) "password" 4) "P1pp0" 5) "age" 6) "34" //更改哈希中的某一個(gè)值 127.0.0.1:6379> HSET user:001 password 12345 (integer) 0 //再次列出哈希的內(nèi)容 127.0.0.1:6379> HGETALL user:001 1) "username" 2) "antirez" 3) "password" 4) "12345" 5) "age" 6) "34"
有關(guān)hashes的操作,同樣很豐富,需要時(shí),大家可以從這里查詢。
【聊聊redis持久化 – 兩種方式】
redis提供了兩種持久化的方式,分別是RDB(Redis DataBase)和AOF(Append Only File)。
RDB,簡而言之,就是在不同的時(shí)間點(diǎn),將redis存儲的數(shù)據(jù)生成快照并存儲到磁盤等介質(zhì)上;
AOF,則是換了一個(gè)角度來實(shí)現(xiàn)持久化,那就是將redis執(zhí)行過的所有寫指令記錄下來,在下次redis重新啟動(dòng)時(shí),只要把這些寫指令從前到后再重復(fù)執(zhí)行一遍,就可以實(shí)現(xiàn)數(shù)據(jù)恢復(fù)了。
其實(shí)RDB和AOF兩種方式也可以同時(shí)使用,在這種情況下,如果redis重啟的話,則會(huì)優(yōu)先采用AOF方式來進(jìn)行數(shù)據(jù)恢復(fù),這是因?yàn)锳OF方式的數(shù)據(jù)恢復(fù)完整度更高。
如果你沒有數(shù)據(jù)持久化的需求,也完全可以關(guān)閉RDB和AOF方式,這樣的話,redis將變成一個(gè)純內(nèi)存數(shù)據(jù)庫,就像memcache一樣。
【聊聊redis持久化 – RDB】
RDB方式,是將redis某一時(shí)刻的數(shù)據(jù)持久化到磁盤中,是一種快照式的持久化方法。
redis在進(jìn)行數(shù)據(jù)持久化的過程中,會(huì)先將數(shù)據(jù)寫入到一個(gè)臨時(shí)文件中,待持久化過程都結(jié)束了,才會(huì)用這個(gè)臨時(shí)文件替換上次持久化好的文件。正是這種特性,讓我們可以隨時(shí)來進(jìn)行備份,因?yàn)榭煺瘴募偸峭暾捎玫摹?/p>
對于RDB方式,redis會(huì)單獨(dú)創(chuàng)建(fork)一個(gè)子進(jìn)程來進(jìn)行持久化,而主進(jìn)程是不會(huì)進(jìn)行任何IO操作的,這樣就確保了redis極高的性能。
如果需要進(jìn)行大規(guī)模數(shù)據(jù)的恢復(fù),且對于數(shù)據(jù)恢復(fù)的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
雖然RDB有不少優(yōu)點(diǎn),但它的缺點(diǎn)也是不容忽視的。如果你對數(shù)據(jù)的完整性非常敏感,那么RDB方式就不太適合你,因?yàn)榧词鼓忝?分鐘都持久化一次,當(dāng)redis故障時(shí),仍然會(huì)有近5分鐘的數(shù)據(jù)丟失。所以,redis還提供了另一種持久化方式,那就是AOF。
【聊聊redis持久化 – AOF】
AOF,英文是Append Only File,即只允許追加不允許改寫的文件。
如前面介紹的,AOF方式是將執(zhí)行過的寫指令記錄下來,在數(shù)據(jù)恢復(fù)時(shí)按照從前到后的順序再將指令都執(zhí)行一遍,就這么簡單。
我們通過配置redis.conf中的appendonly yes就可以打開AOF功能。如果有寫操作(如SET等),redis就會(huì)被追加到AOF文件的末尾。
默認(rèn)的AOF持久化策略是每秒鐘fsync一次(fsync是指把緩存中的寫指令記錄到磁盤中),因?yàn)樵谶@種情況下,redis仍然可以保持很好的處理性能,即使redis故障,也只會(huì)丟失最近1秒鐘的數(shù)據(jù)。
如果在追加日志時(shí),恰好遇到磁盤空間滿、inode滿或斷電等情況導(dǎo)致日志寫入不完整,也沒有關(guān)系,redis提供了redis-check-aof工具,可以用來進(jìn)行日志修復(fù)。
因?yàn)椴捎昧俗芳臃绞?,如果不做任何處理的話,AOF文件會(huì)變得越來越大,為此,redis提供了AOF文件重寫(rewrite)機(jī)制,即當(dāng)AOF文件的大小超過所設(shè)定的閾值時(shí),redis就會(huì)啟動(dòng)AOF文件的內(nèi)容壓縮,只保留可以恢復(fù)數(shù)據(jù)的最小指令集。舉個(gè)例子或許更形象,假如我們調(diào)用了100次INCR指令,在AOF文件中就要存儲100條指令,但這明顯是很低效的,完全可以把這100條指令合并成一條SET指令,這就是重寫機(jī)制的原理。
在進(jìn)行AOF重寫時(shí),仍然是采用先寫臨時(shí)文件,全部完成后再替換的流程,所以斷電、磁盤滿等問題都不會(huì)影響AOF文件的可用性,這點(diǎn)大家可以放心。
AOF方式的另一個(gè)好處,我們通過一個(gè)“場景再現(xiàn)”來說明。某同學(xué)在操作redis時(shí),不小心執(zhí)行了FLUSHALL,導(dǎo)致redis內(nèi)存中的數(shù)據(jù)全部被清空了,這是很悲劇的事情。不過這也不是世界末日,只要redis配置了AOF持久化方式,且AOF文件還沒有被重寫(rewrite),我們就可以用最快的速度暫停redis并編輯AOF文件,將最后一行的FLUSHALL命令刪除,然后重啟redis,就可以恢復(fù)redis的所有數(shù)據(jù)到FLUSHALL之前的狀態(tài)了。是不是很神奇,這就是AOF持久化方式的好處之一。但是如果AOF文件已經(jīng)被重寫了,那就無法通過這種方法來恢復(fù)數(shù)據(jù)了。
雖然優(yōu)點(diǎn)多多,但AOF方式也同樣存在缺陷,比如在同樣數(shù)據(jù)規(guī)模的情況下,AOF文件要比RDB文件的體積大。而且,AOF方式的恢復(fù)速度也要慢于RDB方式。
如果你直接執(zhí)行BGREWRITEAOF命令,那么redis會(huì)生成一個(gè)全新的AOF文件,其中便包括了可以恢復(fù)現(xiàn)有數(shù)據(jù)的最少的命令集。
如果運(yùn)氣比較差,AOF文件出現(xiàn)了被寫壞的情況,也不必過分擔(dān)憂,redis并不會(huì)貿(mào)然加載這個(gè)有問題的AOF文件,而是報(bào)錯(cuò)退出。這時(shí)可以通過以下步驟來修復(fù)出錯(cuò)的文件:
1.備份被寫壞的AOF文件
2.運(yùn)行redis-check-aof –fix進(jìn)行修復(fù)
3.用diff -u來看下兩個(gè)文件的差異,確認(rèn)問題點(diǎn)
4.重啟redis,加載修復(fù)后的AOF文件
【聊聊redis持久化 – AOF重寫】
AOF重寫的內(nèi)部運(yùn)行原理,我們有必要了解一下。
在重寫即將開始之際,redis會(huì)創(chuàng)建(fork)一個(gè)“重寫子進(jìn)程”,這個(gè)子進(jìn)程會(huì)首先讀取現(xiàn)有的AOF文件,并將其包含的指令進(jìn)行分析壓縮并寫入到一個(gè)臨時(shí)文件中。
與此同時(shí),主工作進(jìn)程會(huì)將新接收到的寫指令一邊累積到內(nèi)存緩沖區(qū)中,一邊繼續(xù)寫入到原有的AOF文件中,這樣做是保證原有的AOF文件的可用性,避免在重寫過程中出現(xiàn)意外。
當(dāng)“重寫子進(jìn)程”完成重寫工作后,它會(huì)給父進(jìn)程發(fā)一個(gè)信號,父進(jìn)程收到信號后就會(huì)將內(nèi)存中緩存的寫指令追加到新AOF文件中。
當(dāng)追加結(jié)束后,redis就會(huì)用新AOF文件來代替舊AOF文件,之后再有新的寫指令,就都會(huì)追加到新的AOF文件中了。
【聊聊redis持久化 – 如何選擇RDB和AOF】
對于我們應(yīng)該選擇RDB還是AOF,官方的建議是兩個(gè)同時(shí)使用。這樣可以提供更可靠的持久化方案。
【聊聊主從 – 用法】
像MySQL一樣,redis是支持主從同步的,而且也支持一主多從以及多級從結(jié)構(gòu)。
主從結(jié)構(gòu),一是為了純粹的冗余備份,二是為了提升讀性能,比如很消耗性能的SORT就可以由從服務(wù)器來承擔(dān)。
redis的主從同步是異步進(jìn)行的,這意味著主從同步不會(huì)影響主邏輯,也不會(huì)降低redis的處理性能。
主從架構(gòu)中,可以考慮關(guān)閉主服務(wù)器的數(shù)據(jù)持久化功能,只讓從服務(wù)器進(jìn)行持久化,這樣可以提高主服務(wù)器的處理性能。
在主從架構(gòu)中,從服務(wù)器通常被設(shè)置為只讀模式,這樣可以避免從服務(wù)器的數(shù)據(jù)被誤修改。但是從服務(wù)器仍然可以接受CONFIG等指令,所以還是不應(yīng)該將從服務(wù)器直接暴露到不安全的網(wǎng)絡(luò)環(huán)境中。如果必須如此,那可以考慮給重要指令進(jìn)行重命名,來避免命令被外人誤執(zhí)行。
【聊聊主從 – 同步原理】
從服務(wù)器會(huì)向主服務(wù)器發(fā)出SYNC指令,當(dāng)主服務(wù)器接到此命令后,就會(huì)調(diào)用BGSAVE指令來創(chuàng)建一個(gè)子進(jìn)程專門進(jìn)行數(shù)據(jù)持久化工作,也就是將主服務(wù)器的數(shù)據(jù)寫入RDB文件中。在數(shù)據(jù)持久化期間,主服務(wù)器將執(zhí)行的寫指令都緩存在內(nèi)存中。
在BGSAVE指令執(zhí)行完成后,主服務(wù)器會(huì)將持久化好的RDB文件發(fā)送給從服務(wù)器,從服務(wù)器接到此文件后會(huì)將其存儲到磁盤上,然后再將其讀取到內(nèi)存中。這個(gè)動(dòng)作完成后,主服務(wù)器會(huì)將這段時(shí)間緩存的寫指令再以redis協(xié)議的格式發(fā)送給從服務(wù)器。
另外,要說的一點(diǎn)是,即使有多個(gè)從服務(wù)器同時(shí)發(fā)來SYNC指令,主服務(wù)器也只會(huì)執(zhí)行一次BGSAVE,然后把持久化好的RDB文件發(fā)給多個(gè)下游。在redis2.8版本之前,如果從服務(wù)器與主服務(wù)器因某些原因斷開連接的話,都會(huì)進(jìn)行一次主從之間的全量的數(shù)據(jù)同步;而在2.8版本之后,redis支持了效率更高的增量同步策略,這大大降低了連接斷開的恢復(fù)成本。
主服務(wù)器會(huì)在內(nèi)存中維護(hù)一個(gè)緩沖區(qū),緩沖區(qū)中存儲著將要發(fā)給從服務(wù)器的內(nèi)容。從服務(wù)器在與主服務(wù)器出現(xiàn)網(wǎng)絡(luò)瞬斷之后,從服務(wù)器會(huì)嘗試再次與主服務(wù)器連接,一旦連接成功,從服務(wù)器就會(huì)把“希望同步的主服務(wù)器ID”和“希望請求的數(shù)據(jù)的偏移位置(replication offset)”發(fā)送出去。主服務(wù)器接收到這樣的同步請求后,首先會(huì)驗(yàn)證主服務(wù)器ID是否和自己的ID匹配,其次會(huì)檢查“請求的偏移位置”是否存在于自己的緩沖區(qū)中,如果兩者都滿足的話,主服務(wù)器就會(huì)向從服務(wù)器發(fā)送增量內(nèi)容。
增量同步功能,需要服務(wù)器端支持全新的PSYNC指令。這個(gè)指令,只有在redis-2.8之后才具有。
【聊聊redis的事務(wù)處理】
眾所周知,事務(wù)是指“一個(gè)完整的動(dòng)作,要么全部執(zhí)行,要么什么也沒有做”。
在聊redis事務(wù)處理之前,要先和大家介紹四個(gè)redis指令,即MULTI、EXEC、DISCARD、WATCH。這四個(gè)指令構(gòu)成了redis事務(wù)處理的基礎(chǔ)。
1.MULTI用來組裝一個(gè)事務(wù);
2.EXEC用來執(zhí)行一個(gè)事務(wù);
3.DISCARD用來取消一個(gè)事務(wù);
4.WATCH用來監(jiān)視一些key,一旦這些key在事務(wù)執(zhí)行之前被改變,則取消事務(wù)的執(zhí)行。
紙上得來終覺淺,我們來看一個(gè)MULTI和EXEC的例子:
復(fù)制代碼代碼如下:
redis> MULTI //標(biāo)記事務(wù)開始 OK redis> INCR user_id //多條命令按順序入隊(duì) QUEUED redis> INCR user_id QUEUED redis> INCR user_id QUEUED redis> PING QUEUED redis> EXEC //執(zhí)行 1) (integer) 1 2) (integer) 2 3) (integer) 3 4) PONG
在上面的例子中,我們看到了QUEUED的字樣,這表示我們在用MULTI組裝事務(wù)時(shí),每一個(gè)命令都會(huì)進(jìn)入到內(nèi)存隊(duì)列中緩存起來,如果出現(xiàn)QUEUED則表示我們這個(gè)命令成功插入了緩存隊(duì)列,在將來執(zhí)行EXEC時(shí),這些被QUEUED的命令都會(huì)被組裝成一個(gè)事務(wù)來執(zhí)行。
對于事務(wù)的執(zhí)行來說,如果redis開啟了AOF持久化的話,那么一旦事務(wù)被成功執(zhí)行,事務(wù)中的命令就會(huì)通過write命令一次性寫到磁盤中去,如果在向磁盤中寫的過程中恰好出現(xiàn)斷電、硬件故障等問題,那么就可能出現(xiàn)只有部分命令進(jìn)行了AOF持久化,這時(shí)AOF文件就會(huì)出現(xiàn)不完整的情況,這時(shí),我們可以使用redis-check-aof工具來修復(fù)這一問題,這個(gè)工具會(huì)將AOF文件中不完整的信息移除,確保AOF文件完整可用。
有關(guān)事務(wù),大家經(jīng)常會(huì)遇到的是兩類錯(cuò)誤:
1.調(diào)用EXEC之前的錯(cuò)誤
2.調(diào)用EXEC之后的錯(cuò)誤
“調(diào)用EXEC之前的錯(cuò)誤”,有可能是由于語法有誤導(dǎo)致的,也可能時(shí)由于內(nèi)存不足導(dǎo)致的。只要出現(xiàn)某個(gè)命令無法成功寫入緩沖隊(duì)列的情況,redis都會(huì)進(jìn)行記錄,在客戶端調(diào)用EXEC時(shí),redis會(huì)拒絕執(zhí)行這一事務(wù)。(這時(shí)2.6.5版本之后的策略。在2.6.5之前的版本中,redis會(huì)忽略那些入隊(duì)失敗的命令,只執(zhí)行那些入隊(duì)成功的命令)。我們來看一個(gè)這樣的例子:
復(fù)制代碼代碼如下:
127.0.0.1:6379> multi OK 127.0.0.1:6379> haha //一個(gè)明顯錯(cuò)誤的指令 (error) ERR unknown command 'haha' 127.0.0.1:6379> ping QUEUED 127.0.0.1:6379> exec //redis無情的拒絕了事務(wù)的執(zhí)行,原因是“之前出現(xiàn)了錯(cuò)誤” (error) EXECABORT Transaction discarded because of previous errors.
而對于“調(diào)用EXEC之后的錯(cuò)誤”,redis則采取了完全不同的策略,即redis不會(huì)理睬這些錯(cuò)誤,而是繼續(xù)向下執(zhí)行事務(wù)中的其他命令。這是因?yàn)?,對于?yīng)用層面的錯(cuò)誤,并不是redis自身需要考慮和處理的問題,所以一個(gè)事務(wù)中如果某一條命令執(zhí)行失敗,并不會(huì)影響接下來的其他命令的執(zhí)行。我們也來看一個(gè)例子:
復(fù)制代碼代碼如下:
127.0.0.1:6379> multi OK 127.0.0.1:6379> set age 23 QUEUED //age不是集合,所以如下是一條明顯錯(cuò)誤的指令 127.0.0.1:6379> sadd age 15 QUEUED 127.0.0.1:6379> set age 29 QUEUED 127.0.0.1:6379> exec //執(zhí)行事務(wù)時(shí),redis不會(huì)理睬第2條指令執(zhí)行錯(cuò)誤 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) OK 127.0.0.1:6379> get age "29" //可以看出第3條指令被成功執(zhí)行了
好了,我們來說說最后一個(gè)指令“WATCH”,這是一個(gè)很好用的指令,它可以幫我們實(shí)現(xiàn)類似于“樂觀鎖”的效果,即CAS(check and set)。
WATCH本身的作用是“監(jiān)視key是否被改動(dòng)過”,而且支持同時(shí)監(jiān)視多個(gè)key,只要還沒真正觸發(fā)事務(wù),WATCH都會(huì)盡職盡責(zé)的監(jiān)視,一旦發(fā)現(xiàn)某個(gè)key被修改了,在執(zhí)行EXEC時(shí)就會(huì)返回nil,表示事務(wù)無法觸發(fā)。
復(fù)制代碼代碼如下:
127.0.0.1:6379> set age 23 OK 127.0.0.1:6379> watch age //開始監(jiān)視age OK 127.0.0.1:6379> set age 24 //在EXEC之前,age的值被修改了 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set age 25 QUEUED 127.0.0.1:6379> get age QUEUED 127.0.0.1:6379> exec //觸發(fā)EXEC (nil) //事務(wù)無法被執(zhí)行
【教你看懂redis配置 – 簡介】
我們可以在啟動(dòng)redis-server時(shí)指定應(yīng)該加載的配置文件,方法如下:
復(fù)制代碼代碼如下:
$ ./redis-server /path/to/redis.conf
接下來,我們就來講解下redis配置文件的各個(gè)配置項(xiàng)的含義,注意,本文是基于redis-2.8.4版本進(jìn)行講解的。
redis官方提供的redis.conf文件,足有700+行,其中100多行為有效配置行,另外的600多行為注釋說明。
在配置文件的開頭部分,首先明確了一些度量單位:
復(fù)制代碼代碼如下:
# 1k => 1000 bytes # 1kb => 1024 bytes # 1m => 1000000 bytes # 1mb => 1024*1024 bytes # 1g => 1000000000 bytes # 1gb => 1024*1024*1024 bytes
可以看出,redis配置中對單位的大小寫不敏感,1GB、1Gb和1gB都是相同的。由此也說明,redis只支持bytes,不支持bit單位。
redis支持“主配置文件中引入外部配置文件”,很像C/C++中的include指令,比如:
復(fù)制代碼代碼如下:
include /path/to/other.conf
如果你看過redis的配置文件,會(huì)發(fā)現(xiàn)還是很有條理的。redis配置文件被分成了幾大塊區(qū)域,它們分別是:
1.通用(general)
2.快照(snapshotting)
3.復(fù)制(replication)
4.安全(security)
5.限制(limits)
6.追加模式(append only mode)
7.LUA腳本(lua scripting)
8.慢日志(slow log)
9.事件通知(event notification)
下面我們就來逐一講解。
【教你看懂redis配置 -通用】
默認(rèn)情況下,redis并不是以daemon形式來運(yùn)行的。通過daemonize配置項(xiàng)可以控制redis的運(yùn)行形式,如果改為yes,那么redis就會(huì)以daemon形式運(yùn)行:
復(fù)制代碼代碼如下:
daemonize no
當(dāng)以daemon形式運(yùn)行時(shí),redis會(huì)生成一個(gè)pid文件,默認(rèn)會(huì)生成在/var/run/redis.pid。當(dāng)然,你可以通過pidfile來指定pid文件生成的位置,比如:
復(fù)制代碼代碼如下:
pidfile /path/to/redis.pid
默認(rèn)情況下,redis會(huì)響應(yīng)本機(jī)所有可用網(wǎng)卡的連接請求。當(dāng)然,redis允許你通過bind配置項(xiàng)來指定要綁定的IP,比如:
復(fù)制代碼代碼如下:
bind 192.168.1.2 10.8.4.2
redis的默認(rèn)服務(wù)端口是6379,你可以通過port配置項(xiàng)來修改。如果端口設(shè)置為0的話,redis便不會(huì)監(jiān)聽端口了。
復(fù)制代碼代碼如下:
port 6379
有些同學(xué)會(huì)問“如果redis不監(jiān)聽端口,還怎么與外界通信呢”,其實(shí)redis還支持通過unix socket方式來接收請求??梢酝ㄟ^unixsocket配置項(xiàng)來指定unix socket文件的路徑,并通過unixsocketperm來指定文件的權(quán)限。
復(fù)制代碼代碼如下:
unixsocket /tmp/redis.sock unixsocketperm 755
當(dāng)一個(gè)redis-client一直沒有請求發(fā)向server端,那么server端有權(quán)主動(dòng)關(guān)閉這個(gè)連接,可以通過timeout來設(shè)置“空閑超時(shí)時(shí)限”,0表示永不關(guān)閉。
復(fù)制代碼代碼如下:
timeout 0
TCP連接?;畈呗?,可以通過tcp-keepalive配置項(xiàng)來進(jìn)行設(shè)置,單位為秒,假如設(shè)置為60秒,則server端會(huì)每60秒向連接空閑的客戶端發(fā)起一次ACK請求,以檢查客戶端是否已經(jīng)掛掉,對于無響應(yīng)的客戶端則會(huì)關(guān)閉其連接。所以關(guān)閉一個(gè)連接最長需要120秒的時(shí)間。如果設(shè)置為0,則不會(huì)進(jìn)行保活檢測。
復(fù)制代碼代碼如下:
tcp-keepalive 0
redis支持通過loglevel配置項(xiàng)設(shè)置日志等級,共分四級,即debug、verbose、notice、warning。
復(fù)制代碼代碼如下:
loglevel notice
redis也支持通過logfile配置項(xiàng)來設(shè)置日志文件的生成位置。如果設(shè)置為空字符串,則redis會(huì)將日志輸出到標(biāo)準(zhǔn)輸出。假如你在daemon情況下將日志設(shè)置為輸出到標(biāo)準(zhǔn)輸出,則日志會(huì)被寫到/dev/null中。
復(fù)制代碼代碼如下:
logfile ""
如果希望日志打印到syslog中,也很容易,通過syslog-enabled來控制。另外,syslog-ident還可以讓你指定syslog里的日志標(biāo)志,比如:
復(fù)制代碼代碼如下:
syslog-ident redis
而且還支持指定syslog設(shè)備,值可以是USER或LOCAL0-LOCAL7。具體可以參考syslog服務(wù)本身的用法。
復(fù)制代碼代碼如下:
syslog-facility local0
對于redis來說,可以設(shè)置其數(shù)據(jù)庫的總數(shù)量,假如你希望一個(gè)redis包含16個(gè)數(shù)據(jù)庫,那么設(shè)置如下:
復(fù)制代碼代碼如下:
databases 16
這16個(gè)數(shù)據(jù)庫的編號將是0到15。默認(rèn)的數(shù)據(jù)庫是編號為0的數(shù)據(jù)庫。用戶可以使用select <DBid>來選擇相應(yīng)的數(shù)據(jù)庫。
【教你看懂redis配置 – 快照】
快照,主要涉及的是redis的RDB持久化相關(guān)的配置,我們來一起看一看。
我們可以用如下的指令來讓數(shù)據(jù)保存到磁盤上,即控制RDB快照功能:
復(fù)制代碼代碼如下:
save <seconds> <changes>
舉例來說:
復(fù)制代碼代碼如下:
save 900 1 //表示每15分鐘且至少有1個(gè)key改變,就觸發(fā)一次持久化 save 300 10 //表示每5分鐘且至少有10個(gè)key改變,就觸發(fā)一次持久化 save 60 10000 //表示每60秒至少有10000個(gè)key改變,就觸發(fā)一次持久化
如果你想禁用RDB持久化的策略,只要不設(shè)置任何save指令就可以,或者給save傳入一個(gè)空字符串參數(shù)也可以達(dá)到相同效果,就像這樣:
復(fù)制代碼代碼如下:
save ""
如果用戶開啟了RDB快照功能,那么在redis持久化數(shù)據(jù)到磁盤時(shí)如果出現(xiàn)失敗,默認(rèn)情況下,redis會(huì)停止接受所有的寫請求。這樣做的好處在于可以讓用戶很明確的知道內(nèi)存中的數(shù)據(jù)和磁盤上的數(shù)據(jù)已經(jīng)存在不一致了。如果redis不顧這種不一致,一意孤行的繼續(xù)接收寫請求,就可能會(huì)引起一些災(zāi)難性的后果。
如果下一次RDB持久化成功,redis會(huì)自動(dòng)恢復(fù)接受寫請求。
當(dāng)然,如果你不在乎這種數(shù)據(jù)不一致或者有其他的手段發(fā)現(xiàn)和控制這種不一致的話,你完全可以關(guān)閉這個(gè)功能,以便在快照寫入失敗時(shí),也能確保redis繼續(xù)接受新的寫請求。配置項(xiàng)如下:
復(fù)制代碼代碼如下:
stop-writes-on-bgsave-error yes
對于存儲到磁盤中的快照,可以設(shè)置是否進(jìn)行壓縮存儲。如果是的話,redis會(huì)采用LZF算法進(jìn)行壓縮。如果你不想消耗CPU來進(jìn)行壓縮的話,可以設(shè)置為關(guān)閉此功能,但是存儲在磁盤上的快照會(huì)比較大。
復(fù)制代碼代碼如下:
rdbcompression yes
在存儲快照后,我們還可以讓redis使用CRC64算法來進(jìn)行數(shù)據(jù)校驗(yàn),但是這樣做會(huì)增加大約10%的性能消耗,如果你希望獲取到最大的性能提升,可以關(guān)閉此功能。
復(fù)制代碼代碼如下:
rdbchecksum yes
我們還可以設(shè)置快照文件的名稱,默認(rèn)是這樣配置的:
復(fù)制代碼代碼如下:
dbfilename dump.rdb
最后,你還可以設(shè)置這個(gè)快照文件存放的路徑。比如默認(rèn)設(shè)置就是當(dāng)前文件夾:
復(fù)制代碼代碼如下:
dir ./
【教你看懂redis配置 – 復(fù)制】
redis提供了主從同步功能。
通過slaveof配置項(xiàng)可以控制某一個(gè)redis作為另一個(gè)redis的從服務(wù)器,通過指定IP和端口來定位到主redis的位置。一般情況下,我們會(huì)建議用戶為從redis設(shè)置一個(gè)不同頻率的快照持久化的周期,或者為從redis配置一個(gè)不同的服務(wù)端口等等。
復(fù)制代碼代碼如下:
slaveof <masterip> <masterport>
如果主redis設(shè)置了驗(yàn)證密碼的話(使用requirepass來設(shè)置),則在從redis的配置中要使用masterauth來設(shè)置校驗(yàn)密碼,否則的話,主redis會(huì)拒絕從redis的訪問請求。
復(fù)制代碼代碼如下:
masterauth <master-password>
當(dāng)從redis失去了與主redis的連接,或者主從同步正在進(jìn)行中時(shí),redis該如何處理外部發(fā)來的訪問請求呢?這里,從redis可以有兩種選擇:
第一種選擇:如果slave-serve-stale-data設(shè)置為yes(默認(rèn)),則從redis仍會(huì)繼續(xù)響應(yīng)客戶端的讀寫請求。
第二種選擇:如果slave-serve-stale-data設(shè)置為no,則從redis會(huì)對客戶端的請求返回“SYNC with master in progress”,當(dāng)然也有例外,當(dāng)客戶端發(fā)來INFO請求和SLAVEOF請求,從redis還是會(huì)進(jìn)行處理。
你可以控制一個(gè)從redis是否可以接受寫請求。將數(shù)據(jù)直接寫入從redis,一般只適用于那些生命周期非常短的數(shù)據(jù),因?yàn)樵谥鲝耐綍r(shí),這些臨時(shí)數(shù)據(jù)就會(huì)被清理掉。自從redis2.6版本之后,默認(rèn)從redis為只讀。
復(fù)制代碼代碼如下:
slave-read-only yes
只讀的從redis并不適合直接暴露給不可信的客戶端。為了盡量降低風(fēng)險(xiǎn),可以使用rename-command指令來將一些可能有破壞力的命令重命名,避免外部直接調(diào)用。比如:
復(fù)制代碼代碼如下:
rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
從redis會(huì)周期性的向主redis發(fā)出PING包。你可以通過repl_ping_slave_period指令來控制其周期。默認(rèn)是10秒。
復(fù)制代碼代碼如下:
repl-ping-slave-period 10
在主從同步時(shí),可能在這些情況下會(huì)有超時(shí)發(fā)生:
1.以從redis的角度來看,當(dāng)有大規(guī)模IO傳輸時(shí)。 2.以從redis的角度來看,當(dāng)數(shù)據(jù)傳輸或PING時(shí),主redis超時(shí) 3.以主redis的角度來看,在回復(fù)從redis的PING時(shí),從redis超時(shí)
用戶可以設(shè)置上述超時(shí)的時(shí)限,不過要確保這個(gè)時(shí)限比repl-ping-slave-period的值要大,否則每次主redis都會(huì)認(rèn)為從redis超時(shí)。
復(fù)制代碼代碼如下:
repl-timeout 60
我們可以控制在主從同步時(shí)是否禁用TCP_NODELAY。如果開啟TCP_NODELAY,那么主redis會(huì)使用更少的TCP包和更少的帶寬來向從redis傳輸數(shù)據(jù)。但是這可能會(huì)增加一些同步的延遲,大概會(huì)達(dá)到40毫秒左右。如果你關(guān)閉了TCP_NODELAY,那么數(shù)據(jù)同步的延遲時(shí)間會(huì)降低,但是會(huì)消耗