redis入門指南讀書筆記

以下介紹使用redis版本為5.0.4

簡(jiǎn)介

redis(remote dictionary server 遠(yuǎn)程字典服務(wù)器)是一個(gè)開源高性能的鍵值對(duì)數(shù)據(jù)庫,通過提供多種鍵值數(shù)據(jù)類型來適應(yīng)不同場(chǎng)景下的存儲(chǔ)需求,并借助高層次的接口來勝任緩存、隊(duì)列等角色。

功能

以字典結(jié)構(gòu)存儲(chǔ)數(shù)據(jù),并允許其他應(yīng)用通過tcp連接來讀寫字典中的內(nèi)容。

支持的鍵值類型

  • 字符串
  • 散列類型
  • 列表
  • 集合
  • 有序集合

相對(duì)于mysql等二維表形式存儲(chǔ)數(shù)據(jù)的關(guān)系型數(shù)據(jù)庫有點(diǎn)

  • 存儲(chǔ)數(shù)據(jù)更接近于程序中的數(shù)據(jù),操作數(shù)據(jù)更方便
  • 提供簡(jiǎn)潔、高效的操作
  • 數(shù)據(jù)存儲(chǔ)于內(nèi)存中,相對(duì)于硬盤存儲(chǔ)更為高效(提供有異步持久化寫入硬盤)

緩存系統(tǒng)

redis提供有豐富的功能,通過給鍵值設(shè)置ttl(time to live)生存時(shí)間,可以作為緩存系統(tǒng)來實(shí)現(xiàn),并且支持持久化和多種數(shù)據(jù)類型,因此與memcached緩存系統(tǒng)存在競(jìng)爭(zhēng)關(guān)系。

redis是單線程,memcached是多線程,所以在多核服務(wù)器上,memcached的性能占優(yōu)勢(shì),但是redis是高性能的,所以一般不會(huì)存在性能瓶頸,所以在不考慮性能的情況下,兩者的使用選擇,主要看使用場(chǎng)景,例如redis提供有多種數(shù)據(jù)類型和持久化功能,如果需要使用這些高級(jí)數(shù)據(jù)類型或者持久化能力,則可以選擇redis

redis的列表類型鍵支持阻塞式讀取,可以實(shí)現(xiàn)優(yōu)先級(jí)隊(duì)列。并且在更高層面支持“發(fā)布、訂閱”的消息模式,因此可以構(gòu)建聊天室等系統(tǒng)。

啟動(dòng)配置

通過redis-server命令可以在啟動(dòng)配置相關(guān)參數(shù),例如--port <port>配置redis服務(wù)的端口號(hào),如果參數(shù)較多,可以在redis-server后加配置文件路徑,redis-server /redis/path/config,在配置文件后加參數(shù)可以覆蓋配置文件中的配置 redis-server /redis/path/config --loglevel warning,在redis目錄下存在一個(gè)配置模板

部分參數(shù)在啟動(dòng)后可以通過config命令動(dòng)態(tài)修改,例如:config set loglevel warning

實(shí)例下數(shù)據(jù)庫多個(gè)字典

redis服務(wù)器下有多個(gè)字典,類似于一個(gè)數(shù)據(jù)庫實(shí)例可以有多個(gè)數(shù)據(jù)庫,字典以編號(hào)排列,不能進(jìn)行命名,從0開始,默認(rèn)為16個(gè)。而且多個(gè)字典面向同一個(gè)客戶端,即一個(gè)redis實(shí)例面向一個(gè)客戶端,多個(gè)字典對(duì)于一個(gè)客戶端,要么可以全部訪問,要么一個(gè)都不能訪問。所以建議一個(gè)redis實(shí)例對(duì)應(yīng)存儲(chǔ)一個(gè)應(yīng)用的數(shù)據(jù),可以將不同數(shù)據(jù)存儲(chǔ)于多個(gè)字典中,不建議一個(gè)redis實(shí)例存儲(chǔ)多個(gè)應(yīng)用的數(shù)據(jù),因?yàn)檫@些字典,或數(shù)據(jù)庫不存在隔離性。

符號(hào) 含義
? 匹配一個(gè)字符
* 匹配任意個(gè)(包括0個(gè))字符
[] 匹配括號(hào)內(nèi)任一字符,使用“-”可以匹配范圍
\x 匹配字符x,\用作轉(zhuǎn)義

基礎(chǔ)命令

命令 作用
keys <keyName> 返回鍵名,keys *,返回所有鍵名
exists <keyName> 判斷鍵是否存在,存在返回1,不存在返回0
del <name1 name2 ..> 刪除鍵,可以為多個(gè),返回刪除的個(gè)數(shù)
type <keyName> 返回鍵值的類型,可能是字符串string、散列hash、列表list、集合set、有序集合zset

del命令不支持通配符刪除,可以通過keys命令拿到多個(gè)鍵名來作為輸入進(jìn)行一次刪除,示例:
./redis-cli -p 6380 del `./redis-cli -p 6380 keys \*`

