Redis入門使用手冊(cè) (Redis入門指南筆記)


網(wǎng)站推薦


Redis優(yōu)點(diǎn)簡(jiǎn)述

來(lái)自Redis快速入門

  • 異??焖?/strong> : Redis是非常快的,每秒可以執(zhí)行大約110000設(shè)置操作,81000個(gè)/每秒的讀取操作。
  • 支持豐富的數(shù)據(jù)類型 : Redis支持最大多數(shù)開發(fā)人員已經(jīng)知道如列表,集合,可排序集合,哈希等數(shù)據(jù)類型。這使得在應(yīng)用中很容易解決的各種問(wèn)題,因?yàn)槲覀冎滥男﹩?wèn)題處理使用哪種數(shù)據(jù)類型更好解決。
  • 操作都是原子的 : 所有 Redis 的操作都是原子,從而確保當(dāng)兩個(gè)客戶同時(shí)訪問(wèn) Redis 服務(wù)器得到的是更新后的值(最新值)。
  • MultiUtility工具:Redis是一個(gè)多功能實(shí)用工具,可以在很多如:緩存,消息傳遞隊(duì)列中使用(Redis原生支持發(fā)布/訂閱),在應(yīng)用程序中,如:Web應(yīng)用程序會(huì)話,網(wǎng)站頁(yè)面點(diǎn)擊數(shù)等任何短暫的數(shù)據(jù);
  • Redis 有三個(gè)主要使其有別于其它很多競(jìng)爭(zhēng)對(duì)手的特點(diǎn)
    Redis是完全在內(nèi)存中保存數(shù)據(jù)的數(shù)據(jù)庫(kù),使用磁盤只是為了持久性目的;
    Redis相比許多鍵值數(shù)據(jù)存儲(chǔ)系統(tǒng)有相對(duì)豐富的數(shù)據(jù)類型;
    Redis可以將數(shù)據(jù)復(fù)制到任意數(shù)量的從服務(wù)器中。

Redis環(huán)境安裝

  • 在 Ubuntu 上安裝 Redis,打開終端輸入以下命令:
$sudo apt-get update
$sudo apt-get install redis-server
# 啟動(dòng) Redis
$redis-server
# 啟動(dòng) 客戶端
$redis-cli

安全訪問(wèn)

  • Redis的安全設(shè)計(jì)是在“Redis運(yùn)行在可信環(huán)境”這個(gè)前提下做出的。在生產(chǎn)環(huán)境運(yùn)行時(shí)不能允許外界直接連接到Redis服務(wù)器上,而應(yīng)該通過(guò)應(yīng)用程序進(jìn)行中轉(zhuǎn),運(yùn)行在可信的環(huán)境中是保證Redis安全的最重要方法。
  • Redis的默認(rèn)配置會(huì)接受來(lái)自任何地址發(fā)送來(lái)的請(qǐng)求,即在任何一個(gè)擁有公網(wǎng)IP的服務(wù)器上啟動(dòng)Redis服務(wù)器,都可以被外界直接訪問(wèn)到。配置文件中bind參數(shù)的設(shè)置參考配置redis外網(wǎng)可訪問(wèn)。
  • 自由地設(shè)置訪問(wèn)規(guī)則需要通過(guò)防火墻來(lái)完成。
  • 數(shù)據(jù)庫(kù)密碼
    • 通過(guò)配置件中的requirepass參數(shù)為Redis設(shè)置一個(gè)密碼。例如 requirepass asdfghjkl??蛻舳嗣看芜B接到Redis時(shí)都需要發(fā)送密碼,否則Redis會(huì)拒絕執(zhí)行客戶端發(fā)來(lái)的命令。發(fā)送密碼需要使用AUTH命令,例如 AUTH asdfghjkl。
    • 由于Redis性能極高,并且輸入密碼錯(cuò)誤后Redis并不會(huì)進(jìn)行主動(dòng)延遲(考慮到Redis的單線程模型),所以攻擊者可以通過(guò)窮舉法破解Redis的密碼(1秒鐘能夠嘗試十幾萬(wàn)個(gè)密碼),因此在設(shè)置時(shí)一定要選擇復(fù)雜的密碼
    • 此外,配置Redis復(fù)制的時(shí)候如果主數(shù)據(jù)庫(kù)設(shè)置了密碼,需要在從數(shù)據(jù)庫(kù)的配置文件中通過(guò)masterauth參數(shù)設(shè)置主數(shù)據(jù)庫(kù)的密碼,以使從數(shù)據(jù)庫(kù)連接主數(shù)據(jù)庫(kù)時(shí)自動(dòng)使用AUTH命令認(rèn)證。

