分散式架構-Redis 從入門到精通 完整案例 附源碼

来源:https://www.cnblogs.com/chenyanbin/archive/2019/12/24/12073107.html
-Advertisement-
Play Games

導讀 篇幅較長,乾貨十足,閱讀需要花點時間,全部手打出來的字,難免出現錯別字,敬請諒解。珍惜原創,轉載請註明出處,謝謝~! NoSql介紹與Redis介紹 什麼是Redis? Redis是用C語言開發的一個開源的高性能鍵值對(key-value)記憶體資料庫。 它提供五種數據類型來存儲值:字元串類型、 ...


導讀

  篇幅較長,乾貨十足,閱讀需要花點時間,全部手打出來的字,難免出現錯別字,敬請諒解。珍惜原創,轉載請註明出處,謝謝~!

NoSql介紹與Redis介紹

什麼是Redis?

  Redis是用C語言開發的一個開源的高性能鍵值對(key-value)記憶體資料庫

  它提供五種數據類型來存儲值:字元串類型、散列類型、列表類型、集合類型、有序類型

  它是一種NoSql資料庫。

什麼是NoSql?

  • NoSql,即Not-Only Sql(不僅僅是SQL),泛指非關係型的資料庫
  • 什麼是關係型資料庫?數據結構是一種有行有列的資料庫。
  • NoSql資料庫是為瞭解決高併發、高可用、高可擴展、大數據存儲問題而產生的資料庫解決方案。
  • NoSql可以作為關係型資料庫的良好補充,但是不能替代關係型資料庫

NoSql資料庫分類

鍵值(key-value)存儲資料庫

  • 相關產品:Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley Db等
  • 典型應用:記憶體緩存,主要用於處理大量數據的高訪問負載
  • 數據模型:一系列鍵值對
  • 優勢:快速查詢
  • 劣勢:存儲的數據缺少結構化

列存儲資料庫

  • 相關產品:Cassandra、Hbase、Riak
  • 典型應用:分散式的文件系統
  • 數據模型:以列簇式存儲,將同一列數據存在一起
  • 優勢:查找速度快,可擴展性強,更容易進行分散式擴展
  • 劣勢:功能相對局限

文檔型資料庫

  • 相關產品:CouchDB、MongoDB
  • 典型應用:web應用(與key-value類似,value是結構化的)
  • 數據模型:一系列鍵值對
  • 優勢:數據結構要求不嚴格
  • 劣勢

圖形(Graph)資料庫

  • 相關資料庫:Neo4J、InfoGrid、Infinite、Graph
  • 典型應用:社交網路
  • 數據模型:圖結構
  • 優勢:利用圖結構先關演算法
  • 劣勢:需要對整個圖做計算才能得出結果,不容易做分散式的集群方案。

Redis歷史發展

  2008年,義大利的一家創業公司Merzia推出了一款給予MySql的網站實時統計系統LLOOGG,然而沒過多久該公司的創始人Salvatore Sanfilippo便對MySql的性能感到失望,於是他決定親力為LLOOGG量身定做一個資料庫,並於2009年開發完成,這個資料庫就是Redis

  不過Salvatore Sanfilippo不滿足將RedisLLOOGG這一款產品,而是希望更多的人使用它,於是在同一年Salvatore Sanfilippo將Redis開源發佈

  並開始和Redis的另一名主要的代碼貢獻者Pieter Noordhuis一起繼續Redis開發直到今天

  Salvatore Sanfilippo自己也沒有想到,短短的幾年時間,Redis就擁有了龐大的用戶群體。Hacker News在2012年發佈一份資料庫的使用請款調查,結果顯示有近12%的公司在使用Redis。國內如新浪微博、街旁網、知乎網、國外如GitHub、Stack、Overflow、Flickr等都是Redis的用戶

  VmWare公司從2010年開始贊助Redis的開發,Salvatore Sanfilippo和Pieter Noordhuis也分別在3月和5月加入VMware全職開發Redis

Redis的應用場景

  • 記憶體資料庫(登錄信息、購物車信息、用戶瀏覽記錄等)
  • 緩存伺服器(商品數據、廣告數據等等)(最多使用)
  • 解決分散式集群架構中的Session分離問題(Session共用)
  • 任務隊列。(秒殺、搶購、12306等等)
  • 支持發佈訂閱的消息模式
  • 應用排行榜
  • 網站訪問統計
  • 數據過期處理(可以精確到毫秒)

Redis安裝及配置

  • 官網地址:https://redis.io/
  • 中文官網地址:http://www.redis.cn
  • 下載地址:http://download.redis.io/releases/

