Redis大全手冊(上)

蔣老溼發表於2019-03-03

Redis大全手冊(上)

API的理解和使用

通用命令

keys
dbsize #計算key的總數
exists key  #檢查key是否存在
del key [key]   #刪除指定key-value
type key    #返回key的型別
expire key seconds      #key在seconds過期
ttl key     #檢視key剩餘的過期時間
persist key     #去掉key的過期時間
複製程式碼

Redis大全手冊(上)
-2表示過期
Redis大全手冊(上)
-1代表key存在,並且沒有過期時間

kyes基本不在生產環境使用

keys *    #遍歷所有key
key [pattern]

複製程式碼
命令 時間複雜度
keys O(n)
dbsize O(1)
del O(1)
exists O(1)
expire O(1)
type O(1)

資料結構和內部編碼

Redis大全手冊(上)
redis為什麼這麼快?

  1. 純記憶體
  2. 非阻塞IO
  3. 避免執行緒切換和競態消耗

單執行緒需要注意什麼?

  1. 一次只執行一條命令
  2. 拒絕長(慢)命令 keys, flushall, flushdb, slow lua script, mutil/exec, operate big value(collention)
  3. 其實不是單執行緒
    fysnc file descriptor
    close file descriptor
    複製程式碼

字串

get key     #獲取key對應的value O(1)
set key value   #設定key-value  O(1)
del key     #刪除key-value  O(1)
mset key value key value    #批量設定key-value   O(n)
mget key key   #批量獲取key-value    O(n)

incr key    #key自增1,如果key不存在,自增後get(key)=1   O(1)
decr key    #key自減1,如果key不存在,自減後get(key)=-1   O(1)
incrby key k    #key自增k,如果key不存在,自增後get(key)=k   O(1)
decrby key k    #key自減k,如果key不存在,自減後get(key)=-k   O(1)

set key value   #不管key是否存在,都設定   O(1)
setnx key value  #key不存在,才設定     O(1)
set key value xx    #key存在,才設定     O(1)

getset key newvalue     #set key newvalue並返回舊的value
append key value    #將value追加到舊的value
strlen key  #返回字串的長度(注意中文)

incrbyfloat key 3.5   #增加key對應的值3.5
getrange key start end   #獲取字串指定下標的所有的值
setrange key index value    #設定下標所有對應的值
複製程式碼

Redis大全手冊(上)
Redis大全手冊(上)

hash

雜湊鍵值結構

Redis大全手冊(上)

hmset key field value field value   #批量設定   O(n)
hmget key field field   #批量獲取       O(n)
hget key field   #獲取hash key對應的field的value    O(1)
hset key field value    #設定hash key對應field的value   O(1)
hdel key field  #刪除hash key對應的field的value     O(1)
hexists key field   #判斷hash key 是否有field   O(1)
hlen key    #獲取hash key field的數量       O(1)
hgetall key     #h返回hash key對應所有的field和value    O(n)
hvals key   #返回hash key對應所有field的value   O(n)
hkeys key   #返回hash key對應所有field      O(n)
hsetnx key field value  #設定hash key對應field的value(如field存在,則失敗)    O(1)
hincrby key field intCounter    #hash key 對應的field的value自增intCounter  O(1)
hincrbyfloat key field floatCounter     #hincrby浮點數版    O(1)
複製程式碼

記錄網站每個使用者個人主頁的訪問量

#redis實現
incr userid:pagevies(單執行緒,無競爭)
hincrby user:1:info pageview count

#java模擬程式碼
public VideoInfo get(long id){
    String redisKey = redisPrefix + id;
    Map<String,String> hashMap = redis.hgetAll(redisKey);
    VideoInfo videoInfo = transferMapToVideo(hashMap);
    if(videoInfo == null){
        videoInfo = mysql.get(id);
        if(videoInfo != null){
            redis.hmset(redisKey, transferMapToVideo(videoInfo))
        }
    }
    return videoInfo;
}
複製程式碼

小心使用hgetall(redis單執行緒) 例子:如儲存一個使用者的資訊的實現,下面說3種情形,當然還有更多種其他方式

  • String v1
    Redis大全手冊(上)
  • String v2
    Redis大全手冊(上)
  • hash
    Redis大全手冊(上)
    比較
命令 優點 缺點
string v1 程式設計簡單,可能節約記憶體 1. 序列號開銷
2. 設定屬性要操作整個資料
string v2 直觀,可以部分更新 1. 記憶體佔用較大
2. key較為分散
hash 直觀、節省空間、可以部分更新 1. 程式設計稍微複雜
2. ttl不好控制

list

特點:有序、可以重複、左右兩邊插入彈出