命令重命名

  • Redis支持在配置文件中將命令重命名,比如將FLUSHALL命令重命名為一個(gè)比較復(fù)雜的名字,以保證只有自己的應(yīng)用可以使用該命令。例如rename-command FLUSHALL asdfghjkl。如果希望禁用命令可以重命名為空字符串""
  • 無(wú)論設(shè)置密碼還是重命名命令,都需要保證配置文件的安全性,否則就沒(méi)有任何意義了。

管理工具

  • Redis-cli 即原版客戶端
    1. 當(dāng)一條命令的執(zhí)行時(shí)間超過(guò)限制時(shí),Redis會(huì)將該命令的執(zhí)行時(shí)間等信息加入耗時(shí)命令日志以供開發(fā)者查看。
      可以通過(guò)配置文件的slowlog-log-slower-than參數(shù)設(shè)置這一限制,要注意單位是微秒,默認(rèn)值是10000。
      耗時(shí)命令日志存儲(chǔ)在內(nèi)存中,可以通過(guò)配置文件的slowlog-max-len參數(shù)來(lái)限制記錄的條數(shù)。
      使用SLOWLOG GET命令獲取。
      每條日志由以下4個(gè)部分組成:
      (1)該日志唯一ID;
      (2)該命令執(zhí)行的Unix時(shí)間;
      (3)該命令的耗時(shí)時(shí)間,單位是微秒;
      (4)命令及參數(shù)。
    2. 命令監(jiān)控 MONITOR。一個(gè)客戶端使用該命令會(huì)降低Redis將近一半的負(fù)載能力。可以使用Redis-faina (Instagram團(tuán)隊(duì)開發(fā)的基于MONITOR命令的Redis查詢分析程序)
  • phpRedisAdmin,圖形化管理工具
    安裝:

git clone https://github.com/ErikDubbelboer/phpRedisAdmin.git
cd phpRedisAdmin
git submodule init
git submodule update

  - 配置:?默認(rèn)phpRedisAdmin會(huì)連接到127.0.0.1,端口6379,如果需要更改或者添加數(shù)據(jù)庫(kù)信息可以編輯includes文件夾中的config.inc.php文件。
  - 由于Redis使用單線程處理命令,所以對(duì)生產(chǎn)環(huán)境下?lián)碛写髷?shù)據(jù)量的數(shù)據(jù)庫(kù)來(lái)說(shuō)不適宜使用該管理器。
