[Redis] 萬字長文帶你總結Redis,助你面試升級打怪

来源:https://www.cnblogs.com/chenyongblog/archive/2020/03/30/12599885.html
-Advertisement-
Play Games

文章目錄 "Redis的介紹、優缺點、使用場景" Linux中的安裝 常用命令 "Redis各個數據類型及其使用場景" Redis字元串(String) Redis哈希(Hash) Redis列表(List) Redis集合(Set) Redis有序集合(sorted set) "Redis 瑞士軍 ...


文章目錄

Redis的介紹、優缺點、使用場景

  • Redis是什麼: 開源的,基於鍵值的存儲服務系統,支持多種數據類型,性能高,功能豐富
  • 特性(主要有8個特性):
    • 速度快:官方給出的結果是10W OPS,每秒10W的讀寫(為什麼是10W,因為記憶體的相應時間是100納秒-10萬分之一秒)。數據存儲在記憶體中;使用C語言開發;Redis使用單線程,減少上下文切換。本質原因是電腦存儲介質的速度,記憶體比硬碟優幾個數量級)。MemoryCache可以使用多核,性能上優於Redis。
    • 持久化:Redis所有的數據保持在記憶體中,對數據的更新將非同步地保存到磁碟上。斷掉,宕機? RDB快照/AOF日誌模式來確保。MemoryCache不提供持久化
    • 多種數據結構:Redis提供字元串,HashTable, 鏈表,集合,有序集合;另外新版本的redis提供BitMaps點陣圖,HyperLogLog超小記憶體唯一值計數,GEORedis3.2提供的地理位置定位。相比memocache只提供字元串的key-value結構
    • 支持多種編程語言:Java,PHP,Ruby,Lua,Node
    • 功能豐富: 發佈訂閱,支持Lua腳本,支持簡單事務,支持pipline來提高客戶端的併發效率
    • 簡單:單機核心代碼23000行,讓開發者容易吃透和定製化;不依賴外部庫;單線程模型
    • 主從複製:主伺服器的數據可以同步到從伺服器上,為高可用提供可能
    • 高可用、分散式:2.8版本後提供Redis-Sentinel支持高可用;3.0版本支持分散式
  • 典型應用場景:
    • 緩存系統:緩存一些數據減少對資料庫的訪問,提高響應速度
    • 計數器:類似微博的轉發數,評論數,incr/decr的操作是原子性的不會出錯
    • 消息隊列系統:發佈訂閱的模型,消息隊列不是很強
    • 排行版: 提供的有序集合能提供排行版的功能,例如粉絲數,關註數
    • 實時系統:利用點陣圖實現布隆過濾器,秒殺等

安裝

  • Linux中安裝
    wget http://download.redis.io/releases/redis-5.0.7.tar.gz
    tar -zxvf redis-5.0.7.tar.gz
    mv redis-5.0.7 /usr/local/redis 不需要先創建/usr/local.redis文件夾
    cd /usr/local/redis
    make
    make install
    vi redis.conf
    * bind 0.0.0.0 開發訪問
    * daemonize yes 設置後臺運行
    redis-server ./redis.conf 啟動
    redis-cli 進入命令行,進行簡單的命令操作
    vi redis.conf
    > requirepass password 修改密碼
    redis-cli 再次進入cmd
    > shutdown save 關閉redis,同時持久化當前數據
    redis-server ./redis.conf 再次啟動redis
    redis-cli 進入命令行
    > auth password
    將redis配置成系統服務,redis/utils中自帶命令,我們只需修改參數
    /usr/local/redis/utils/./install_server.sh
    [root~ utils]# ./install_server.sh
    Welcome to the redis service installer
    Please select the redis port for this instance: [6379] 預設埠不管
    Selecting default: 6379
    Please select the redis config file name [/etc/redis/6379.conf] /usr/local/redis/redis.conf 修改配置文件路徑
    Please select the redis log file name [/var/log/redis_6379.log] /usr/local/redis/redis.log 修改日誌文件路徑
    Please select the data directory for this instance [/var/lib/redis/6379] /usr/local/redis/data 修改數據存儲路徑
    Please select the redis executable path [/usr/local/bin/redis-server]
    Selected config:
    Port           : 6379
    Config file    : /usr/local/redis/redis.conf
    Log file       : /usr/local/redis/redis.log
    Data dir       : /usr/local/redis/data
    Executable     : /usr/local/bin/redis-server
    Cli Executable : /usr/local/bin/redis-cli
    chkconfig --list | grep redis 查看redis服務配置項
    redis_6379      0:off   1:off   2:on    3:on    4:on    5:on    6:off
    服務名是redis_6379
    
  • 可執行文件說明
    • redis-server: Redis伺服器,啟動Redis的
    • redis-cli: Redis命令行客戶端連接
    • redis-benchmark: 對Redis做性能測試
    • redis-check-aof: AOF文件修複工具
    • redis-check-dump: RDB文件檢查工具
    • redis-sentinel: Sentinel伺服器(2.8以後)
  • 啟動方式
    • redis-server: 最簡單的預設啟動,使用redis的預設參數
    • 動態參數啟動:redis-server --port yourorderpoint
    • 配置文件的方式: redis-server configpath
    • 比較:
      • 生產環境選擇配置啟動;單機多實例配置文件可以選擇配置文件分開
  • Redis客戶端返回值
    • 狀態回覆:ping->pong
    • 錯誤恢復:執行錯誤的回覆
    • 整數回覆:例如incr會返回一個整數
    • 字元串回覆: get
    • 多行字元串回覆:mget
  • 常用配置
    • daemonize: 是否是守護進程(y/n)
    • port埠:預設是6379
    • logfile:Redis系統日誌
    • dir:Redis工作目錄
  • 常用命令:線上練習http://try.redis.io/
    redis-cli -h x.x.x.x -p x 連接
    auth "password" 驗證密碼
    redis-cli --raw可以避免中文亂碼
    exit 退出
    select index 切換到指定的資料庫
    keys * 顯示所有key,如果鍵值對多不建議使用,keys會遍歷所有key,可以在從節點使用;時間複雜度O(N)
    dbsize 算出所有的key的數量,只是數量;時間複雜度O(1)
    exists key key是否存在,存在返回1,不存在返回0;時間複雜度O(1)
    incr key 將key的值加一,是原子操作
    decr key 將key的值加一,會出現複數,是原子操作
    del key 刪除key,刪除成功返回1,失敗返回0;時間複雜度O(1)
    expire key seconds 設置過期時間,過期之後就不存在了;時間複雜度O(1)
    ttl key 查看key剩餘的過期時間,key不存在返回-2;key存在沒設置過期時間返回-1;
    persist key 去掉key的過期時間,再查看ttl key,返回值是-1,表示key存在並且沒有設置過期時間
    type key 查看類型;時間複雜度O(1)
    config get * 獲取配置信息
    set key value插入值
    sadd myset 1 2 3 4 插入set
    get key獲取值
    del key刪除key
    cat redis.conf | grep -v "#" | grep -v "^$" 查看配置文件,去除所有的#,去除所有的空格
    setnx key value #key不存在,才設置
    set key value xx #可以存在,才設置
    set key value [exporation EX seconds | PX milliseconds] [NX|EX]
    mget key1 key2 key3 批量獲取 1次mget=1次網路時間+n次命令時間;時間複雜度O(n)
    mset key1 value1 key2 value2 批量插入;時間複雜度O(n)
    n次get = n次網路時間 + n次命令時間,mget一次就能完成,省去大量的網路時間
    getset key newvalue # set key newvalue並返回舊的value
    append key value #將value追加到舊的value
    strlen key #獲取value的長度,中文占2個位元組
    incrbyfloat key 3.5 #增加key對應的值
    set/get/del, incr(自增1)/decr(自減1)/incrby(incrby key n自增n)/decrby
    getrange key start end #獲取value從start到end的值
    setrange key index value #設置指定下標為一個新的值
    hset key field value #給key的field設置值
    hget key field #獲取key的field的值
    hdel key field #刪除key的field的值
    hgetall key #獲取key的所有值
    hexists key field # 判斷key的field是否存在
    hlen key #獲取key field的數量
    hmset key field1 value1 field2 value2
    hmget key field1 field2
    hsetnx/hincrby/hdecry/hincrbyfloat
    lpush key value1 value2...valueN #從左邊插入
    rpush key value1 value2...valueN #從右邊插入
    linsert key before|after value newValue
    rinsert key before|after value newValue
    lpop key #從左邊彈出一個item
    rpop key #從右邊彈出一個item
    lrem key count value #若count等於0或者不填,表示刪除所有的value值相等的item;若count>0,表示從左到右刪除最多count個value相等的item;若count<0,表示從右到左,刪除最多Math.abs(count)個value相等的項
    ltrim key start end #按照索引範圍修剪列表,可以用來慢刪除,因為全刪除可能會阻塞redis
    lrang key start end #獲取key中從start到end的值
    lindex key index #取第index的值
    llen key #算出列表的長度
    lset key index newValue #修改index的值為newValue
    blpop key timeout #lpop阻塞版本,timeout是阻塞時間,timeout=0表示死等,lpop會立馬返回,有時候數據更新不那麼及時,或者消息隊列中消息未及時處理,我們可以使用這個
    brpop key timeout
    lpush + LPOP = STACK
    lpush + RPOP = QUEUE
    lpush  + ltrim = 有序的集合
    lpush + rpop = 消息隊列
    sadd key value #不支持插入重覆元素,失敗返回0
    srem key element #刪除集合中的element元素
    smembers key #查看集合元素
    sinter key1 key2 #取出相同:交集
    sdiff key1 key2 #取出key1中key2沒有的元素:差集
    sunion key1 key2 #取出二者所有的元素:並集
    sdiff|sinter|sunion store key #將結果存到key中,有時候計算一次耗時
    scard key #計算集合大小
    sismember key element #判斷element是否在集合中
    srandmember #返回所有元素,結果是無序的,小心使用,可能結果很大
    smembers key #獲取集合中的所有元素
    spop key #從集合中隨機彈出一個元素
    scan
    SADD = Tagging
    SPOP/SRANDMEMBER = Random item
    SADD + SINTER = Social Graph
    zadd key score element #添加score和element O(logN): 使用xx和跳錶的數據結構
    zrem key element #刪除元素
    zscore key element #返回元素的分數
    zincrby key increScore element #增加或減少元素分數
    zcard key #返回元素的總個數
    zrank key element #獲取element的排名
    zrange key start end [withscores] #返回指定索引範圍內的升序元素
    zrangebyscore key minScore maxScore [withscore] #返回分數在minScore和maxScore之間的元素
    zcount key minScore maxScore #返回有序集合內在指定分數範圍內的個數
    zremrangebyrank key start end #刪除指定排名內的元素
    zremrangebyscore key minScore maxScore #刪除指定分數內的元素
    zrevrang/zrevrange/集合間的操作zsetunion
    info replication 查看分片,能夠獲取到主從的數量和狀態
    config get databases 獲取所有資料庫
    