rpush key value value ...valueN     #從列表右端插入值(1-N個)
lpush key value value ...valueN     #從列表左端插入值(1-N個)
linsert key before|after value newValue     #在list指定的前|後插入newValue
lpop key    #從列表左側彈出一個item
rpop key    #從列表右側彈出一個item

#根據count值,從列表中刪除所有value相等的項
#count > 0,從左到右,刪除最多count個value相等的項
#count < 0,從右到左,刪除最多Math.abs(count)個value相等的項
#count = 0,刪除所有value相等的項
lrem key count value

ltrim key start end     #按照索引範圍修剪列表      O(n)
lrange key start end    #獲取列表指定索引範圍所有item   O(n)
llen key    #獲取列表長度   O(1)
lset key index newValue     #設定列表指定索引值為newValue   O(n)
blpop key timeout   #lpop阻塞版本,timeout是阻塞超時時間,timeout=0為永遠不阻塞     O(1)
brpop key timeout   #rpop阻塞版本,timeout是阻塞超時時間,timeout=0為永遠不阻塞     O(1)

##小建議-資料結構類比
lpush + lpop = stack
lpush + rpop = queue
lpush + ltrim = capped collection
lpush + brpop = message quere
複製程式碼

慢查詢

Redis大全手冊(上)
Redis大全手冊(上)

  • slowlog-max-len
    1. 先進先出佇列
    2. 固定長度
    3. 儲存在記憶體中

慢查詢命令

slowlog get [n]     #獲取慢查詢佇列
slowlog len     #獲取慢查詢佇列長度
slowlog reset   #清空慢查詢佇列
複製程式碼
  • 慢查詢閥值(單位:微妙)
  • slowlog-log-slower-than=0 記錄所有命令
  • slowlog-log-slower=than<0 不記錄任何命令 配置方式
1. 預設值
config get slowlog-max-len = 128
config get slowlog-log-slower-than = 1000
2. 修改配置檔案重啟
3. 動態配置
config set slowlog-max-len 1000
config set slowlog-log-slower-than 1000
複製程式碼

運維經驗

  1. slowlog-max-len不要設定過大,預設10ms,通常設定1ms
  2. slowlog-log-slower-than不要設定過小,通常設定1000左右
  3. 理解命令生命週期
  4. 定期持久化慢查詢

pipeline

批量網路命令通訊模型

Redis大全手冊(上)
什麼是流水線
Redis大全手冊(上)
流水線作用

命令 N個命令操作 1次pipeline(n個命令)
時間 n次網路 + n次命令 1次網路 + n次命令
資料量 1條命令 n條命令
  • redis的命令時間是微秒級別
  • pipeline每次條數要控制(網路原因)

Redis大全手冊(上)
從上圖舉例,redis命令的執行時間是很快的,但是由於資料需要通過網路傳輸,由於2個地區相隔很遠,資料以光速度傳播也需要時間,然而這個時間有可能比redis執行時間要長。

pipeline-Jedis實現

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
    <type>jar</type>
</dependency>

#沒用pipeline, 1W次hset需要50s
Jedis jedis = new Jedis("127.0.0.1", 6379);
for(int i=0;i<10000;i++){
    jedis.hset("hashkey:"+i,"field"+i, "value"+i);
}

#使用pipeline
Jedis jedis = new Jedis("127.0.0.1", 6379);
for(int i=0;i<100;i++){
    Pipeline pipeline = jedis.pipelined();
    for(int j=i*100; j<(i+1)*100;j++){
        pipeline.hset("hashkey:"+j,"field"+j, "value"+j);
    }
    pipeline.syncAndReturnAll();
}
複製程式碼

使用建議

  1. 注意每次pipeline攜帶的資料量
  2. pipeline每次只能作用在一個Redis節點上
  3. M操作和pipeline的區別

釋出訂閱

角色 釋出者(publisher) 訂閱者(subscriber) 頻道(channel) 模型

Redis大全手冊(上)

publish channel message     #釋出訊息
subscribe [channel]     #一個或多個
unsubscribe [channel]     #一個或多個
psubscribe [pattern...]     #訂閱模式
punsubscribe [pattern...]   #退訂指定的模式
pubsub channels     #列出至少有一個訂閱者的頻道
pubsub numsub [channel...]      #列出給定頻道的訂閱者數量
pubsub numpat    #列出被訂閱模式的數量
複製程式碼

Redis大全手冊(上)
Redis大全手冊(上)

點陣圖

Redis大全手冊(上)
Redis大全手冊(上)

setbit key offset value     #給點陣圖指定索引設定值
getbit key offset       #獲取點陣圖指定索引的值
bitcount key [start end]    #獲取點陣圖指定範圍(start到end,單位為位元組,如果不指定就是獲取全部)位值為1的個數
bitop key targetBit [start] [end]   #計算點陣圖指定範圍第一個偏移量對應的值等於targetBit的位置
複製程式碼