- **Redis桌面管理**可從 [redisdesktop](http://redisdesktop.com/download) 下載。Redis 桌面管理器會(huì)提供管理 Redis 鍵和數(shù)據(jù)的用戶界面。
- **Rdbtools**是一個(gè)Redis的快照文件解析器,可以根據(jù)快照文件導(dǎo)出JSON數(shù)據(jù)文件、分析Redis中每個(gè)鍵的占用空間情況等。

### 使用實(shí)例與技巧
1. 文章訪問(wèn)量統(tǒng)計(jì),**使用字符串類型的`INCR posts:文章ID:page.view`來(lái)記錄文章的訪問(wèn)量**。每次訪問(wèn)時(shí)鍵值遞增。(另外文章數(shù)據(jù)也可以在序列化之后使用字符串類型存儲(chǔ))
2. 利用位操作(對(duì)于字符串類型鍵使用)命令可以非常緊湊地存儲(chǔ)布爾值。比如如果網(wǎng)站的每個(gè)用戶都有一個(gè)遞增的整數(shù)ID,如果**使用一個(gè)字符串類型鍵配合位操作來(lái)記錄每個(gè)用戶的性別**(用戶ID作為索引,二進(jìn)制位值1和0表示男性和女性),那么記錄100萬(wàn)個(gè)用戶的性別只需占用100KB多的空間,而且由于GETBIT和SETBIT的時(shí)間復(fù)雜度都是O(1),所以**讀取二進(jìn)制位值性能很高**。(在一臺(tái)2014年的MacBookPro筆記奔上,設(shè)置偏移量232-1的值(即分配500MB的內(nèi)存)需要耗費(fèi)將近1秒的時(shí)間)。要注意的是分配過(guò)大的偏移量不僅會(huì)造成服務(wù)器阻塞,還會(huì)造成空間浪費(fèi)。

位操作:
SETBIT key offset value,GETBIT key offset
BITCOUNT key 可以獲得字符串類型鍵中值是1的二進(jìn)制位個(gè)數(shù)
BITOP可以對(duì)多個(gè)字符串類型鍵進(jìn)行位運(yùn)算,并將結(jié)果存儲(chǔ)在destkey參數(shù)指定的鍵中

3. 利用散列類型存儲(chǔ)文章數(shù)據(jù)。**使用`post:文章ID鍵+title/author/time/content等字段`存儲(chǔ)**。美中不足的是散列類型沒(méi)有類似字符串類型的MGET命令那樣可以通過(guò)一條命令同時(shí)獲得多個(gè)鍵的鍵值的版本,所以對(duì)于每個(gè)文章ID都需要請(qǐng)求一次數(shù)據(jù)庫(kù),也就都會(huì)產(chǎn)生一次`往返時(shí)延(round-trip delay time)`,可以使用**管道和腳本**來(lái)優(yōu)化這個(gè)問(wèn)題。
4. 由于列表類型內(nèi)部是使用雙向鏈表實(shí)現(xiàn),獲取頭尾的元素的速度很快。**使用列表類型實(shí)現(xiàn)社交網(wǎng)站的新鮮事**(關(guān)心的只是最新的內(nèi)容)。由于在兩端插入記錄的時(shí)間復(fù)雜度O(1),**使用列表類型來(lái)記錄日志**,可以保證新加入日志的速度不會(huì)受到已有日志數(shù)量的影響。另外還可以做隊(duì)列使用。
另外可以**使用列表類型鍵posts:list記錄文章ID列表 和 文章評(píng)論列表**。當(dāng)發(fā)布新文章時(shí)使用LPUSH命令把新文章的ID加入這個(gè)列表中,另外刪除文章時(shí)把列表中的文章ID刪除,就像這樣:`LREM posts:list 1 要?jiǎng)h除的文章ID`。存儲(chǔ)評(píng)論時(shí):
    # 將評(píng)論序列化成字符串
    $serializedComment = serialize($author, $email, $time, $content)
    LPUSH post:42:comments, $serializedComment
5. **利用集合類型存儲(chǔ)文章標(biāo)簽(tag)**
6. **使用有序集合類型來(lái)實(shí)現(xiàn)按訪問(wèn)量排序的文章存儲(chǔ)。**在集合類型的基礎(chǔ)上有序集合類型為集合中的每個(gè)元素都關(guān)聯(lián)了一個(gè)分?jǐn)?shù),使我們可以獲得分?jǐn)?shù)最高的前N個(gè)元素、指定分?jǐn)?shù)范圍的元素等。集合中的元素不同,但分?jǐn)?shù)可以相同。在這個(gè)鍵中以文章ID作為元素,以該文章的點(diǎn)擊量作為該元素的分?jǐn)?shù)。將該鍵命名為`posts:page.view`,每次用戶訪問(wèn)一次文章時(shí),博客程序就通過(guò)`ZINCRBY posts:page.view 1 文章ID` 更新訪問(wèn)量。獲取文章訪問(wèn)量通過(guò)`ZSCORE posts:page.view 文章ID` 來(lái)實(shí)現(xiàn)。
另外可以**實(shí)現(xiàn)文章按發(fā)布時(shí)間排序**,使元素的分?jǐn)?shù)為文章發(fā)布的Unix時(shí)間。借助`ZREVRANGEBYSCORE`命令還可以輕松獲取指定時(shí)間范圍的文章列表,可以實(shí)現(xiàn)按月份查看文章的功能。
7. **使用列表類型鍵實(shí)現(xiàn)訪問(wèn)頻率限制**。如果要精確地保證每分鐘最多訪問(wèn)10次,需要記錄下用戶每次訪問(wèn)的時(shí)間。因此對(duì)每個(gè)用戶,我們使用一個(gè)列表類型的鍵來(lái)記錄他最近10次訪問(wèn)博客的時(shí)間。一旦鍵中的元素超過(guò) 10 個(gè),就判斷時(shí)間最早的元素距現(xiàn)在的時(shí)間是否小于1分鐘。如果是則表示用戶最近1分鐘的訪問(wèn)次數(shù)超過(guò)了10次;如果不是就將現(xiàn)在的時(shí)間加入到列表中,同時(shí)把最早的元素刪除。