數據結構和內部編碼

  • Reids支持5中存儲的數據格式: String, Hash, List, Set, Sorted Set
    • string

      • redis 的 string 可以包含任何數據。比如jpg圖片或者序列化的對象,最大能存儲 512MB。
      • 使用場景:緩存/計數器/分散式鎖...
      • 常用命令:
      • 實戰:實現分散式的id生成器,可以使用incr的思路,但是實際中會比這複雜
    • hash

      • 是一個鍵值(key=>value)對集合。Redis hash 是一個 string 類型的 field 和 value 的映射表,hash 特別適合用於存儲對象。
      • 實戰:統計用戶主頁的訪問量, hincrby user:1:info pageview count
    • list

      • Redis 列表是簡單的字元串列表,按照插入順序排序。列表最多可存儲 232 - 1 元素 (4294967295, 每個列表可存儲40多億)。
      • 實戰:微博按時間順序展示消息
    • set

      • 是 string 類型的無序集合,不允許插入重覆元素,插入重覆元素失敗返回0。集合是通過哈希表實現的,所以添加,刪除,查找的複雜度都是 O(1)。
      • 實戰:抽獎系統(量不是很大的時候);like,star可以放到集合中;標簽tag
    • zset

      • 有序集合:有序且無重覆元素,和 set 一樣也是string類型元素的集合,且不允許重覆的成員。不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。zset的成員是唯一的,但分數(score)卻可以重覆。
      • 實戰:排行榜

Redis客戶端: Java的Jedis(Socket通信),Python的redis-py

瑞士軍刀

慢查詢

  • 生命周期

兩點說明:

  1. 慢查詢發生在第3階段,比如keys *等這些需要掃描全表的操作
  2. 客戶端超時不一定慢查詢,但慢查詢是客戶端超時的一個可能因素
  • 兩個配置
    • slowlog-log-slower-than=n(微秒):命令執行時間超過x微秒,會被丟到一個固定長度的慢查詢queue中;n<0表示不配置
    • slowlog-max-len: 先進先出的隊列,固定長度,保存在記憶體中(重啟redis會消失)
    • 配置方法
      • 預設值
        • config get slowlog-max-len=128
        • config get slowlog-log-slower-than=10000
      • 修改配置文件重啟
      • 動態配置
        • config set slowlog-max-len 1000
        • config set slowlog-log-slower-than 1000
    • 常用命令
      • slowlog get [n]:獲取慢查詢隊列
      • slowlog len: 獲取慢查詢隊列的長度
      • slowlog reset: 清空慢查詢隊列
    • 運維經驗
      • slowlog-max-len不要設置過大,預設10ms,通常設置1ms,根據QPS來設置
      • slowlog-log-slower-than不要設置過小,通常設置1000左右
      • 定期持久化慢查詢

pipeline流水線(批量操作)

當遇到批量網路命令的時候,n次時間=n次網路時間+n次命令時間。舉個例子,北京到上海的距離是1300公裡,光速是3萬公裡/秒,假設光纖傳輸速度是光速的2/3,也就是萬公裡/秒,那麼一次命令的傳輸時間是 1300/20000*2(來回)=13毫秒,
什麼是pipeline流水線,1次pipeline(n條命令)=1次網路時間+n次命令時間;pipeline命令在redis服務端會被拆分,因此pipeline命令不是一個原子的命令。註意每次pipeline攜帶數據量;pipeline每次只能作用在一個Redis節點上;M操作和pipeline的區別,M(mset)操作是redis的原生命令,是原子操作,pipeline不是原子操作。