字符串類型

字符串作為redis中的基礎(chǔ)類型,可以存儲(chǔ)任意類型的字符串,包括二進(jìn)制字符串,或者json化的對(duì)象,甚至一張圖片,字符串類型鍵允許存儲(chǔ)的最大容量是512M。

字符串類型是其他幾種類型的基礎(chǔ),其他類型與字符串類型的不同只是組織字符串方式的差異,例如list列表類型只是以列表的形式來組織字符串,集合只是以集合的方式來組織字符串。

設(shè)置、獲取鍵值

命令 作用
set <keyName> <value> 設(shè)置鍵值
get <keyName> 獲取鍵值,當(dāng)鍵不存在,返回空(nil)

redis對(duì)于鍵的命名無要求,但推薦使用 對(duì)象類型:對(duì)象id:對(duì)象屬性 來命名一個(gè)鍵,例如 user:1:friends 來存儲(chǔ)用戶id為1的好友列表,多個(gè)單詞則使用.符號(hào)進(jìn)行分隔

incr與decr

命令 作用
incr <keyName> 遞增鍵值
incrby <keyName> <increment> 指定步長(zhǎng)遞增鍵值
decr <keyName> 遞減鍵值
decrby <keyName> <increment> 指定步長(zhǎng)遞減鍵值
incrbyfloat <keyName> <increment> 指定浮點(diǎn)數(shù)步長(zhǎng)遞增鍵值

append添加字符串

命令 作用
append <keyName> <value> 增加字符串,返回添加后字符串長(zhǎng)度

strlen返回字符串長(zhǎng)度

命令 作用
strlen <keyName> 返回字符串長(zhǎng)度

redis可以保存二進(jìn)制數(shù)據(jù),可以保存任何類型的字符串,對(duì)于中文,使用utf-8進(jìn)行編碼保存,一個(gè)中文字符對(duì)應(yīng)三個(gè)字節(jié)長(zhǎng)度

同時(shí)設(shè)置、獲取多個(gè)鍵值

命令 作用
mset k1 v1 k2 v2 ... 同時(shí)設(shè)置多個(gè)鍵值
mget k1 k2 ... 同時(shí)獲取多個(gè)鍵值

二進(jìn)制位操作

命令 作用
setbit <keyName> <pos> <value> 設(shè)置鍵值對(duì)應(yīng)位的二進(jìn)制,返回該位置的舊值
getbit <keyName> <pos> 獲取鍵值對(duì)應(yīng)位的二進(jìn)制
bitcount <keyName> 獲取鍵值二進(jìn)制中1的個(gè)數(shù)
bitop [or|xor|and|not] <result> <key1> <key2> 二進(jìn)制運(yùn)算,并將結(jié)果賦予result

散列類型

redis使用鍵值對(duì)形式的字典結(jié)構(gòu),散列類型也是一種鍵值對(duì)形式的字典結(jié)構(gòu),存儲(chǔ)字段到字段值的映射,但字段值只能是字符串,不能是其他類型,即不支持嵌套類型,一個(gè)散列類型的鍵最多可以有2^{32}-1個(gè)字段。

redis中其他類型同樣不支持嵌套類型,例如集合中元素只能是字符串,不能是其他集合或列表類型

散列類型適合存儲(chǔ)對(duì)象,使用對(duì)象和id作為鍵名,字段名作為屬性,字段值作為屬性值。該結(jié)構(gòu)相對(duì)于關(guān)系型數(shù)據(jù)庫的優(yōu)點(diǎn)是,可以隨意增刪一個(gè)對(duì)象的屬性,而不用像二維表結(jié)構(gòu)那樣修改表結(jié)構(gòu)。

設(shè)置、獲取屬性值

命令 作用
hset <keyName> <field> <value> 設(shè)置屬性值
hget <keyName> <field> 獲取屬性值

散列類型不區(qū)分插入和更新,插入屬性操作返回1,更新操作返回0,如果鍵不存在,則會(huì)自動(dòng)創(chuàng)建

設(shè)置、獲取多個(gè)屬性值

命令 作用
hmset <keyName> <field> <value> <field1> <value1> ... 設(shè)置多個(gè)屬性值
hmget <keyName> <field> <field1> ... 獲取多個(gè)屬性值
hgetall <keyName> 獲取所有屬性值

判斷屬性是否存在

命令 作用
hexists <keyName> <field> 屬性存在返回1,不存在返回0

屬性不存在時(shí)賦值

命令 作用
hsetnx <keyName> <field> 屬性不存在時(shí)賦值,存在不作操作

屬性值增加數(shù)字

命令 作用
hincrby <keyName> <field> <increment> 屬性值增加數(shù)字,不存在則創(chuàng)建

刪除屬性

命令 作用
hdel <keyName> <field> 刪除屬性,存在返回1,不存在返回0