Linux環境下安裝Redis

註:將下載後的Redis拖進Linux需要安裝下,VMware Tools,參考鏈接

將下載後的Redis拖進linux

安裝C語言需要的GCC環境

yum install gcc-c++

解壓Redis源碼壓縮包

tar -zxf redis-4.0.11.tar.gz

編譯Redis源碼

make

安裝Redis

make install PREFIX=/user/local/redis

格式:make install PREFIX=安裝目錄

Redis啟動

前端啟動

  • 啟動命令:redis-server,直接運行bin/redis-server將以前端模式啟動。

關閉服務

ctrl+c

啟動缺點:客戶端視窗關閉,則redis-server程式結束,不推薦使用

後端啟動(守護進程啟動)

拷貝redis

cp redis.conf /usr/local/redis/bin

格式:cp 拷貝文件夾 拷貝路徑

 修改redis.conf,將daemonize由no改為yes

vim redis.conf

 執行命令

 ./redis-server redis.conf

格式:啟動服務 指定配置文件

 關閉服務(粗暴方式)

kill -9 42126

格式:kill -9 進程號

 正常關閉

./redis-cli shutdown

其他命令說明

redis-server :啟動redis服務
redis-cli :進入redis命令客戶端
redis-benchmark: 性能測試的工具
redis-check-aof : aof文件進行檢查的工具
redis-check-dump :  rdb文件進行檢查的工具
redis-sentinel :  啟動哨兵監控服務

Redis客戶端

自帶命令行客戶端

語法

./redis-cli -h 127.0.0.1 -p 6379 

修改redis.conf配置文件(解決ip綁定問題)

#bind 127.0.0.1 綁定的ip才能訪問redis伺服器,註釋掉該配置

protected-mode yes 是否開啟保護模式,由yes改為no

參數說明

  • -h:redis伺服器的ip地址
  • -p:redis實例的埠號

預設方式

如果不制定主機和埠號也可以

./redis-cli

預設的主機地址是:127.0.0.1
預設的埠號是:6379

Redis數據類型

官網命令大全網址

http://www.redis.cn/commands.html

  • String(字元類型)
  • Hash(散列類型)
  • List(列表類型)
  • Set(集合類型)
  • SortedSet(有序集合類型,簡稱zset)

註:命令不區分大小寫,而key是區分大小寫的。

String類型

賦值

語法:SET key value

取值

語法:GET key

取值並賦值

語法:GETSET key value

演示

 數值增減

前提條件:

  1. value整數數據時,才能使用以下命令操作數值的增減。
  2. 數值增減都是原子操作。

遞增數字

語法:INCR key

 增加指定的整數

語法:INCRBY key increment

 遞減數值

語法:DECR key

減少指定的整數 

語法:DECRBY key decrement

 僅當不存在時賦值

註:該命令可以實現分散式鎖的功能,後續講解!!!!

語法:setnx key value

向尾部追加值

註:APPEND命令,向鍵值的末尾追加value如果鍵不存在則該鍵的值設置為value,即相當於set key value。返回值是追加後字元串的總長度。

 獲取字元串長度

註:strlen命令,返回鍵值的長度,如果鍵不存在則返回0

 語法:STRLEN key

同時設置/獲取多個鍵值

語法:

  1. MSET key value [key value ....]
  2. MGET key [key ....]

 應用場景之自增主鍵

需求:商品編號、訂單號採用INCR命令生成。

設計:key明明要有一定的設計

實現:定義商品編號key:items:id

 Hash類型

  Hash叫散列類型,它提供了欄位和欄位值的映射。欄位值只能是字元串類型,不支持散列類型、集合類型等其他類型。

賦值 

  HSET命令不區分插入更新操作,當執行插入操作時HSET命令返回1,當執行更新操作時返回0

一次只能設置一個欄位值

語法:HSET key field value

 一次設置多個欄位值

語法:HMSET key field value [field value ...]

 當欄位不存在時

類似HSET,區別在於如何欄位存在,該命令不執行任何操作

語法:HSETNX key field value

取值

一次只能獲取一個欄位值

語法:HGET key field

 一次可以獲取多個欄位值

語法:HMGET key field [field ....]

獲取所有欄位值

語法:HGETALL key

 刪除欄位

可以刪除一個或多個欄位,返回值是被刪除的欄位個數

語法:HDEL key field [field ...]

 增加數字

語法:HINCRBY key field increment

 判斷欄位是否存在

語法:HEXISTS key field

只獲取欄位名或欄位值