for(int i = 0; i < 10000; i++>) {
    jedis.hset(key, field, value); //1萬次hset差不多要50秒
for(0->100) {
  Pipeline pipeline = jedis.pipelined();
  for(0->100) {
    pipeline.hset(key,field,value);
  }
  pipeline.syncAndReturnAll(); //拆分100次,每次100個命令,大概需要0.7秒
}

發佈訂閱:類似生產者消費者模型

  • 角色:發佈者(publisher),頻道(channel),訂閱者(subscriber); 發佈者將消息發佈到頻道中,訂閱者訂閱相關的頻道;
  • API: publish/subscribe/unsubscribe
    • publish channel message : publish sohu:tv "hello world"
    • subscribe sohu:tv
    • unsubscribe [channel]
    • psubscribe [pattern] #訂閱模式 sohu*

bitmap:點陣圖:數據量很大的時候節省存儲記憶體,數據量小了,不節省

hyperloglog(演算法,數據結構):

  • 極小空間完成獨立數量統計,本質是個string
  • api: pfadd key element[s]:向hyperloglog添加元素 pfcount key[s]:計算hyperloglog的獨立總數 pfmerge key1 key2合併

GEO: 3.2提供的用於計算地理位置信息;數據類型是zset,可以使用zset的刪除命令

  • 使用場景:微信搖一搖看附近好友

  • api:

    • geo key longitude latitude member #增加地理位置信息
    • geopos key member[n] #獲取地理位置信息
    • geodist key member1 membe2 [unit] m米 km千米 mi英里 ft尺 獲取兩地位置的距離
    • georadius #算出指定範圍內的地址位置信息的集合,語法複雜了點
  • 總結下Redis數據結構和類型的常見用法

類型 簡介 特性 使用場景
String 二進位安全 可以包含任何數據,比如JPG圖片或者序列化的對象,一個鍵最大能存儲512M
Hash 鍵值對集合,即編程中的Map 適合存儲對象,並且可以向資料庫中那樣update一個屬性(Memcache中需要將字元串反序列化成對象之後再修改屬性,然後序列化回去) 存取/讀取/修改 用戶信息
List 雙向鏈表 增刪快,提供了操作某一元段元素的API 1. 最新消息,按照時間線顯示
2. 消息隊列
Set 哈希表實現,元素不重覆 添加/刪除/修改的複雜度都是O(1),為集合提供求交集/並集/差集的操作 1. 打label/tag,如文章
2. 查找共同好友
3. 抽獎系統
Zset 將Set中的元素增加一個double類型的權重score,按照score排序 數據插入集合就好序了 排行榜
Hyperloglog 本質是string 極小空間完成獨立數據量統計 統計基數,不完全正確
GEO 數據類型是zset 存儲地理位置信息,並提供計算距離等操作 微信搖一搖查看附近好友
Bitmap 點陣圖 數據量很大的時候節省存儲記憶體,數據量小了不節省 1. 設置用戶的狀態
2. BitMap解決海量數據尋找重覆、判斷個別元素是否在

Redis持久化

  • 持久化的作用:redis所有數據保存在記憶體中,對數據的更新將非同步地保存到磁碟上。

  • 主流資料庫持久化實現方式:快照(MySQL Dump/Redis RDB),寫日誌(MySQL Binlog/Redis AOF)

  • RDB:

    • 創建RDB文件(二進位文件)到硬碟中,啟動後載入RDB文件到記憶體
    • 三種觸發機制
      • save(同步) - 會產生阻塞

        • 文件策略:如存在老的RDB文件,新的替換老的,新的會先生成到一個臨時文件
      • bgsave(非同步) - 不會阻塞

        • 客戶端執行bgsave之後,redis會使用linux的一個fork()命令生成主進程的一個子進程(fork的操作會執行一個記憶體頁的拷貝,使用copy-on-write策略),子進程會創建RDB文件,創建完畢後將成功的消息返回給redis。fork()出來的子進程執行快的話不會阻塞主進程,否則也會阻塞redis,阻塞的實際點就是生成出來這個子進程。由於是非同步,在創建的過程中還有其他命令在執行,如何保證RDB文件是最新的呢?在數據量大的時候bgsave才能突出優點。
        命令 save bgsave
        IO類型 同步 非同步
        阻塞 是(阻塞發生在fork子進程
        複雜度 O(n) O(n)
        優點 不會消耗額外記憶體 不阻塞客戶端命令
        缺點 阻塞客戶端命令 需要fork,消耗記憶體
      • 自動觸發:多少秒內有多少changes會非同步(bgsave)生成一個RDB文件,如60秒內有1W條changes,預設的規則,可以改;不是很好吧,無法控制頻率;另外兩條是900秒內有1條changes, 300秒內有10條changes;

      • 配置

        • dbfilename dump.rdb
        • dir ./
        • stop-writes-on-bgsave-error yes 當bgsave發生錯誤是停止寫RDB文件
        • rdbcompression yes 採用壓縮格式
        • rdbchecksum yes 採用校驗和
      • 其他不能忽視的點:

        • 全量複製;debug reload;shutdown save會執行rdb文件的生成
  • AOF:

    • RDB現存問題:耗時,耗性能(fork,IO),不可控(突然宕機)
    • AOF:redis中的cmd會先刷新到緩衝區,然後更具配置AOF的策略,非同步存追加到AOF文件中,發生宕機後,可以通過AOF恢復,基本上數據是完整的
    • AOF的三種策略(配置的三種屬性)
      • always:來一條命令寫一條;不丟失數據,IO開銷較大
      • everysec:每秒把緩衝區fsync到AOF文件;丟1秒數據
      • no:操作系統決定什麼時候把緩衝區同步到AOF就什麼時候追加;不用配置,但是不可控,取決於操作系統
    • AOF重寫
      • 如果AOF文件很大的話,恢復會很慢,AOF的重寫是優化一些命名,使其變成1條,對於過期數據沒必要Log,本質是把過期的沒有用的,重覆的過濾掉,以此減少磁碟占用量,加速恢復。極端的例子,1億次incr,實際只需要set counter n就夠了
      • 重寫的兩種方式
        • bgrewriteaof:非同步執行,redis fork出一個子進程,然後進行AOF重寫
        • AOF重寫配置
          • 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
    • 其他配置
      • appendonly yes
      • appendfilename ""
      • appendfsync everysec
      • dir /xx
      • no-appendfsync-on-rewrite yes AOF在重啟之後恢復,要權衡是否開啟AOF日誌追加的功能,這個時候IO很大,如果設置為yes,也就意味著在恢復之前的日誌數據會丟失
  • RDB & AOF最佳策略:RDB優先於AOF先啟用

    • RDB:建議關掉,集中管理,在從節點開RDB
    • AOF:建議開啟,每秒刷盤
    • 最佳策略:小分片(log文件分片)
  • 常見問題

    • fork操作:是一個同步操作,做一個記憶體頁的拷貝;與記憶體量息息相關,記憶體越大,耗時越長;執行info命令,有個latest_fork_usec的值,看下上次fork執行耗時
    • 進程外開銷:
      • CPU:RDB AOF文件生成,屬於CPU密集型操作(不要和CPU密集型應用部署在一起,減少RDB AOF頻率);記憶體:fork記憶體開銷;硬碟:IO開銷大,選用SSD磁碟
    • AOF追加阻塞:主線程將命令刷到AOF緩衝區,同步線程同步命令到硬碟,同時主線程會對比上次fsync的時間,如果大於2秒就阻塞主線程,否則不阻塞,主線程這麼做是為了達到每秒刷盤的目的,讓子線程完成AOF,以此來達到數據同步。AOF發生阻塞怎麼定位:redis日誌/info persistence(aof_delayed_fsync累計阻塞次數,是累計,不好分清什麼時候發生阻塞)

    • 單機多實例部署

高可用

Redis主從複製
  • 主從複製:單機故障/容量瓶頸/QPS瓶頸;一個master可以有多個slave,一個slave只能有一個master,數據必須是單流向,從master流向slave

  • 複製的配置:

    • 使用slaeof命令,在從redis中執行slave masterip:port使其成為master的從伺服器,就能從master拉取數據了;執行slaveof no one清除掉不成為從節點,但是數據不清楚;
    • 修改配置, slaveof ip port / slave-read-only yes(從節點只做都操作);配置要更改的話,要重啟,所以選擇的時候謹慎
  • 全量複製

    • run_id(使用info server可以看到run_id),重啟之後run_id就沒有了,當從伺服器去複製主伺服器,主伺服器run_id會在從伺服器上做一個標識,當從伺服器發現主伺服器的run_id發生了變化,說明主伺服器發生了變化(重啟或者什麼的),那麼從伺服器就要把主伺服器的數據都同步過來
    • 偏移量:部分複製中的一個依據,後面說

    • 解析下上面的全量複製的過程,slave向master發送psync的命令要去master全量複製數據(PSYNC <MASTER_RUN_ID> ,其中?表示我不知道master的runId啊,第一次連嘛,-1表示我都要,這時候slava咱啥也不知道),master大人收到了小弟的請求之後,大方的把自己的runId/offset發了過去,小弟收到後先存下來;在master大人把自個的信息發給小弟之後,立馬投入了創建快照RDB的工作,一個bgsave命令立馬開工,RDB生產了就發給slave;咦,細心的我們發現你這不對啊,你master創建快照到創建完成這之間新增的數據咋辦,master吭吭了兩聲,我在開始快照的那一刻,後期的所有寫命令都額外往buffer中存了一份,來保證我給你的是完整的,當我發送完RDB之後,立馬給你發buffer;slave小弟內心對master大人產生了膜拜之情,收到了RDB/buffer之後,先把自己的老數據flush掉,然後load RDB,把最新的buffer刷一遍,分分鐘讓自己向master看齊。
    • 開銷:bgsave時間, RDB文件網路傳輸時間,從節點清空數據時間,從節點載入RDB的時間,可能的AOF重寫時間
  • 部分複製:

    • 解釋下上面的部分複製的過程,當遇到網路抖動,那這段時間內數據在slave上就會發生丟失,那麼這些數據slave是不知道的,在2.8之前redis會重新做一次全量複製,但是很顯然這樣做開銷很大,2.8之後提出部分複製的功能;當matster發現slave連接不上的時候,master在進行寫操作的時候,也會往緩衝區寫,等到下一次slave連上之後,slave會發送一條pysnc {offset}{runId}的命令,其中offset是slave自己的,相當於告訴master我的偏移量是多少,master判斷slave的offset在緩衝區內(緩衝區有start/end offset)就向slave發送continue命令,然後把這部分數據發送給slave;當master發現slave這個offset偏移量很大的時候,也就意味著slave丟失了很多數據,那麼就進行一次全量複製
  • 故障處理:

    • master/slave宕機的情況,主從模式沒有實現故障的完全自動轉移
    • 常見問題:
      • 讀寫分離:讀流量分攤到從節點,可能遇到複製數據延遲,也可能讀到過期的數據,從節點故障怎麼辦
      • 主從配置不一致:主從maxmemory不一致,可能會丟失數據;主從記憶體不一致
      • 規避全量複製:第一次不可避免;小主節點,低峰處理(夜間);主節點重啟後runId發生了變化
      • 規避複製風暴
        • 單機主節點複製風暴,如果是1主N從,當master重啟之後,所有的slave都會發生全量複製,可想而知這樣非常容易造成redis服務的不可用
Redis-Sentinel
  • 主從複製高可用?
    • 手動故障轉移,例如選出新的slave做master;寫能力和存儲能力受限;
  • 架構說明
    • Redis Sentinel是一個監控redis主從以及實施故障轉移的工具,sentinel不是一個是多個的(會選舉出一個master sentinel),這樣可以保證sentinel的高可用和公平(不是一個sentinel判斷不可用就不可用),可以把Redis Sentinel看成一個redis的額外進程,用來監控reids服務的可用與不可用;客戶端不再記住redis的地址,而是記錄sentinel的地址,sentinel知道誰是真的master;當多個sentinel發現並確認master有問題,sentinel內部會先選出來一個領導,讓這個領導來完成故障的轉移(因為執行slave no noe/new master這些命令只需要一個sentinel就夠了),sentinel從slave中選舉一個作為master,然後通知其他的slave去新的master獲取數據。sentinel可以監控多套master-slave,
  • 安裝配置
    • 配置開啟主從節點
    • 配置開啟sentinel監控主節點(sentinel是特殊的redis):sentinel預設埠是23679
      sentinel monitor mymaster 127.0.0.1 7000 2 監控的主節點名字是mymaster,2表示2個sentinel覺得當前master有問題提才發生故障轉移
      sentinel down-after-milliseconds mymaster 30000 表示30秒不通之後就停掉master
      sentinel parallel-syncs mymaster 1 表示每次併發的複製是1個在複製,這樣可以減少master的壓力
      sentinel failover-timeout mymaster 180000 故障轉移時間
      
  • 實現原理: redis sentinel做失敗判定和故障轉移
    • redis sentinel內部有三個定時任務
        1. 每10秒每個sentinel對master和slave執行info:可以從replication中發現slave節點,確認主從關係
        1. 每2秒每個sentinel通過master節點的channel交換信息(pub/sub): 什麼意思呢,master節點上有個發佈訂閱的頻道用於sentinel節點進行信息交換,利用的原理就是每個sentinel發佈一個信息其他sentinel都可以收到這樣一個原理,這個信息都包含當前sentinel節點的信息,以及它當前對master/slave做出的判斷。這個頻道是啥呢,sentinel:hello,這個名字內部規定的
        1. 每1秒每個sentinel對其他sentinel和redis執行ping操作-心跳檢測,是失敗判斷的依據
  • 主觀下線和客觀下線:
    • sentinel monitor quorum是法定人數,有quorum個sentinel認為master不可用了那麼master就會被客觀下線
    • sentinel down-after-milliseconds 一個sentinel如果在timeout毫秒內沒收到master的回覆就做主動下線的操作
    • 主觀下線:每個sentinel節點對redis節點失敗都有自己的判斷,這裡的節點可以是master,也可以是slave
    • 客觀下線:所有的sentinel節點對某個redis節點認為失敗的個數達到quorum個才下線
  • 領導者選舉
    • 為啥要選領導者,因為只需要一個sentinel節點就能完成故障轉移。怎麼選舉呢?
      • 每個做完主觀下線的sentinel節點(就是發現某個節點不可用了,併發出了自己的判斷的節點)都會向其他sentinel節點發送sentinel is-master-down-by-addr命令,要求將自己設置為領導者。那麼收到這個命令的sentinel如果在之前沒有同意過其他sentinel的話,就會同意這個請求,否則拒絕,換句話說每個sentinel只有一個同意票,這個同意票給第一個問自己的節點。好了,票發完了,如果這個sentinel節點發現自己擁有的票數超過sentinel集合半數並且操作quorum,那麼它將成為領導者;如果此過程有多個sentinel節點成為領導者,那麼過段時間將重新進行一次選舉。領導者選舉使用的是一個Raft演算法,以上是抽象過程。所以sentinel的個數(>=3最好為奇數)和quorum的個數需要合理配置。
  • 故障轉移(sentinel領導者節點完成)
      1. 從slave節點中選舉一個“合適的”節點作為新的master節點
      1. 對上面的slave節點執行slaveof no one命令讓其成為master節點
      1. 向剩餘的slave節點發送命令,讓它們成為新的master節點的slave節點,複製規則和parallel-syncs(允許並行複製的個數)參數有關。複製的過程master是做了優化的,只需要一個RDB的生成,然後同時向slave節點發送RDB和buffer,有一定的開銷,特別是網路
      1. 更新對原來master節點配置為slave,並保持對其“關註”,當其恢復後命令它去複製新的master節點
    • 如何選擇合適的slave的節點
        1. 選擇slave-priority(slave節點優先順序)最高的slave節點,如果存在就返回,不存在則繼續。一般不配置這個參數,什麼情況下配置呢,當有一臺slave節點的機器配置很高,我們希望當master掛了之後它能成為新的master時,做這個設置。
        1. 選擇複製偏移量最大的slave節點(複製的最完整),如果存在則返回,不存在則繼續。
        1. 選擇runId最小的slave節點。runId最小就是最早的slave節點,假設它複製的最多。

運維和開發

  • 節點運維:上下和下線
    • 機器下線:機器因為不能用了或者過保等什麼原因要換機器
    • 機器性能不足:例如CPU、記憶體、硬碟、網路等
    • 節點自生保障:例如服務不穩定等
      • sentinel failover 主節點主動故障轉移,已經選舉了sentinel領導者,所以上述過程可以省略
      • 從節點下線:臨時下線還是永久下線。永久下線可能要清理掉一些配置文件,從節點下線的時候也要考慮讀寫分離的情況,因為這時候有可能正在讀。
      • 節點上線: 主節點執行sentinel failover進行替換,從節點slaveof即可,sentinel節點可以感知

JedisSentinelPool的實現
看源代碼

    • switch-master: 切換主節點
    • convert-to-slave: 切換從節點
    • sdown:主觀下線
Redis-Cluster
  • 為什麼需要集群
    • 併發量/
    • 數據量: 業務需要500G怎麼辦,機器記憶體是16~256G
    • 網路流量
      Redis 3.0版本提供了分散式技術
  • 數據分佈
    • 數據分區

    兩種方式

    • 順序分區
    • 哈希分區
      • 節點取餘分區
        • 新增節點之後基本所有數據要產生飄逸,一般產生多倍擴容節點,飄逸量少
      • 一致性哈希分區
        • 解決了哈希分區中新增節點造成書飄逸的問題
      • 虛擬槽分區
        • 好複雜沒咋懂
  • 搭建集群
    • 原生安裝(模擬3主3從)
        1. 配置開啟節點 enable-sync yes
        port ${port}
        daemonize yes
        dir "/path"
        dbfilename "dump-${port}.rdb"
        logfile "${port}.log"
        cluster-enabled yes
        cluster-config-file nodes-${port}.conf #redis啟動後這個nodes-port.conf會自動生成
        

        開啟命令 redis-server redis-7000.conf / redis-server redis-7001.conf /...,
        此時開啟的節點都是相互孤立的沒有任何通信,
        查看當前狀態

        127.0.0.1:7000> cluster info
        cluster_state:fail
        cluster_slots_assigned:0
        cluster_slots_ok:0
        cluster_slots_pfail:0
        cluster_slots_fail:0
        cluster_known_nodes:1
        cluster_size:0
        cluster_current_epoch:0
        cluster_my_epoch:0
        cluster_stats_messages_sent:0
        cluster_stats_messages_received:0
        

        顯示當前是cluster狀態

        1. meet(節點的握手)
        redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7001
        [root@xx cluster]# redis-cli -p 7000 cluster meet 47.x.x.16 7001
        OK
        [root@xx cluster]# redis-cli -p 7000 cluster nodes
        862e370d2342cf6bf883421003846e171770234e :7000@17000 myself,master - 0 0 0 connected
        886b6e6c29f985f7f85acb1bf548d0937918eca3 4.x.x.6:7001@17001 handshake - 0 0 0 connected
        
        redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7002
        redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7003
        redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7004
        redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7005
        

        這地方註意啊,手動配置的時候有個巨坑(坑了我1天):在redis cluster架構中,每個redis要開發兩個埠,比如一個是6379,那麼另一個就是加10000之後的埠號,比如16379。16379埠是用來進行節點間通信的,也就是cluster bus集群匯流排,cluster bus的通信用來進行故障檢測、配置更新、故障轉移授權等操作。

        1. 指派槽
        redis-cli -h 127.0.0.1 -p 7000 cluster addslots {0...5461}
        redis-cli -h 127.0.0.1 -p 7001 cluster addslots {5462...10922}
        redis-cli -h 127.0.0.1 -p 7002 cluster addslots {10923...16383}
        
        1. 主從關係的分配
        redis-cli -h 127.0.0.1 -p 7003 cluster replicate ${node-id-7000}
        redis-cli -h 127.0.0.1 -p 7004 cluster replicate ${node-id-7001}
        redis-cli -h 127.0.0.1 -p 7005 cluster replicate ${node-id-7002}
        
      • 常用命令

        cluster notes 查看集群的node幾點情況
        cluster slots 查看slots的分佈
        cluster info 查看當前集群的狀態,這個命令可以看到集群是否屬於可用狀態
        redis-cli -c -p x 註意加-c,表示集群
        


        開始的一串字元是nodeid, 整行表示的意識是我的nodeId是多少,哪個ip的那個埠,我是主還是從,是從的話從的誰(誰的nodeId),是主的話還能看到slots的分佈情況

    • 官方工具安裝
      • redis-trib.rb實現對redis集群的自動化安裝
  • 集群伸縮
  • 客戶端路由
  • 集群原理
    • 集群是怎麼通信的:16379 埠號是用來進行節點間通信的,也就是 cluster bus 的東西,cluster bus 的通信,用來進行故障檢測、配置更新、故障轉移授權。cluster bus 用了另外一種二進位的協議,gossip 協議,用於節點間進行高效的數據交換,占用更少的網路帶寬和處理時間。
  • 開發運維常見問題
Redis事務
  • Redis 事務可以一次執行多個命令, 並且帶有以下三個重要的保證:

    • 批量操作在發送 EXEC 命令前被放入隊列緩存。
    • 收到 EXEC 命令後進入事務執行,事務中任意命令執行失敗,其餘的命令依然被執行。
    • 在事務執行過程,其他客戶端提交的命令請求不會插入到事務執行命令序列中。
  • Redis事務從開始到執行會經歷以下三個階段:開始事務 -> 命令入隊 -> 執行事務。單個 Redis 命令的執行是原子性的,但 Redis 沒有在事務上增加任何維持原子性的機制,所以 Redis 事務的執行並不是原子性的。事務可以理解為一個打包的批量執行腳本,但批量指令並非原子化的操作,中間某條指令的失敗不會導致前面已做指令的回滾,也不會造成後續的指令不做。這是官網上的說明 From redis docs on transactions: It's important to note that even when a command fails, all the other commands in the queue are processed – Redis will not stop the processing of commands.

  • Redis 通過監聽一個 TCP 埠或者 Unix socket 的方式來接收來自客戶端的連接,當一個連接建立後,Redis 內部會進行以下一些操作:

    • 首先,客戶端 socket 會被設置為非阻塞模式,因為 Redis 在網路事件處理上採用的是非阻塞多路復用模型。
    • 然後為這個 socket 設置 TCP_NODELAY 屬性,禁用 Nagle 演算法
    • 然後創建一個可讀的文件事件用於監聽這個客戶端 socket 的數據發送
  • Redis 管道技術可以在服務端未響應時,客戶端可以繼續向服務端發送請求,並最終一次性讀取所有服務端的響應。管道技術最顯著的優勢是提高了 redis 服務的性能。

  • Redis 分區

    • 分區是分割數據到多個Redis實例的處理過程,因此每個實例只保存key的一個子集。
    • 分區的優勢:
      • 通過利用多台電腦記憶體的和值,允許我們構造更大的資料庫。
      • 通過多核和多台電腦,允許我們擴展計算能力;通過多台電腦和網路適配器,允許我們擴展網路帶寬。
    • 分區的不足:
      • 涉及多個key的操作通常是不被支持的。舉例來說,當兩個set映射到不同的redis實例上時,你就不能對這兩個set執行交集操作。
      • 涉及多個key的redis事務不能使用。
      • 當使用分區時,數據處理較為複雜,比如你需要處理多個rdb/aof文件,並且從多個實例和主機備份持久化文件。
      • 增加或刪除容量也比較複雜。redis集群大多數支持在運行時增加、刪除節點的透明數據平衡的能力,但是類似於客戶端分區、代理等其他系統則不支持這項特性。然而,一種叫做presharding的技術對此是有幫助的。
    • 分區類型:Redis 有兩種類型分區。 假設有4個Redis實例 R0,R1,R2,R3,和類似user:1,user:2這樣的表示用戶的多個key,對既定的key有多種不同方式來選擇這個key存放在哪個實例中。也就是說,有不同的系統來映射某個key到某個Redis服務。
      • 範圍分區
        • 最簡單的分區方式是按範圍分區,就是映射一定範圍的對象到特定的Redis實例。比如,ID從0到10000的用戶會保存到實例R0,ID從10001到 20000的用戶會保存到R1,以此類推。這種方式是可行的,並且在實際中使用,不足就是要有一個區間範圍到實例的映射表。這個表要被管理,同時還需要各 種對象的映射表,通常對Redis來說並非是好的方法。
      • 哈希分區
        • 另外一種分區方法是hash分區。這對任何key都適用,也無需是object_name:這種形式,像下麵描述的一樣簡單:用一個hash函數將key轉換為一個數字,比如使用crc32 hash函數。對key foobar執行crc32(foobar)會輸出類似93024922的整數。對這個整數取模,將其轉化為0-3之間的數字,就可以將這個整數映射到4個Redis實例中的一個了。93024922 % 4 = 2,就是說key foobar應該被存到R2實例中。註意:取模操作是取除的餘數,通常在多種編程語言中用%操作符實現。【當分區較多或發生變化的時候需要處理一些額外的情況】

其他

  • Redis設置port為6379的原因

I/O多路復用技術(multiplexing)

關於I/O多路復用(又被稱為“事件驅動”),首先要理解的是,操作系統為你提供了一個功能,當你的某個socket可讀或者可寫的時候,它可以給你一個通知。這樣當配合非阻塞的socket使用時,只有當系統通知我哪個描述符可讀了,我才去執行read操作,可以保證每次read都能讀到有效數據而不做純返回-1和EAGAIN的無用功。寫操作類似。操作系統的這個功能通過select/poll/epoll/kqueue之類的系統調用函數來使用,這些函數都可以同時監視多個描述符的讀寫就緒狀況,這樣,多個描述符的I/O操作都能在一個線程內併發交替地順序完成,這就叫I/O多路復用,這裡的“復用”指的是復用同一個線程。

下麵舉一個例子,模擬一個tcp伺服器處理30個客戶socket。假設你是一個老師,讓30個學生解答一道題目,然後檢查學生做的是否正確,你有下麵幾個選擇:1. 第一種選擇:按順序逐個檢查,先檢查A,然後是B,之後是C、D。。。這中間如果有一個學生卡住,全班都會被耽誤。這種模式就好比,你用迴圈挨個處理socket,根本不具有併發能力。2. 第二種選擇:你創建30個分身,每個分身檢查一個學生的答案是否正確。 這種類似於為每一個用戶創建一個進程或者線程處理連接。3. 第三種選擇,你站在講臺上等,誰解答完誰舉手。這時C、D舉手,表示他們解答問題完畢,你下去依次檢查C、D的答案,然後繼續回到講臺上等。此時E、A又舉手,然後去處理E和A。。。 這種就是IO復用模型,Linux下的select、poll和epoll就是乾這個的。將用戶socket對應的fd註冊進epoll,然後epoll幫你監聽哪些socket上有消息到達,這樣就避免了大量的無用操作。此時的socket應該採用非阻塞模式。這樣,整個過程只在調用select、poll、epoll這些調用的時候才會阻塞,收發客戶消息是不會阻塞的,整個進程或者線程就被充分利用起來,這就是事件驅動,所謂的reactor模式。

什麼時候適合用緩存

  • 數據訪問頻率
    • 訪問頻率高,適合用緩存,效果好
    • 訪問頻率低,不建議使用,效果不佳
  • 數據讀寫比例
    • 讀多寫少,適合緩存,效果好
    • 讀少寫多,不建議使用,效果不佳
  • 數據一致性
    • 一致性要求低,適合緩存,效果好
    • 一致性要求高,不建議緩存,效果不佳

Redis中的緩存穿透、緩存雪崩、緩存擊穿

  • 緩存穿透
緩存穿透,是指查詢一個資料庫一定不存在的數據。正常的使用緩存流程大致是,數據查詢先進行緩存查詢,如果key不存在或者key已經過期,再對資料庫進行查詢,並把查詢到的對象,放進緩存。如果資料庫查詢對象為空,則不放進緩存。
  1. 參數傳入對象主鍵ID
  2. 根據key從緩存中獲取對象
  3. 如果對象不為空,直接返回
  4. 如果對象為空,進行資料庫查詢
  5. 如果從資料庫查詢出的對象不為空,則放入緩存(設定過期時間)

想象一下這個情況,如果傳入的參數為-1,會是怎麼樣?這個-1,就是一定不存在的對象。就會每次都去查詢資料庫,而每次查詢都是空,每次又都不會進行緩存。假如有惡意攻擊,就可以利用這個漏洞,對資料庫造成壓力,甚至壓垮資料庫。即便是採用UUID,也是很容易找到一個不存在的KEY,進行攻擊。

小編在工作中,會採用緩存空值的方式,也就是【代碼流程】中第5步,如果從資料庫查詢的對象為空,也放入緩存,只是設定的緩存過期時間較短,比如設置為60秒。
redisTemplate.opsForValue().set(String.valueOf(goodsId), null, 60, TimeUnit.SECONDS); // 設置過期時間

解決方案

  • 介面層增加校驗,如用戶鑒權校驗,id做基礎校驗,id<=0的直接攔截;

  • 從緩存取不到的數據,在資料庫中也沒有取到,這時也可以將key-value對寫為key-null,緩存有效時間可以設置短點,如30秒(設置太長會導致正常情況也沒法使用)。這樣可以防止攻擊用戶反覆用同一個id暴力攻擊

  • 緩存雪崩

緩存雪崩,是指在某一個時間段,緩存集中過期失效。

產生雪崩的原因之一,比如在寫本文的時候,馬上就要到雙十二零點,很快就會迎來一波搶購,這波商品時間比較集中的放入了緩存,假設緩存一個小時。那麼到了凌晨一點鐘的時候,這批商品的緩存就都過期了。而對這批商品的訪問查詢,都落到了資料庫上,對於資料庫而言,就會產生周期性的壓力波峰。

小編在做電商項目的時候,一般是採取不同分類商品,緩存不同周期。在同一分類中的商品,加上一個隨機因數。這樣能儘可能分散緩存過期時間,而且,熱門類目的商品緩存時間長一些,冷門類目的商品緩存時間短一些,也能節省緩存服務的資源。

其實集中過期,倒不是非常致命,比較致命的緩存雪崩,是緩存伺服器某個節點宕機或斷網。因為自然形成的緩存雪崩,一定是在某個時間段集中創建緩存,那麼那個時候資料庫能頂住壓力,這個時候,資料庫也是可以頂住壓力的。無非就是對資料庫產生周期性的壓力而已。而緩存服務節點的宕機,對資料庫伺服器造成的壓力是不可預知的,很有可能瞬間就把資料庫壓垮。

解決方案:

  • 緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。

  • 如果緩存資料庫是分散式部署,將熱點數據均勻分佈在不同搞得緩存資料庫中。

  • 設置熱點數據永遠不過期。

  • redis 持久化,一旦重啟,自動從磁碟上載入數據,快速恢復緩存數據。

  • 緩存擊穿

緩存擊穿,是指存在hot key,在不停的扛著大併發,大併發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大併發就穿破緩存,直接請求資料庫,就像在一個屏障上鑿開了一個洞。

小編在做電商項目的時候,把這貨就成為“爆款”。
其實,大多數情況下這種爆款很難對資料庫伺服器造成壓垮性的壓力。達到這個級別的公司沒有幾家的。所以,務實主義的小編,對主打商品都是早早的做好了準備,讓緩存永不過期。即便某些商品自己發酵成了爆款,也是直接設為永不過期就好了。

解決方案

  • 設置熱點數據永遠不過期。
  • 加互斥鎖,互斥鎖參考代碼如下:
    說明:
    1)緩存中有數據,直接走上述代碼13行後就返回結果了
    2)緩存中沒有數據,第1個進入的線程,獲取鎖並從資料庫去取數據,沒釋放鎖之前,其他並行進入的線程會等待100ms,再重新去緩存取數據。這樣就防止都去資料庫重覆取數據,重覆往緩存中更新數據情況出現。
    3)當然這是簡化處理,理論上如果能根據key值加鎖就更好了,就是線程A從資料庫取key1的數據並不妨礙線程B取key2的數據,上面代碼明顯做不到這點。