獲取鍵屬性名、屬性值

命令 作用
hkeys <keyName> 獲取所有屬性名
hvals <keyName> 獲取所有屬性值
hlen <keyName> 獲取所有屬性個(gè)數(shù)

列表類型

redis列表內(nèi)部使用雙向鏈表實(shí)現(xiàn),所以無論列表大小是多大,從頭尾獲取一定長(zhǎng)度的數(shù)據(jù)速度很快??梢杂脕肀4嫘迈r事或者日志,不用考慮列表本身有多大,只需要從一端獲取數(shù)據(jù)即可。最大列表項(xiàng)為2^{32}-1個(gè)。

添加元素

命令 作用
lpush <keyName> <value> 從左側(cè)添加元素,返回列表長(zhǎng)度
rpush <keyName> <value> 從右側(cè)添加元素,返回列表長(zhǎng)度

彈出元素

命令 作用
lpop <keyName> 從左側(cè)彈出元素
rpop <keyName> 從右側(cè)彈出元素

redis列表的雙向鏈表特性,可以用于實(shí)現(xiàn)棧和隊(duì)列,作為棧使用時(shí),lpush+lpop或rpush+rpop;作為隊(duì)列使用時(shí),lpush+rpop或rpush+lpop。

列表長(zhǎng)度

命令 作用
llen <keyName> 返回列表長(zhǎng)度

列表切片

命令 作用
lrange <keyName> <start> <end> 返回指定范圍的列表元素,包括終點(diǎn)位置,可以為負(fù)數(shù)

常用lrange 0 -1獲取列表所有元素

列表中刪除指定個(gè)數(shù)的元素值

命令 作用
lrem <keyName> <count> <value> 從列表中刪除count表示個(gè)數(shù)的元素值,返回刪除的個(gè)數(shù)
  • 當(dāng)count為正數(shù)時(shí),從左側(cè)開始刪除count個(gè)value元素
  • 當(dāng)count為負(fù)數(shù)時(shí),從右側(cè)開始刪除|count|個(gè)value元素
  • 當(dāng)count為0時(shí),刪除列表中全部value元素

獲取、設(shè)置指定位置元素

命令 作用
lindex <keyName> <index> 返回指定位置列表元素
lset <keyName> <index> <value> 設(shè)置指定位置列表元素值

刪除列表指定范圍之外的所有元素

命令 作用
ltrim <keyName> <start> <end> 刪除列表指定范圍之外的所有元素

可用于保存指定數(shù)量的日志信息,在lpush添加新日志記錄后,使用ltrim 0 99保存指定個(gè)數(shù)的日志記錄。

在指定元素旁插入元素

命令 作用
linsert <keyName> <before after> <pivot> <value> 在指定元素前后插入元素

將元素從一個(gè)列表移動(dòng)到另一個(gè)列表

命令 作用
rpoplpush <source> <destination> 將source列表右側(cè)元素彈出,在destination列表左側(cè)插入

當(dāng)source和destination相同時(shí),可以用于監(jiān)控程序,循環(huán)執(zhí)行該命令,在不影響新元素加入的情況下,可以對(duì)元素進(jìn)行循環(huán)檢測(cè)

集合類型

集合類型存儲(chǔ)不重復(fù)的元素,元素唯一,但無需,內(nèi)部使用值為空的散列表實(shí)現(xiàn),所以查詢?cè)氐臅r(shí)間復(fù)雜度為O(n),一個(gè)集合最多存儲(chǔ)2^{32}-1個(gè)元素。

集合常見的操作,除了加入、刪除和判斷元素是否存在外,還能提供靈活的交集、并集和差集操作。

添加、刪除集合元素

命令 作用
sadd <keyName> <value> <value2> ... 添加集合元素,返回成功添加的元素個(gè)數(shù)
srem <keyName> <value> <value2> ... 刪除集合元素,返回成功刪除的元素個(gè)數(shù)

返回集合中所有元素

命令 作用
smembers <keyName> 返回集合中所有元素

判斷元素是否在集合中

命令 作用
sismember <keyName> <value> 元素在集合中返回1,不在返回0

集合間操作

命令 作用
sdiff <seta> <setb> 元素在seta中,且不在setb中,集合的差集,支持傳入多個(gè)集合
sinter <seta> <setb> 元素在seta中,且在setb中,集合的交集,支持傳入多個(gè)集合
sunion <seta> <setb> 元素在seta中,或在setb中,集合的并集,支持傳入多個(gè)集合

獲得集合中元素個(gè)數(shù)

命令 作用
scard <keyName> 返回集合中元素個(gè)數(shù)

集合運(yùn)算并存儲(chǔ)結(jié)果

命令 作用
sdiffstore <des> <seta> <setb> 將seta與setb的差集賦予des
sinterstore <des> <seta> <setb> 將seta與setb的交集賦予des
sunionstore <des> <seta> <setb> 將seta與setb的并集賦予des

