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的過時時間
複製代碼

-2表示過時
-1表明key存在,而且沒有過時時間

kyes基本不在生產環境使用html

keys *    #遍歷全部key
key [pattern]

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

數據結構和內部編碼

redis爲何這麼快?

  1. 純內存
  2. 非阻塞IO
  3. 避免線程切換和競態消耗

單線程須要注意什麼?java

  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    #設置下標全部對應的值
複製代碼

hash

哈希鍵值結構mysql

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)
複製代碼

記錄網站每一個用戶我的主頁的訪問量linux

#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種情形,固然還有更多種其餘方式ios

  • String v1
  • String v2
  • hash
    比較
命令 優勢 缺點
string v1 編程簡單,可能節約內存 1. 序列號開銷
2. 設置屬性要操做整個數據
string v2 直觀,能夠部分更新 1. 內存佔用較大
2. key較爲分散
hash 直觀、節省空間、能夠部分更新 1. 編程稍微複雜
2. ttl很差控制

list

特色:有序、能夠重複、左右兩邊插入彈出git

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
複製代碼

慢查詢

  • slowlog-max-len
    1. 先進先出隊列
    2. 固定長度
    3. 保存在內存中

慢查詢命令redis

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
複製代碼

運維經驗sql

  1. slowlog-max-len不要設置過大,默認10ms,一般設置1ms
  2. slowlog-log-slower-than不要設置太小,一般設置1000左右
  3. 理解命令生命週期
  4. 按期持久化慢查詢

pipeline

批量網絡命令通訊模型 編程

什麼是流水線
流水線做用

命令 N個命令操做 1次pipeline(n個命令)
時間 n次網絡 + n次命令 1次網絡 + n次命令
數據量 1條命令 n條命令
  • redis的命令時間是微秒級別
  • pipeline每次條數要控制(網絡緣由)

從上圖舉例,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) 模型

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

位圖

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
複製代碼

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持久化RDB

觸發機制
save 阻塞的 文件策略:若是存在老的RDB文件,替換 時間複雜度O(n)

bgsave

save與bgsave

命令 save bgsave
IO類型 同步 異步
阻塞 是(阻塞發生再fork)
複雜度 O(n) O(n)
優勢 不會消耗額外內存 不阻塞客戶端命令
缺點 阻塞客戶端命令 須要fork,消耗內存

# 配置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有什麼問題 耗時、耗性能 不可控、丟失數據

AOF運行原理-建立

AOF運行原理-恢復

AOF的三種策略 always

everysec
no

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

AOF重寫

  • 減小硬盤佔用量
  • 加速回復速度

AOF重寫2種方式

  • bgrewriteaof命令

  • 自動

    配置名 含義
    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重寫流程

AOF阻塞問題

大於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瓶頸

簡單總結

  1. 一個master能夠有多個slave
  2. 一個slave只能有一個master
  3. 數據流向是單向的,master到slave

redis配置參數說明 Redis主從複製和集羣配置

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

全量複製

開銷:

  1. bgsave時間
  2. RDB文件網絡傳輸時間
  3. 從節點清空數據時間
  4. 從節點加載RDB的時間
  5. 可能的AOF重寫時間

部分複製

Redis主從複製和集羣配置

主從複製的常見問題 讀寫分離

  1. 讀流量分攤到從節點,提升訪問速度
  2. 可能遇到問題:複製數據延遲、讀到過時數據、從節點故障

配置不一致

  1. 例如maxmemory不一致,丟失數據
  2. 例如數據結構優化參數(例如hash-max-ziplist-entries):內存不一致

規避全量複製

  1. 第一次全量複製
    • 第一次不可避免,從節點必須全量
    • 解決:小主節點(maxmemory)分數據量,訪問低峯時刻
  2. 節點運行ID不匹配
    • 主節點重啓(運行ID改變)
    • 解決:故障轉移,例如哨兵或集羣
  3. 複製積壓緩衝區不足
    • 網絡中斷,部分複製沒法知足
    • 解決:增大複製緩衝區配置rel_backlog_size,網絡加強

規避複製風暴

  1. 單主節點複製風暴:
    • 問題:主節點重啓,多從節點複製
    • 解決:更換複製拓撲
      slave-1從master複製數據以後,接下來的slave都從slave-1複製數據,減輕master壓力
  2. 單機器複製風暴
    • 如圖:機器宕機後,大量全量複製
    • 主節點分散多機器

Redis Sentinel

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

Redis Cluster

緩存設計與優化

Redis雲平臺CacheCloud

阿里雲Redis開發規範

內存管理

Redis 數據結構與內存管理策略(上)
Redis 數據結構與內存管理策略(下)
原理、方法左右開弓,大神帶你細解Redis內存管理和優化

開發運維常見坑

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

相關文章
相關標籤/搜索