百問

Redis番外篇

Redis 最開始的設計可能就是想做一個緩存來用。但是分散式環境複雜,暴露的問題可能比較多,所以 Redis 就要做集群。做集群後,可能和 Memcahed 效果類似了,我們要超越它,所以可能就有了多數據類型的存儲結構。光做緩存,如何已宕機數據就丟失了。我們的口號是超越 Memcahed,所以我們要支持數據持久化。於是可能就有了 AOF 和 RDB,就可以當資料庫來用來。這樣 Redis 的高效可靠的設計,所以它又可以用來做消息中間件。這就是 Redis 的三大特點,可以用來做:緩存、資料庫和消息中間件。

再來說說,Redis 如何設計成但進程單線程的?

根據官方的測試結果《How fast is Redis?》來看,在操作記憶體的情況下,CPU 並不能起到決定性的作用,反而可能帶來一些其他問題。比如鎖,CPU 切換帶來的性能開銷等。這一點我們可以根據官方的測試報告,提供的數據來證明。而且官方提供的數據是可以達到100000+的QPS(每秒內查詢次數),這個數據並不比採用單進程多線程 Memcached 差!所以在基於記憶體的操作,CPU不是 Redis 瓶頸的情況下,官方採用來單進程單線程的設計。

Redis單線程為什麼這麼快?