隨機(jī)獲得集合中元素

命令 作用
srandmember <keyName> count 根據(jù)count值,隨機(jī)返回集合中元素
  • 當(dāng)count為正數(shù)時(shí),隨機(jī)返回count個(gè)不重復(fù)元素,當(dāng)count大于集合中元素個(gè)數(shù)時(shí),返回所有元素
  • 當(dāng)count為負(fù)數(shù)時(shí),隨機(jī)|count|個(gè)元素,可能重復(fù)

因?yàn)閞edis集合內(nèi)部是散列表的實(shí)現(xiàn),如果存在散列沖突,則以鏈表形式存儲(chǔ)元素,在鏈表上隨機(jī)獲取元素,所以對(duì)于不沖突的元素,可能srandmember返回的概率更高一些。

從集合彈出隨機(jī)元素

命令 作用
spop <keyName> 從集合彈出隨機(jī)元素

有序集合類型

有序集合相對(duì)于集合,給元素增加了一個(gè)關(guān)聯(lián)的分?jǐn)?shù),以此提供獲得最高或最低的N個(gè)元素,或者指定分?jǐn)?shù)范圍的元素等操作。

有序集合通過散列表和跳躍表實(shí)現(xiàn),相對(duì)于集合和列表更耗費(fèi)內(nèi)存。

向有序集合添加元素

命令 作用
zadd <keyName> <score> <value> <score1> <value1> ... 向有序集合添加元素

獲取有序集合元素的分?jǐn)?shù)

命令 作用
zscore <keyName> <value> 返回元素的分?jǐn)?shù)

根據(jù)分?jǐn)?shù)排名獲得在某個(gè)下標(biāo)范圍的元素列表

命令 作用
zrange <keyName> <start> <end> 獲取排名在某個(gè)范圍的元素列表,包括首尾位置

該命令與lrange返回指定下標(biāo)范圍的列表元素很相似,并且提供附加參數(shù)withscores,返回元素與分?jǐn)?shù)。zrevrange與zrange相似,只是按照分?jǐn)?shù)由大到小輸出。

獲得在某個(gè)分?jǐn)?shù)范圍的元素列表

命令 作用
zrangebyscore <keyName> <start> <end> 獲取分?jǐn)?shù)在某個(gè)范圍的元素列表,包括首尾位置

可以通過(符號(hào)來表示分?jǐn)?shù)小于該分?jǐn)?shù)值,同樣支持正負(fù)無窮,-inf和+inf,withscores參數(shù)用法同上

獲得在某個(gè)分?jǐn)?shù)范圍內(nèi)指定偏移量的元素列表

命令 作用
zrangebyscore <keyName> <start> <end> limit <offset> <count> 獲取分?jǐn)?shù)在某個(gè)范圍內(nèi),從offset開始的count個(gè)元素列表,包括首尾位置

逆序的偏移量使用方式為zrevrangebyscore方式,start和end為由大到小方式

給元素增加分?jǐn)?shù)

命令 作用
zincrby <keyName> <increment> <value> 給元素增加分?jǐn)?shù)

獲得集合中元素的個(gè)數(shù)

命令 作用
zcard <keyName> 返回元素的個(gè)數(shù)

獲得集合中指定范圍的元素個(gè)數(shù)

命令 作用
zcount <keyName> <start> <end> 返回集合中指定范圍的元素個(gè)數(shù)

默認(rèn)包括首尾數(shù)值,通過(符號(hào)來不包含首尾值

刪除集合中元素

命令 作用
zrem <keyName> <value> 返回刪除成功的元素個(gè)數(shù)

按照排名范圍刪除集合中元素

命令 作用
zremrangebyrank <keyName> <start> <end> 返回刪除成功的元素個(gè)數(shù)

按照分?jǐn)?shù)范圍刪除集合中元素

命令 作用
zremrangebyscore <keyName> <start> <end> 返回刪除成功的元素個(gè)數(shù)

獲得元素排名

命令 作用
zrank <keyName> <value> 返回元素按照分?jǐn)?shù)正序排名
zrevrank <keyName> <value> 返回元素按照分?jǐn)?shù)逆序排名

有序集合間操作

命令 作用
zinterstore <desSet> <numkeys> <set1> ... 集合間交集運(yùn)算,元素分?jǐn)?shù)默認(rèn)為相加
zunionstore <desSet> <numkeys> <set1> ... 集合間并集運(yùn)算,元素分?jǐn)?shù)默認(rèn)為相加

通過添加weights <key1Weight> <key2Weight> 可以給集合元素分?jǐn)?shù)設(shè)置不同權(quán)重,通過添加aggregate sum|min|max可以對(duì)元素分?jǐn)?shù)取不同操作

事務(wù)

事務(wù)是一組命令的集合,同命令一樣,都是redis的最小執(zhí)行單元。事務(wù)中的命令,要么都執(zhí)行,要么全部不執(zhí)行。