語法:

  1. HKEYS key
  2. HVALS key

 獲取欄位數量

語法:HLEN key

 獲取所有欄位

作用:獲取hash的所有信息,包括key和value

語法:hgetall key

 應用之存儲商品信息

註意事項:存在哪些對象數據,特別是對象屬性經常發生增刪改操作的數據。

商品信息欄位

  【商品id,商品名稱,商品描述,商品庫存,商品好評】

定義商品信息的key

  商品id為1001的信息在Redis中的key為:[items.1001]

示例

 List類型

  ArrayList使用數組方式存儲數據,所以根據索引查詢數據速度快,而新增或者刪除元素時需要涉及到位移操作,所以比較慢。

  LinkedList使用雙向鏈表方式存儲數據,每個元素都記錄前後元素的指針,所以插入、刪除數據時只是更改前後元素的指針即可,速度非常快。然後通過下標查詢元素時需要從頭開始索引,所以比較慢,但是如果查詢前幾個元素或後幾個元素速度比較快。

 List介紹

  Redis的列表類型(list)可以存儲一個有序的字元串列表,常用的操作是向列表兩端添加元素,或者獲取列表的某一個片段

  列表類型內部是使用雙向鏈表(double linked list)實現的,所以向列表兩端添加元素的時間複雜度為0/1,獲取越接近兩端的元素速度就越快。意味著即使是一個有幾千萬個元素的列表,獲取頭部或尾部的10條記錄也是極快的。

向列表兩端添加元素

向列表左邊添加元素

語法:LPUSH key value [value ...]

 向列表右邊添加元素

語法:RPUSH key value [value ....]

 查看列表

語法:LRANGE key start stop

  LRANGE命令是列表類型最常用的命令之一,獲取列表中的某一片段,將返回start、stop之間的所有元素(包括兩端的元素),索引從0開始。索引可以是負數,“-1”代表最後一邊的一個元素

 從列表兩端彈出元素

LPOP命令從列表左邊彈出一個元素,會分兩步完成:

  1. 將列表左邊的元素從列表中移除
  2. 返回被移除的元素值

語法:

  1. LPOP key
  2. RPOP key

獲取列表中元素的個數

語法:LLEN key 

 刪除列表中指定個數的值

  LREM命令會刪除列表中前count個數為value的元素,返回實際刪除的元素個數。根據count值不同,該命令的執行方式會有所不同。

語法:LREM key count value

  1. 當count>0時,LREM會從列表左邊開始刪除
  2. 當count<0時,LREM會從列表右邊開始刪除
  3. 當count=0時,LREM會刪除所有值為value的元素

獲取/設置指定索引的元素值

獲取指定索引的元素值

語法:LINDEX key index

設置指定索引的元素值

語法:LSET key index value

向列表中插入元素 

  該命令首先會在列表中從左到右查詢值為pivot的元素,然後根據第二個參數是BEFORE還是AFTER來決定將value插入到該元素的前面還是後面。

語法:LINSERT key BEFORE|AFTER pivot value

 將元素從一個列表轉移到另一個列表中

語法:RPOPLPUSH source destination

 應用之商品評論列表

需求1:用戶針對某一商品發佈評論,一個商品會被不同的用戶進行評論,存儲商品評論時,要按時間順序排序。

需要2:用戶在前端頁面查詢該商品的評論,需要按照時間順序降序排序。

思路:

  使用list存儲商品評論信息,key是該商品的id,value是商品評論信息商品編號為1001的商品評論key【items:comment:1001】 

 Set類型

set類型即集合類型,其中的數據時不重覆且沒有順序

集合類型和列表類型的對比:

   集合類型的常用操作是向集合中加入或刪除元素、判斷某個元素是否存在等,由於集合類型的Redis內部是使用值為空散列標實現,所有這些操作的時間複雜度都為0/1。

  Redis還提供了多個集合之間的交集、並集、差集的運算。

添加/刪除元素

語法:SADD key member [member ...]

語法:SREM key member [member ...]

獲取集合中的所有元素 

語法:SMEMBERS key

 判斷元素是否在集合中

語法:SISMEMBER key member

 集合運算命令

集合的差集運算 A-B

屬於A並且不屬於B的元素構成的集合

 語法:SDIFF key [key ...]

集合的交集運算 A∩B

屬於A且屬於B的元素構成的集合。 

 語法:SINTER key [key ...]

集合的並集運算 A ∪ B

屬於A或者屬於B的元素構成的集合

 

 

 語法:SUNION key [key ...]

獲取集合中的元素個數

語法:SCARD key

