Redis开发与运维教程
Redis 教程
Redis特性
Redis可以做什么
緩存;排行榜系統;計數器應用;社交網絡:點贊,喜好,推送;消息隊列。
Redis為什么這么快
1.通過內存訪問數據
2.采用非阻塞IO,不在網絡IO上浪費過多時間
3.單線程避免了線程切換和多線程競爭資源的問題
Redis與Memcahce比較
1.Redis為單線程系統,memcache為多線程
2.Redis支持持久化,memcache不支持
3.Redis天然支持主從復制,memcache需要自己實現
4.Redis內存分配臨時申請空間,存在碎片;memcache使用預分配內存池的方法,可以省去內存分配的空間
5.數據量大時redis會通過swap把冷數據讀到硬盤中,mencache不會
6.Redis的數據類型比mencache多,因此應用場景更廣
終上所訴:追求高可用,項目業務比較復雜時建議用redis,追求讀寫速度用memcahce
Redis 數據類型與API
redis數據類型與內部編碼
字符串
字符串類型的實際值可以是字符串,數字(整數,浮點),二進制圖片,但最大值不超過512MB
常用命令
1.設置值:set key value [ex seconds] [px milliseconds] [nx|xx]
ex seconds:設置秒級過期時間(對應快捷鍵setex)
px milliseconds:設置毫秒級過期時間
nx:key不存在才設置成功(對應快捷鍵setnx)
xx:key存在才設置成功
2.批量設置值:mset key value [key value …]
3.獲取值:get key
4.批量獲取值:mget key [key …]
5.計數:incr key
值需要是整數,否則返回錯誤
每次操作自增+1
Key不存在按照值為0自增,返回1
內部編碼
int:8個字節的長整型
embstr:小于等于39個字節的字符串
raw:大于39個字節的字符串
哈希
類似map或者對象的結構,適合存儲對象信息
常用命令
1.設置值:hset key field value
2.獲取值:hget key field
3.刪除field:hdel key field
4.計算filed個數:hlen key
5.批量設置:hmget key field [key field …]
6.批量獲取: hmget key field [field …]
7.判斷field是否存在:hexists key field
8.獲取所有field: hkeys key
9.獲取所有value:hvals key
10.獲取所有field-value:hgetall key
內部編碼
當元素個數小于hash-max-ziplist-entries(默認512個),所有值小于hash-ziplist-value(默認64字節)時,使用ziplist,否則用hashtable;ziplist內存小,hashtable讀寫速度快
列表
可以用來存儲多個字符串,列表最多存儲2^32-1個元素;
使用場景:lpush+lpop=棧,lpush+rpop=隊列,lpush+ltrim=有限集合,lpush+brpop = 消息隊列。
常用命令
1.從右邊插入元素:rpush key value [value …]
2.從左邊插入元素:lpush key value [value …]
3.向某個元素前面或者后買插入元素:linsert key before|after pivot value
4.查找指定范圍的元素: lrange key start end
5.獲取指定下標的元素:lindex key index
6.獲取列表長度:llen key
7.從左側刪除元素:lpop key
8.從右側刪除元素:rpop key
9.刪除指定元素:lrem key count value
count>0從左到右刪除count個元素
count<0從右到左刪除count個元素
Conut=0刪除所有
10.按照索引范圍刪除:ltrim key start end
11.修改指定下標元素:lset key index newValue
12.阻塞式彈出:blpop|brpop key [key …] timeout
timeout為阻塞時間,單位秒,為0時如果無對應數據會一直阻塞可用于實現消息隊列
集合
可以保存多個字符串對象,與列表區別在于集合為無序的不能通過下標查詢元素,集合不允許有重復對象;
使用場景:sadd = 標簽,spop/srandmember = 生成隨機數,sadd+sinter = 社交需求
常用命令
1.添加元素:sadd key element [element …]
2.刪除元素:srem key element [element …]
3.計算元素個數:scard key
4.判斷元素是否在集合中: sismember key element
5.隨機返回指定個數元素:srandmember key [count]
默認為1個
6.從集合中隨機彈出一個元素:spop key
彈出后集合元素會減少
7.獲取所有元素:smembers key
8.求集合交集:sinter key [key …]
9.求集合差集:sdiff key [key …]
10.求集合并集:suinon key [key …]
11.保存交集,差集,并集結果:sinterstore|suionstore|sdiffstore destion key [key …]
其中destion 為新集合的key
內部編碼
當元素數量小于set-max-inset-entries(默認512個)時用inset否則用hashtable
有序集合
有序集合與集合類似,區別在于給每個元素增加了一個分數,做為排序的依據。
使用場景:zadd+zincrby = 排行榜系統
常用命令
1.添加成員:zadd key score member [score member]
2.計算成員個數:zcard key
3.計算某個成員的分數:zscore key member
4.計算成員的排名從低到高:zrank key member
5.計算成員的排名從高到低:zrevrank key member
6.刪除成員:zrem key member
7.增加成員分數:zincrby key increment member
increment 為增加的分值
8.返回指定排名范圍的成員從低到高:zrange key start end [withsorce]
9.返回指定排名范圍的成員從高到低:zrevrange key start end [withsorce]
10.返回指定分數范圍的成員從低到高:zrangebyscore key min max [withsorce]
min取-inf代表無限小,max 取+inf代表無限大
11.返回指定分數范圍的成員從高到低:zrevrangebyscore key min max [withsorce]
12.刪除指定排名的升序元素:zremrangebyrank key start end
13.刪除指定分數范圍的成員:zremrangebyscore key min max
14.交集:zinterstroe destination numkeys key [key …] [weights weight [weight …]] [aggregate sum|min|max]
numkeys:需要做交集的個數
weights weight 每個鍵的權重
Aggregate sum|min|max 交集后分值匯總方式
15.并集:zuniontroe destination numkeys key [key …] [weights weight [weight …]] [aggregate sum|min|max]
內部編碼
當元素個數小于zset-max-ziplist-entries(默認128個),同時zset-max-ziplist-value(默認64)時用ziplist否則用skiplist(跳表)
鍵管理
1.刪除鍵:del key
2.重命名:rename key newkey
3.隨機返回一個鍵:randomkey
4.設置鍵在seconds秒后過期: expire key seconds
5.鍵在秒級時間戳timestamp后過期:expireat key timestamp
6.遍歷鍵:keys pattern
7.漸進式遍歷鍵: scan cursor [match pattern] [count number]
cursor 為上一個游標,從0開始,每次遍歷產生新的cursor,遍歷完所有鍵后cursor返回0
match pattern 匹配參數
count number 每次遍歷個數
漸進式相比普通遍歷,不會造成阻塞,但是在遍歷時如果鍵有變化不保證能遍歷所有鍵
8.清除所在數據庫數據所有的鍵:flushdb
9.清除所有的鍵:flushall
info 命令詳解
server
一般 Redis 服務器信息,包含以下域:
redis_version : Redis 服務器版本
redis_git_sha1 : Git SHA1
redis_git_dirty : Git dirty flag
os : Redis 服務器的宿主操作系統
arch_bits : 架構(32 或 64 位)
multiplexing_api : Redis 所使用的事件處理機制
gcc_version : 編譯 Redis 時所使用的 GCC 版本
process_id : 服務器進程的 PID
run_id : Redis 服務器的隨機標識符(用于 Sentinel 和集群)
tcp_port : TCP/IP 監聽端口
uptime_in_seconds : 自 Redis 服務器啟動以來,經過的秒數
uptime_in_days : 自 Redis 服務器啟動以來,經過的天數
lru_clock : 以分鐘為單位進行自增的時鐘,用于 LRU 管理
clients
已連接客戶端信息,包含以下域:
connected_clients : 已連接客戶端的數量(不包括通過從屬服務器連接的客戶端)
client_longest_output_list : 當前連接的客戶端當中,最長的輸出列表
client_longest_input_buf : 當前連接的客戶端當中,最大輸入緩存
blocked_clients : 正在等待阻塞命令(BLPOP、BRPOP、BRPOPLPUSH)的客戶端的數量
memory
內存信息,包含以下域:
used_memory : 由 Redis 分配器分配的內存總量,以字節(byte)為單位
used_memory_human : 以人類可讀的格式返回 Redis 分配的內存總量
used_memory_rss : 從操作系統的角度,返回 Redis 已分配的內存總量(俗稱常駐集大小)。這個值和 top 、 ps 等命令的輸出一致。
used_memory_peak : Redis 的內存消耗峰值(以字節為單位)
used_memory_peak_human : 以人類可讀的格式返回 Redis 的內存消耗峰值
used_memory_lua : Lua 引擎所使用的內存大小(以字節為單位)
mem_fragmentation_ratio : used_memory_rss 和 used_memory 之間的比率
mem_allocator : 在編譯時指定的, Redis 所使用的內存分配器。可以是 libc 、 jemalloc 或者 tcmalloc 。
在理想情況下, used_memory_rss 的值應該只比 used_memory 稍微高一點兒。
當 rss > used ,且兩者的值相差較大時,表示存在(內部或外部的)內存碎片。
內存碎片的比率可以通過 mem_fragmentation_ratio 的值看出。
當 used > rss 時,表示 Redis 的部分內存被操作系統換出到交換空間了,在這種情況下,操作可能會產生明顯的延遲。
Because Redis does not have control over how its allocations are mapped to memory pages, high used_memory_rss is often the result of a spike in memory usage.
當 Redis 釋放內存時,分配器可能會,也可能不會,將內存返還給操作系統。
如果 Redis 釋放了內存,卻沒有將內存返還給操作系統,那么 used_memory 的值可能和操作系統顯示的 Redis 內存占用并不一致。
查看 used_memory_peak 的值可以驗證這種情況是否發生。
其他信息
persistence :RDB 和 AOF 的相關信息
stats : 一般統計信息
replication : 主/從復制信息persistence
cpu : CPU 計算量統計信息
commandstats : Redis 命令統計信息
cluster : Redis 集群信息
keyspace : 數據庫相關的統計信息
除上面給出的這些值以外,參數還可以是下面這兩個:
all : 返回所有信息
default : 返回默認選擇的信息
Redis 超級對象
Bitmaps(位圖)
使用場景:取日期為key,可以做日活分析。用戶數量很大(活躍用戶比重較大時適合做)
常用命令
1.設置值:setbit key offset value
offset 為位置取整數
value 為值取0或者1
2.獲取值: getbit key offset
3.獲取某個key元素的個數: bitcount key [start] [end]
4.bitop op destkey key [key …]
op 取 and(交集)|or(并集)|not(非)|xor(異或)
destkey 為新key名稱
5 . 計算第一個值為targetbit的偏移量:bitops key targetBit [start] [end]
HyperLogLog
可以用極小的內存空間完成獨立總數的統計。但是存在一定誤差率,誤差率約為0.81%。
使用場景:計算獨立用戶數,百萬用戶相比時,內存空間要比set小幾千倍。
常用命令
1.添加元素:pfadd key element [element …]
2.計算元素數目:pfcount key [key …]
3.合并: pfmerge destkey sourcekey [sourcekey]
destkey 為新key名稱
Sourcekey為被合并的key
合并后相同元素算一個元素
GEO
實現地理信息定位的功能。
使用場景:附近的位置,搖一搖
常用命令
1.增加地理信息:geoadd key longitude latitude member [ longitude latitude member …]
longitude latitude member分別為經度,緯度,成員
2.獲取地理信息geopos key member [key member …]
3.獲取兩個地理位置的距離:geodist key member1 member2 [m|km|mi|ft]
4.根據某經緯度為中心指定范圍內的地理信息位置集合:georadius key longitude latitude radius m|km|ft|mi [withcoord] [withdist] [withhash] [asc|desc] [store key] [storedist key]
withdist:在返回位置元素的同時,將位置元素與中心之間的距離也一并返回.距離的單位和用戶給定的范圍單位保持一致。
withcoord:將位置元素的經度和緯度也一并返回。
withhash:以52位有符號整數的形式,返回位置元素經過原始geohash編碼的有序集合分值。這個選項主要用于底層應用或者調試,實際中的作用不大。
命令默認返回未排序的位置元素。通過以下兩個參數,用戶可以指定被返回位置元素的排序方式:
asc:根據中心的位置,按照從近到遠的方式返回位置元素
desc:根據中心的位置,按照從遠到近的方式返回位置元素。
store key: 將返回結果保存在指定鍵
storedist key:將距離中心的距離保存到指定鍵
5.根據某成員為中心指定范圍內的地理信息位置集合:georadiusbymember key member radius m|km|ft|mi [withcoord] [withdist] [withhash] [asc|desc] [store key] [storedist key]
操作同georadius
6.獲取geohash(地理位置對應的hash字符串):geohash key member
GEO數據類型為zset,地理位置以geohash保存在zset中。
字符串越長代表位置越精確。
兩個字符串越相似,代表地理位置月近。
Geohash可以與經緯度互相轉換。
Redis 常用功能
Pipeline
可以將一組redis命令組裝,一次性傳給redis進行處理,從而減少反復交付的次數。Pipeline使用要避免數據量過大,否則會增加客戶端等待時間,造成網絡阻塞
Lua
通過multi命令開啟任務,discard暫停任務,exec執行任務。執行完任務后才完成事物。如果在執行任務時,事物中的key有被修改。任務將不執行。可在multi前執行watch key命令,防止key被修改。
Redis 持久化
RDB
把當前數據生成快照保存到硬盤。
觸發機制
1.手動觸發:執行save或者bgsave命令
2.自動觸發:
使用配置save m n 在m秒n次修改時觸發save
從節點全量復制主節點時執行bgsave
執行debug reload 時觸發save
執行shutdown時觸發save
save與bgsave
save 命令會阻塞當前Redis服務器,bgsave是對save阻塞問題做的優化,bgsave流程:
1.執行bgsave命令
2.fork開啟子進程
3.子進程生成RDB文件
4.告知父進程已完成,父進程更新統計信息
AOF
以日志形式實現數據的實時持久化,是現在持久化的主流方式
工作流程
1.Redis所有寫入命令會追加到aof_buf(緩沖區)
2.根據文件同步策略同步數據到aof文件
3.當文件太大時對aof文件進行重寫
4.Redis重啟時加載aof文件恢復數據
文件同步
1.always:每次命令寫入都同步
2.everysec:每秒執行一次
3.no:同步操作交給操作系統負責,通常同步周期最長為30秒
AOF文件重寫觸發機制
手動觸發:執行bgrewriteaof命令
自動觸發:在aof文件大小超過auto-aof-rewrite-min-size,且文件大小-上次重寫時文件大小
/上次重新時文件大小>auto-aof-rewrite-percentage 時觸發
AOF文件重寫流程
1.執行aof重寫請求
2.Fork子進程
3-1 完成fork后有新的寫入繼續寫入aof_buf緩沖區
3-2 為了防止fork后的數據丟失,fork后的數據寫入aof_rewrite_buf 重寫緩沖區
4. 子進程根據內存快照,按照合并規則寫入到新aof文件
5-1 通知父進程,記錄info persistence信息
5-2 把重寫緩沖區的數據寫入新aof文件
5-3 用新aof文件替換舊aof文件
重啟加載機制
如果開啟aof,加載aof文件;
如果未開啟aof或者aof文件不存在則使用rdb,加載rdb文件
如果rdb文件不存在,就不加載任何文件直接重啟
RDB與AOF比較
1.RDB比AOF文件小
2.Redis恢復數據時加載RDB比AOF快很多
3.RDB沒有辦法實時持久化
4.RDB版本用二進制文件保存版本兼容沒AOF好
問題定位與優化
fork操作
問題原因:fork操作是重量級操作,會復制父進程的空間內表頁(理論上需要復制與父進程同樣的內存,但是linux有寫時復制機制,父子進程貢獻相同的物理內存頁,實際會小很多,10G大概只需要20MB)
問題定位: 通過info stats 統計排查 lastest fork查看最近一次的fork的耗時
優化方法:
1.Xen虛擬機對fork操作支持不好,因避免使用
2.控制Redis實列最大內存,線上建議10GB以內
3.合理配置linux內存?
4.降低fork頻率,適當放寬aof觸發時機
子進程
問題原因:子進程負責重寫操作,需要消耗cpu,內存和硬盤的資源
問題定位:
info persistence 查看當多個Redis實例部署在一臺服務器時的重寫狀態
查看重寫時的cpu消耗
查看重寫時的redis 日志輸出觀察內存消耗狀態
優化方法:
cpu:
1.把內存數據寫入文件會消耗大量cpu,因此盡量不要用單核cpu;
2.一臺機器如果多個實例,盡量保證不同時重寫
內存:
1.同cpu盡量保證多個實列不同時重寫
2.設置no-appendfsync-on-rewrite yes避免在大量寫入時重寫
硬盤:
1.將文件寫入硬盤時,會造成比較多的硬盤壓力,因此不要和其他高硬盤操作放在一起。如:存儲服務,消息隊列
2.設置no-appendfsync-on-rewrite yes避免在大量寫入時重寫
3.開啟aof功能時且流量高時,盡量不要用普通機械盤
4.單機開啟多個實例,可以把文件分盤存儲
AOF追加阻塞
問題原因:常用的同步策略為everysec,此時當Redis有大量寫入時,會導致fsync和重寫同時發生,此時對比上次fsync時間有可能大于2秒,如果超過2秒主線程將堵塞,直到同步完成。
問題定位:每次阻塞 aof_delayed_fsync指標會累加,通過info persistence 查看 aof_delayed_fsync指標
優化方法:同子進程硬盤優化
相關配置與命令
1.指定RDB保存文件名:dbfilename
2.動態更改RDB文件名:執行config set dbfilename {newfilename}
3.動態更改RDB保存目錄:config set dir {newDir}
4.是否采用LZF壓縮RDB文件:config set rdbcompression {yes|no},默認開啟,建議開啟,方便傳輸和保存到硬盤
5.開啟AOF持久化:appendonly yes
6.設置AOF文件名:appendfilename 默認為appendonly.aof
7.設置AOF文件同步方式:appendfsync 默認everysec
8.重新AOF文件:執行bgrewriteaof
9.AOF文件重寫體積:auto-aof-rewrite-min-size 默認64MB
10.AOF文件重寫子進程根據內存快照每次批量寫入新AOF文件的數據大小:aof-rewirte-incremental-fsync 默認大小為32MB
11.在AOF重寫期間是否執行fysnc(把緩存信息寫入aof文件):no-appendfsync-on-rewrite
Redis主從復制
為Redis配置副本,實現故障的恢復和負載均衡
建立復制
一共有三種方式建立復制:
1.從節點配置文件 中加入slaveof {masterHost} {masterPort}
2.在redis-server 啟動命令后加入 --slaveof {masterHost} {masterPort}
3.執行slaveof {masterHost} {masterPort}
斷開復制
在從節點上執行slaveof no one,從節點與主節點斷開鏈接,并保留斷開前的數據
切換主節點
對斷開復制的節點,重新執行建立復制,實現切換主節點的操作,此時:
從節點會清空數據,然后復制新主節點的數據
拓撲
一主一從
主要用于故障轉移,可只打開從節點的AOF功能,以提高主節點性能,但此時重啟時從節點要斷開與主節點的復制關系,以免清空數據。
一主多從
主要用于讀占比比較大的場景,用從節點分攤主節點的讀寫壓力
樹狀結構
通過引入中間層,降低主節點負載
數據同步
復制偏移量
每次寫入命令主從節點都會增加復制偏移量
復制積壓緩存區
當主節點響應寫命令時,不但把命令發送給從節點,頁把命令寫入復制積壓緩存區
全量復制流程
1.一般在第一次建立鏈接和重啟主節點導致主節點運行id發生變化后引起,如果是debug reload,運行id不會發生變化
2.第一次建立鏈接后,因為從節點沒有復制偏移量和主節點的運行id,會進行全量復制,并保存主節點此時的運行id和復制偏移量
3.主節點執行bgsave保存rdb文件到本地,并將rdb文件發送給從節點,需要注意傳輸的總時間不能超過 repl-timeout(默認 60秒),否則從節點清空rdb文件,復制失敗。
4.在從節點接受rdb文件期間,主節點會將此時的寫命令寫入復制積壓緩沖區,并累積偏移量
5.從節點清空自身數據,加載RDB文件,加載完成后,如果開啟AOF就馬上重寫
部分復制流程
1.一般在主從節點網絡斷開時間超過repl-timeout(默認60秒)時發生
2.斷開后主節點依然把數據寫入復制積壓緩存區(默認大小為1MB)
3.重新鏈接后把復制積壓緩存區的信息傳輸給從節點
心跳
1.主節點默認每隔repl-ping-slave-period (默認10)秒發送ping命令檢驗從節點是否鏈接正常
2.從節點每隔一秒發送replconf ack {offset}命令,主動上報主節點自己的復制偏移量,檢查數據是否丟失,如果丟失就從主節點的復制積壓緩存區中拉取數據
異步復制流程
1.主節點接受處理命令
2.命令結束后返回結果
3.異步發送命令給從節點,從節點執行復制的命令
問題定位與優化
主從配置不一致
1.從節點內存溢出,數據丟失:maxmemory與比主節點配置小造成,應該配置一致。
全量復制
1.全量復制消耗大,盡量在低峰時進行
2.節點運行ID不匹配:主節點故障重啟造成,可以升級從節點為主節點,防止從節點以為鏈接了新的節點而造成的全量復制
3.復制積壓緩沖區不足時,偏移量會不在主節點的復制積壓緩沖區內,而造成全量復制,可以適當加大repl_backlog_size(默認1MB)
單主節點復制風暴
由大量從節點對同一主節點發起全量復制造成:會同時向多個從節點發送rdb文件,網絡消耗太大,建議改為原拓撲結構為樹狀結構
單機器復制風暴
由一臺機器部署很多個主節點故障造成:建議每臺機器部署的主節點數目盡量少,提供主節點故障轉移方案:哨兵,集群等。
相關配置與命令
1.設置復制:slaveof {masterHost} {masterPort}
2.復制命令:slaveof {masterHost} {masterPort}
3.斷開復制:slaveof no one
4.設置redis鏈接密碼:requirepass
5.設置節點為只讀:slave-read-only 從節點默認為yes
6.傳輸延遲機制:rep-disable-tcp-nodelay
關閉時,無論產生的命令數據大小都會傳給從節點
開啟時,會合并較小的tcp數據包從而節省寬帶,網絡環境差時推薦開啟
7.從節點心跳超時時間:repl-timeout默認60秒
8.心跳檢驗間隔時間:repl-ping-slave-period 默認10秒
9.執行復制:psync {offset} {runId}
10.配置最大內存:maxmemory
11.復制積壓緩沖區大小:repl_backlog_size(默認1MB)
Redis 阻塞問題
如何發現問題
1.客戶端報JedisConnectionExpection異常
2.使用CacheCloud進行管理
內在原因
API或者數據結構使用不合理解決方案
1.通過慢查詢日志slowlog get {n} 查詢不合理的命令,改為低算法度的命令代替
2.執行 redis-cli -h {ip} -p {port} --bigkeys 查看大對象,盡量用更合理的數據類型代替
CPU飽和
1.每秒請求次數過多:通過redis -cli --stat 觀察requests數量,如果一個redis實例每秒有好幾萬次請求,建議做水平擴展優化
2.使用了耗時高的命令:通過info commandstats 查看user_per查看命令平均耗時,找出耗時高的命令,把耗時高的命令換成低的命令,或者放寬zipList條件
持久化阻塞
解決方案見 Redis 持久化-問題定位與優化
外在原因
CPU競爭
1.Redis是CPU高密集應用,建議不要和其他CPU密集應用部署在同一臺服務器
2.對與開啟了持久化或參與復制的主節點,建議不要綁定CPU,讓其可以充分利用多核CPU
內存交換
即防止把內存數據換出到硬盤
問題定位:
1.查詢Redis進程號:info server | grep process_id
2.根據進程號查詢內存交換信息:cat /proc/{process_id}/smaps | grep Swap
問題優化:
1.保證機器內存充足
2.設置正確的redis最大內存maxmemory
3.降低系統使用swap優先級
網絡問題
1.網絡閃斷:盡量避免,異地機房調用
2.Redis鏈接拒絕:適當增加maxclients最大連接數,盡量采用連接池方式
3.連接溢出too many open files:通過unlimit -n {連接數量},適當增加連接數量
4.backlog隊列溢出:適當增加redis backlog的長度(默認511)
5.網絡延遲:增加寬帶,減少大對象傳輸
相關配置與命令
查看慢查詢日志:執行slowlog get {n} n為查詢最近日志的條數
查看大對象:執行 redis-cli -h {ip} -p {port} --bigkeys
Redis 內存詳解
內存使用統計
1.利用info memory 查看詳見Redis 數據類型與API-info命令詳情
2.重點關注:used-memery_rss和used_memery以及他們的比值memory_fragmentation_ratio
3.memory_fragmentation_ratio>1,存在內存碎片
4.memory_fragmentation_ratio<1,存在內存交換
內存消耗劃分
Redis內存知識要點
內存碎片
1.內存碎片定義:采用固定范圍的空間存儲對象,剩余的空間為內存碎片
2.正常碎片率在1.03左右
3.容易產生內存碎片的行為:頻繁更新操作;大量將過期
4.優化內存碎片的方案:數據對齊,盡量用數字類型或者固定長度的字符串;安全重啟,重啟后內存碎片會得到整理
子進程內存消耗
子進程內存消耗主要在AOF/RDB重寫時創建子進程產生。
優化方案:
1.雖然redis的子進程不需要消耗1倍的父進程,但是也要預留一些內存空間
2.設置sysctl vm.overcommit_memory=1,允許內核可以分配所有物理內存
3.查看當前系統是否開啟THP,建議關閉,防止copy-on-write期間內存過度消耗
刪除過期鍵策略
1.惰性刪除:在客戶端讀取過期鍵時把鍵刪除,如果過期鍵一直沒有被訪問,內存將得不到及時釋放
2.定時任務刪除:Redis執行定時任務,默認10秒執行一次,每次隨機刪除20個鍵,如果其中超過25%的都過期,循環執行。
內存溢出策略
1.noeviction:默認策略,不刪除數據,不再響應寫操作,報OOM command not allowed when used memory 異常
2.volatile-lru:根據LRU(最后一次訪問時間)算法刪除超時的鍵,如果沒有可刪除對象,返回noevicition策略
3.allkeys-lru:根據LRU算法刪除鍵,不過鍵是否過期
4.allkeys-random:隨機刪除鍵,不過鍵是否過期
5.volatile-random:隨機刪除過期的鍵
6.volatile-ttl:刪除最近要過期的鍵,如果沒有返回noeviction策略
內存優化
1.縮減鍵值長度
2.Redis維護著[0-9999]的整數共享對象池,因此盡量使用整數對象(共享對象池設置LRU時無法使用)
3.盡量減少字符串append操作(append操作為了防止不停的append會給一個比較大的內存空間),降低預分配帶來的內存碎片話
4.Redis的內部編碼類型轉換不可逆(只能重啟的時候可以),盡量減少內存少的編碼到大內存編碼的轉換
5.當集合(set)類型,元素都是整數,且數量不超過set-max-inset-entries時,內部編碼為inset,可以減少內存消耗
6.控制鍵的數量,可以用hash代替sting,當hash內部編碼為ziplist時可以大幅度減少內存,雖然耗時會增加,當value字節小時ziplist耗時逐漸降低
相關配置與命令
動態修改Redis內存上限:執行 config set maxmemory
溢出策略:maxmemory-policy
zipList編碼轉換控制-最大值:{type}-max-ziplist-value
zipList編碼轉換控制-最多元素:{type}-max-ziplist-entries
Inset編碼轉換控制:set-max-inset-entries (元素需都為整數)
Redis sentinel(哨兵)
為什么用redis sentinel
實現主從復制,主節點故障時可以自動檢查,并推舉它的其中一個從節點做主節點,解決主從復制的高可用問題
故障轉移流程
1.主節點發生了故障
2.多個sentinel節點對主節點故障達成一致
3.選出一個sentinel為領導者負責故障轉移
4.領導者選取其中一個從節點升級為主節點
安裝和部署
部署步驟
1.部署redis主從復制
2.配置sentinel節點:
Sentinel節點可以配置多個,最好配置奇數個,每個節點除了端口號其他配置一樣
在redis.conf文件中加入:
sentinel monitor {master-name} {ip} {port} {quorum} #master-name為主節點別名,{ip} {port}為監控的主節點ip和port,quorum代表至少要quorum個sentinel節點同意才判定客觀下線,一般sentinel節點數的設一半加1
sentinel down-after-milliseconds {master-name} {times} #用ping的方式判斷當redis節點與其余節點是否可達,超過times(單位毫秒)不可達,認定主觀不可達
sentinel parallel-syncs {master-name} {nums} #每次有nums從節點對新主節點進行復制
sentinel failover-timout {master-name} {times} # times為故障轉移的超時時間
如果sentinel有多個主節點,在redis.conf繼續加入其他主節點就可:
sentinel monitor {master-name-2} {ip} {port} {quorum}
sentinel down-after-milliseconds {master-name-2} {times}
sentinel parallel-syncs {master-name-2} {nums}
sentinel failover-timout {master-name-2} {times}
3.啟動sentinel 節點:
使用redis-sentinel {redis.conf} 啟動或者使用redis-sever {redis.conf} --sentinel 啟動
4.確認啟動是否成功:redis-cli -h {sentinel節點ip} -p {sentinel 節點 port} info Sentinel
部署技巧
1.sentinel節點不應該部署在一臺機器上
2.部署至少三個奇數個的sentinel節點
3.建議讓一套sentinel監控一個業務的多個主節點集合
實現原理
三個定時監控任務
1.每個10秒sentinel向主節點發送info命令,獲取主節點的從節點信息
2.每隔2秒sentinel進行交換信息,并通過_sentinel_:hello了解其他節點
3.每個(down-after-milliseconds秒(默認1秒)sentinel節點向其他sentinel節點和主節點,從節點發送ping請求,檢查是否可達
主觀下線和客觀下線
1.主觀下線:通過上述3中的定時任務,判斷節點是否可達,如果不可達時間超過down-after-milliseconds沒有回復,判斷為主觀下線
2.客觀下線:主觀下線數量數超過{quorum}(通常為所有sentinel節點的一半)數量,判斷主節點為客觀下線
領導者sentinel節點選舉
1.在判斷主節點客觀下線后,每個sentinel節點通過sentinel is-master-down-by-addr 詢問自己是否可以是領導者
2.通過raft算法(每個節點只能投一張票的算法)決定誰是領導者:
節點收到投票請求后會根據以下情況決定是否接受投票請求(每個 follower 剛成為 Candidate 的時候會將票投給自己):
請求節點的 Term 大于自己的 Term,且自己尚未投票給其它節點,則接受請求,把票投給它;
請求節點的 Term 小于自己的 Term,且自己尚未投票,則拒絕請求,將票投給自己。
一輪選舉過后,正常情況下,會有一個 Candidate 收到超過半數節點(N/2 + 1)的投票,它將勝出并升級為 Leader。然后定時發送心跳給其它的節點,其它節點會轉為 Follower 并與 Leader 保持同步,到此,本輪選舉結束。
注意:有可能一輪選舉中,沒有 Candidate 收到超過半數節點投票,那么將進行下一輪選舉。
故障轉移
1.按步驟選出從節點:過濾ping命令超過5秒沒回應,與主節點失聯超過down-after-milliseconds*10秒的節點->選擇slave-priority高的優先級->選擇復制偏移量大的從節點->選擇runid最小的從節點
2.升級選出的從節點為主節點
3.原來的主節點恢復聯系后將其轉為新主節點的從節點
實現高可用讀寫分離
1.從節點可做主節點的備份,實現故障轉移
2.從節點可以拓展主節點的讀能力
3.但是sentinel只會對從節點進行主觀下線操作,不會做故障轉移。因此需要配置多從節點,并實行實時監控來實現高可用的讀寫分離
相關配置與命令
動態調整sentinel配置:執行sentinel set ,如sentinel set mymaster quorum 2
查詢主節點統計信息:sentinel masters
查詢指定主節點統計信息:sentinel masters {master-name}
指定主節點的從節點統計信息:sentinel slaves {master-name}
指定主節點的sentinel節點集合:sentinel sentinels {master-name}
指定主節點的ip和端口:sentinel get-master-addr-by-name {master-name}
強制故障轉移指定節點:sentinel failover {master-name}
查詢當前節點主觀下線個數:sentinel ckquorum {master-name}
取消當前sentinel對指定節點的監控:sentinel remove {master name}
設置從節點優先級:slave-priority
Redis cluster(集群)
Redis cluster數據分區
Redis cluster采用虛擬槽分區,所有節點映射到0-16383整數槽內。計算公式:slot=crc16(key)&16383,沒個節點維護一部分槽及槽所映射的鍵值數據:
集群功能限制
1.key批量支持有限,mset,mget無法執行
2.如果key在不同的節點,無法執行事物操作
3.Hash,list等鍵值不能映射到不同的節點
4.不支持多庫
5.主從結構只能支持一層
集群搭建
準備節點
1.劃分為三個目錄:conf,data,log用來同一存放配置,數據和日志
2.修改redis.conf文件如下:
#節點端口,{port}為端口號
port {port}
#開啟集群模式
cluster-enabled yes
#節點超時時間,單位毫秒
Cluster-node-timeout 15000
#集群內部配置文件,第一次啟動會生成一份
cluster-config-file “nodes-6379.conf”
3.按照2配置完所有節點后,啟動所有節點
節點握手
1.在一個節點下對所有其他節點執行:cluster meet {ip} {port},完成節點握手
2.通過cluster nodes 查看節點是否已經通過握手組成集群
分配槽
1.為每個主節點分配槽:執行cluster addslots 如:
Redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0…5461}
Redis-cli -h 127.0.0.1 -p 6380 cluster addslots {5642…10922}
Redis-cli -h 127.0.0.1 -p 6381 cluster addslots {10923…16383}
為主節點添加從節點
2.在從節點下,通過cluster replicate {nodeId}命令為主節點添加從節點,其中nodeId為主節點的id,可以在主節點下通過cluster nodes 命令查到
使用redis-trib.rb搭建集群
1.下載并安裝redis-trib.rb
2.準備節點
3.執行redis-trib.rb create --replicas 1 127.0.0.1:6841 127.0.0.1:6842 127.0.0.1:6843 127.0.0.1:6844 127.0.0.1:6845 127.0.0.1:6846
其中 1 表示為為每個主節點分配1個從節點,按照順序決定主節點,主節點為前面3個
節點通信
通信流程
Redis集群采用gossip進行通信,流程為:
1.每個節點單獨開辟一個tcp接口,端口號為節點來端口加上10000
2.每個節點在固定周期選擇幾個節點發送ping消息
3.接受到瓶消息后節點發送pong做為響應
Gossip消息
1.Gossip消息分:
meet消息:通知新節點加入。
ping消息:檢查節點是否在線,交換彼此信息
pong消息:接收到ping和pong消息是回應pong確認接收到了信息
fail消息:當一個節點下線時發送fail消息,廣播其他節點自己已下線
2.節點選擇:
ping消息默認每秒執行10次,隨機選取5個節點,找出其中最久沒有通信的節點進行通信。
如果發現有大于cluster_node_timeout/2的時間沒有通信,則立刻發送ping消息。
在redis 集群中節點數量并不是越多越好,因為gossip的消息體包含一定數量的其他節點信息,會增加通信壓力。
擴容集群
1.把新節點加入集群在任意節點執行:cluster meet {新節點ip} {新節點port} (線上建議用redis-trib.rb add-node因為cluster meet不會檢查新節點是否已經加入)
2.把槽分配給新節點,老節點移出一些槽,達到槽平均分配的目的。這個流程直接執行會比較繁瑣建議用 redis-trib.rb rehard 命令執行
收縮集群
1.把要下線的幾點的槽分配給其他節點:執行命令redis-trib.rb rehard {被遷移節點ip} {被遷移節點port}
2.忘記節點,停止對要下線節點發送Gossip消息:執行命令 redis-trib.rb del-node {被遷移節點ip:被遷移節點port} {被遷移節點id}
3.下線節點
請求路由
1.鍵通過crc16(key)&16383計算得到對應槽位
2.通過槽位找到對應節點,如果剛好是當前節點就直接完成操作
3.否則進行重定向,由客戶端根據接受到的moved信息再次發送請求,完成操作
故障轉移
故障發現
1.主觀下線:ping最后一次通信成功時間超過cluster-node-timeout判定為主觀下線
2.客觀下線:一個節點判定另一個節點主觀下線后,將通過gossip向其他節點群發pfail通知
3.其他節點接受到通知后判定節點是否主觀下線
4.計算主觀下線成立的節點數量,如果超過一半的數量認定主觀下線,則客觀下線生效(需要在cluster-node-timeout*2的時間內完成,否則客觀下線會失敗,因此cluster-node-timeout的值不能太小)
5.通知所有節點該節點主觀下線
6.通知該節點的從節點觸發故障轉移流程
故障恢復
1.通過從節點和主節點的斷線時間是否超過cluster-node-timeoutcluster-slave-validity-factor判斷,從節點是否有資格做故障轉移
2.在有資格的節點中,復制偏移量大的節點優先發起選舉
3.主節點對從節點點進行投票,每個主節點只有一張票
4.當有一個從節點超過半數主節點投票時(故障主節點的數量也算,集群主節點最好分布在3臺物理機以上,防止選不出從節點),選擇為主節點;如果在cluster-node-timeout2節點時間內沒有選舉出從節點,就判斷選舉失敗
5.選出從節點后,該從節點變為主節點
6.主節點的槽被分配到從節點
7.向集群廣播該從節點升級為主節點的信息。
集群運維注意事項
1.16384個槽必須全部分配,否則報錯
2.Gossip寬帶消耗隨著節點數而增加,建議控制節點數在1000以內
3.防止數據傾斜:
防止節點與槽分配不均
防止不同槽對應鍵數差異太大,一般由hash_tag引起(當一個key包含 {} 的時候,就不對整個key做hash,而僅對 {} 包括的字符串做hash。如user:{user1}:ids)
防止集合對象含有大量元素
防止內存相關配置不一致
4.防止請求傾斜
Redis云平臺CacheCloud
cacheCloud為redis的監控運營平臺,可以實時對redis集群情況進行監控,并實現可視化的redis部署和操作。
詳情見:https://blog.csdn.net/xiaofengbuhuimai/article/details/90449779
相關配置與命令
節點握手:執行 cluster meet {ip} {port}
查看節點握手狀態情況:執行cluster nodes
查看集群運行情況:cluster info
信息交換頻率:cluster-node-timeout(默認為15秒,即主觀下線時間,主觀下線傳播時間<=cluster-node-timeout/2,從節點有效時間cluster-node-timeoutcluster-slave-validity-factor ,p判斷客觀下線有效時間:cluster-node-timeout2,選舉從節點時間:cluster-node-timeout*2)
遷移槽:執行 redis-trib.rb reshard
忘記節點:執行redis-trib.rb del-node {被遷移節點ip:被遷移節點port} {被遷移節點id}
從節點有效因子:cluster-slave-validity-factor (默認10)
Redis 緩存設計
緩存鍵刪除策略
1.在內存過大時,根據maxmemory-policy算法剔除
2.設置鍵的超時時間剔除
3.根據業務場景,由客戶端主動刪除
穿透優化
即訪問不存在的key,導致直接訪問數據庫的問題的優化
1.對數據庫不存在的key,保存null在redis中,并設置一個較短的過期時間
2.利用bitmaps實現布隆過濾器,把key設置在bitmaps中(由算法工程師實現),對不存在的key實現攔截
無底洞優化
即增加節點也不能達到優化的問題的優化
1.利用pipeline命令,實現多個命令一次執行
2.并行IO
3.利用hash_tag把key強行分配到同一個節點
雪崩優化
即對緩存發生故障的情況進行的優化
1.保證redis的高可用性,多準備備用節點。
2.為redis失敗,在業務層面上設計降級方案
3.提前演練
熱點key重建優化
即對訪問頻率特別高的key進行優化
1.互斥鎖,只讓一個線程去完成查詢數據庫把值放返回緩存的操作
2.緩存層面設計key不過期,在邏輯層面設計一個key的過期時間,用客戶端的一條單獨線程去對key進行更新的操作
總結
以上是生活随笔為你收集整理的Redis开发与运维教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Arch Linux 安装总结
- 下一篇: 《Redis开发与运维》----- 客户