redis事務(wù)的執(zhí)行原理,是發(fā)送一個(gè)事務(wù)命令,然后將待執(zhí)行命令存儲(chǔ)于事務(wù)隊(duì)列之中,然后發(fā)送exec命令執(zhí)行隊(duì)列中的命令。

事務(wù)操作

命令 作用
multi 標(biāo)志事務(wù)塊開始
exec 執(zhí)行事務(wù)中命令

redis中不提供回滾的功能,如果是語法錯(cuò)誤,在發(fā)送exec命令后,redis會(huì)識(shí)別錯(cuò)誤,并放棄執(zhí)行所有命令,如果是運(yùn)行錯(cuò)誤,則redis會(huì)執(zhí)行所有能執(zhí)行的命令,因?yàn)樵趫?zhí)行前并不能識(shí)別出哪些命令可以執(zhí)行,哪些不可以執(zhí)行。需要用戶對(duì)這些執(zhí)行錯(cuò)誤的命令進(jìn)行修復(fù)。

watch

多線程環(huán)境中,對(duì)鍵值的非原子操作可能存在競(jìng)態(tài)條件,例如先判斷鍵值,再修改鍵值,這種非原子操作在并發(fā)情況下可能得到一些非預(yù)想結(jié)果。通過watch命令可以設(shè)置對(duì)鍵值的監(jiān)聽,后續(xù)的操作根據(jù)監(jiān)聽來選擇是否執(zhí)行。

watch命令對(duì)一個(gè)或多個(gè)鍵值進(jìn)行監(jiān)聽,當(dāng)一個(gè)或多個(gè)鍵值發(fā)生變化時(shí),則后續(xù)的一個(gè)事務(wù)取消執(zhí)行;若監(jiān)聽的鍵值都沒有發(fā)生變化,則執(zhí)行事務(wù),exec后,取消對(duì)鍵值的監(jiān)聽。

unwatch命令可以取消對(duì)鍵值的監(jiān)聽,事務(wù)中通過discard命令取消事務(wù),也可以做到取消對(duì)鍵值的監(jiān)聽。

expire

expire命令可以設(shè)置鍵的生存時(shí)間,單位為秒,過期后刪除該鍵。通過ttl命令可以查看鍵的剩余生存時(shí)間,如果沒有對(duì)鍵設(shè)置生存時(shí)間,則返回-1,如果鍵不存在或到期后被刪除,則返回-2。

通過set命令對(duì)鍵進(jìn)行修改,相當(dāng)于設(shè)置鍵的生存時(shí)間為永久,即相當(dāng)于沒有設(shè)置生存時(shí)間。生存時(shí)間的操作可以應(yīng)用于一些限制訪問頻率的場(chǎng)景中。

緩存

為了提高網(wǎng)站的負(fù)載能力,往往將一些cpu或內(nèi)存消耗較高的操作結(jié)果存入redis,并設(shè)置過期時(shí)間,以此來提供緩存的功能。如果生存時(shí)間設(shè)置較長(zhǎng),則可能存在內(nèi)存一直被占用的問題,如果生存時(shí)間設(shè)置較短,則可能導(dǎo)致命中率過低的問題。

對(duì)redis中緩存鍵的淘汰有如下幾種規(guī)則:

規(guī)則 說明
volatile-lru 使用lru算法清除一個(gè)鍵(對(duì)于設(shè)置了生存時(shí)間的鍵)
allkeys-lru 使用lru算法清除一個(gè)鍵
volatile-random 隨機(jī)清除一個(gè)鍵(對(duì)于設(shè)置了生存時(shí)間的鍵)
allkeys-random 隨機(jī)清除一個(gè)鍵
volatile-ttl 刪除ttl時(shí)間最少的一個(gè)鍵
noeviction 不刪除鍵,返回錯(cuò)誤

sort

sort命令提供對(duì)集合、有序集合、列表的排序功能,默認(rèn)將元素轉(zhuǎn)為雙精度浮點(diǎn)數(shù)進(jìn)行遞增排序,通過alpha參數(shù)可以按照字典序進(jìn)行排序,通過desc參數(shù)可以進(jìn)行遞減排序,通過limit offset count參數(shù)可以獲取指定偏移量的count個(gè)元素。

對(duì)有序集合的排序,是按照元素自身來排序的,與分?jǐn)?shù)無關(guān)。

如果使用by參考鍵來進(jìn)行排序,則排序操作不依賴自身元素字典值,而是將自身元素替換掉參考鍵的第一個(gè)*符號(hào),并取其值作為排序依據(jù)進(jìn)行排序。

示例:
集合tag:ruby:posts,存儲(chǔ)文章的id,post:<id>哈希鍵,存儲(chǔ)文章對(duì)象的多個(gè)屬性,例如time、id、title等,此處對(duì)集合tag:ruby:posts進(jìn)行排序,排序的依據(jù)是文章的更新時(shí)間降序排列