從集合中彈出一個元素 

註意:集合是無序的,所有spop命令會從集合中隨機選擇一個元素彈出

語法:SPOP key

 SortedSet類型zset

  在集合類型的基礎上,有序集合為集合中的每個元素都關聯一個分數,這使得我們不僅可以完成插入、刪除和判斷元素是否存在集合中,還能夠獲得最高或最低的前N個元素、獲取指定分數範圍內的元素等與分蘇有關的操作。

在某些方面有序集合和列表類型有些相似。

  1. 二者都是有序的。
  2. 二者都可以獲得某一範圍的元素

但是二者有著很大的區別:

  1. 列表類型是通過鏈表實現的,後去靠近兩端的數據速度極快,而當元素增多後,訪問中間數據的速度會變慢。
  2. 有序集合類型使用散列實現,所有即使讀取位於中間部分的數據也很快。
  3. 列表中不能簡單的調整某個元素的位置,但是有序集合可以(通過更改分數實現)。
  4. 有序集合要比列表類型更耗記憶體。

添加元素

  向有序集合中加入一個元素和該元素的分數,如果該元素已經存在則會用新的分數替換原有的分數。返回值是新加入到集合中的元素個數,不不含之前已經存在的元素。

語法:ZADD key score member [score member ...]

獲取排名在某個範圍的元素列表

按照元素分數從小到大的順序返回索引從start到stop之間的所有元素(包含兩端的元素)

語法:ZRANGE key start stop [WITHSCORES]

 如果需要獲取元素的分數的可以在命令尾部加上WITHSCORES參數

 獲取元素的分數

語法:ZSCORE key member

 刪除元素

移除有序集key中的一個或多個成員,不存在的成員將被忽略。

當key存在但不是有序集類型時,返回錯誤。

語法:ZREM key member [member ...]

 獲取指定分數範圍的元素

語法:ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

 增加某個元素的分數

返回值是更改後的分數

語法:ZINCRBY key increment member

 獲取集合中元素的數量

語法:ZCARD key

 獲得指定分數範圍內的元素個數

語法:ZCOUNT key min max

 按照排名範圍刪除元素

語法:ZREMRANGEBYRANK key start stop

 按照分數範圍刪除元素

語法:ZREMRANGEBYSCORE key min max

 獲取元素的排名

從小到大

語法:ZRANK key member

從大到小

語法:ZREVRANK key member

 

 應用之商品銷售排行榜

需求:根據商品銷售對商品進行排序顯示

思路:定義商品銷售排行榜(sorted set集合),key為items:sellsort,分數為商品小數量。

寫入商品銷售量:

>商品編號1001的銷量是9,商品編號1002的銷量是10

>商品編號1001銷量家1

>商品銷量前10名

 

 通用命令

keys

語法:keys pattern

del

語法:DEL key

exists

作用:確認一個key是否存在

語法:exists key

expire

  Redis在實際使用過程中更多的用作緩存,然後緩存的數據一般都是需要設置生存時間的,即:到期後數據銷毀。

EXPIRE key seconds             設置key的生存時間(單位:秒)key在多少秒後會自動刪除

TTL key                     查看key生於的生存時間

PERSIST key                清除生存時間 

PEXPIRE key milliseconds    生存時間設置單位為:毫秒

例子:
192.168.101.3:7002> set test 1        設置test的值為1
OK
192.168.101.3:7002> get test            獲取test的值
"1"
192.168.101.3:7002> EXPIRE test 5    設置test的生存時間為5秒
(integer) 1
192.168.101.3:7002> TTL test            查看test的生於生成時間還有1秒刪除
(integer) 1
192.168.101.3:7002> TTL test
(integer) -2
192.168.101.3:7002> get test            獲取test的值,已經刪除
(nil)

rename

作用:重命名key

語法:rename oldkey newkey

type

作用:顯示指定key的數據類型

語法:type key

Redis事務

事務介紹

  • Redis的事務是通過MULTI,EXEC,DISCARD和WATCH這四個命令來完成。
  • Redis的單個命令都是原子性的,所以這裡確保事務性的對象是命令集合
  • Redis將命令集合序列化並確保處於一事務的命令集合連續且不被打斷的執行。
  • Redis不支持回滾的操作。