獨立使用者統計

  1. 使用set和Bitmap兩種方式
  2. 1億使用者,5千萬獨立
資料型別 每個userid佔用空間 需要儲存的使用者量 全部儲存量
set 32位(假設userid用的是整型,實際場景很多用長整型) 50000000 32位*50000000=190.7348633MB
Bitmap 1位 100000000 1位*100000000=11.920929MB
一天 一個月 一年
set 200M 6G 72G 大約值
Bitmap 12.5M 375M 4.5G 大約值

只有十萬獨立使用者呢?

資料型別 每個userid佔用空間 需要儲存的使用者量 全部儲存量
set 32位(假設userid用的是整型,實際場景很多用長整型) 100000 32位*1000000=0.3814697MB
Bitmap 1位 100000 1位*100000000=0.0119209MB

使用建議

  1. type = string,最大512MB
  2. 注意setbit的偏移量,可能有較大耗時
  3. 點陣圖不是絕對好

HyperLogLog

  1. 極小空間完成獨立數量統計
  2. 本質還是字串
  3. pfcount 統計有一定錯誤率0.81%
  4. 無法取出單條資料
pfadd key element [element...]      #向hyperloglog新增元素
pfcount key [key]       #計算hyperloglog的獨立總數
pfmerge destkey sourcekey [sourcekey]   #合併多個hyperloglog
複製程式碼

Redis大全手冊(上)
Redis大全手冊(上)
Redis大全手冊(上)

geo地理資訊定位

  1. 3.2版本以後才有geo
  2. geoKey的型別是zset,type geoKey = zset
  3. 沒有刪除的API,可以使用zrem key member
geo key longitude latitude member [longitude latitude member...]    #增加地理位置資訊
geopos key member [member...]       #獲取地理位置資訊
geodist key member1 member2 [unit]    #獲取兩個地理位置的距離,unit:m、km、mi、ft
複製程式碼

更多命令參考

Redis大全手冊(上)
Redis大全手冊(上)
Redis大全手冊(上)

Redis持久化的取捨和選擇

redis持久化RDB

Redis大全手冊(上)
觸發機制
save 阻塞的 檔案策略:如果存在老的RDB檔案,替換 時間複雜度O(n)

bgsave

Redis大全手冊(上)

save與bgsave

命令 save bgsave
IO型別 同步 非同步
阻塞 是(阻塞發生再fork)
複雜度 O(n) O(n)
優點 不會消耗額外記憶體 不阻塞客戶端命令
缺點 阻塞客戶端命令 需要fork,消耗記憶體

Redis大全手冊(上)

# 配置redis.conf
 save 900 1
 save 300 10
 save 60 10000
 dbfilename dump.rdb
 dir ./
 stop-write-on-bgsave-error yes
 rdbcompression yes
 rdbchecksum yes
 
#最佳配置
 dbfilename dump-${port}.rdb        #指定對應哪個redis的備份
 dir /bigdiskpath       #指定具體檔案目錄
 stop-write-on-bgsave-error yes
 rdbcompression yes
複製程式碼

觸發機制

  1. 全量複製
  2. debug reload
  3. shutdown

RDB總結

  1. RDB是redis記憶體到硬碟的快照,用於持久化
  2. save通常會阻塞redis
  3. bgsave不會阻塞redis,但是會fork新程式
  4. save自動配置滿足任一就會被執行
  5. 有些觸發機制不容忽視

AOF

RDB有什麼問題 耗時、耗效能 不可控、丟失資料

Redis大全手冊(上)
Redis大全手冊(上)

AOF執行原理-建立

Redis大全手冊(上)
AOF執行原理-恢復
Redis大全手冊(上)

AOF的三種策略 always

Redis大全手冊(上)
everysec
Redis大全手冊(上)
no
Redis大全手冊(上)

命令 always everysec no
優點 不丟失資料 每秒一次fsync丟一秒資料 不用管
缺點 IO開銷較大,一般的sata盤只有幾百TPS 丟一秒資料 不可控

AOF重寫

  • 減少硬碟佔用量
  • 加速回復速度
    Redis大全手冊(上)

AOF重寫2種方式

  • bgrewriteaof命令

    Redis大全手冊(上)

  • 自動

    配置名 含義
    auto-aof-rewrite-min-size AOF檔案重寫需要的尺寸
    auto-aof-rewrite-percentage AOF檔案增長率
    統計名 含義
    aof_current_size AOF當前尺寸(單位:位元組)
    aof_base_size AOF上次啟動和重寫的尺寸(單位:位元組)

    自動觸發實際(同時滿足)

    • aof_current_size > auto-aof-rewrite-min-size
    • aof_current_size - aof_base_size/aof_base_size > auto-aof-rewrite-percentage
    #配置redis.conf
    appendonly yes
    appendfilename "appendonly-${port}.aof"
    appendfsync everysec
    dir /bigdiskpath
    no-appendfsync-on-rewrite yes
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    複製程式碼