sort tag:ruby:posts by post:*->time desc

該命令作用為使用文章對(duì)象的time屬性降序排列文章的id集合

get

get命令可以搭配sort命令,獲取排序后的屬性值,同樣使用*符號(hào)替換屬性名
示例:
在依據(jù)文章的時(shí)間對(duì)id集合進(jìn)行排序后,根據(jù)id獲取文章的title

sort tag:ruby:posts by post:*->time desc get post:*->title

可以填寫多個(gè)get,同時(shí)獲取多個(gè)屬性值

獲取待排序集合自身,可以使用get #

store

sort執(zhí)行的結(jié)果默認(rèn)直接返回,也可以將結(jié)果存儲(chǔ)為一個(gè)鍵,作為結(jié)果集使用

sort tag:ruby:posts by post:*->time desc get post:*->title store sort.result

排序后的結(jié)果解store后為list類型,常用的方式為搭配expire命令,作為sort后的緩存結(jié)果使用

expire sort.result <seconds>

sort命令的時(shí)間復(fù)雜度為O(n+mlogm),其中n為待排序列表(集合或有序集合)的元素個(gè)數(shù),m為待返回的元素個(gè)數(shù)。為了避免sort命令稱為性能瓶頸,使用時(shí)需要注意sort的使用方式,常見的優(yōu)化方式為:
1)減少待排序元素
2)使用limit獲取盡量少的元素
3)數(shù)據(jù)量較大時(shí),使用store+expire建立緩存

任務(wù)隊(duì)列

使用列表可以實(shí)現(xiàn)任務(wù)隊(duì)列,例如lpush+rpop,可以使用rpop循環(huán)獲取列表中元素,如果元素存在則處理,不存在則等待一定時(shí)間繼續(xù)從隊(duì)列中獲取元素。

redis中提供有brpop/blpop命令來阻塞獲取隊(duì)列中元素

brpop <queue> <time>

time為阻塞時(shí)間,當(dāng)time為0時(shí),表示一直阻塞。

brpop/blpop阻塞等待命令可以同時(shí)監(jiān)控多個(gè)隊(duì)列,所以可以將不同優(yōu)先級(jí)的任務(wù)放在不同隊(duì)列中,優(yōu)先級(jí)高的隊(duì)列放在前面,這樣當(dāng)高優(yōu)先級(jí)的隊(duì)列中有元素時(shí),則取元素并執(zhí)行任務(wù)。

brpop que1 que2 0

發(fā)布、訂閱模式

除了任務(wù)隊(duì)列外,redis提供有發(fā)布/訂閱模式來實(shí)現(xiàn)進(jìn)程間通信。訂閱者可以訂閱若干個(gè)頻道,發(fā)布者可以向指定頻道發(fā)送消息,所有訂閱次頻道的訂閱者都可以接收到該消息。

發(fā)布到某頻道的消息不會(huì)進(jìn)行持久化,即訂閱者只能收到訂閱此頻道之后發(fā)布到該頻道的消息。

publish <channel> <message>
subscribe <channel> ...

publish命令會(huì)返回接收該消息的訂閱者數(shù)量,subscribe有三種返回類型:

  • 第一個(gè)值為subscribe類型,第二個(gè)值是訂閱頻道名稱,第三個(gè)值是當(dāng)前訂閱者訂閱的頻道數(shù)量
  • 第一個(gè)值為message類型,第二個(gè)值是頻道名稱,第三個(gè)值是消息內(nèi)容
  • 第一個(gè)值為unsubscribe類型,第二個(gè)值是取消訂閱的頻道名稱,第三個(gè)值是當(dāng)前訂閱者訂閱的頻道數(shù)量

通過psubscribe可以使用通配符形式訂閱頻道

psubscribe <channel> 

接收publish消息的返回會(huì)多一項(xiàng)通配符形式的頻道:第一個(gè)值為message類型,第二個(gè)值是通配符頻道名稱,第三個(gè)是具體的頻道名稱,第四個(gè)值是消息內(nèi)容

punsubscribe用于退訂psubscribe訂閱的頻道,unsubscribe用于退訂subscribe訂閱的頻道,對(duì)應(yīng)使用。

管道

客戶端與redis的通信過程是通過tcp連接進(jìn)行的,命令的傳輸與結(jié)果的返回都是存在網(wǎng)絡(luò)傳輸時(shí)延的,當(dāng)要執(zhí)行的命令較多時(shí),如果每次傳輸執(zhí)行一條命令,后續(xù)的每條命令都等待前一條命令執(zhí)行結(jié)束后,才能進(jìn)行傳輸執(zhí)行,時(shí)延造成的性能影響較大。

redis底層通信對(duì)管道(pipelining)提供了支持,如果多個(gè)命令的結(jié)果不相互依賴,可以通過管道一次傳輸多個(gè)命令,并將結(jié)果一次返回,通過降低通信次數(shù)來減少傳輸時(shí)延。