相關命令

  • MULTI

    註:用於標記事務塊的開始

    Redis會將後續的命令逐個放入隊列中,然後使用EXEC命令原子化地執行這個命令序列。

    語法:MULTI

  • EXEC

    在一個事務中執行所有先前放入隊列的命令,然後恢復正常的連接狀態。

    語法:EXEC

  • DISCARD

    清楚所有先前在一個事務中放入隊列的命令,然後恢復正常的連接狀態。

    語法:DISCARD

  • WATCH

    當某個事務需要按條件執行時,就要使用這個命令將給定的鍵設置為受監控狀態

    語法:WATCH key [key ....]

    註:該命令可以實現redis的樂觀鎖

  • UNWATCH

    清除所有先前為一個事務監控的鍵。

    語法:UNWATCH

 

 事務失敗處理

  • Redis語法錯誤(編譯器錯誤)

 

  •  Redis類型錯誤(運行期錯誤)

 

為什麼redis不支持事務回滾?

  1. 大多數事務失敗是因為語法錯誤或者類型錯誤,這兩種錯誤,再開發階段都是可以避免的
  2. Redis為了性能方面就忽略了事務回滾

Redis實現分散式鎖

鎖的處理

  單應用中使用鎖:單線程多線程

    synchronize、Lock

  分散式應用中使用鎖:多進程

分散式鎖的實現方式

  1. 資料庫的樂觀鎖
  2. 給予zookeeper的分散式鎖
  3. 給予redis的分散式鎖

分散式鎖的註意事項

  1. 互斥性:在任意時刻,只有一個客戶端能持有鎖
  2. 同一性:加鎖和解鎖必須是同一個客戶端,客戶端自己不能把別人加的鎖給解了。
  3. 避免死鎖:即使有一個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證後續其他客戶端能加鎖。

實現分散式鎖

獲取鎖

方式一(使用set命令實現)

方式二(使用setnx命令實現)

package com.cyb.redis.utils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class jedisUtils {
    private static String ip = "192.168.31.200";
    private static int port = 6379;
    private static JedisPool pool;
    static {
        pool = new JedisPool(ip, port);
    }
    public static Jedis getJedis() {
        return pool.getResource();
    }
    public static boolean getLock(String lockKey, String requestId, int timeout) {
        //獲取jedis對象,負責和遠程redis伺服器進行連接
        Jedis je=getJedis();
        //參數3:NX和XX
        //參數4:EX和PX
        String result = je.set(lockKey, requestId, "NX", "EX", timeout);
        if (result=="ok") {
            return true;
        }
        return false;
    }

    public static synchronized boolean getLock2(String lockKey, String requestId, int timeout) {
        //獲取jedis對象,負責和遠程redis伺服器進行連接
        Jedis je=getJedis();
        //參數3:NX和XX
        //參數4:EX和PX
        Long result = je.setnx(lockKey, requestId);
        if (result==1) {
            je.expire(lockKey, timeout); //設置有效期
            return true;
        }
        return false;
    }
}

釋放鎖

package com.cyb.redis.utils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class jedisUtils {
    private static String ip = "192.168.31.200";
    private static int port = 6379;
    private static JedisPool pool;
    static {
        pool = new JedisPool(ip, port);
    }
    public static Jedis getJedis() {
        return pool.getResource();
    }
    /**
     * 釋放分散式鎖
     * @param lockKey
     * @param requestId
     */
    public static void releaseLock(String lockKey, String requestId) {
        Jedis je=getJedis();
        if (requestId.equals(je.get(lockKey))) {
            je.del(lockKey);
        }
    }
}

Redis持久化方案

導讀

  Redis是一個記憶體資料庫,為了保證數據的持久性,它提供了兩種持久化方案。

  1. RDB方式(預設)
  2. AOF方式

RDB方式

  RDB是Redis預設採用的持久化方式。

  RDB方式是通過快照(snapshotting)完成的,當符合一定條件時Redis會自動將記憶體中的數據進行快照並持久化到硬碟。

RDB觸發條件

  1. 符合自定義配置的快照規則
  2. 執行save或者bgsave命令
  3. 執行flushall命令
  4. 執行主從複製操作

在redis.conf中設置自定義快照規則

1、RDB持久化條件

  格式:save <seconds> <changes>

示例:

  save 900 1:表示15分鐘(900秒)內至少1個鍵更改則進行快照。

  save 300 10:表示5分鐘(300秒)內至少10個鍵被更改則進行快照。

  save 60 10000:表示1分鐘內至少10000個鍵被更改則進行快照。

2、配置dir指定rdb快照文件的位置

# Note that you must specify a directory here, not a file name.
dir ./

3、配置dbfilename指定rdb快照文件的名稱

# The filename where to dump the DB
dbfilename dump.rdb