快的原因有主要三點:

  1. 純記憶體操作:Redis是基於記憶體的,所有的命令都在記憶體中完成,記憶體的響應速度相比硬碟是非常快的,記憶體的響應時間大約是100納秒,Redis官方給出的OPS是10W
  2. 編程語言:Redis採用C語言編寫,不依賴第三方類庫,執行速度快
  3. 線程模型:Redis使用單線程操作,避免了線程的切換和競態消耗
  4. 採用了非阻塞IO多路復用機制:多路I/O復用模型是利用 select、poll、epoll 可以同時監察多個流的 I/O 事件的能力,在空閑的時候,會把當前線程阻塞掉,當有一個或多個流有 I/O 事件時,就從阻塞態中喚醒,於是程式就會輪詢一遍所有的流(epoll 是只輪詢那些真正發出了事件的流),並且只依次順序的處理就緒的流,這種做法就避免了大量的無用操作。這裡“多路”指的是多個網路連接,“復用”指的是復用同一個線程。加上Redis自身的事件處理模型將epoll中的連接,讀寫,關閉都轉換為了事件,不在I/O上浪費過多的時間
  5. 由於是單線程,所以就存在一個順序讀寫的問題,順序讀寫比隨機讀寫的速度快。
  6. Redis的數據結構是經過專門的研究和設計的,所以操作起來簡單且快。
    最後,再說一點,Redis 是單進程和單線程的設計,並不是說它不能多進程多線程。比如備份時會 fork 一個新進程來操作;再比如基於 COW 原理的 RDB 操作就是多線程的。