節(jié)省空間

redis的數(shù)據(jù)是保存在內(nèi)存之中的,所以優(yōu)化存儲(chǔ),減少空間的占用對(duì)成本控制是很重要的。

精簡(jiǎn)鍵名和鍵值

例如very.import.person:20 可以精簡(jiǎn)為 VIP:20,male和female可以改為m和f。

內(nèi)部編碼優(yōu)化

redis未每種數(shù)據(jù)類型提供了兩種內(nèi)部編碼方式,以散列類型為例,散列類型以散列表實(shí)現(xiàn),實(shí)現(xiàn) O(1) 時(shí)間復(fù)雜度查找和賦值操作,但是當(dāng)鍵中元素?cái)?shù)較少時(shí),散列類型會(huì)以一種緊湊但性能較差的內(nèi)部編碼方式。當(dāng)數(shù)據(jù)量較少時(shí),O(1)O(n) 相差不大。

查詢鍵的內(nèi)部編碼方式

object encoding <key>

持久化

redis的高性能在于其數(shù)據(jù)保存于內(nèi)存中,并且為了避免重啟后丟失數(shù)據(jù),提供了將數(shù)據(jù)同步到硬盤的方式,即持久化。redis提供有兩種方式的持久化,rdb和aof,可以使用其中一種或兩種結(jié)合使用。

rdb(redis database)

rdb方式的持久化是通過快照完成的,當(dāng)符合一定條件會(huì)自動(dòng)將內(nèi)存中數(shù)據(jù)進(jìn)行快照并存儲(chǔ)在硬盤中。執(zhí)行快照的條件是在配置文件中自定義的,包括兩個(gè)參數(shù):時(shí)間和改動(dòng)的鍵個(gè)數(shù)。當(dāng)在指定時(shí)間內(nèi),改動(dòng)的鍵個(gè)數(shù)達(dá)到條件后會(huì)觸發(fā)快照。

rdb是redis默認(rèn)的持久化方式,在redis.conf配置文件中預(yù)置了三個(gè)條件:

save 900 1
save 300 10
save 60 10000

save參數(shù)指定了快照條件,多個(gè)條件之間是“或”的關(guān)系。

redis 默認(rèn)快照存儲(chǔ)位置為當(dāng)前目錄的dump.rdb文件中,通過dir指定目錄,通過dbfilename指定文件名??煺瘴恢煤涂煺諚l件都可以在redis.conf文件中進(jìn)行修改。

快照過程:

  1. redis使用fork函數(shù)復(fù)制當(dāng)前進(jìn)程(父進(jìn)程)的副本(子進(jìn)程)
  2. 父進(jìn)程繼續(xù)接收并處理客戶端命令,子進(jìn)程將內(nèi)存中數(shù)據(jù)寫入硬盤的臨時(shí)文件中
  3. 子進(jìn)程寫入完畢,將該臨時(shí)文件替換舊的rdb文件

fork函數(shù)執(zhí)行使用寫時(shí)復(fù)制(copy on write)策略,父、子進(jìn)程共享同一內(nèi)存空間,當(dāng)父進(jìn)程執(zhí)行寫命令時(shí),操作系統(tǒng)會(huì)將數(shù)據(jù)復(fù)制一份給子進(jìn)程,避免子進(jìn)程數(shù)據(jù)受影響。所以rdb文件存儲(chǔ)的是fork函數(shù)執(zhí)行那一刻的數(shù)據(jù),如果redis異常退出,則會(huì)丟失最后一次備份之后的修改。

處理自動(dòng)備份外,還可以手動(dòng)執(zhí)行save、bgsave使用主進(jìn)程或fork子進(jìn)程進(jìn)行備份。

aop(append only file)

redis默認(rèn)沒有開啟aop持久化,通過修改appendonly參數(shù)為yes開啟該持久化功能。aof的持久化文件目錄與rdb相同,同樣有dir參數(shù)配置,文件名由appendfilename參數(shù)配置。

啟用aof后,每條修改數(shù)據(jù)的命令都會(huì)被記錄。這里其實(shí)并不是直接記錄到硬盤文件中,而是寫入硬盤緩存,通過appendfsync參數(shù)配置同步緩存到硬盤文件的時(shí)間,默認(rèn)為everysec,即每秒鐘都會(huì)進(jìn)行同步,no表示由操作系統(tǒng)進(jìn)行同步,即每30秒進(jìn)行同步,always表示每次執(zhí)行命令都會(huì)進(jìn)行同步。

如果同時(shí)啟動(dòng)了rdb和aof,則啟動(dòng)redis時(shí)會(huì)根據(jù)aof文件進(jìn)行恢復(fù)數(shù)據(jù)。

如果配置文件中設(shè)置appendonly為yes,沒有aof文件生成,需要執(zhí)行如下命令:
redis-cli config set appendonly yes
redis-cli config set save ""

復(fù)制