說明

  1. Redis啟動後會讀取RDB快照文件,將數據從硬碟載入到記憶體
  2. 根據數據量大小與結構和伺服器性能不同,這個時間也不同。通常將記錄1千萬個字元串類型鍵,大小為1GB的快照文件載入到記憶體中需要花費20-30秒鐘。

快照的實現原理

快照過程

  1. redis使用fork函數複製一份當前進程副本(子進程)
  2. 父進程繼續接受並處理客戶端發來的命令,而子進程開始將記憶體中的數據寫入到硬碟臨時文件
  3. 子進程寫入完所有數據後用該臨時文件替換舊的RDB文件,至此,一次快照操作完成。

註意

  1. redis在進行快照的過程中不會修改RDB文件,只有快照結束後才會將舊的文件替換成新的,也就是說任何時候RDB文件都是完整的。
  2. 這就使得我們可以通過定時備份RDB文件來實現redis資料庫的備份,RDB文件是經過壓縮的二進位文件,占用的空間會小於記憶體中的數據,更加利於傳輸。

RDB優缺點

缺點

  使用RDB方式實現持久化,一旦redis異常退出,就會丟失最後一次快照以後更改的所有數據。這個時候我們就需要根據具體的應用場景,通過組合設置自動快照條件的方式將可能發生的數據損失控制在能夠接受範圍。如果數據相對來說比較重要,希望將損失降到最小,則可以使用AOF方式進行持久化

優點

  RDB可以最大化redis的性能:父進程在保存RDB文件時唯一要做的就是fork出一個字進程,然後這個子進程就會處理接下來的所有保存工作,父進程無需執行任何磁碟I/O操作。同時這個也是一個缺點,如果數據集比較大的時候,fork可能比較耗時,造成伺服器在一段時間內停止處理客戶端的請求。

AOF方式

介紹

  預設情況下Redis沒有開啟AOF(append only file)方式的持久化

  開啟AOF持久化後每執行一條會更改Redis中的數據命令,Redis就會將該命令寫入硬碟中的AOF文件,這一過程顯示會降低Redis的性能,但大部分下這個影響是能夠接受的,另外使用較快的硬碟可以提高AOF的性能

配置redis.conf

設置appendonly參數為yes

appendonly yes

AOF文件的保存位置和RDB文件的位置相同,都是通過dir參數設置的

dir ./

預設的文件名是appendonly.aof,可以通過appendfilename參數修改

appendfilename appendonly.aof

AOF重寫原理(優化AOF文件)

  1. Redis可以在AOF文件體積變得過大時,自動地後臺對AOF進行重寫
  2. 重寫後的新AOF文件包含了恢復當前數據集所需的最小命令集合
  3. 整個重寫操作是絕對安全的,因為Redis在創建新的AOF文件的過程中,會繼續將命令追加到現有的AOF文件裡面,即使重寫過程中發生停機,現有的AOF文件也不會丟失。而一旦新AOF文件創建完畢,Redis就會從舊AOF文件切換到新AOF文件,並開始對新AOF文件進行追加操作。
  4. AOF文件有序地保存了對資料庫執行的所有寫入操作,這些寫入操作以Redis協議的格式保存,因此AOF文件的內容非常容易被人讀懂,對文件進行分析(parse)也很輕鬆。

參數說明

  1. #auto-aof-rewrite-percentage 100:表示當前aof文件大小超過上次aof文件大小的百分之多少的時候會進行重寫。如果之前沒有重寫過,以啟動時aof文件大小為基準。
  2. #auto-aof-rewrite-min-size 64mb:表示限制允許重寫最小aof文件大小,也就是文件大小小於64mb的時候,不需要進行優化

同步磁碟數據

  Redis每次更改數據的時候,aof機制都會將命令記錄到aof文件,但是實際上由於操作系統的緩存機制數據實時寫入到硬碟,而是進入硬碟緩存再通過硬碟緩存機制去刷新到保存文件中

參數說明

  1. appendfsync always:每次執行寫入都會進行同步,這個是最安全但是效率比較低
  2. appendfsync everysec:每一秒執行
  3. appendfsync no:不主動進行同步操作,由於操作系統去執行,這個是最快但是最不安全的方式

AOF文件損壞以後如何修複

  伺服器可能在程式正在對AOF文件進行寫入時停機,如果停機造成AOF文件出錯(corrupt),那麼Redis在重啟時會拒絕載入這個AOF文件,從而確保數據的一致性不會被破壞。

  當發生這種情況時,可以以以下方式來修複出錯的AOF文件:

    1、為現有的AOF文件創建一個備份。

    2、使用Redis附帶的redis-check-aof程式,對原來的AOF文件進行修複。

    3、重啟Redis伺服器,等待伺服器字啊如修複後的AOF文件,併進行數據恢復。

