摘要:單線程執(zhí)行命令。文件描述符事件。內(nèi)部原因不合理使用或數(shù)據(jù)結(jié)構(gòu)可能由此導(dǎo)致慢查詢等飽和是單線程,只會使用單個持久化阻塞操作產(chǎn)生阻塞,對硬盤的操作產(chǎn)生阻塞或?qū)懖僮髯枞?。?nèi)存達(dá)到時執(zhí)行內(nèi)存溢出控制策略。
最近在看《Redis開發(fā)與運(yùn)維》,把自己學(xué)會的知識點(diǎn)記錄下來,畢竟好記性不如爛筆頭。
一.Redis是什么。
Redis是一個Key-Value的NoSQL數(shù)據(jù)庫.
二.Redis的特點(diǎn)。
1.支持的數(shù)據(jù)類型:hash,list,set,zset,string(memacached只支持string)。
2.單線程執(zhí)行命令。因?yàn)槭菃尉€程,所以減少了線程上下文切換的開銷,同時如果一個命令執(zhí)行時間過長就會引起阻塞。
3.數(shù)據(jù)持久化到內(nèi)存中,一定時間后會存儲到硬盤中。
三.操作數(shù)據(jù)的命令
1.常用的命令
命令 | 含義 |
---|---|
keys * | 查看全部的鍵,會遍歷Redis所有的鍵,時間復(fù)雜度是O(n) |
scan cursor match pattern | 遍歷鍵,cursor是游標(biāo) |
type key | 查看鍵的類型,key是鍵的名稱 |
dbsize | 查看鍵的數(shù)量:dbsize 是直接獲取Redis內(nèi)置的鍵總量,時間復(fù)雜度是O(1) |
exists key | 判斷某個鍵是否存在,存在返回1,不存在返回0. |
del key[ key...] | 返回成功刪除鍵的個數(shù) |
expire key time | 設(shè)置鍵的過期時間 |
ttl key | 查詢某個鍵的剩余過期時間 |
object encoding key | 查詢鍵的內(nèi)部編碼 |
rename key newkey | 鍵重命名 |
renamenx key newkey | 當(dāng)newkey不存在,鍵重命名成功 |
randomkey | 隨機(jī)選擇一個鍵 |
persist key | 清除鍵的過期時間 |
move key db | 在Redis內(nèi)部進(jìn)行數(shù)據(jù)庫遷移 |
dump + restore | 在不同Redis實(shí)例間遷移數(shù)據(jù) |
migrate | 在數(shù)據(jù)庫實(shí)例間遷移數(shù)據(jù) |
2.操作String數(shù)據(jù)類型的命令
注意:操作String數(shù)據(jù)類型的命令基本以s作為前綴開頭
命令 | 含義 |
---|---|
set key value | 插入鍵值對,key是鍵,value是值 |
get key | 查看鍵的值 |
del key | 刪除鍵 |
setnx key value | 當(dāng)key不存在時,設(shè)置值 |
setex key seconds value | seconds是過期時間,設(shè)置鍵值對 |
mset key value[key value..] | 批量獲取值 |
mget key [key ...] | 批量獲取值 |
incr key | 對值做自增1 |
decr key | 對值做自減1 |
incrby key incrment | 自增指定的數(shù)目 increment 數(shù)字 |
decrby key incrment | 自減指定的數(shù)目 increment 數(shù)字 |
incrbyfloat key incrment | 自增指定的浮點(diǎn)數(shù) increment 數(shù)字 |
內(nèi)部編碼有三種:int,embstr和raw
使用場景:
setnx和setex可用于分布式鎖
incr等可以用于計(jì)數(shù)
統(tǒng)一管理用戶的session
3.操作hash數(shù)據(jù)類型的命令
注意:操作hash數(shù)據(jù)類型的命令基本以h作為前綴開頭
命令 | 含義 |
---|---|
hset key field value | 設(shè)置hash的內(nèi)容key=[{field:value}{field:value}] |
hget key field | 獲取字段值 |
hdel key field | 刪除字段值 |
hlen key | 獲取key的字段數(shù) |
hmset key field value [field value...] | 批量設(shè)置key的field-value |
hmget key field1[field2...] | 批量獲得key的field的字段值 |
hexists key field | 判斷key的field是否存在 |
hkeys key | 獲取key的全部字段 |
hvals key | 獲取key的全部value值 |
hgetall key | 獲取key的全部field,value |
hincrby key field incrment | key的字段field自增increment |
hincrbyfloat key field increment | key的字段field自增浮點(diǎn)數(shù)increment |
hstrlen key field | 計(jì)算field的value的長度 |
內(nèi)部編碼:ziplist和hashtable
4.操作list數(shù)據(jù)類型的命令
注意:操作list數(shù)據(jù)類型的命令基本以l或r或b作為前綴開頭
命令 | 含義 |
---|---|
rpush key value[value...] | 從列表右邊添加元素 |
lpush key value[value...] | 從列表左邊添加元素 |
lrange key start end | 獲取指定索引范圍的元素,0表示第一個,-1表示最后一個 |
linsert key before/after pivot value | 在pivot元素前/后插入value元素 |
lindex key index | 獲取列表指定下標(biāo)的元素 |
llen key | 獲取列表的長度 |
lpop key | 從列表的左側(cè)彈出元素 |
rpop key | 從列表的右側(cè)彈出元素 |
lrem key count value | 從左到右刪除count個值為value的元素 |
lset key index value | 設(shè)置index位置的值 |
brpop/blpop key timeout | 阻塞彈出,timeout是超時時間,0表示一直等待下去 |
內(nèi)部編碼:ziplist(壓縮列表),linkedlist(鏈表)和quicklist
使用場景:
lpush+brpop=阻塞隊(duì)列(消息隊(duì)列)。
lpush+lpop=Stack(棧)
lpush+rpop=Queue(隊(duì)列)
lpush+ltrim=Capped Collection(有限集合)
5.操作set數(shù)據(jù)類型的命令
注意:操作set數(shù)據(jù)類型的命令基本以s作為前綴開頭
命令 | 含義 |
---|---|
sadd key element[element...] | 添加元素 |
srem key element[element...] | 刪除元素 |
scard key | 計(jì)算元素個數(shù) |
sismember key element | 判斷element元素是否在集合中 |
srandmember key [count] | 隨機(jī)生成count個元素,默認(rèn)是1個 |
spop key | 隨機(jī)彈出一個元素 |
smembers key | 查詢?nèi)康脑?/td> |
sinter key [key...] | 查詢多個集合的并集 |
sunion key [key...] | 查詢多個集合的交集 |
sdiff key [key...] | 查詢多個集合的差集 |
sinterstore destination key [key...] | 查詢多個集合的并集,存儲到destination中 |
sunionstore destination key [key...] | 查詢多個集合的交集,存儲到destination中 |
sdiffstore destination key [key...] | 查詢多個集合的差集,存儲到destination中 |
內(nèi)部編碼:intset,hashtable
使用場景:
sadd=Tagging(標(biāo)簽)
spop/srandmember=Random item(隨機(jī)數(shù)抽獎)
sadd+sinter=Social Graph(社交需求)
6.操作zset數(shù)據(jù)類型的命令
注意:操作zset數(shù)據(jù)類型的命令基本以z作為前綴開頭
命令 | 含義 |
---|---|
zadd key score memeber[score memeber...] | 添加成員 |
zcard key | 計(jì)算成員個數(shù) |
zscore key member | 計(jì)算成員的分?jǐn)?shù) |
zrank/zrevrank key member | 計(jì)算成員的排名 |
zrem key member[member...] | 刪除成員 |
zincrby key increment member | 增加成員的分?jǐn)?shù) |
zrange/zrevrange key start end [withscores] | 從低到高,返回指定排名范圍的成員 |
zrangebyscore key min max [withscores] [limit offset count] | 從低到高,返回指定分?jǐn)?shù)范圍的成員 |
zrevrangebyscore key max min [withscores] [limit offset count] | 返回指定分?jǐn)?shù)范圍的成員 |
zcount key min max | 返回指定范圍的成員個數(shù) |
zremrangebyrank key start end | 刪除指定排名內(nèi)的升序元素 |
zremrangebyscore key min max | 刪除指定分?jǐn)?shù)范圍的成員 |
zinterstore destination numberkeys key [key...] [weights weight [weight...]] [aggregate sum/min/max] | 兩個有序集合的交集,numberkeys指有序集合進(jìn)行交集的個數(shù) |
zunionstore destination numberkeys key [key...] [weights weight [weight...]] [aggregate sum/min/max] | 兩個有序集合的并集,numberkeys指有序集合進(jìn)行并集的個數(shù) |
內(nèi)部編碼:ziplist(壓縮列表)和skiplist(跳躍表)
使用場景:
排行榜(點(diǎn)贊)
7.Jedis對五種數(shù)據(jù)類型的操作
Jedis jedis = null; try { jedis = new Jedis("127.0.0.1", 6379, 10000); //1.string String result1 = jedis.set("string1", "value1"); String result2 = jedis.get("string1"); System.out.println(result1);//OK System.out.println(result2);//value1 //2.list long result3 = jedis.lpush("list1", "math","math","score","score","name","xiaoming"); Listresult4 = jedis.lrange("list1", 0, -1); System.out.println(result1);//OK System.out.println(result4);//xiaoming, name, score, score, math, math //3.hash jedis.hset("hash1", "subject","math"); jedis.hset("hash1", "score","99"); jedis.hset("hash1", "name","xiaoming"); List result5 = jedis.hmget("hash1", "subject","score","name"); System.out.println(result5);//[math, 99, xiaoming] //4.set jedis.sadd("set1", "math","math","english","chinese"); jedis.sadd("set2", "math","chinese","art"); jedis.sinterstore("set3", "set1","set2"); System.out.println(jedis.smembers("set3"));//[math, chinese] //5.zset jedis.zadd("zset1", 100, "math"); jedis.zadd("zset1", 200, "chinese"); jedis.zadd("zset1", 300, "english"); Set result6 = jedis.zrangeByScore("zset1", 100, 200); result6.forEach(string -> { System.out.print(string+" "); });//math chinese }catch(Exception e) { e.printStackTrace(); }finally { if(jedis != null) { jedis.close(); } }
四.客戶端操作
1.client list
列出與Redis服務(wù)器相連的所有客戶端信息。
屬性如下:
名稱 | 含義 |
---|---|
id | 客戶端的唯一標(biāo)識。自增,重啟后重置為0。 |
addr | 客戶端連接的地址和端口。 |
fd | socket的文件描述符。 |
name | 客戶端的名稱。 |
age | 當(dāng)前客戶端的連接時間。 |
idle | 當(dāng)前客戶端的最近一次空閑時間。當(dāng)age等于idle表示連接一直處于空閑狀態(tài)。 |
flags | 標(biāo)識當(dāng)前客戶端的類型。 |
db | 當(dāng)前客戶端正在使用的數(shù)據(jù)庫索引下標(biāo)。 |
sub | 當(dāng)前客戶端訂閱的頻道或者模式數(shù)。 |
psub | 當(dāng)前客戶端訂閱的頻道或者模式數(shù)。 |
multi | 當(dāng)前事務(wù)中已執(zhí)行命令個數(shù)。 |
qbuf | 輸入緩沖區(qū)總?cè)萘俊?/td> |
qbuf-free | 輸入緩沖區(qū)的剩余容量。 |
obl | 輸出緩沖區(qū)的固定緩沖區(qū)的大小。 |
oll | 輸出緩沖區(qū)的動態(tài)緩沖區(qū)的大小。 |
omem | 輸出緩沖區(qū)使用的字節(jié)數(shù)。 |
events | 文件描述符事件。 |
cmd | 當(dāng)前客戶端最后一次執(zhí)行的命令。 |
2.輸入緩沖區(qū)
作用:客戶端發(fā)送的命令不是直接發(fā)送給Redis服務(wù)器,而是先存放在輸入緩沖區(qū),Redis服務(wù)器從輸入緩沖區(qū)中獲得命令并執(zhí)行。
當(dāng)輸入緩沖區(qū)的輸入速度大于Redis服務(wù)器的處理速度且存在大量的bigkey或是Redis服務(wù)器發(fā)生阻塞,短期不能執(zhí)行命令時,都會造成輸入緩沖區(qū)過大,可以通過client list查看qbuf和qbuf-free的大小或是通過info clients命令找到最大的輸入緩沖區(qū)。
3.輸出緩沖區(qū)
作用:Redis服務(wù)器執(zhí)行命令后的結(jié)果不是直接返回給客戶端,而是先存放在輸出緩沖區(qū)。
輸出緩沖區(qū)分為固定緩沖區(qū)和動態(tài)緩沖區(qū),固定緩沖區(qū)是字節(jié)數(shù)組,動態(tài)緩沖區(qū)是列表,固定緩沖區(qū)使用完之后才會使用動態(tài)緩沖區(qū)。
通過client list和info clients可以監(jiān)控輸出緩沖區(qū)的異常情況。
4.客戶端的分類
(1)普通客戶端
(2)發(fā)布訂閱客戶端
(3)slave客戶端
5.客戶端操作
命令 | 含義 |
---|---|
config set maxclients value | 設(shè)置最大連接數(shù) |
config get maxclients | 設(shè)置最大連接數(shù) |
info clients | 查看當(dāng)前已經(jīng)連接的客戶端數(shù)量 |
config set timeout value | 設(shè)置超時時間,空閑時間一旦大于超時時間,客戶端連接就會自動斷開。 |
client setName value | 設(shè)置客戶端的名稱 |
client getName | 獲得客戶端的名稱 |
client kill ip:port | 關(guān)閉指定的ip:port的客戶端 |
client pause timeout | (時間單位毫秒) 阻塞客戶端timeout毫秒 |
五.持久化
1.RDB
(1)概念:將當(dāng)前線程數(shù)據(jù)生成快照保存在磁盤中。
(2)方式
a.手動觸發(fā)
bgsave命令:Redis進(jìn)程執(zhí)行fork操作創(chuàng)建子進(jìn)程,RDB的序列化由子進(jìn)程完成,在fork階段會出現(xiàn)堵塞。
b.自動觸發(fā)
在某些情況下自動觸發(fā)bgsave命令或是save命令。
(3)RDB的優(yōu)缺點(diǎn)
a.優(yōu)點(diǎn)
緊湊壓縮的二進(jìn)制文件,能夠代表Redis在某個時間點(diǎn)的數(shù)據(jù)備份,可復(fù)制到不同的機(jī)器進(jìn)行災(zāi)難恢復(fù)。Redis加載RDB恢復(fù)數(shù)據(jù)的速度快于AOF。
b.缺點(diǎn)
無法實(shí)現(xiàn)實(shí)時持久化,執(zhí)行fork操作創(chuàng)建子進(jìn)程是重量級操作,頻繁執(zhí)行成本較高,且老版本的Redis服務(wù)無法兼容新版本的RDB格式文件。
2.AOF
(1)概念:記錄每次的寫命令,重啟后執(zhí)行AOF文件中的命令以達(dá)到恢復(fù)數(shù)據(jù)的目的。可以用aof_enabled開啟aof功能。
(2)特點(diǎn):
a.AOF命令以文本協(xié)議格式的形式寫入內(nèi)容到aof_buf中,再由aof_buf同步到硬盤中。文本協(xié)議格式具有很好的兼容性以及避免了二次處理的開銷。而寫入到aof_buf中是為了避免直接寫入硬盤,以免硬盤的容量決定了追加寫入的性能。
b.aof重寫將無效的命令如del去掉,將多個命令合并成一個命令,以達(dá)到壓縮文件體積,加快Redis加載aof文件的速度。
六.復(fù)制
1.從節(jié)點(diǎn)和主節(jié)點(diǎn)之間建立關(guān)系有以下的方式:
(1)在配置文件(redis.conf)中加入slaveof {masterofhost} {masterofport}
(2)啟動redis-server時執(zhí)行:redis-server -slaveof {masterofhost} {masterofport}
2.主節(jié)點(diǎn)和從節(jié)點(diǎn)斷開和切換:
(1)斷開:slaveof no one
(2)切換:執(zhí)行命令slaveof {masterofhost} {masterofport}
3.復(fù)制的特點(diǎn)
(1)只能將主節(jié)點(diǎn)的數(shù)據(jù)復(fù)制到從節(jié)點(diǎn)。
(2)slaveof是異步命令,從節(jié)點(diǎn)保存了主節(jié)點(diǎn)的信息后返回,而不需要等到完全復(fù)制完畢才返回。
(3)可以通過命令info replication查看復(fù)制信息。
(4)從節(jié)點(diǎn)斷開與主節(jié)點(diǎn)的復(fù)制關(guān)系后,會晉升為主節(jié)點(diǎn)。
(5)從節(jié)點(diǎn)切換主節(jié)點(diǎn)之后,會刪除從節(jié)點(diǎn)當(dāng)前的所有數(shù)據(jù),對新節(jié)點(diǎn)數(shù)據(jù)進(jìn)行復(fù)制。
4.Redis的復(fù)制關(guān)系
(1)一主一從:用于主節(jié)點(diǎn)宕機(jī)時,從節(jié)點(diǎn)提供故障轉(zhuǎn)移支持。
(2)一主多從:用于讀寫分離,主節(jié)點(diǎn)執(zhí)行寫命令,從節(jié)點(diǎn)執(zhí)行讀命令,當(dāng)高并發(fā)寫時,將寫命令的數(shù)據(jù)復(fù)制到從節(jié)點(diǎn)就需要消耗比較多的網(wǎng)絡(luò)帶寬。
(3)樹狀主從:從節(jié)點(diǎn)不僅可以復(fù)制主節(jié)點(diǎn)的數(shù)據(jù),還可以作為其他從節(jié)點(diǎn)的主節(jié)點(diǎn)進(jìn)行向下復(fù)制??梢杂行Ы档椭鞴?jié)點(diǎn)的負(fù)載和傳輸給從節(jié)點(diǎn)的數(shù)據(jù)量。
5.全量復(fù)制和部分復(fù)制
全量復(fù)制:將主節(jié)點(diǎn)的數(shù)據(jù)一次性發(fā)生給從節(jié)點(diǎn)。一般用于初次復(fù)制場景。
部分復(fù)制:僅復(fù)制主節(jié)點(diǎn)的部分?jǐn)?shù)據(jù)給從節(jié)點(diǎn)。一般用于處理主從復(fù)制中網(wǎng)絡(luò)閃斷等原因造成的數(shù)據(jù)丟失場景。
從節(jié)點(diǎn)執(zhí)行命令:psync {runId} {offse7dxzt}
runId是主節(jié)點(diǎn)的運(yùn)行id,offset是從節(jié)點(diǎn)已復(fù)制的偏移量。主節(jié)點(diǎn)響應(yīng)寫命令時,會把寫命令發(fā)送給從節(jié)點(diǎn),還會將寫命令寫入復(fù)制積壓緩沖區(qū)。
七.Redis的阻塞
利用日志對Redis的異常進(jìn)行監(jiān)控。
內(nèi)部原因:不合理使用API或數(shù)據(jù)結(jié)構(gòu)(可能由此導(dǎo)致慢查詢等)、CPU飽和(Redis是單線程,只會使用單個CPU)、持久化阻塞(fork操作產(chǎn)生阻塞,AOF對硬盤的操作產(chǎn)生阻塞或HugePage寫操作阻塞)等。
外在原因:CPU競爭、內(nèi)存交換、網(wǎng)絡(luò)問題等。
八.Redis的內(nèi)存
1.Redis進(jìn)程內(nèi)存消耗
可以通過config set maxmemory value設(shè)置最大內(nèi)存以達(dá)到伸縮內(nèi)存的目的
2.Redis內(nèi)存的回收
(1)刪除已過期的鍵對象。包括惰性刪除(查詢時判斷鍵對象是否過期,如果過期執(zhí)行刪除操作并返回空)和定時刪除。
(2)內(nèi)存達(dá)到maxmemory時執(zhí)行內(nèi)存溢出控制策略。內(nèi)存溢出策略包括noeviction,volatile-lru,allkeys-lru,allkeys-random,volatile-random和volatile-ttl,可以通過config set maxmemory-policy {policy}動態(tài)設(shè)置。
3.內(nèi)存優(yōu)化
(1)縮短鍵和值得長度,使用高效二進(jìn)制序列化工具。
(2)使用對象共享池優(yōu)化小整數(shù)對象。
(3)避免字符串的追加操作,因?yàn)樽址芳訒?dǎo)致內(nèi)存的預(yù)分配,降低內(nèi)存的分配次數(shù)。
(4)ziplist壓縮編碼的原則是追求時間和空間的平衡,hash,zset,list的內(nèi)部編碼可以是ziplist,可以通過{type}-max-ziplist-value和{type}-max-ziplist-entries進(jìn)行編碼的控制。
(5)intset是set的內(nèi)部編碼,整數(shù)集合盡量使用intset編碼,
(6)數(shù)據(jù)優(yōu)先使用整數(shù),比字符串類型更節(jié)省內(nèi)存。
九.Redis Sentinel(哨兵)
1.Redis Sentinel是什么?
一個分布式架構(gòu),包括Sentinel節(jié)點(diǎn),Redis數(shù)據(jù)節(jié)點(diǎn)和分布在多個物理機(jī)的客戶端應(yīng)用。完成主節(jié)點(diǎn)不可用時的故障轉(zhuǎn)移處理工作,提供了高可用的解決方案。
2.Sentinel節(jié)點(diǎn)發(fā)現(xiàn)故障轉(zhuǎn)移前的內(nèi)容:
(1)每個Sentinel節(jié)點(diǎn)會對所有的數(shù)據(jù)節(jié)點(diǎn)(包括主節(jié)點(diǎn)和從節(jié)點(diǎn))和其他的Sentinel節(jié)點(diǎn)進(jìn)行監(jiān)控。
(2)當(dāng)半數(shù)以上的節(jié)點(diǎn)認(rèn)為主節(jié)點(diǎn)故障不可用,就會選擇其中一個Sentinel節(jié)點(diǎn)作為領(lǐng)導(dǎo)者進(jìn)行故障轉(zhuǎn)移處理。
3.故障轉(zhuǎn)移處理的步驟如下:
(1)對某一個從節(jié)點(diǎn)執(zhí)行slaveof no one,晉升為主節(jié)點(diǎn)。
(2)其他的從節(jié)點(diǎn)復(fù)制新的主節(jié)點(diǎn)命令(slaveof new master)。
(3)舊的主節(jié)點(diǎn)恢復(fù)后也要復(fù)制新的主節(jié)點(diǎn)命令(slaveof new master)。
(4)通知應(yīng)用方新的主節(jié)點(diǎn)。
4.為什么需要多個Rentinel節(jié)點(diǎn)?
由多個Rentinel節(jié)點(diǎn)對主節(jié)點(diǎn)不可達(dá)進(jìn)行判斷,可以防止誤判。如果有個別Rentinel節(jié)點(diǎn)失效,整個Rentinel集合依然可用。
5.Redis Sentinel的搭建
(1)建立配置文件,逐一開啟。(配置文件的寫法可以去看《Redis開發(fā)與設(shè)計(jì)》第九章)
開啟主節(jié)點(diǎn):redis-server redis-6379.conf
開啟從節(jié)點(diǎn) redis-server redis-6380.conf
redis-server redis-6381.conf
開啟sentinel節(jié)點(diǎn) redis-server redis-sentinel-26379.conf --sentinel
redis-server redis-sentinel-26380.conf --sentinel redis-server redis-sentinel-26381.conf --sentinel
查看主節(jié)點(diǎn)的從節(jié)點(diǎn):redis-cli -h 127.0.0.1 -p 6379 info replication
查看從節(jié)點(diǎn)的主節(jié)點(diǎn) redis-cli -h 127.0.0.1 -p 6380 info replication
查看sentinel節(jié)點(diǎn)監(jiān)控的主節(jié)點(diǎn) redis-cli -h 127.0.0.1 -p 26379 info sentinel
(2)sentinel配置文件的一些參數(shù)
參數(shù) | 含義 |
---|---|
sentinel monitor |
sentinel節(jié)點(diǎn)要監(jiān)控名字叫 |
sentinel down-after-milliseconds |
sentinel節(jié)點(diǎn)會向數(shù)據(jù)節(jié)點(diǎn)和其他sentinel節(jié)點(diǎn)發(fā)送ping命令,如果節(jié)點(diǎn)在 |
sentinel parallel-syncs |
一次故障轉(zhuǎn)移后,每次向新節(jié)點(diǎn)發(fā)起復(fù)制操作的從節(jié)點(diǎn)個數(shù)。 |
sentinel failover-timeout |
故障轉(zhuǎn)移的超時時間。 |
sentinel authpass |
添加主節(jié)點(diǎn)的密碼。 |
sentinel notification-script 閱讀需要支付1元查看
|