通過持久化功能,redis保證了即使服務(wù)器重啟,也不會(huì)損失多少數(shù)據(jù),但是當(dāng)服務(wù)器硬盤故障時(shí),仍然會(huì)導(dǎo)致數(shù)據(jù)丟失。為了避免這種單點(diǎn)故障的情況,需要將數(shù)據(jù)存儲(chǔ)在多個(gè)服務(wù)器上,當(dāng)一臺(tái)服務(wù)器上redis更新數(shù)據(jù)時(shí),通過復(fù)制功能將數(shù)據(jù)同步到其他服務(wù)器上。

配置

同步數(shù)據(jù)庫存在兩種角色,提供讀寫功能的主數(shù)據(jù)庫,提供只讀功能的從數(shù)據(jù)庫,在從數(shù)據(jù)庫的配置文件中加入如下配置即可,主數(shù)據(jù)庫無需配置。

slaveof <master ip> <master port>

該命令可以在啟動(dòng)redis服務(wù)時(shí)指定--slaveof ,也可以在運(yùn)行時(shí)指定/更新主數(shù)據(jù)庫

復(fù)制原理

從數(shù)據(jù)庫啟動(dòng)后,向主數(shù)據(jù)庫發(fā)送sync命令,主數(shù)據(jù)庫接收到sync命令后,開始后臺(tái)保存快照(rdb持久化過程),并將快照期間接收到的命令緩存起來??煺胀瓿珊?,redis將快照文件和所有緩存命令發(fā)送給從數(shù)據(jù)庫。從數(shù)據(jù)庫收到后,會(huì)載入快照文件并執(zhí)行收到的緩存的命令。若主從斷開后,會(huì)重新執(zhí)行上述快照、緩存命令、發(fā)送快照和緩存命令方式,不支持?jǐn)帱c(diǎn)續(xù)傳。

從數(shù)據(jù)庫會(huì)將接收到的內(nèi)容寫入硬盤臨時(shí)文件中,當(dāng)寫入完成后會(huì)用該臨時(shí)文件替換rdb快照文件,然后根據(jù)快照文件恢復(fù)數(shù)據(jù)。從數(shù)據(jù)庫在同步期間并不會(huì)阻塞,可以繼續(xù)接收客戶端命令。默認(rèn)情況為使用同步前的數(shù)據(jù)對(duì)命令進(jìn)行響應(yīng)??梢耘渲?slave-server-stale-data參數(shù)為no,在同步完成前對(duì)客戶端命令回復(fù)錯(cuò)誤"sync with master in progress"。

無論是否啟用了rdb持久化方式(刪除save參數(shù)),redis啟動(dòng)時(shí)都會(huì)嘗試讀取dir和dbfilename參數(shù)指定的rdb快照文件恢復(fù)數(shù)據(jù)。

讀寫分離

在常見的場(chǎng)景中,讀的頻率大于寫,當(dāng)單機(jī)的redis無法應(yīng)付大量的讀請(qǐng)求時(shí),可以通過復(fù)制功能建立多個(gè)從數(shù)據(jù)庫,主數(shù)據(jù)庫只進(jìn)行寫操作,從數(shù)據(jù)庫負(fù)責(zé)讀操作,即實(shí)現(xiàn)讀寫分離來提供服務(wù)器的負(fù)載能力。

從數(shù)據(jù)庫持久化

持久化操作相對(duì)較為耗時(shí),為了提供性能,可以通過復(fù)制功能建立一個(gè)或多個(gè)從數(shù)據(jù)庫,并在從數(shù)據(jù)庫上啟用持久化,同時(shí)在主數(shù)據(jù)庫上禁用持久化(刪除save配置條件)。當(dāng)從數(shù)據(jù)庫崩潰后恢復(fù),可以由主數(shù)據(jù)庫同步數(shù)據(jù);當(dāng)主數(shù)據(jù)庫崩潰時(shí),從數(shù)據(jù)庫使用slaveof no one命令提升為新的主數(shù)據(jù)庫提供服務(wù),恢復(fù)后的原主數(shù)據(jù)庫使用slaveof命令變?yōu)閺臄?shù)據(jù)庫,并同步回?cái)?shù)據(jù)。

安全

redis以簡(jiǎn)潔為美,在安全層面沒有太多的工作。redis設(shè)計(jì)前提為運(yùn)行在可信環(huán)境中,所以redis默認(rèn)會(huì)接收來自任何地址發(fā)送來的請(qǐng)求,可以通過配置bind參數(shù),限制只允許指定地址的連接。

通信協(xié)議

redis支持兩種通信協(xié)議,二進(jìn)制安全的統(tǒng)一請(qǐng)求協(xié)議(unified request protocol)和較為直觀的便于在telnet程序中輸入的簡(jiǎn)單協(xié)議。兩種協(xié)議在命令格式上有差異,命令的返回值格式是一樣的。

最后編輯于
?著作權(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ù)。

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