$listLength = LLEN rate.limiting:$IP
if $listLength < 10
LPUSH rate.limiting:$IP, now()
else
$time = LINDEX rate.limiting:$IP, -1
if now() - $time < 60
print 訪問(wèn)頻率超過(guò)了限制,請(qǐng)稍后再試。
else
LPUSH rate.limiting:$IP, now()
LTRIM rate.limiting:$IP, 0, 9

    代碼中now()的功能是獲得當(dāng)前的Unix時(shí)間。由于需要記錄每次訪問(wèn)的時(shí)間,所以當(dāng)要限制"A時(shí)間最多訪問(wèn)B次"時(shí),如果"B"的數(shù)值較大,此方法會(huì)占用較多的存儲(chǔ)空間,實(shí)際使用時(shí)還需要開發(fā)者自己去權(quán)衡。除此之外該方法也會(huì)出現(xiàn)**競(jìng)態(tài)條件**,同樣可以通過(guò)腳本功能避免。
8. **對(duì)列表,集合,有序集合類型鍵使用sort … by … 排序**。SORT命令可以使用于 集合,列表,有序集合。針對(duì)有序集合類型排序時(shí)會(huì)忽略元素的分?jǐn)?shù),只針對(duì)元素自身的值進(jìn)行排序。除了可以排列數(shù)字外,sort命令還可以通過(guò)ALPHA參數(shù)實(shí)現(xiàn)按照字典順序排列非數(shù)字元素。
  - `SORT tag:ruby:posts BY post:*->time DESC` 由 `tag:ruby:posts`獲得的值替換`*`,一般為ID,即依據(jù)`post:ID`的`time`的散列值來(lái)對(duì)`tag:ruby:posts`排序
  - 可以再加上`GET`參數(shù),同樣可以使用*號(hào),`GET #`得到元素本身,還有`STORE key`參數(shù)

SORT tag:ruby:posts
BY post:->time DESC
GET post:
->title GET post:*->time GET #
STORE sort.result

 - 使用SORT命令時(shí)注意使用LIMIT參數(shù)只獲取需要的數(shù)據(jù)(M),盡可能減少待排序鍵中元素的數(shù)量(N),盡可能在數(shù)據(jù)量大時(shí)緩存結(jié)果。時(shí)間復(fù)雜度為`O(n+mlog(m))`
9. **BRPOP實(shí)現(xiàn)任務(wù)隊(duì)列,以及通知消費(fèi)者的優(yōu)先級(jí)隊(duì)列**。BRPOP和RPOP的區(qū)別是當(dāng)列表中沒(méi)有元素時(shí)BRPOP命令會(huì)一直阻塞住連接,直到有新元素加入。BRPOP接收兩個(gè)參數(shù),第一個(gè)是鍵名(可以多個(gè)鍵),第二個(gè)是超時(shí)時(shí)間(0表示不限)。如果有多個(gè)鍵,當(dāng)多個(gè)鍵都有元素則按照從左到右的順序取第一個(gè)鍵中的一個(gè)元素。借此特性可以實(shí)現(xiàn)區(qū)分優(yōu)先級(jí)的任務(wù)隊(duì)列。

### Redis概念拾遺
- 利用Redis中的**事務(wù)**(transaction)來(lái)進(jìn)行多個(gè)連續(xù)命令的原子操作。