AOF重寫流程

Redis大全手冊(上)

AOF阻塞問題

Redis大全手冊(上)
大於2秒會造成主執行緒阻塞,無法進行後續客戶端發來的命令 每秒刷盤的策略不止是隻丟失1秒的資料,也有可能是幾秒

如何定位

  • Redis日誌
  • reids命令info Persistence(無法看到具體時間點)
  • linux top 命令觀察IO使用率

RDB和AOF選擇

命令 RDB AOF
啟動優先順序
體積
恢復速度
資料安全性 丟資料 根據策略決定
輕重

最佳策略

  • 小分片
  • 快取或儲存
  • 監控(硬碟、記憶體、負載、網路)
  • 足夠的記憶體

fork操作

  1. 同步操作(阻塞)
  2. 與記憶體量息息相關:記憶體越大,耗時越長(與機器型別無關)
  3. info:latest_fork_usec

改善fork

  1. 優先使用物理機或者高效支援fork操作的虛擬化技術
  2. 控制redis例項最大可用記憶體:maxmemory
  3. 合理配置Linux記憶體分配策略:vm.overommit_memory=1
  4. 降低fork頻率:例如放寬AOF重寫自動觸發機制,不必要的全量複製

子程式開銷與優化

  1. CPU:
    • 開銷:RDB和AOF檔案生成,屬於CPU密集型
    • 優化:不做主reids CPU繫結,不和密集型CPU部署在一起
  2. 記憶體
    • 開銷:fork記憶體開銷,Linux:copy-on-write
    • 優化:Linux:echo never > /sys/kernel/mm/transparent_hugepage/enabled(關閉增加fork速度)
  3. 硬碟
    • 開銷:RDB和AOF檔案寫入,可以結合iostat,iotop分析
    • 優化:
      1. 不和高硬碟負載服務部署再一起:儲存服務,訊息佇列等。
      2. no-appendfsync-on-rewrite = yes
      3. 根據寫入量決定磁碟型別:例如SSD
      4. 單機多例項持久化檔案目錄可以考慮分盤儲存

redis複製的原理與優化

單機有什麼問題? 機器故障 容量瓶頸 QPS瓶頸

Redis大全手冊(上)
簡單總結

  1. 一個master可以有多個slave
  2. 一個slave只能有一個master
  3. 資料流向是單向的,master到slave

redis配置引數說明 Redis主從複製和叢集配置

slaveof ip port
slave-read-only yes
slaveof on one
複製程式碼

全量複製

Redis大全手冊(上)
開銷:

  1. bgsave時間
  2. RDB檔案網路傳輸時間
  3. 從節點清空資料時間
  4. 從節點載入RDB的時間
  5. 可能的AOF重寫時間

部分複製

Redis主從複製和叢集配置

Redis大全手冊(上)
主從複製的常見問題 讀寫分離

  1. 讀流量分攤到從節點,提高訪問速度
  2. 可能遇到問題:複製資料延遲、讀到過期資料、從節點故障

配置不一致

  1. 例如maxmemory不一致,丟失資料
  2. 例如資料結構優化引數(例如hash-max-ziplist-entries):記憶體不一致

規避全量複製

  1. 第一次全量複製
    • 第一次不可避免,從節點必須全量
    • 解決:小主節點(maxmemory)分資料量,訪問低峰時刻
  2. 節點執行ID不匹配
    • 主節點重啟(執行ID改變)
    • 解決:故障轉移,例如哨兵或叢集
  3. 複製積壓緩衝區不足
    • 網路中斷,部分複製無法滿足
    • 解決:增大複製緩衝區配置rel_backlog_size,網路增強

規避複製風暴

  1. 單主節點複製風暴:
    • 問題:主節點重啟,多從節點複製
    • 解決:更換複製拓撲
      Redis大全手冊(上)
      slave-1從master複製資料之後,接下來的slave都從slave-1複製資料,減輕master壓力
  2. 單機器複製風暴
    • 如圖:機器當機後,大量全量複製
    • 主節點分散多機器
      Redis大全手冊(上)

Redis Sentinel

主從複製-master宕掉故障處理

Redis大全手冊(上)

Redis大全手冊(上)

Redis Cluster

快取設計與優化

Redis雲平臺CacheCloud

阿里雲Redis開發規範

記憶體管理

Redis 資料結構與記憶體管理策略(上)
Redis 資料結構與記憶體管理策略(下)
原理、方法雙管齊下,大神帶你細解Redis記憶體管理和優化

開發運維常見坑

redis調整核心引數
Redis安全
redis 熱點Key的發現與解決之道
redis4.0之基於LFU的熱點key發現機制

相關文章