如何選擇RDB和AOF

  1. 一般來說,如果對數據的安全性要求非常高的話,應該同時使用兩種持久化功能。
  2. 如果可以承受數分鐘以內的數據丟失,那麼可以只使用RDB持久化。
  3. 有很多用戶都只使用AOF持久化,但並不推薦這種方式:因為定時生成RDB快照(snapshot)非常便於進行資料庫備份,並且RDB恢複數據集的速度也要比AOF恢復的速度要快
  4. 兩種持久化策略可以同時使用,也可以使用其中一種。如果同時使用的話,那麼Redis啟動時,會優先使用AOF文件來還原數據。

Redis的主從複製

什麼是主從複製

  持久性保證了即使redis服務重啟也不會丟失數據,因為redis服務重啟後將硬碟上持久化的數據恢復到記憶體中,但是當redis伺服器的硬碟損壞了可能導致數據丟失,不過通過redis的主從複製機制舊可以避免這種單點故障,如下圖:

 

 說明:

  1. 主redis中的數據有兩個副本(replication)即從redis1和從redis2,即使一臺redis伺服器宕機其他兩台redis服務也可以繼續提供服務。
  2. 主redis中的數據和從redis上的數據保持實時同步,當主redis寫入數據時通過主從複製機制會複製到兩個從redis服務上。
  3. 只有一個主redis,可以有多個從redis。
  4. 主從複製不會阻塞master,在同步數據時,master可以繼續處理client請求
  5. 一個redis可以即是主從,如下圖:

 

主從配置

主redis配置

  無需特殊配置

從redis配置

  修改從伺服器上的redis.conf文件

# slaveof <masterip> <masterport>
slaveof 192.168.31.200 6379

  上邊的配置說明當前【從伺服器】對應的【主伺服器】的ip是192.168.31.200,埠是6379.

實現原理

  1. slave第一次或者重連到master發送一個SYNC的命令。
  2. master收到SYNC的時候,會做兩件事
    1. 執行bgsave(rdb的快照文件)
    2. master會把新收到的修改命令存入到緩衝區

缺點:沒有辦法對master進行動態選舉

Redis Sentinel哨兵機制

簡介

  Sentinel(哨兵)進程是用於監控redis集群中Master主伺服器工作的狀態,在Master主伺服器發生故障的時候,可以實現Master和Slave伺服器的切換,保證系統的高可用,其已經被集成在redis2.6+的版本中,Redis的哨兵模式到2.8版本之後就穩定了下來。

哨兵進程的作用

  1. 監控(Monitoring):哨兵(Sentinel)會不斷地檢查你的Master和Slave是否運作正常。
  2. 提醒(Notification):當被監控的某個Redis節點出現問題時,哨兵(Sentinel)可以通過API向管理員或者其他應用程式發送通知。
  3. 自動故障遷移(Automatic failover):當一個Master不能正常工作時,哨兵(Sentinel)會開始一次自動故障遷移操作。
    1. 它會將失效Master的其中一個Slave升級為新的Master,並讓失效Master的其他Slave改為複製新的Master;
    2. 當客戶端視圖連接失效的Master時,集群也會向客戶端返回新Master的地址,使得集群可以使用現在的Master替換失效的Master。
    3. Master和Slave伺服器切換後,Master的redis.conf、Slave的redis.conf和sentinel.conf的配置文件的內容都會發生相應的改變,即Master主伺服器的redis.conf配置文件中會多一行Slave的配置,sentinel.conf的監控目標會隨之調換。

哨兵進程的工作方式

  1. 每個Sentinel(哨兵)進程以每秒鐘一次的頻率向整個集群中的Master主伺服器Slave從伺服器以及其他Sentinel(哨兵)進程發送一個PING命令
  2. 如果一個實例(instance)距離最後一次有效回覆PING命令的時間超過down-after-milliseconds選項所指定的值,則這個實例會被Sentinel(哨兵)進程標記為主觀下線(SDOWN)。
  3. 如果一個Master主伺服器被標記為主觀下線(SDOWN),則正在監視這個Master主伺服器的所有Sentinel(哨兵)進程要以每秒一次的頻率確認Master主伺服器確實進入主觀下線狀態
  4. 當有足夠數量的Sentinel(哨兵)進程(大於等於配置文件指定的值)在指定的時間範圍內確認Master主伺服器進入了主觀下線狀態(SDOWN),則Master主伺服器會被標記為客觀下線(ODOWN)
  5. 在一般情況下,每個Sentinel(哨兵)進程會以每10秒一次的頻率向集群中的所有Master主伺服器、Slave從伺服器發送INFO命令。
  6. 當Master主伺服器被Sentinel(哨兵)進程標記為客觀下線(ODOWN)時,Sentinel(哨兵)進程向下線的Master主伺服器的所有Slave從伺服器發送INFO命令的頻率會從10秒一次改為每秒一次。
  7. 若沒有足夠數量的Sentinel(哨兵)進程同意Master主伺服器下線,Master主伺服器的客觀下線狀態就會被移除。若Master主伺服器重新向Sentinel(哨兵)進程發送PING命令返回有效回覆,Master主伺服器的主觀下線狀態就會被移除。