def follow($currentUser, $targetUser)
SADD user:$currentUser:following, $targetUser
SADD user:$targetUser:followers, $currentUser

這種操作容易產(chǎn)生導(dǎo)致錯(cuò)誤的競(jìng)態(tài)

利用事務(wù)來(lái)使多條數(shù)據(jù)庫(kù)操作變?yōu)樵硬僮?

MUTLI

EXEC

Redis保證**一個(gè)事務(wù)中執(zhí)行的命令要么都執(zhí)行,要么都不執(zhí)行**。如果在發(fā)送EXEC之前客戶端斷線了,則Redis會(huì)清空事務(wù)隊(duì)列,事務(wù)中的所有命令都不會(huì)執(zhí)行。Redis的事務(wù)能夠保證一個(gè)事務(wù)中的命令依次執(zhí)行不被其他命令插入。
- 限制Redis**最大可用內(nèi)存大小**,修改配置文件的maxmemory參數(shù)(單位是字節(jié)),當(dāng)超出了這個(gè)限制時(shí)Redis會(huì)依據(jù)maxmemory-policy參數(shù)指定的策略來(lái)刪除不需要的鍵直到Redis占用的內(nèi)存小于指定內(nèi)存。
- Redis具有**發(fā)布/訂閱模式**:publish/subscribe。與ROS(機(jī)器人操作系統(tǒng))中的發(fā)布/訂閱類似。
- 使用**管道、腳本**優(yōu)化往返延時(shí)。
- 修改配置文件實(shí)現(xiàn)**內(nèi)部編碼優(yōu)化**。
- Redis的每個(gè)鍵值都是使用一個(gè)**redisObject結(jié)構(gòu)體**保存的,
```C
typedef struct redisObject {  
     unsigned type:4;  
     unsigned notused:2;     /* Not used */  
     unsigned encoding:4;  
     unsigned lru:22;        /* lru time (relative to server.lruclock) */  
     int refcount;  
     void *ptr;  
} robj; 

關(guān)于里面的特殊語(yǔ)法參考Redis源碼閱讀筆記,講解很清楚。
Redis使用一個(gè)sdshdr類型的變量來(lái)存儲(chǔ)字符串,而redisObject的ptr字段指向的是該變量的地址。sdshdr的定義如下:

struct sdshdr {  
     int len;  
     int free;  
     char buf[];  
}; 

其中l(wèi)en字段表示的是字符串的長(zhǎng)度,free字段表示buf中的剩余空間,而buf字段存儲(chǔ)的才是字符串的內(nèi)容。所以當(dāng)執(zhí)行SET key foobar時(shí),存儲(chǔ)鍵值需要占用的空間是sizeof(redisObject) + sizeof(sdshdr) + strlen("foobar") = 30字節(jié)。而當(dāng)鍵值內(nèi)容可以用一個(gè)64位有符號(hào)整數(shù)表示時(shí),Redis會(huì)將鍵值轉(zhuǎn)換成long類型來(lái)存儲(chǔ)。如SET key 123456,實(shí)際占用的空間是sizeof(redisObject) = 16字節(jié),比存儲(chǔ)"foobar"節(jié)省了一半的存儲(chǔ)空間。

  • 列表類型和有序集合類型辯異
    • 相似:
    1. 二者都是有序的。
    2. 二者都可以獲得某一范圍的元素。
  • 不同:
    1. 列表類型是通過(guò)鏈表實(shí)現(xiàn)的,獲取靠近兩端的數(shù)據(jù)速度極快,而當(dāng)元素增多后,訪問(wèn)中間數(shù)據(jù)的速度會(huì)較慢,所以它更加適合實(shí)現(xiàn)如“新鮮事”或“日志”這樣很少訪問(wèn)中間元素的應(yīng)用。
    2. 有序集合類型是使用散列表和跳躍表(Skip list)實(shí)現(xiàn)的,所以即使讀取位于中間部分的數(shù)據(jù)速度也很快(時(shí)間復(fù)雜度是O(log(N)))。
    3. 列表中不能簡(jiǎn)單地調(diào)整某個(gè)元素的位置,但是有序集合可以(通過(guò)更改這個(gè)元素的分?jǐn)?shù))。
    4. 有序集合要比列表類型更耗費(fèi)內(nèi)存。

持久化

  • RDB方式,通過(guò)快照,即當(dāng)符合一定條件時(shí)Redis會(huì)自動(dòng)將內(nèi)存中的所有數(shù)據(jù)生成一份副本并存儲(chǔ)在硬盤上。
    條件:
    1. 根據(jù)配置規(guī)則進(jìn)行自動(dòng)快照
    2. 用戶執(zhí)行SAVE(同步)或BGSAVE(異步)命令
    3. 執(zhí)行FLUSHALL命令
    4. 執(zhí)行復(fù)制(replication)時(shí)
  • AOF方式,默認(rèn)沒(méi)有開啟,通過(guò)appendonly yes配置參數(shù)啟動(dòng)。開啟后每執(zhí)行一條會(huì)更改Redis中的數(shù)據(jù)的命令,Redis就會(huì)將該命令寫入硬盤中的AOF文件。AOF文件的保存位置和RDB文件的位置相同。此外,由于操作系統(tǒng)的緩存機(jī)制,數(shù)據(jù)并沒(méi)有真正地寫入硬盤,而是進(jìn)入了系統(tǒng)的硬盤緩存。在默認(rèn)情況下系統(tǒng)每30秒會(huì)執(zhí)行一次同步操作,以便將硬盤緩存中的內(nèi)容真正寫入硬盤。
    可以通過(guò)appendfsync參數(shù)設(shè)置同步的時(shí)機(jī):
# appendfsync always
appendfsync everysec
# appendfsync no

lua腳本簡(jiǎn)要

使用腳本可以

  1. 減少網(wǎng)絡(luò)開銷:減少往返時(shí)延
  2. 原子操作:Redis會(huì)將整個(gè)腳本作為一個(gè)整體執(zhí)行,中間不會(huì)被其他命令插入。無(wú)需擔(dān)心競(jìng)態(tài),無(wú)需使用事務(wù)。
  3. 復(fù)用:客戶端發(fā)送的腳本會(huì)永久儲(chǔ)存在Redis中,這就意味著其他客戶端(可以是其他語(yǔ)言開發(fā)的項(xiàng)目)可以復(fù)用這一腳本而不需要使用代碼完成同樣的邏輯。

利用腳本實(shí)現(xiàn)訪問(wèn)頻率限制:

local times = redis.call( 'incr', KEYS[1] )

if times == 1 then
    --KEYS[1]鍵剛創(chuàng)建,所以為其設(shè)置生存時(shí)間
    redis.call( 'expire', KEYS[1], ARGV[1] )
end

if times > tonumber(ARGV[2]) then
    return 0
end

return 1

通過(guò)

$redis-cli --eval /path/to/ratelimiting.lua rate.limiting:IP , 10 3

--eval是告訴redis-cli讀取并運(yùn)行后面的Lua腳本
腳本參數(shù)以‘空格,空格’分隔的是KEYS和ARGV參數(shù),該命令的作用是將訪問(wèn)頻率限制為每10秒最多3次。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 《Redis 入門指南》(第二版) 第一章 Redis 是什么 Redis (REmote Dictionary ...
    EdenPP閱讀 67,460評(píng)論 3 10
  • K1517由沈陽(yáng)東至西寧的列車,車箱老舊的綠顏色,列車員操著東北口聲,活干的也粗糙,車箱門口收了票,開車后才換牌兒...
    牧羊海閱讀 179評(píng)論 0 0
  • OK問(wèn)候來(lái)接全中國(guó),各地最優(yōu)秀,最渴望成功的伙伴們,大家晚上好。非常高興,今天我們能夠在空中跟大家共同見(jiàn)面。來(lái)探討...
    寶貝奇奇閱讀 684評(píng)論 0 0
  • 1. 夜深人靜,星稀月隱。京城繁華熱鬧的集市此刻靜寂無(wú)聲。附近的房屋隱沒(méi)在黑暗里,融進(jìn)一片靜謐中。 暮色低沉,一黑...
    千淘閱讀 550評(píng)論 1 5

友情鏈接更多精彩內(nèi)容