Redis如何處理過期數據?Slave不能處理數據,那數據過期了怎麼辦?

1主2從的模式中,當master掛掉之後怎麼辦?

這種典型的模式就不上圖了,master讀寫,slave1/2只讀,當master掛掉之後,redis服務不可用,需要立馬手動處理。兩種處理方式,第一種是把master重新啟動起來,不用改變現有的主從結構,缺點是什麼呢,master重新啟動並完成RDB/AOF的恢復是個耗時的過程,另外會造成slave1/2發生全量複製;第二種就是重新選舉新的master,具體怎麼做呢?選折其中一個slave,執行命令 slave no one來解除自己是從伺服器的身份,使其稱為一個master,註意的點是這個slave要改成讀寫模式;連到另一個slave,執行slave new master,讓它去找master。整個過程是一個手動的過程,Redis Sentinel就是這樣一個功能,自動完成切換,帥的一比。

Redis分散式鎖你真的會用嗎?
https://www.xttblog.com/?p=4598

Redis使用註意的點

  1. 由於是單線程模型,因此一次只運行一條命令
  2. 拒絕長(慢)命令:keys, flushall,flushdb, slow lua script, mutil/exec, operate big value(collection)

Redis Sentinel和Redis Cluster的區別

  1. sentinel
    實現高可用,但是沒有分區
    監控,能持續監控Redis的主從實例是否正常工作;
    通知,當被監控的Redis實例出問題時,能通過API通知系統管理員或其他程式;
    自動故障恢復,如果主實例無法正常工作,Sentinel將啟動故障恢復機制把一個從實例提升為主實例,其他的從實例將會被重新配置到新的主實例,且應用程式會得到一個更換新地址的通知。
    Redis Sentinel是一個分散式系統,可以部署多個Sentinel實例來監控同一組Redis實例,它們通過Gossip協議來確定一個主實例宕機,通過Agreement協議來執行故障恢復和配置變更。
  2. Redis Cluster特點如下:
  • 所有的節點相互連接;
  • 集群消息通信通過集群匯流排通信,,集群匯流排埠大小為客戶端服務埠+10000,這個10000是固定值;
  • 節點與節點之間通過二進位協議進行通信;
  • 客戶端和集群節點之間通信和通常一樣,通過文本協議進行;
  • 集群節點不會代理查詢;