實現

修改從機的sentinel.conf

sentinel monitor mymaster  192.168.127.129 6379 1

啟動哨兵伺服器

redis-sentinel

Redis Cluster集群

redis-cluster架構圖

 

 架構細節

  1. 所有的redis節點彼此互聯(PING-PING機制),內部使用二進位協議優化傳輸速度和帶寬。
  2. 節點的fail是通過集群中超過半數的節點檢測失效時才生效。
  3. 客戶端與redis節點直連,不需要中間proxy層,客戶端不需要連接集群所有節點,連接集群中任何一個可用節點即可。
  4. redis-cluster把所有的物理節點映射到[0-16383]slot上,cluster負責維護node<->slot<->value
    Redis集群中內置了16384個哈希槽,當需要在Redis集群中放置一個key-value時,redis先對key使用crc16演算法算出一個結果,然後把結果對16384求餘數,這樣每個key都會對應一個編號在0-16384之間的哈希槽,redis會根據節點數量大致均等的將哈希槽映射到不同節點。

 

redis-cluster投票:容錯 

 

  1.  集群中所有master參與投票,如果半數以上master節點與其中一個master節點通信超過(cluster-node-timeout),認為該master節點掛掉。
  2. 什麼時候整個集群不可用(cluster_state:fail)?
    1. 如果集群任意master掛掉,且當前master沒有slave,則集群進入fail狀態。也可以理解成集群的[0-16384]slot映射不完全時進入fail狀態。
    2. 如果集群超過半數以上master
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.引入方式不同 query要用path來引入 this.$router.push({ path: 'test', query: { type: 2, detail: '哈哈' } }) params要用name來引入 params要用name來引入 this.$router.push({ name ...
  • var map = {}; map['key1']=value1; map['key2']=value2; map['key3']=value3; map['key4']=value4; map['key5']=value5; 問題解決! ...
  • 對於CSS的white space屬性,我想大部分人應該和我差不多,最常用的就是nowrap屬性,最多用來做超長省略號顯示的時候會用到【hiahiahia~】 起因是這樣的:產品doggie策划了一個元旦活動,活動主頁最下邊需要顯示配置的活動規則,註意,是配置的活動規則,所以,免不了運營小妹要在後臺 ...
  • 為什麼我們要學習Node.js? 1. 認為: Node.js就學習一周,時間比較短,不重要 將來工作我後端又不用Node.js做,我們又java/python/php/c,為什麼要在意它 Node.js接下來項目中又不用,不用好好學,不用在意 2. 重要性? Node.js可以說是對js功能的擴展 ...
  • 1 <input type="date" id="date_info" placeholder="請輸入時間"/> 2 3 ...... 4 5 <script> 6 $(document).ready(function () { 7 var time = new Date(); 8 var day ...
  • 1.側邊欄划出一個信息框 通常滑鼠浮動側邊欄都會划出一個信息框,每個信息框距離側邊欄的距離是相等的,所以處理此問題的方法就是 量取信息框距離側邊欄的距離,信息框設置絕對定位,父元素設置相對定位之後,信息框就會相對於父元素的相對定位進行絕對定位,取父元素的width:100%,然後加上距離信息框距離側 ...
  • 對於更簡單的Ajax調用,XMLHttpRequest是低級別的,更複雜的,並且你需要封裝函數。不幸的是,一旦你開始考慮超時,中止調用和錯誤捕獲的複雜性,Fetch也會如此。 ...
  • 首先用一個例子指出來constructor存在形式。 由上面的代碼我們總結出結論1:上面的代碼在控制台可以看出constructor是指向構造器Fruit的引用。 這個地方就有點奇怪了。這個constructor到底指向的是那個實例的構造器? 根據上面的代碼總結出結論2:constructor指向的 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...