在一臺服務器上部署一個Redis節點,若是機器發生主板損壞,硬盤損壞等問題,不能在短期修復完成,就不能處理Redis操做了,這就是單機可能存在的問題mysql
一樣的,服務器正常運行,可是Redis主進程發生宕機事件,此時只須要重啓Redis就能夠了。若是不考慮在Redis重啓期間的性能損失,能夠考慮Redis的單機部署git
Redis單機部署出現故障時,把Redis遷移到另外一臺服務器上,此時須要把發生故障的Redis中的數據同步到新部署的Redis節點,這也須要很高的成本redis
一臺服務器有16G內存,此時分配12G內存運行Redissql
若是有新需求:Redis須要佔用32G或者64G等更多的內存,此時這臺服務器就不能知足需求了,此時能夠考慮更換一臺更大內存的服務器,也能夠用多臺服務器組成一個Redis集羣來知足這個需求api
根據Redis官方的說法,單臺Redis能夠支持10萬的QPS,若是如今的業務須要100萬的QPS,此時能夠考慮使用Redis分佈式安全
一個Redis節點爲master節點(主節點),負責對外提供服務。服務器
另外一個節點爲slave節點(從節點),負責同步主節點的數據,以達到備份的效果。當主節點發生宕機等故障時,從節點也能夠對外提供服務網絡
以下圖所示數據結構
一個Redis節點爲master節點(主節點),負責對外提供服務。架構
多個節點爲slave節點(從節點)。每一個slave都會對主節點中的數據進行備份,以達到更加高可用的效果。這種狀況下就算master和一個slave同時發生宕機故障,其他的slave仍然能夠對外讀提供服務,並保證數據不會丟失
當master有不少讀寫,達到Redis的極限閥值,可使用多個slave節點對Redis的讀操做進行分流,有效實現流量的分流和負載均衡,因此一主多從也能夠作讀寫分離
master節點負責寫數據,同時客戶端能夠從slave節點讀取數據
對數據提供了多個備份,這些備份數據能夠大大提升Redis的讀性能,是Redis高可用或者分佈式的基礎
取消複製
修改Redis配置文件/etc/redis.conf
slaveof <masterip> <masterport> # masterip爲主節點IP地址,masterport爲主節點端口 slave-read-only yes # 從節點只作讀操做,不作寫操做,保證主從設備數據相同
使用命令行配置無需重啓Redis,能夠實現統一配置 使用配置文件方式配置不變於管理,並且須要重啓Redis
有兩臺虛擬機,操做系統都是CentOS 7.5
一臺虛擬機的IP地址爲192.168.81.100,作master 一臺虛擬機的IP地址爲192.168.81.101,作slave
[root@mysql ~]# vi /etc/redis.conf # 修改Redis配置文件 bind 0.0.0.0 # 能夠從外部鏈接Redis服務端 slaveof 192.168.81.100 6379 # 設置master的IP地址和端口
而後保存修改,啓動Redis
[root@mysql ~]# systemctl stop firewalld # 關閉firewalld防火牆 [root@mysql ~]# systemctl start redis # 啓動slave上的Redis服務端 [root@mysql ~]# ps aux | grep redis-server # 查看redis-server的進程 redis 2319 0.3 0.8 155204 18104 ? Ssl 09:55 0:00 /usr/bin/redis-server 0.0.0.0:6379 root 2335 0.0 0.0 112664 968 pts/2 R+ 09:56 0:00 grep --color=auto redis [root@mysql ~]# redis-cli # 啓動Redis客戶端 127.0.0.1:6379> info replication # 查看Redis的複製信息 查看192.168.81.101機器上的Redis的info # Replication role:slave # 角色爲slave master_host:192.168.81.100 # 主節點IP爲192.168.81.100 master_port:6379 # 主節點端口爲6379 master_link_status:up master_last_io_seconds_ago:5 master_sync_in_progress:0 slave_repl_offset:155 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
[root@localhost ~]# systemctl stop firewalld # 關閉firewalld防火牆 [root@localhost ~]# vi /etc/redis.conf # 修改Redis配置文件 bind 0.0.0.0 而後保存修改,啓動Redis [root@localhost ~]# systemctl start redis # 啓動master上的Redis [root@localhost ~]# ps aux | grep redis-server # 查看redis-server進程 redis 2529 0.2 1.8 155192 18192 ? Ssl 17:55 0:00 /usr/bin/redis-server 0.0.0.0:6379 root 2536 0.0 0.0 112648 960 pts/2 R+ 17:56 0:00 grep --color=auto redis [root@localhost ~]# redis-cli # 啓動master上的redis-cli客戶端 127.0.0.1:6379> info replication # 查看192.168.81.100機器上Redis的信息 # Replication role:master # 角色爲主節點 connected_slaves:1 # 鏈接一個從節點 slave0:ip=192.168.81.101,port=6379,state=online,offset=141,lag=2 # 從節點的信息 master_repl_offset:141 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:140 127.0.0.1:6379> set hello world # 向主節點寫入數據 OK 127.0.0.1:6379> info server # Server redis_version:3.2.10 redis_git_sha1:00000000 redis_git_dirty:0 redis_build_id:c8b45a0ec7dc67c6 redis_mode:standalone os:Linux 3.10.0-514.el7.x86_64 x86_64 arch_bits:64 multiplexing_api:epoll gcc_version:4.8.5 process_id:2529 run_id:7091f874c7c3eeadae873d3e6704e67637d8772b # 注意這個run_id tcp_port:6379 uptime_in_seconds:488 uptime_in_days:0 hz:10 lru_clock:12784741 executable:/usr/bin/redis-server config_file:/etc/redis.conf
127.0.0.1:6379> get hello # 獲取'hello'的值,能夠獲取到 "world" 127.0.0.1:6379> set a b # 向192.168.81.101從節點寫入數據,失敗 (error) READONLY You can't write against a read only slave. 127.0.0.1:6379> slaveof no one # 取消從節點設置 OK 127.0.0.1:6379> info replication # 查看192.168.81.101機器,已經再也不是從節點,而變成主節點了 # Replication role:master # 變成主節點了 connected_slaves:0 master_repl_offset:787 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 127.0.0.1:6379> dbsize # 查看192.168.81.101上Redis全部數據大小 (integer) 2
127.0.0.1:6379> mset a b c d e f # 向192.168.81.100上的Redis集合中寫入數據 OK 127.0.0.1:6379> dbsize # Redis中數據大小爲5 (integer) 5
[root@localhost ~]# tail /var/log/redis/redis.log # 查看Redis最後10行日誌 2529:M 14 Oct 17:55:09.448 * DB loaded from disk: 0.026 seconds 2529:M 14 Oct 17:55:09.448 * The server is now ready to accept connections on port 6379 2529:M 14 Oct 17:55:10.118 * Slave 192.168.81.101:6379 asks for synchronization 2529:M 14 Oct 17:55:10.118 * Partial resynchronization not accepted: Runid mismatch (Client asked for runid '9f93f85bce758b9c48e72d96a182a2966940cf52', my runid is '7091f874c7c3eeadae873d3e6704e67637d8772b') # 與192.168.81.100設備上經過info命令查看到的run_id相同 2529:M 14 Oct 17:55:10.118 * Starting BGSAVE for SYNC with target: disk # 執行BGSAVE命令成功 2529:M 14 Oct 17:55:10.119 * Background saving started by pid 2532 2532:C 14 Oct 17:55:10.158 * DB saved on disk 2532:C 14 Oct 17:55:10.159 * RDB: 12 MB of memory used by copy-on-write 2529:M 14 Oct 17:55:10.254 * Background saving terminated with success 2529:M 14 Oct 17:55:10.256 * Synchronization with slave 192.168.81.101:6379 succeeded # 向192.168.81.101同步數據成功
127.0.0.1:6379> slaveof 192.168.81.100 6379 # 把192.168.81.101從新設置爲192.168.81.100的從節點 OK 127.0.0.1:6379> dbsize (integer) 5 127.0.0.1:6379> mget a 1) "b"
[root@mysql ~]# tail /var/log/redis/redis.log # 查看Redis最後10行日誌 2319:S 14 Oct 09:55:17.625 * MASTER <-> SLAVE sync started 2319:S 14 Oct 09:55:17.625 * Non blocking connect for SYNC fired the event. 2319:S 14 Oct 09:55:17.626 * Master replied to PING, replication can continue... 2319:S 14 Oct 09:55:17.626 * Trying a partial resynchronization (request 9f93f85bce758b9c48e72d96a182a2966940cf52:16). 2319:S 14 Oct 09:55:17.628 * Full resync from master: 7091f874c7c3eeadae873d3e6704e67637d8772b:1 # 從master節點全量複製數據 2319:S 14 Oct 09:55:17.629 * Discarding previously cached master state. 2319:S 14 Oct 09:55:17.763 * MASTER <-> SLAVE sync: receiving 366035 bytes from master # 顯示從master同步的數據大小 2319:S 14 Oct 09:55:17.765 * MASTER <-> SLAVE sync: Flushing old data # slave清空原來的數據 2319:S 14 Oct 09:55:17.779 * MASTER <-> SLAVE sync: Loading DB in memory # 加載同步過來的RDB文件 2319:S 14 Oct 09:55:17.804 * MASTER <-> SLAVE sync: Finished with success
Redis每次啓動時,都有一個隨機ID來標識Redis,這個隨機ID就是上面經過info命令查看獲得的run_id
查看192.168.81.101虛擬機上的run_id和偏移量
[root@localhost ~]# redis-cli info server |grep run_id run_id:7e366f6029d3525177392e98604ceb5195980518 [root@localhost ~]# redis-cli info |grep master_repl_offset master_repl_offset:0
查看192.168.91.100虛擬機上的run_id和偏移量
[root@mysql ~]# redis-cli info server | grep run_id run_id:7091f874c7c3eeadae873d3e6704e67637d8772b [root@mysql ~]# redis-cli info | grep master_repl_offset master_repl_offset:4483
run_id是一個很是重要的標識。
在上面的例子裏,192.168.81.101作爲slave去複製192.168.81.100這個master上的數據,會獲取192.168.81.100機器上對應的run_id在192.168.81.101上作一個標識
當192.168.81.100機器上的Redis的run_id發生改變,意味着192.168.81.100機器上的Redis發生重啓操做或者別的重大變化,192.168.81.101就會把192.168.81.100上的數據所有同步到192.168.81.101上,這就是全量複製的概念
偏移量(offset)就是數據寫入量的字節數。
在192.168.81.100的Redis上寫入數據時,master就會記錄寫了多少數據,並記錄在偏移量中。
在192.168.81.100上的操做,會同步到192.168.81.101機器上,192.168.81.101上的Redis也會記錄偏移量。
當兩臺機器上的偏移量相同時,表明數據同步完成
偏移量是部分複製很重要的依據
查看192.168.81.100機器上Redis的偏移量
127.0.0.1:6379> info replication # 查看複製信息 # Replication role:master connected_slaves:1 slave0:ip=192.168.81.101,port=6379,state=online,offset=8602,lag=0 master_repl_offset:8602 # 此時192.168.81.100上的偏移量是8602 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:8601 127.0.0.1:6379> set k1 v1 # 向192.168.81.100寫入數據 OK 127.0.0.1:6379> set k2 v2 # 向192.168.81.100寫入數據 OK 127.0.0.1:6379> set k3 v3 # 向192.168.81.100寫入數據 OK 127.0.0.1:6379> info replication # 查看複製信息 # Replication role:master connected_slaves:1 slave0:ip=192.168.81.101,port=6379,state=online,offset=8759,lag=1 master_repl_offset:8759 # 寫入數據後192.168.81.100上的偏移量是8759 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:8758
查看192.168.81.101機器上Redis的偏移量
127.0.0.1:6379> info replication # 查看複製信息 # Replication role:slave master_host:192.168.81.100 master_port:6379 master_link_status:up master_last_io_seconds_ago:8 master_sync_in_progress:0 slave_repl_offset:8602 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 # 此時192.168.81.101上的偏移量是8602 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0 127.0.0.1:6379> get k1 "v1" 127.0.0.1:6379> get k2 "v2" 127.0.0.1:6379> get k3 "v3" 127.0.0.1:6379> info replication # 查看複製信息 # Replication role:slave master_host:192.168.81.100 master_port:6379 master_link_status:up master_last_io_seconds_ago:7 master_sync_in_progress:0 slave_repl_offset:8759 # 同步數據後192.168.81.101上的偏移量是8759 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0 repl_backlog_active:0 repl_backlog_size:1048576 repl_backlog_first_byte_offset:0 repl_backlog_histlen:0
若是主從節點上的offset差距太大,說明從節點沒有從主節點同步數據,主從節點之間的鏈接出現問題:好比網絡,阻塞,緩衝區等
若是一個主節點上已經寫入不少數據,此時從節點不只同步已經有的數據,同時同步slave在同步期間master上被寫入的數據(若是在同步期間master被寫入數據),以達到數據徹底同步的目的,這就是Redis的全量複製的功能
Redis的master會把當前的RDB文件同步給slave,在此期間master中寫入的數據會被寫入複製緩衝區(repl_back_buffer)
中,當RDB文件同步到slave完成,master經過偏移量的對比
,把複製緩衝區(repl_back_buffer)
中的數據同步給slave
Redis使用psync
命令進行數據全量複製和部分複製
psync命令有兩個參數:run_id和偏移量
psync命令的步驟:
1.在slave第一次向master同步數據時,不知道master的run_id和offset,使用`psync ? -1`命令向master發起同步請求 2.master接受請求後,知道slave是作全量複製,master就會把run_id和offset響應給slave 3.slave保存master發送過來的run_id和offset 4.master響應slave後,執行BGSAVE命令把當前全部數據生成RDB文件,而後將RDB文件同步給slave 5.Redis中的repl_back_buffer複製緩衝區能夠記錄生成RDB文件以後到同步完成這個時間段時寫入的數據,而後把這些數據也同步給slave 6.slave執行flushall命令清空slave中原有的數據,而後從RDB文件讀取全部的數據,保證slave與master中數據的同步
以下圖所示:
可能的AOF文件重寫的時間:RDB文件加載完成,若是slave節點的AOF功能開啓,則會執行AOF重寫操做,保證AOF文件中保存最新的數據
除了上面提到的開銷,若是master和slave之間的網絡出現問題,則在一段時間內slave上同步的數據就會丟失
解決這個問題的最好辦法就是再作一次全量複製,同步master中全部數據
Redis 2.8版本中添加了部分複製的功能,若是發生master和slave之間的網絡出現問題時,使用部分複製儘量的減小丟失數據的可能,而不用所有複製
當master與slave之間的鏈接斷開時,master在寫入數據同時也會把寫入的數據保存到repl_back_buffer複製緩衝區中
當master與slave之間的網絡連通後,slave會執行psync {offset} {run_id}
命令,offset是slave節點上的偏移量
master接收到slave傳輸的偏移量,會與repl_back_buffer複製緩衝區中的offset作對比,
若是接收到的offset小於repl_back_buffer中記錄的偏移量,master就會把兩個偏移量之間的數據發送給slave,slave同步完成,slave中的數據就與master中的數據一致
以下圖所示
這種架構讀寫分離狀況下,宕機的slave沒法從master中同步數據
Redis的master就沒法提供服務了,只有slave能夠提供數據讀取服務
解決方法:把其中一個slave爲成master,以提供寫入數據功能,另一臺slave從新作爲新的master的從節點,提供讀取數據功能,這種解決方法依然須要手動完成
主從模式沒有實現故障的自動轉移,這就是Redis的sentinel的做用了
讀寫分離:master負責寫入數據,把讀取數據的流量分攤到slave節點
讀寫分離一方面能夠減輕master的壓力,另外一方面又擴展了讀取數據的能力
讀寫分離能夠遇到的問題:
大多數狀況下,master採用異步方式將數據同步給slave,在這個過程當中會有一個時間差
當slave遇到阻塞時,接收數據會有必定延遲,在這個時間段內從slave讀取數據可能會出現數據不一致的狀況
能夠對master和slave的offset值進行監控,當offset值相差過多時,能夠把讀流量轉換到master上,可是這種方式有必定的成本
Redis刪除過時數據的方式
當Redis操做這個數據時,纔會去看這個數據是否過時,若是數據已通過期,會返回一個-2給客戶端,表示查詢的數據已通過期
每隔一個週期,Redis會採集一部分key,看這些key是否過時 若是過時key很是多或者採樣速度慢於key過時速度時,就會有不少過時key沒有被刪除 此時slave會同步包括過時key在內的master上的全部數據 因爲slave沒有刪除數據的權限,此時基於讀寫分離的模式,客戶端會從slave中讀取一些過時的數據,也即髒數據
在圖9中,slave宕機,從slave節點遷移爲master節點的成本很高
在考慮使用讀寫分離以前,首先要考慮優化master節點的問題
Redis的性能很高,能夠知足大部分場景,能夠優化一些內存的配置參數或者AOF的策略,也能夠考慮使用Redis分佈式
第一種狀況是:例如maxmemory
不一致:丟失數據
如master節點分配的內存爲4G,而slave節點分配的內存只有2G時,此時雖然能夠進行正常的主從複製
但當slave從master同步的數據大於2G時,slave不會拋出異常,但會觸發slave節點的maxmemory-policy
策略,對同步的數據進行一部分的淘汰,此時slave中的數據已經不完整了,形成丟失數據的狀況
另外一種主從配置不一致的狀況是:對master節點進行數據結構優化,可是沒有對slave作一樣的優化,會形成master和slave的內存不一致
第一次爲一個master配置一個slave時,slave中沒有任何數據,進行全量複製不可避免
解決方法:主從節點的maxmemory
不要設置過大,則傳輸和加載RDB文件的速度會很快,開銷相對會小一些,也能夠在用戶訪問量比較低時進行全量複製
當master重啓時,master的run_id會發生變化。slave在同步數據時發現以前保存的master的run_id與如今的run_id不匹配,會認爲當前master不安全
解決方法:
作一次全量複製,當master發生故障時,slave轉換爲master提供數據寫入,或者使用Redis哨兵和集羣
Redis4.0版本中提供新的方法:當master的run_id發生改變時,作故障轉移能夠避免作全量複製
複製緩衝區的做用是把新的命令寫入到緩衝區中
複製緩衝區實際是一個隊列,默認大小爲1MB,即複製緩衝區只能保存1MB大小的數據
若是slave與master的網絡斷開,master就會把新寫入的數據保存到複製緩衝區中
當寫入到複製緩衝區內的數據小於1MB時,就能夠作部分複製,避免全量複製的問題 若是新寫入的數據大於1MB時,就只能作全量複製了
在配置文件中修改rel_backlog_size
選項來加大複製緩衝區的大小
,來減小全量複製的狀況出現
主從架構中,master節點重啓時,則master的run_id會發生變化,全部的slave節點都會進行主從複製
master生成RDB文件,而後全部slave節點都會同步RDB文件,在這個過程當中對master節點的CPU,內存,硬盤有很大的開銷,這就是複製風暴
單主節點複製風暴解決方法
更換複製拓樸
單機多部署複製風暴
一臺服務器上的全部節點都是master,若是這臺服務器系統發生重啓,則全部的slave節點都從這臺服務器進行全量複製,會對服務器形成很大的壓力 主節點分散多機器 將master分配到不一樣的服務器上
一個master能夠有多個slave 一個slave還能夠有slave 一個slave只能有一個master 數據流向是單向的,只能從master到slave