redis3.0以後推出了cluster,具有Sentinel的監控和自動Failover能力,同時提供一種官方的分區解決方案

Redis分散式鎖/Redis的setnx命令如何設置key的失效時間(同時操作setnx和expire

Redis的setnx命令是當key不存在時設置key,但setnx不能同時完成expire設置失效時長,不能保證setnx和expire的原子性。我們可以使用set命令完成setnx和expire的操作,並且這種操作是原子操作。
下麵是set命令的可選項:

set key value [EX seconds] [PX milliseconds] [NX|XX]
EX seconds:設置失效時長,單位秒
PX milliseconds:設置失效時長,單位毫秒
NX:key不存在時設置value,成功返回OK,失敗返回(nil)
XX:key存在時設置value,成功返回OK,失敗返回(nil)

案例:設置name=p7+,失效時長100s,不存在時設置
1.1.1.1:6379> set name p7+ ex 100 nx

假如Redis裡面有1億個key,其中有10w個key是以某個固定的已知的首碼開頭的,如何將它們全部找出來?

可以使用keys [pattern]來列舉出來,由於Redis是單線程的,在使用keys命令的時候會導致線程阻塞一段時間,我們也可以使用scan指令以無阻塞的方式取出來,但會有一定重覆的概率,客戶端去重就可以了。如果是主從模式的話,可以在從伺服器執行keys命令,儘量不影響現有業務。

使用過Redis做非同步隊列麽,你是怎麼用的?

Redis如何實現延時隊列?

RDB的原理是什麼?

你給出兩個辭彙就可以了,fork和cow。fork是指redis通過創建子進程來進行RDB操作,cow指的是copy on write,子進程創建後,父子進程共用數據段,父進程繼續提供讀寫服務,寫髒的頁面數據會逐漸和子進程分離開來。

Pipeline有什麼好處,為什麼要用pipeline?

可以將多次IO往返的時間縮減為一次,前提是pipeline執行的指令之間沒有因果相關性。使用redis-benchmark進行壓測的時候可以發現影響redis的QPS峰值的一個重要因素是pipeline批次指令的數目。

是否使用過Redis集群,集群的高可用怎麼保證,集群的原理是什麼?

  • Redis Sentinal 著眼於高可用,在master宕機時會自動將slave提升為master,繼續提供服務。
  • Redis Cluster 著眼於擴展性,在單個redis記憶體不足時,使用Cluster進行分片存儲。

Redis集群是如何通信的?

Redis集群採用gossip(流言)協議來通信,Gossip協議的主要職責就是信息交換。信息交換的載體就是節點彼此發送的Gossip消息,常用的Gossip消息可分為:ping消息、pong消息、meet消息、fail消息。集群中的每個節點都會單獨開闢一個TCP通道,用於節點之間的彼此通信,通信埠是在基礎埠的基礎上加上1萬,每個節點在固定周期內通過特定規則選擇幾個節點發送ping消息,接收到ping消息的節點用pong消息作為響應。ping消息發送封裝了自身節點和部分其他節點的狀態數據,pong消息:當接收到ping、meet消息時,作為響應消息回覆給發送方確認消息正常通信。pong消息內部封裝了自身狀態數據。節點也可以向集群內廣播自身的pong消息來通知整個集群對自身狀態進行更新,一段時間後整個集群達到了狀態的一致性。
PS:定時任務預設每秒執行10次,每秒會隨機選取5個節點找出最久沒有通信的節點發送ping消息,用於保證Gossip信息交換的隨機性

Reids Key是如何定址的

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 記錄下引起這個報錯的原因: JSON.parse()參數應該是string類型 由於傳遞的參數是object 類型 那麼JSON.parse() 就會把object類型預設轉成string 結果為“[object object]” "["字元就當是數組去解析,然後遇到第二個字元“o” 他就蒙圈了 然 ...
  • <iframe>標簽規定一個內聯框架,一個內聯框架被用來在當前 HTML 文檔中嵌入另一個文檔,通過使用框架,你可以在同一個瀏覽器視窗中顯示不止一個頁面。 1 <iframe src="URL"></iframe><!--iframe語法--> 該URL指向不同的網頁,將視窗內容顯示為URL地址指向 ...
  • 全稱 (非同步 和`XML`),是一種用於創建快速動態網頁的技術。它在不重載全部網頁頁面的情況下,實現了對部分網頁的更新。 請求和瀏覽器地址請求區別 瀏覽器發起的請求,請求結果展示在瀏覽器上 發起的請求,結果保存在 變數里 適用場景 註冊用戶時,對用戶名的唯一性進行驗證 發送手機驗證碼 只要是不刷新網 ...
  • 快速、簡潔的 框架,設計宗旨: _write Less, Do More_ 。 作用:簡化原生 的語法,解決瀏覽器相容性問題。 引入 可以直接引入線上地址,也可以在 [官方網站][1]上下載,然後使用 屬性引入: [1]: https://jquery.com/ 基礎語法: :`jQuery`對象 ...
  • 關於函數 函數是可重覆執行的包含特定功能的代碼段。 中的 命名函數 和 匿名函數 點擊我吧 點擊它吧 關於 `DOM Document Object Model DOM DOM DOM`樹進行修改、刪除、新增等操作,讓結構化文檔動態化。 模型中的節點 文檔可以說是由節點構成的集合。在 模型中有以下3 ...
  • 簡介 _ 俗稱 ,`js ECMAScript Netscape web`游戲,伺服器腳本開發等;_ 特點 是一種解釋性腳本編程語言 是基於對象的腳本編程語言 簡單性 安全性(不能訪問本地硬碟) 動態性的 跨平臺性(依賴瀏覽器,與操作系統無關) 三種使用方法 (1)使用 :屬性構建執行 代碼 點擊 ...
  • 一、裝飾者模式 1、裝飾者模式(Decorator Pattern):指在不改變原有對象的基礎之上,將功能附加到對象上,提供了比繼承更有彈性的替代方案(擴展原有對象的功能)。(屬於結構型模式) 2、適用場景 用於擴展一個類的功能或給一個類增加附加職責 動態的給一個對象添加功能,這些功能可以再動態的撤 ...
  • 離開了園子很久很久了 疫情期間,沒有辦法出差,正好當前時間是自己規劃的查漏補缺時間,把缺少的Web模塊的統計分析圖表加進去 Webassembly 老早是聽說了,但由於項目的原因,也一直沒有精力去關註,倒是 netcore3.1期待了很久,雖然最後測試了一下,自己需要的核心介面還沒有添加進去,但是W ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...