01 . Redis簡介及部署主從複製

簡介

Remote Dictionary Server, 翻譯爲遠程字典服務, Redis是一個徹底開源的基於Key-Value的NoSQL存儲系統,他是一個使用ANSIC語言編寫的,遵照BSD協議,支持網絡、可基於內存的可持久化的日誌型、Key-Value數據庫,並提供多種語言的API.node

它一般被稱爲數據結構服務器,由於值(value)能夠是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等類型。python

# Redis架構主要有兩個程序:
# 	Redis客戶端  redis-cli
# 	Redis服務器  redis-server
起源

Redis做者是Salvatore Sanfilippo,來自意大利的西西里島.mysql

1588830623480

2008年他在開發一個LLOOGG的網站時,須要一個高性能的隊列功能,最開始使用MySQL來實現,無奈性能老是提不上去,因此決定本身實現一個專屬於LLOOGG的數據庫,他就是Redis的前身。後臺 Sanfilippoj將 Redis1.0放到Github上大受歡迎。git

BSD協議

Redis基於BSD開源協議,BSD開源協議是一個給於使用者很大自由的協議。能夠自由的使用,修改源代碼,也能夠將修改後的代碼做爲開源或者專有軟件再發布。當你發佈使用了BSD協議的代碼,或者以BSD協議代碼爲基礎作二次開發本身的產品時,須要知足三個條件:程序員

  • 若是再發布的產品中包含源代碼,則在源代碼中必須帶有原來代碼中的BSD協議。
  • 若是再發布的只是二進制類庫/軟件,則須要在類庫/軟件的文檔和版權聲明中包含原來代碼中的BSD協議。
  • 不能夠用開源代碼的做者/機構名字和原來產品的名字作市場推廣。

BSD代碼鼓勵代碼共享,但須要尊重代碼做者的著做權。BSD因爲容許使用者修改從新發布代碼,也容許使用或在BSD代碼上開發商業軟件發佈和銷售,所以是對商業集成很友好的協議。redis

不少的公司企業在選用開源產品的時候都首選BSD協議,由於能夠徹底控制這些第三方的代碼,在必要的時候能夠修改或者 二次開發。sql

Redis原理

命令執行結構數據庫

image.png

# 1. 客戶端發送命令後,Redis服務器將爲這個客戶端連接創造一個'輸入緩存',將命令放到裏面
# 2. 再由Redis服務器進行分配挨個執行,順序是隨機的,這將不會產生併發衝突問題,也就不須要事務了.
# 3. 再將結果返回到客戶端的'輸出緩存'中,輸出緩存先存到'固定緩衝區',若是存滿了,就放入'動態緩衝區',客戶端再得到信息結果

# 若是數據時寫入命令,例如set name:1  zhangsan 方式添加一個字符串.
# Redis將根據策略,將這對key:value來用內部編碼格式存儲,好處是改變內部編碼不會對外有影響,正常操做便可,
# 同時不一樣狀況下存儲格式不同,發揮優點.
爲何須要Redis?

傳統數據庫在存儲數據時,須要先定義schema,以肯定類型(字節寬度),並以行存儲,因此每行所佔的字節寬度是一致的(便於索引數據)。數據庫內部數據分爲多個datapage(通常是4kb)存儲,隨着數據量的增大,數據庫查詢速度會愈來愈慢,其主要瓶頸在於磁盤I/O:vim

# 尋址時間(ms)級別
# 帶寬(G/M)

因爲數據量增大查找datapage的時間也會變長,因此索引出現了。索引是一個B+T,存儲在內存中,根據索引記錄的信息,能夠快速定位到datapage的位置。索引雖然會大大加快查詢速度,可是由於增刪改須要維護索引的B+T,因此會把增刪改的速度拖慢,因此索引不適合頻繁寫的表。api

此外,當數據庫高併發查詢的狀況下,單位時間內所需的數據量也是很大的,此時可能會受到磁盤帶寬的影響,影響磁盤的查詢速度。

在I/O上,內存相比較於磁盤,擁有較好的性能;

# 尋址時間(ns級別,磁盤是其10w倍)
# 帶寬(雙通道DDR400的寬帶爲6.4GBps)

因此,出現了一批基於內存的關係型數據庫,好比SAP HAHA數據庫,其物理機器內存2T,包含軟件以及服務,購買須要1億元,因爲內存關係型數據庫的昂貴价格,因此大部分公司採用了折中的方案,使用磁盤關係型數據庫+內存緩存,好比 Oracle+Memcached,Mysql+Redis

爲何使用Redis?

我的以爲項目中使用redis,主要從兩個角度去考慮性能和併發,固然,redis還具有能夠作分佈式鎖等其餘功能,可是若是隻是爲了分佈式鎖這些其餘功能,徹底還有其餘中間件(如zookeeper等)代替,並非非要使用redis,所以,這個問題主要從性能和併發兩個角度去答.

性能

以下圖所示,我摩恩碰到須要執行耗時特別久,且結果不頻繁變更的SQL,就特別適合將運行結果放入緩存,這樣,後面的請求就去緩存中讀取,使得請求可以迅速響應.

image.png

迅速響應的標準,根據交互效果的不一樣,這個響應時間沒有固定標準。不過曾經有人這麼告訴我:"在理想狀態下,咱們的頁面跳轉須要在瞬間解決,對於頁內操做則須要在剎那間解決。另外,超過一彈指的耗時操做要有進度提示,而且能夠隨時停止或取消,這樣才能給用戶最好的體驗。"

那麼瞬間、剎那、一彈指具體是多少時間呢?

一剎那者爲一念,二十念爲一瞬,二十瞬爲一彈指,二十彈指爲一羅預,二十羅預爲一須臾,一日一晚上有三十須臾。

通過周密的計算,一瞬間爲0.36秒,一剎那有0.018秒,一彈指changda7.2秒

併發

以下圖所示,在大併發的狀況下,全部的請求直接訪問數據庫,數據庫會出現鏈接異常,這個時候,就須要使用redis作一個緩衝操做,讓請求先訪問到redis,而不是直接訪問數據庫.

image.png

單線程的Redis爲何這麼快?
# 1. 基於內存的訪問,非阻塞I/O,Redis使用事件驅動模型epoll多路複用實現,鏈接,讀寫,關閉都轉換爲事件不在網絡I/O上浪費過多的時間.
# 2. 單線程避免高併發的時候,多線程有鎖的問題和線程切換的CPU開銷問題.《雖然是單線程,但能夠開多實例彌補》
# 3. 使用C語言編寫,更好的發揮服務器性能,而且代碼簡介,性能高.
Redis的特色
  • 高性能: Redis將全部數據集存儲在內存中,能夠在入門級Linux機器中每秒寫(SET)11萬次,讀(GET)8.1萬次。Redis支持Pipelining命令,可一次發送多條命令來提升吞吐率,減小通訊延遲。
  • 持久化:當全部數據都存在於內存中時,能夠根據自上次保存以來通過的時間和/或更新次數,使用靈活的策略將更改異步保存在磁盤上。Redis支持僅附加文件(AOF)持久化模式。
  • 數據結構: Redis支持各類類型的數據結構,例如字符串,散列,集合,列表,帶有範圍查詢的有序集,位圖,超級日誌和帶有半徑查詢的地理空間索引。
  • 原子操做:處理不一樣數據類型的Redis操做是原子操做,所以能夠安全地設置或增長鍵,添加和刪除組中的元素,增長計數器等。
  • 支持的語言: Redis支持許多語言,如ActionScript,C,C ++,C#,Clojure,Common Lisp,D,Dart,Erlang,Go,Haskell,Haxe,Io,Java,JavaScript(Node.js),Julia,Lua ,Objective-C,Perl,PHP,Python,R,Ruby,Rust,Scala,Smalltalk和Tcl。
  • 主/從複製: Redis遵循很是簡單快速的主/從複製。配置文件中只須要一行來設置它,而Slave在Amazon EC2實例上完成10 MM key集的初始同步須要21秒。
  • 分片: Redis支持分片。與其餘鍵值存儲同樣,跨多個Redis實例分發數據集很是容易。
  • 可移植: Redis是用ANSI C編寫的,適用於大多數POSIX系統,如Linux,BSD,Mac OS X,Solaris等。
Redis與其餘key-value存儲有什麼不一樣
  • Redis有着更爲複雜的數據結構而且提供對他們的原子性操做,這是一個不一樣於其餘數據庫的進化路徑。Redis的數據類型都是基於基本數據結構的同時對程序員透明,無需進行額外的抽象。
  • Redis運行在內存中可是能夠持久化到磁盤,因此在對不一樣數據集進行高速讀寫時須要權衡內存,由於數據量不能大於硬件內存。在內存數據庫方面的另外一個優勢是,相比在磁盤上相同的複雜的數據結構,在內存中操做起來很是簡單,這樣Redis能夠作不少內部複雜性很強的事情。同時,因RDB和AOF兩種磁盤持久化方式是不適合隨機訪問,因此他們能夠是緊湊的以追加的方式生成。

Redis應用場景

緩存

合理的使用 緩存 可以明顯加快訪問的速度,同時下降數據源的壓力。這也是 Redis 最經常使用的功能。Redis 提供了 鍵值過時時間EXPIRE key seconds)設置,而且也提供了靈活控制 最大內存內存溢出 後的 淘汰策略

排行榜

每一個網站都有本身的排行榜,例如按照 熱度排名 的排行榜,發佈時間 的排行榜,答題排行榜 等等。Redis 提供了 列表list)和 有序集合zset)數據結構,合理的使用這些數據結構,能夠很方便的構建各類排行榜系統。

計數器

計數器 在網站應用中很是重要。例如:點贊數1瀏覽數1。還有經常使用的 限流操做,限制每一個用戶每秒 訪問系統的次數 等等。Redis 支持 計數功能INCR key),並且計數的 性能 也很是好,計數的同時也能夠設置 超時時間,這樣就能夠 實現限流

社交網絡

贊/踩,粉絲,共同好友/喜愛,推送,下拉刷新等是社交網站必備的功能。因爲社交網站 訪問量一般比較大,並且 傳統的數據庫 不太適合保存這類數據,Redis 提供的 數據結構 能夠相對比較容易實現這些功能。

消息隊列

Redis 提供的 發佈訂閱PUB/SUB)和 阻塞隊列 的功能,雖然和專業的消息隊列比,還 不夠強大,但對於通常的消息隊列功能基本知足。

Redis五種數據類型應用場景

此時只作介紹,數據類型具體介紹請看後面

  • 對於string 數據類型,常規的set/get操做,由於string 類型是二進制安全的,能夠用來存放圖片,視頻等內容,另外因爲Redis的高性能讀寫功能,而string類型的value也能夠是數字,通常作一些複雜的計數功能的緩存,還能夠用做計數器(INCR,DECR),好比分佈式環境中統計系統的在線人數,秒殺等。
  • 對於 hash 數據類型,value 存放的是鍵值對結構化後的對象,比較方便操做其中某個字段,好比能夠作單點登陸存放用戶信息,以cookiele做爲key,設置30分鐘爲緩存過時時間,能很好的模擬出相似session的效果.
  • 對於 list 數據類型,能夠實現簡單的消息隊列,另外能夠利用lrange命令,作基於redis的分頁功能,性能極佳,用戶體驗好.
  • 對於 set 數據類型,因爲底層是字典實現的,查找元素特別快,另外set 數據類型不容許重複,利用這兩個特性咱們能夠進行全局去重,好比在用戶註冊模塊,判斷用戶名是否註冊;另外就是利用交集、並集、差集等操做,能夠計算共同喜愛,所有的喜愛,本身獨有的喜愛等功能。
  • 對於 zset 數據類型,有序的集合,能夠作範圍查找,排行榜應用,取 TOP N 操做等,還能夠作延時任務.

Redis事務

事務表示

一組動做,要麼所有執行,要麼所有不執行,例如在社交網站上用戶A關注了
用戶B, 那麼須要在用戶A的關注表中加入用戶B, 而且在用戶B的粉絲表中
添加用戶A, 這兩個行爲要麼所有執行, 要麼所有不執行, 不然會出現數據
不一致的狀況。

Redis 提供了簡單的事務功能, 將一組須要一塊兒執行的命令放到 multi
exec 兩個命令之間。 multi 命令表明事務開始, exec 命令表明事務結束, 它們
之間的命令是原子順序執行的, 例以下面操做實現了上述用戶關注問題。

之間執行命令將不執行,在緩衝中,等exec後裁真正開始執行

若是其中有語法錯誤,命令打錯了,那整個事務將結束.
若是把值寫錯了,多個字母,但語法正確,那事務是正確的,要手動恢復,不支持回滾.
在事務開始前,用watch key能夠檢要操做的key,若是key在事務開始後有變化,例如multi開始修改時,這個key被其餘客戶端修改,事務將不進行操做.

一個Redis從開始到執行會經歷如下階段

# 1. 事務開始
# 2. 命令入隊
# 3. 執行事務
Redis事務相關命令
命令 描述
DISCARD 取消事物,放棄執行事物內的全部命令
EXEC 執行全部事物塊內的命令 EXEC{執行事務}
MULTI 標誌一個事務塊的開始 MULTI{啓動一個事務}
UNWATCH 取消WATCH命令多全部key的監視

| WAHCH key [key …] | 監視一個(或多個)key,若是在事務執行以前這個(或這些) key 被其餘命令所改動,那麼事務將被打斷。

Redis事務與Mysql事務區別
# 1. Redis不支持回滾,即一條命令當作事務執行時,當有一箇中間的命令發生錯誤,mysql將會把以前的操做取消並結束事務.

# 2. 可是Redis不會,Redis還會繼續把剩下的命令執行下去,忽略發生錯誤的命令.

Redis的過時策略以及內存淘汰機制

好比Redis只能存5G數據,但是你寫了10G,那會刪5G的數據。怎麼刪的,這個問題思考過麼?還有,你的數據已經設置了過時時間,可是時間到了,內存佔用率仍是比較高,有思考過緣由麼?

Redis採用的是按期刪除 + 惰性刪除策略

爲何不用定時刪除策略?

定時刪除,用一個定時器來負責監視key,過時則自動刪除,雖然內存及時釋放,可是十分消耗CPU資源,大併發狀況下,CPU要將時間應用在處理請求,而不是刪除key,所以沒有采用這一策略.

按期刪除+惰性刪除是如何工做的?

按期刪除,redis默認每一個100ms檢查,是否有過時的key,有過時key則刪除。須要說明的是,redis不是每一個100ms將全部的key檢查一次,而是隨機抽取進行檢查(若是每隔100ms,所有key進行檢查,redis豈不是卡死)。所以,若是隻採用按期刪除策略,會致使不少key到時間沒有刪除。

因而,惰性刪除派上用場,也就是說你獲取某個key的時候,redis會檢查一下,這個key若是設置了過時時間那麼是否過時了?若是過時了此時就會刪除.

採用按期刪除+惰性刪除就沒其餘問題了嗎?
並非,若是按期刪除刪除沒刪除key。而後你也沒即時去請求key,也就是說惰性刪除也沒生效。這樣,redis的內存會愈來愈高。那麼就應該採用內存淘汰機制.
在redis.conf中有一行配置

# maxmemory-policy volatile-lru

#該配置就是配內存淘汰策略的
# 1. noeviction: 當內存不足以容納寫入數據時,新寫入會報錯,應該沒人用.
# 2. allkeys-lru: 當內存不足以容納新寫入數據時,在鍵空間中,移除最少使用的那個key.推薦使用
# 3. allkeys-random: 當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key,應該也沒人用,不刪最少使用,隨機刪?
# 4. volatile-lru: 當內存不足以容納寫入數據時,在設置了過時時間的鍵空間中,移除最少使用的key,
# 通常是吧redis既當緩存又當持久化存儲才用,不推薦.
# 5. volatile-random: 當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,隨機移除某個key,依然不推薦.
# 6. volatile-ttl: 當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,有更早過時的key優先移除,不推薦.

# 若是沒有設置expire的key,不知足先決條件(prerequisites);
# 那麼volatile-lru,volatile-random和volatile-ttl策略行爲和noeviction(不刪)基本一致

Redis單機部署

此篇文章只作單機服務器搭建,高可用哨兵和集羣請看下一篇

環境
[Redis-Server]
	主機名 = redis-master-1
	系統 = CentOS7.6.1810
	地址 = 121.36.43.223
	軟件 = redis-4.0.14

# 版本
# Redis的奇數版本爲非穩定版本,例如2.7.3.1,若是爲偶數則爲穩定版本,例如3.2,3.4;
節點名 IP 軟件版本 硬件 網絡 說明
redis-master 192.168.171.136 list 裏面都有 2C4G Nat,內網 測試環境
下載解壓Redis源碼包
yum -y install gcc
wget http://download.redis.io/releases/redis-4.0.14.tar.gz
tar xvf redis-4.0.14.tar.gz -C /opt/
cd /opt/redis-4.0.14
編譯安裝
# Redis的編譯,只將命令文件編譯,將會在當前目錄生成bin目錄
make && make install  PREFIX=/usr/local/redis
cd ..
mv redis-4.0.14 /usr/local/redis

# 建立環境變量
echo 'PATH=$PATH:/usr/local/redis/src/' >> /etc/profile
source /etc/profile

# 此時在任何目錄位置均可以是用redis-server等相關命令
[root@redis1 ~]# redis-
redis-benchmark  redis-check-rdb  redis-sentinel   redis-trib.rb    
redis-check-aof  redis-cli        redis-server
Redis可執行文件
可執行文件 做用
redis-server 啓動redis服務
redis-cli redis 命令行客戶端
redis-benchmark Redis基準測試工具
redis-check-aof redis AOF持久化文件檢測和修復工具
redis-check-dump redis RDB持久化文件檢測和修復工具
redis-sentinel 啓動redis sentinel
修改配置文件
# redis進程是否以守護進程的方式運行,yes爲是,no爲否(不以守護進程的方式運行會佔用一個終端)
daemonize no


# 指定redis進程的PID文件存放位置
pidfile /var/run/redis.pid


# redis進程的端口號
port 6379


# 綁定的主機地址
bind 127.0.0.1


# 客戶端閒置多長時間後關閉鏈接,默認此參數爲0即關閉此功能
timeout 300


# redis日誌級別,可用的級別有debug.verbose.notice.warning
loglevel verbose


# log文件輸出位置,若是進程以守護進程的方式運行,此處又將輸出文件設置爲stdout的話,就會將日誌信息輸出到/dev/null裏面去了
logfile stdout


# 設置數據庫的數量,默認爲0可使用select <dbid>命令在鏈接上指定數據庫id
databases 16


# 指定在多少時間內刷新次數達到多少的時候會將數據同步到數據文件
save <seconds> <changes>


# 指定存儲至本地數據庫時是否壓縮文件,默認爲yes即啓用存儲
rdbcompression yes


# 指定本地數據庫文件名
dbfilename dump.db


# 指定本地數據問就按存放位置
dir ./


# 指定當本機爲slave服務時,設置master服務的IP地址及端口,在redis啓動的時候他會自動跟master進行數據同步
slaveof <masterip> <masterport>


# 當master設置了密碼保護時,slave服務鏈接master的密碼
masterauth <master-password>


# 設置redis鏈接密碼,若是配置了鏈接密碼,客戶端在鏈接redis是須要經過AUTH<password>命令提供密碼,默認關閉
requirepass footbared


# 設置同一時間最大客戶鏈接數,默認無限制。redis能夠同時鏈接的客戶端數爲redis程序能夠打開的最大文件描述符,若是設置 maxclients 0,表示不做限制。當客戶端鏈接數到達限制時,Redis會關閉新的鏈接並向客戶端返回 max number of clients reached 錯誤信息
maxclients 128


# 指定Redis最大內存限制,Redis在啓動時會把數據加載到內存中,達到最大內存後,Redis會先嚐試清除已到期或即將到期的Key。當此方法處理後,仍然到達最大內存設置,將沒法再進行寫入操做,但仍然能夠進行讀取操做。Redis新的vm機制,會把Key存放內存,Value會存放在swap區
maxmemory<bytes>


# 指定是否在每次更新操做後進行日誌記錄,Redis在默認狀況下是異步的把數據寫入磁盤,若是不開啓,可能會在斷電時致使一段時間內的數據丟失。由於redis自己同步數據文件是按上面save條件來同步的,因此有的數據會在一段時間內只存在於內存中。默認爲no。
appendonly no


# 指定跟新日誌文件名默認爲appendonly.aof
appendfilename appendonly.aof


# 指定更新日誌的條件,有三個可選參數 - no:表示等操做系統進行數據緩存同步到磁盤(快),always:表示每次更新操做後手動調用fsync()將數據寫到磁盤(慢,安全), everysec:表示每秒同步一次(折衷,默認值);
appendfsync everysec

咱們須要修改的配置

# 設置後臺啓動
# 因爲Redis默認是前臺啓動,不建議使用.能夠修改成後臺
daemonize yes


# 禁止protected-mode yes/no(保護模式,是否只容許本地訪問)
protected-mode


# 設置遠程訪問
# Redis默認只容許本機訪問,把bind修改成bind 0.0.0.0 此設置會變成容許全部遠程訪問,若是指定限制訪問,可設置對應IP。
# bind指定是redis所在服務器網卡的IP,不指定本機網卡IP,可能致使你的Redis實例沒法啓動
# 若是想限制IP訪問,內網的話經過網絡接口(網卡限定),讓客戶端訪問固定網卡連接redis
# 若是是公網,經過iptables指定某個IP容許訪問
bind 0.0.0.0

# 配置Redis日誌記錄
# 找到logfile,默認爲logfile "",改成自定義日誌格式
logfile  /var/log/redis_6379.log

# 把requirepass修改成123456,修改以後重啓下服務
requirepass "123456"
# 不重啓Redis設置密碼
# 在配置文件中配置requirepass的密碼(當Redis重啓時密碼依然生效)
127.0.0.1:6379> config set requirepass test123
# 查詢密碼
127.0.0.1:6379> config get requirepass
1) "requirepass"
2) "test123"

# 密碼驗證
127.0.0.1:6379> auth test123
OK
127.0.0.1:6379> set name flying
OK
127.0.0.1:6379> get name
"flying"

# 遠程主機鏈接
# redis-cli  -h  redis_ip -p redis_port -a password
啓動測試
# 放到後臺輸出,redis自帶日誌了,能夠輸出到黑洞
nohup redis-server /usr/local/redis/redis.conf &> /usr/local/redis/redis.log &

# 關閉命令
redis-cli -h 127.0.0.1 -p 6379 -a 123456 shutdown
# 注意:不建議使用 kill -9,這種方式不但不會作持久化操做,還會形成緩衝區等資源不能優雅關閉。極端狀況下形成 AOF 和 複製丟失數據 的狀況。
# shutdown 還有一個參數,表明是否在關閉 redis 前,生成 持久化文件,命令爲 redis-cli shutdown nosave|save。


# 設置開機自啓動
echo "redis-server /usr/local/redis.conf" >> /etc/rc.local
註冊Redis爲系統服務

在/etc/init.d目錄添加Redis服務的啓動,暫停和重啓腳本

vim /etc/init.d/redis
#!/usr/bin/env bash
# chkconfig: 2345 10 90  
# description: Start and Stop redis 
PORT=6379
EXEC=/usr/local/redis/src/redis-server
CLIEXEC=/usr/local/redis/src/redis-cli

PIDFILE=/var/run/redis_${PORT}.pid
CONF="/etc/redis/${PORT}.conf"

case "$1" in
    start)
        if [ -f $PIDFILE ]
        then
                echo "$PIDFILE exists, process is already running or crashed"
        else
                echo "Starting Redis server..."
                $EXEC $CONF &>/dev/null &
        fi
        ;;
    stop)
	PID=$(cat $PIDFILE)
        if [ ! -f $PIDFILE ]
        then
                echo "$PIDFILE does not exist, process is not running"
        else
                echo "Stopping ..."
                $CLIEXEC -p $PORT shutdown
                while [ -d /proc/${PID} ]
                do
                    echo "Waiting for Redis to shutdown ..."
                    sleep 1
                done
                echo "Redis stopped"
        fi
        ;;
    restart)
        "$0" stop
        sleep 3
        "$0" start
        ;;
    *)
        echo "Please use start or stop as first argument"
        ;;
esac



chmod +x /etc/init.d/redis  
mkdir /etc/redis  
cp /usr/local/redis/redis.conf /etc/redis/6379.conf  
chkconfig --add redis  
chkconfig redis on  
  
service redis start  
service redis restart

Redis經常使用命令

Redis-value操做

此處Redis五種數據類型具體操做先不演示,只作一個簡單的key操做,具體操做等Python操做Redis再寫詳細,前面主要以運維爲主

# 添加一個key[name]爲youmen
127.0.0.1:6379> set name youmen
OK

# 獲取key
127.0.0.1:6379> get name
"youmen"

# 查看當前數據庫裏面的key
127.0.0.1:6379> keys *
1) "name"

# 判斷key是否存在
127.0.0.1:6379> exists name
(integer) 1

# 刪除key值,也就刪除了這條數據
127.0.0.1:6379> del name
(integer) 1
# 查找不到對應的key返回值就會使(integer)0
127.0.0.1:6379> exists name
(integer) 0



# 設置key的過時時間,過時後key自動刪除
127.0.0.1:6379> set name youmen
OK
127.0.0.1:6379> set name youmen ex 2
127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> ttl name
(integer) 6
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> exists name
(integer) 0
持久化命令
# save:將數據同步保存到磁盤
# bgsave:將數據異步保存到磁盤
# lastsave:返回上次成功將數據保存到磁盤的Unix時戳
# shundown:將數據同步保存到磁盤,而後關閉服務
遠程服務控制
# info:提供服務器的信息和統計
# info clients:  查看客戶端信息.
# monitor:實時轉儲收到的請求
# slaveof:改變複製策略設置
# config:在運行時配置Redis服務器
# client list: 查看連接的客戶端有哪些
# chient kill 127.0.0.1:50390:  殺掉客戶端連接
# config get dir: 查看存儲文件目錄
# config get *:  查看全部配置
# config set requirepass 123 : 修改配置,即時生效
# config get bind :  查看配置文件中的監聽地址
Redis info參數介紹
# 集羣每一個節點獲取的信息不同
127.0.0.1:6379> info
# Server
redis_version:4.0.14
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:5ad4d17d599d7e92
redis_mode:standalone			# 運行模式,單機或集羣
os:Linux 3.10.0-1062.1.1.el7.x86_64 x86_64	# 服務器的宿主操做系統
arch_bits:64					# 架構
multiplexing_api:epoll			# redis所使用的事件循環機制
atomicvar_api:atomic-builtin	# 原子處理api
gcc_version:4.8.5				# 編譯Redis時所使用的GCC版本
process_id:19955				# 服務器進程的PID
run_id:3cd8b85e4c852fc93adbbb51eaee051a0a6a788d	# 標識redis server的隨機值
tcp_port:6379
uptime_in_seconds:4272			# redis server啓動的時間(單位s)
uptime_in_days:0				# redis server啓動的時間(單位d)
hz:10								
# hz:10  edis內部調度(進行關閉timeout的客戶端,刪除過時key等)頻率,程序規定serverCron每秒運行十次.
lru_clock:1935465						
# 自增的時鐘,用於LRU管理,該時鐘ms(hz=10,所以每1000ms/10=100ms執行一次定時任務)更新一次
executable:/usr/local/redis/src/redis-server	# 執行文件
config_file:/etc/redis/6379.conf  # 配置文件路徑
# Clients
connected_clients:1				# 已鏈接客戶端的數量(不包括經過從屬服務器鏈接的客戶端)
client_longest_output_list:0			# 當前鏈接的客戶端中,最長的輸出列表
client_biggest_input_buf:0			# 當前鏈接的客戶端中,最大輸入緩存
blocked_clients:0				# 正在等待阻塞命令(BLPOP、BRPOP、Brpoplpush)的客戶端的數量

# Memory
used_memory:849400				# 由Redis分配器分配的內存總量,以字節(byte)爲單位
used_memory_human:829.49K			# 以人類可讀的格式返回Redis分配的內存總量
used_memory_rss:8278016				
# 從操做系統的角度,返回Redis已分配的內存總量(俗稱常駐集大小),這個值和top,ps等命令輸出一致
used_memory_rss_human:7.89M			
# 以人類可讀的格式,從操做系統角度,返回Redis已分配的內存總量(俗稱常駐集大小),這個值和top,ps等命令輸出一致

used_memory_peak:849472				# redis的內存消耗峯值(以字節爲單位)
used_memory_peak_human:829.56K			# 以人類可讀的格式返回redis的內存消耗峯值
used_memory_peak_perc:99.99%			# (used_memory/ used_memory_peak) *100%

used_memory_overhead:836622
# Redis爲了維護數據集的內部機制所需的內存開銷,
# 包括全部客戶端輸出緩衝區、查詢緩衝區、AOF重寫緩衝區和主從複製的backlog

used_memory_startup:786608			# Redis服務器啓動時消耗的內存
used_memory_dataset:12778			# used_memory—used_memory_overhead
used_memory_dataset_perc:20.35%			
# 100%*(used_memory_dataset/(used_memory—used_memory_startup))

total_system_memory:1926860800			# 整個系統內存
total_system_memory_human:1.79G			# 以人類可讀格式,顯示整個系統內存
used_memory_lua:37888				# lua腳本存儲佔用的內存
used_memory_lua_human:37.00K			# 以人類可讀的格式,顯示lua腳本佔用的內存
maxmemory:0					# Redis實例的最大內存配置
maxmemory_human:0B				# 以人類可讀格式,顯示Redis實例的最大內存配置
maxmemory_policy:noeviction			# 當達到maxmemory時的淘汰策略
mem_fragmentation_ratio:9.74			# used_memory_rss/used_memory
mem_allocator:jemalloc-4.0.3			# 內存分配器
active_defrag_running:0				# 表示沒有活動的defrag任務正在運行(defrag: 表示內存碎片整理)
lazyfree_pending_objects:0			# 0表示不存在延遲釋放(也有資料翻譯末惰性刪除)的掛起對象
# Persistence
loading:0					# 服務器是否正在載入持久化文件
rdb_changes_since_last_save:0			
# 離最近一次成功生成rdb文件,寫入命令的個數,即有多少個寫入命令沒有持久化

rdb_bgsave_in_progress:0			# 服務器是否正在建立rdb文件
rdb_last_save_time:1578992739			
# 離最近一次成功建立rdb文件的時間戳,當前時間戳-rdb_last_save_time=多少秒未成功生成rdb文件
rdb_last_bgsave_status:ok			# 最近一次rdb持久化是否成功
rdb_last_bgsave_time_sec:0			# 最近一次成功生成rdb文件耗時總數
rdb_current_bgsave_time_sec:-1		
# 若是服務器正在建立rdb文件,那麼這個域記錄的就是當前建立操做已經耗費的秒數
rdb_last_cow_size:6537216			 
# RDB過程當中父進程與子進程相比執行了多少修改(包括讀緩衝區,寫緩衝區,數據修改等)
aof_enabled:0					# 是否開啓了aof
aof_rewrite_in_progress:0			# 標識aof的rewrite操做是否正在進行中
aof_rewrite_scheduled:0			
# rewrite任務計劃,當客戶端發送bgrewriteaof指令,若是當前rewrite子進程正在執行,
# 那麼將客戶端請求的bgrewriteaof變爲計劃任務,待aof子進程結束後執行rewrite
aof_last_rewrite_time_sec:-1			# 最近一次aof rewrite耗費的時長
aof_current_rewrite_time_sec:-1			# 若是rewrite操做正在進行,則記錄所使用的時間,單位秒
aof_last_bgrewrite_status:ok			# 上次bgrewriteaof操做的狀態
aof_last_write_status:ok			# 上次aof寫入狀態
aof_last_cow_size:0				
# AOF過程當中父進程與子進程相比執行了多少次修改(包括讀緩衝區,寫緩衝區,數據修改等)

# Stats
total_connections_received:4
# 新建立鏈接個數,若是新建立鏈接過多,過分地建立和銷燬鏈接對性能有影響,
# 說明短鏈接嚴重或鏈接池使用有問題,需調研代碼的鏈接設置
total_commands_processed:87			# Redis處理的命令數
instantaneous_ops_per_sec:0			# redis當前的qps,redis內部較實時的每秒執行的命令數
total_net_input_bytes:2757			# redis網絡入口流量字節數
total_net_output_bytes:53214			# redis網絡出口流量字節數
instantaneous_input_kbps:0.00			# redis網絡入口kps
instantaneous_output_kbps:0.00			# redis網絡入口kps
rejected_connections:0				
# 拒絕的鏈接的個數,redis鏈接個數達到maxclients限制,拒絕新鏈接個數
sync_full:0					# 主從徹底同步成功次數
sync_partial_ok:0				# 主從部分同步成功次數
sync_partial_err:0				# 主從部分失敗次數
expired_keys:1					# 運行以來過時的key的數量
expired_stale_perc:0.00				# 過時的比率
expired_time_cap_reached_count:0		# 過時計數
evicted_keys:0					# 運行以來剔除(超過maxmemory後)的key的數量
keyspace_hits:26				# 命中次數
keyspace_misses:10				# 未命中次數
pubsub_channels:0				# 當前使用中的頻道數量
pubsub_patterns:0				# 當前使用的模式的數量
latest_fork_usec:578				# 最近一次fork操做阻塞redis進程的耗時數,單位微妙
migrate_cached_sockets:0			# 是否已經緩存到了改地址的連接
slave_expires_tracked_keys:0			# 從實例到期key的數量
active_defrag_hits:0				# 主動碎片整理命中次數
active_defrag_misses:0				# 主動碎片整理未命中次數
active_defrag_key_hits:0			# 主動碎片整理key命中次數
active_defrag_key_misses:0			# 主動碎片整理key未命中次數.
# Replication
role:master					# 實例的角色,是master or slave
connected_slaves:0				# 鏈接的slave實例個數
master_replid:54da017499c5257de9b00d168dd49c04b8bbe7ef	# 主實例啓動隨機字符串
master_replid2:0000000000000000000000000000000000000000	# 主實例啓動隨機字符串
master_repl_offset:0			
# 主從同步偏移量,此值若是和上面的offset相同說明主從一致沒延遲,與master_replid可被用來標識主實例複製流的位置.
second_repl_offset:-1 				# 主從同步偏移量2,此值若是和上面的offset相同說明主從一致沒延遲
repl_backlog_active:0				# 複製積壓緩衝區是否開啓
repl_backlog_size:1048576			# 複製積壓緩衝大小
repl_backlog_first_byte_offset:0		# 複製緩衝區偏移量的大小
repl_backlog_histlen:0
# 此值等於 master_repl_offset - repl_backlog_first_byte_offset,該值不會超過repl_backlog_size的大小

# CPU
used_cpu_sys:1.90				# 將全部redis主進程在覈心態所佔用的CPU時求和累積起來
used_cpu_user:1.14				# 將全部redis主進程在用戶態所佔用CPU時求和累計起來
used_cpu_sys_children:0.01		# 將後臺進程在覈心態所佔用的CPU時求和累計起來
used_cpu_user_children:0.00		# 將後臺進程在用戶態所佔用CPU時求和累計起來

# Cluster
cluster_enabled:0

# Keyspace
db0:keys=7,expires=0,avg_ttl=0

Redis主從複製

Redis主從複製原理
# 主從複製,是指將一臺Redis服務器的數據,複製到其餘的Redis服務器。前者稱爲主節點(master),後者稱爲從節點(slave);數據的複製是單向的,只能由主節點到從節點。

# 默認狀況下,每臺Redis服務器都是主節點;且一個主節點能夠有多個從節點(或沒有從節點),但一個從節點只能有一個主節點。

1 . redis的複製功能是支持多個數據庫之間的數據同步。一類是主數據庫(master)一類是從數據庫(slave),主數據庫能夠進行讀寫操做,當發生寫操做的時候自動將數據同步到從數據庫,而從數據庫通常是隻讀的,並接收主數據庫同步過來的數據,一個主數據庫能夠有多個從數據庫,而一個從數據庫只能有一個主數據庫。

2 . 經過redis的複製功能能夠很好的實現數據庫的讀寫分離,提升服務器的負載能力。主數據庫主要進行寫操做,而從數據庫負責讀操做。

Redis主從複製過程

img

過程

# 1:當一個從數據庫啓動時,會向主數據庫發送sync命令,

# 2:主數據庫接收到sync命令後會開始在後臺保存快照(執行rdb操做),並將保存期間接收到的命令緩存起來

# 3:當快照完成後,redis會將快照文件和全部緩存的命令發送給從數據庫。

# 4:從數據庫收到後,會載入快照文件並執行收到的緩存的命令。

 

# 注意:redis2.8以前的版本:當主從數據庫同步的時候從數據庫由於網絡緣由斷開重連後會從新執行上述操做,不支持斷點續傳。

# redis2.8以後支持斷點續傳。

詳細過程

在客戶端輸入創建主從關係的命令以後,創建過程分爲3階段:鏈接創建階段(即準備階段)、數據同步階段、命令傳播階段;

階 段 一 、鏈接創建階段
   [該階段的主要做用是在主從節點之間創建鏈接,爲數據同步作好準備。]
   --> 步驟1:保存主節點信息
         從節點服務器內部維護masterhost和masterport字段,用於存儲主節點的ip和port信息。
         slaveof是異步命令,從節點完成主節點ip和port的保存後,當即回覆客戶端ok信息,實際複製纔剛開始;
   --> 步驟2:創建socket鏈接   
         從節點每秒1次調用複製定時函數replicationCron(),若是發現指定主節點能夠鏈接,便會根據主節點的ip和port,建立socket鏈接。
         若是鏈接成功,則:
            從節點:爲該socket創建一個專門處理複製工做的文件事件處理器,負責後續的複製工做,如接收RDB文件、接收命令傳播等。
            主節點:接收到從節點的socket鏈接後(即accept以後),爲該socket建立相應的客戶端狀態,並將從節點看作是鏈接到主節點的一個客戶端。[後面的數據同步操做會以從節點向主節點發送命令請求的形式來進行]
   --> 步驟3:發送ping命令檢查連接    
         從節點成爲主節點的客戶端以後,發送ping命令進行首次請求,目的是檢查socket鏈接是否可用,以及主節點當前是否可以處理請求。
         從節點發送ping命令後,可能出現3種狀況:
             (1)返回pong:說明socket鏈接正常,且主節點當前能夠處理請求,複製過程繼續。
             (2)超時:必定時間後從節點仍未收到主節點的回覆,說明socket鏈接不可用,則從節點斷開socket鏈接,並重連。
             (3)返回pong之外的結果:若是主節點返回其餘結果,如正在處理超時運行的腳本,說明主節點當前沒法處理命令,則從節點斷開socket鏈接,並重連。[socket暫時不可用]
   --> 步驟4:身份驗證 
       [若是從節點設置masterauth選項,則從節點須要向主節點提交身份驗證;]
         · 從節點向主節點發送auth命令,auth命令的參數即爲配置文件中的masterauth的值。
         · 若是主節點設置密碼的狀態,與從節點masterauth的狀態一致(一致是指都存在,且密碼相同,或者都不存在),則身份驗證經過,複製過程繼續;若是不一致,則從節點斷開socket鏈接,並重連。
   --> 步驟5:從向主發送自身端口信息
       該端口信息除了在主節點中執行info Replication時顯示之外,沒有其餘做用


階 段 二 、數據同步階段[核心階段]
 【該階段能夠理解爲從節點數據的初始化。】
  過程:從節點向主節點發送psync命令(Redis2.8之前是sync命令),開始同步。
      根據主從節點當前狀態的不一樣,能夠分爲全量複製和部分複製。
            [全量複製和部分複製原理見對應筆記]
  注意:數據同步階段以前,從是主的客戶端,主不是從的客戶端;而到了這一階段及之後,主從互爲客戶端。
        緣由在於:在此以前,主節點只須要響應從節點的請求便可,不須要主動發請求,而在數據同步階段和後面的命令傳播階段,主節點須要主動向從節點發送請求(如推送緩衝區中的寫命令),才能完成複製。


階 段 三 、命令傳播階段
【在這個階段主節點將本身緩衝區內的寫命令發送給從節點,從節點接收命令並執行,從而保證主從節點數據的一致性。】
   此階段是最終階段,主從節點開始維持心跳機制:PING和REPLCONF ACK
   --> 此階段問題:一致性延遲;
       命令傳播是異步的過程,即主節點發送寫命令後並不會等待從節點的回覆;
       數據不一致的程度,與主從節點之間的網絡情況、主節點寫命令的執行頻率、以及主節點中的repl-disable-tcp-nodelay配置等有關。
        repl-disable-tcp-nodelay no:    
          默認no,即不開啓延遲發送。設置爲yes時,TCP會對包進行合併從而減小帶寬,可是命令發送的頻率會下降,從節點數據延遲增長,一致性變差;具體發送頻率與Linux內核的配置有關,默認配置爲40ms。
          只有當應用對Redis數據不一致的容忍度較高,且主從節點之間網絡情況很差時,纔會設置爲yes;多數狀況使用默認值no。
Redis主從複製做用及缺點
# 1)數據冗餘:主從複製實現了數據的熱備份;
# 2)故障恢復:主節點出現問題,從節點快速提供服務;其實是一種服務的冗餘。
# 3)讀負載均衡:主從複製配合讀寫分離,能夠大大提升Redis服務器的併發量。
#      [默認開啓讀寫分離,主讀從寫;要使得從可寫,修改配置文件,關鍵字"slave-read-only yes",默認yes表示從節點只讀,修改成no從節點可寫]
# 4)高可用基石:主從複製是哨兵和集羣可以實施的基礎;

Redis主從複製缺點

# 1. 故障恢復沒法自動化
# 2. 寫操做沒法負載均衡
# 3. 存儲能力受到單機的限制
Redis主從複製部署
節點名 IP 軟件版本 硬件 網絡 說明
redis-master 192.168.171.136 list 裏面 2C4G Nat,內網 測試環境
redis-slave 192.168.171.137 list裏面 2C4G Nat,內網 測試環境

請如今兩臺主機安裝好Redis再作下面操做,下面操做是有兩臺初始化好的Redis爲前提的

修改Redis-Master的redis.conf配置文件

[root@redis-slave redis]# mkdir  /usr/local/redis/backup

[root@redis-master redis]# cat redis.conf 
daemonize yes
pidfile /var/run/redis-16379.pid
logfile /var/log/redis-16379.log
port 16379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-16379.db
dir /usr/local/redis/backup
masterauth 123456
requirepass 123456

# 重啓服務使服務生效
[root@redis-master redis]# redis-server ./redis.conf

修改Redis-Slave的redis.conf配置文件

[root@redis-slave redis]# mkdir  /usr/local/redis/backup

[root@redis-slave redis]# cat redis.conf 
daemonize yes
pidfile /var/run/redis-26379.pid
logfile /var/log/redis-26379.log
port 26379
bind 0.0.0.0
timeout 300
databases 16
dbfilename dump-26379.db
dir /usr/local/redis/backup
masterauth 123456
requirepass 123456
slaveof 192.168.171.136 16379

# 重啓服務使服務生效
[root@redis-master redis]# redis-server ./redis.conf
驗證主從複製是否生效
# 主節點
[root@redis-master redis]# redis-cli -h 127.0.0.1 -p 16379 -a 123456
127.0.0.1:16379> set zhou youmen
OK

# 從節點
[root@redis-slave redis]# redis-cli -h 127.0.0.1 -p 26379 -a 123456
Warning: Using a password with '-a' option on the command line interface may not be safe.
127.0.0.1:26379> get zhou
"youmen"

# 至此主從複製OK了

Redis主從複製數據同步詳解

# 在Redis2.8之前,從節點向主節點發送sync命令請求同步數據,此時的同步方式只有全量複製;
# 在Redis2.8及之後,從節點能夠發送psync命令請求同步數據,此時根據主從節點當前狀態的不一樣,同步方式多是全量複製或部分複製。[從節點自己可能有部分數據,如從節點宕機再連的從節點]
  
# 網絡中斷後,從節點會一直嘗試重連
概述
# 全量複製: 用於初次複製或其餘沒法進行復制的狀況,將主節點中的全部數據都發送給從節點,是一個很是重型的操做.

# 部分複製: 用於網絡中斷等狀況後的複製,只將中斷期間主節點執行的寫命令發送給從節點,與全量複製更加高效.

# [若網絡中斷時間過長,或主節點未能完整保存中斷期間自身所執行的寫命令,則沒法進行部分複製,只能全量複製]
全量複製
# 命令:psync

# 條件:從節點判斷沒法進行部分複製,向主節點發送全量複製的請求;或從節點發送部分複製的請求,但主節點判斷沒法進行全量複製;[根據自身所記錄的相關數據標記位判斷]

# 開始執行:
#     一、執行bgsave,fork出子進程,用於在後臺生成RDB文件,並使用一個緩衝區(稱爲複製緩衝區)記錄從如今開始執行的全部寫命令;
#       [此過程十分消耗CPU,內存(頁表複製),硬盤IO;bgsave的性能能夠經過更復雜的方式進行調優]

#     二、主節點的bgsave執行完成後,將RDB文件發送給從節點;從節點接收到RDB文件,存放在本機磁盤中;
#       [此過程對主從節點的帶寬都會帶來很大的消耗]
     
    
#     三、 從節點先清除本身內存中的舊數據,而後載入接收的RDB文件到內存中,將數據庫狀態更新至主節點執行bgsave時的數據庫狀態;
#       [從節點清空老數據、載入新RDB文件的過程是阻塞的,沒法響應客戶端的命令;]
     
#     四、主節點將複製緩衝區中全部寫命令發送從節點,從節點執行這些寫命令,將數據庫狀態更新至主節點的最新狀態;
#       [若是從節點開啓了AOF,則會在數據恢復完成後,觸發bgrewriteaof的執行,保證從節點AOF文件更新至主節點的最新狀態;這會帶來很大的CPU、磁盤IO消耗]
部分複製
# 部分複製的實現,依賴於三個重要的概念:

#    一、複製偏移量
#       主節點和從節點分別維護一個複製偏移量(offset),表明主節點向從節點傳遞的字節數;主節點每次向從節點傳播N個字節數據時,主節點的offset增長N;從節點每次收到主節點傳來的N個字節數據時,從節點的offset增長N。    
#       offset用於判斷主從節點的數據庫狀態是否一致:若是兩者offset相同,則一致;若是offset不一樣,則不一致,此時能夠根據兩個offset找出從節點缺乏的那部分數據。

#    二、複製積壓緩衝區
#       複製積壓緩衝區是由主節點維護的、固定長度的、先進先出(FIFO)隊列,默認大小1MB。
#      建立:當主節點成功擁有第一個從節點時,建立複製積壓緩衝區;
#         做用:備份主節點最近發送給從節點的數據。
#            【不管主節點有一個仍是多個從節點,都只須要一個複製積壓緩衝區。】
#            複製積壓緩衝區做爲寫命令的備份,還存儲了每一個命令的複製偏移量(offset),時間較早的寫命令會被擠出緩衝區,故當主從節點offset的差距過大超過緩衝區長度時,將沒法執行部分複製,只能執行全量複製。

#    三、服務器運行ID(runid)
#       每一個Redis節點(不管主從),在啓動時都會自動生成一個隨機ID(每次啓動都不同);
#       做用:runid用來惟一識別一個Redis節點。[info server命令能夠查看]
#          主從節點初次複製時,主節點將本身的runid發送給從節點,從節點將這個runid保存起來;當斷線重連時,從節點會將這個runid發送給主節點;主節點根據runid判斷可否進行部分複製:
#        --> 若是從節點保存的runid與主節點如今的runid相同,說明主從節點以前同步過,主節點會繼續嘗試使用部分複製(到底能不能部分複製還要看offset和複製積壓緩衝區的狀況);
#        --> 若是從節點保存的runid與主節點如今的runid不一樣,說明從節點在斷線前同步的Redis節點並非當前的主節點,只能進行全量複製。    

# 總結:從節點保留的主節點runid與當前主節點的runid相同,而且主從offset值差之間的全部命令都在主節點的複製積壓緩衝區中,則進行部分複製,任何一個條件不達標則進行全量複製;

Redis主從之間心跳機制

在命令傳播階段,除了發送寫命令,主從節點還維持着心跳機制:PING和REPLCONF ACK; 心跳機制主要用於主從複製的超時判斷、數據安全

主->從:PING
#     每隔指定的時間,主節點會向從節點發送PING命令,這個PING命令的做用,主要是爲了讓主節點進行超時判斷。
#     PING發送的頻率由repl-ping-slave-period參數控制,單位是秒,默認值是10s。
#     關於該PING命令到底是由主節點發給從節點,仍是相反,有一些爭議;由於在Redis的官方文檔中,對該參數的註釋中說明是從節點向主節點發送PING命令;可是根據其源碼的實現邏輯,是主節點發給從節點的。
從->主:REPLCONF ACK
#      在命令傳播階段,從節點會向主節點發送REPLCONF ACK命令,即向主節點報告本身的複製偏移量是多少,頻率是每秒1次;
#      命令格式爲:REPLCONF ACK {offset};其中offset指從節點保存的複製偏移量



# REPLCONF ACK命令的做用包括:
#  (1)實時監測主從節點網絡狀態:該命令會被主節點用於複製超時的判斷。
#       此外,在主節點中使用info Replication,能夠看到其從節點的狀態中的lag值,表明的是主節點上次收到該REPLCONF ACK命令的時間間隔,在正常狀況下,該值應該是0或1 ;
#       [此消息正常狀況下應該一秒收一個];

#  (2)檢測命令丟失:從節點發送了自身的offset,主節點會與本身的offset對比,若是從節點數據缺失(如網絡丟包),主節點會推送缺失的數據(這裏會利用複製積壓緩衝區內的數據備份)。  
#      offset和複製積壓緩衝區,不只能夠用於部分複製,也能夠用於處理命令丟失等情形;

#  (3)用來判斷延遲時間:Redis主節點中使用min-slaves-to-write和min-slaves-max-lag參數,來保證主節點在不安全的狀況下不會執行寫命令;
#        所謂不安全,是指從節點數量太少,或延遲太高。
#        例如min-slaves-to-write和min-slaves-max-lag分別是3和10,含義是若是從節點數量小於3個,或全部從節點的延遲值都大於10s,則主節點拒絕執行寫命令。
#        而這裏從節點延遲值的獲取,就是經過主節點接收到REPLCONF ACK命令以後的時間來判斷的,即前面所說的info Replication中的lag值。

Redis主從中應用問題

讀寫分離應用問題
# 由主節點提供寫服務,由一個或多個從節點提供讀服務(多個從節點既能夠提升數據冗餘程度,也能夠最大化讀負載能力);
       
問題:
 (1)延遲與不一致問題
 --命令傳播階段:[因爲主從複製的命令傳播是異步的,延遲與數據的不一致不可避免。]
     優化:
         · 優化主從節點之間的網絡環境(如在同機房部署);
         · 監控主從節點延遲(經過offset)判斷,若是從節點延遲過大,通知應用再也不經過該從節點讀取數據;        
         · 使用集羣同時擴展寫負載和讀負載等。

--命令傳播階段之外的其餘狀況
    [如主從鏈接在數據同步階段,或從節點失去同主節點的鏈接的狀況下]
      優化:
        設置從節點的slave-serve-stale-data參數
            [此參數控制在高延遲狀況下從節點的表現]
            默認值爲「yes」,即從節點仍可以響應客戶端的命令; 若是爲no,則從節點只能響應info、slaveof等少數命令;
            該參數的設置與應用對數據一致性的要求有關;若是對數據一致性要求很高,則應設置爲no。



(2)數據過時問題        
 在單機版Redis中,存在兩種刪除策略:
     惰性刪除:服務器不會主動刪除數據,只有當客戶端查詢某個數據時,服務器判斷該數據是否過時,若是過時則刪除。
     按期刪除:服務器執行定時任務刪除過時數據,可是考慮到內存和CPU的折中(刪除會釋放內存,可是頻繁的刪除操做對CPU不友好),該刪除的頻率和執行時間都受到了限制。    
     Redis 3.2以上,從節點在讀取數據時,增長了對數據是否過時的判斷,若是該數據已過時,則不返回給客戶端;[儘可能使用新版客戶端]
                
(3)故障切換問題            
     使用哨兵或集羣,要不就是本身寫腳本作計劃任務健康檢查
複製超時問題
主從節點複製超時是致使複製中斷的最重要的緣由之一;

-->超時檢測:
 主從都會判斷鏈接是否超時:
    1)若是主節點判斷鏈接超時,其會釋放相應從節點的鏈接,從而釋放各類資源,不然無效的從節點仍會佔用主節點的各類資源(輸出緩衝區、帶寬、鏈接等);此外鏈接超時的判斷可讓主節點更準確的知道當前有效從節點的個數,有助於保證數據安全(配合min-slaves-to-write等參數)。
    2)若是從節點判斷鏈接超時,則能夠及時從新創建鏈接,避免與主節點數據長期的不一致。


-->判斷機制:
  主從複製超時判斷的核心,在於repl-timeout參數,該參數規定了超時時間的閾值(默認60s),對於主節點和從節點同時有效;
    
    主從超時條件以下:
      主:每秒1次調用複製定時函數replicationCron(),在其中判斷當前時間距離上次收到各個從節點REPLCONF ACK的時間,是否超過了repl-timeout值,若是超過了則釋放相應從節點的鏈接。
      從:一樣是在複製定時函數中判斷,基本邏輯是:    
          -1 若是當前處於鏈接創建階段,主從鏈接創建中,從節點等待時間超過repl-timeout,則釋放與主節點的鏈接;
          -2 若是當前處於數據同步階段,且收到主節點的RDB文件的時間超時,則中止數據同步,釋放鏈接;
          -3 若是當前處於命令傳播階段,且距離上次收到主節點的PING命令或數據的時間已超過repl-timeout值,則釋放與主節點的鏈接。

-->須要注意的坑:
   (1)數據同步階段:                      
        若是RDB文件過大,主節點在fork子進程+保存RDB文件時耗時過多,可能會致使從節點長時間收不到數據而觸發超時;此時從節點會重連主節點,而後再次全量複製,再次超時,再次重連……這是個悲傷的循環;
        爲了不這種狀況的發生,要注意Redis單機數據量不要過大,另外一方面就是適當增大repl-timeout值,具體的大小能夠根據bgsave耗時來調整。
   (2)命令傳播階段:
          在該階段主節點會向從節點發送PING命令,頻率由repl-ping-slave-period控制;該參數應明顯小於repl-timeout值(後者至少是前者的幾倍)。
          若是兩個參數相等或接近,網絡抖動致使個別PING命令丟失,此時恰巧主節點也沒有向從節點發送數據,則從節點很容易判斷超時。
   (3)慢查詢致使的阻塞:
         若是主節點或從節點執行了一些慢查詢(如keys *或者對大數據的hgetall等),致使服務器阻塞;阻塞期間沒法響應複製鏈接中對方節點的請求,可能致使複製超時。
複製中斷問題
除了主從節點超時,還有其餘狀況可能致使複製中斷,其中最主要的是複製緩衝區溢出問題。

--> 在全量複製階段,主節點會將執行的寫命令放到複製緩衝區中,緩衝區存放的數據包括從bgsave開始生成RDB文件,到從節點數據重載完成;
    當主節點數據量較大,或者主從節點之間網絡延遲較大時,可能致使該緩衝區的大小超過了限制,此時主節點會斷開與從節點之間的鏈接,因而開始循環重連,緩衝區溢出,斷開,又重連。。。;
   ``複製緩衝區的大小由
     client-output-buffer-limit slave {hard limit} {soft limit} {soft seconds}配置,默認值爲client-output-buffer-limit slave 256MB 64MB 60,
     其含義是:若是buffer大於256MB,或者連續60s大於64MB,則主節點會斷開與該從節點的鏈接。該參數能夠經過config set命令動態配置(即不重啓Redis也能夠生效)。
    
    [注意分別複製緩衝區和複製積壓緩衝區:
    複製緩衝區是客戶端輸出緩衝區的一種,主節點會爲每個從節點分別分配複製緩衝區;
    複製積壓緩衝區一個主節點只有一個,不管它有多少個從節點。二者都用來存儲寫命令,]
各場景複製的選擇及優化選擇
(1)第一次創建複製
     此時全量複製不可避免,但仍有幾點須要注意:
     一、若是主節點的數據量較大,應該儘可能避開流量的高峯期,避免形成進程阻塞;    
     二、若是有多個從節點須要創建對主節點的複製,能夠考慮將幾個從節點錯開,避免主節點帶寬佔用過大。    
     三、若是從節點過多,也能夠調整主從複製的拓撲結構,由一主多從結構變爲樹狀結構(中間的節點既是其主節點的從節點,也是其從節點的主節點);
      [但使用樹狀結構應該謹慎:雖然主節點的直接從節點減小,下降了主節點的負擔,可是多層從節點的延遲增大,數據一致性變差;且結構複雜,維護至關困難。]

(2)主節點重啓    
    主節點重啓能夠分爲兩種狀況來討論,一種是故障致使宕機,另外一種則是有計劃的重啓。
    一、主節點宕機
      !主節點宕機重啓後,runid會發生變化,所以不能進行部分複製,只能全量複製。
      【實際上在主節點宕機的狀況下,應進行故障轉移處理,將其中的一個從節點升級爲主節點,其餘從節點重新的主節點進行復制;且故障轉移應儘可能的自動化,儘可能使用哨兵或自寫程序完成】
    二、安全重啓:debug reload    
      ```在一些場景下,可能但願對主節點進行重啓,例如主節點內存碎片率太高,或者但願調整一些只能在啓動時調整的參數。若是使用普通的手段重啓主節點,會使得runid發生變化,可能致使沒必要要的全量複製。
      ```Redis提供了debug reload的重啓方式:重啓後,主節點的runid和offset都不受影響,避免了全量複製。
      [但debug會清空當前內存中的數據,從新從RDB文件中加載,這個過程會致使主節點的阻塞,所以也須要謹慎。]

(3)從節點重啓        
    從節點宕機重啓後,其保存的主節點的runid會丟失,所以即便再次執行slaveof,也沒法進行部分複製。只能全量走起;

(4)網絡中斷    
     若是主從節點之間出現網絡問題,形成短期內網絡中斷,能夠分爲多種狀況討論。
       一、網絡問題時間極爲短暫,只形成了短暫的丟包,主從節點都沒有斷定超時(未觸發repl-timeout);此時只須要經過REPLCONF ACK來補充丟失的數據便可。[自動完成]
       二、網絡問題時間很長,主從節點判斷超時(觸發了repl-timeout),且丟失的數據過多,超過複製積壓緩衝區大小;
          此時主從節點沒法進行部分複製,只能進行全量複製。    
          爲了儘量避免這種狀況的發生,應該根據實際狀況適當調整複製積壓緩衝區的大小;此外及時發現並修復網絡中斷,也能夠減小全量複製。
       三、介於前述兩種狀況之間,主從節點判斷超時,且丟失的數據仍然都在複製積壓緩衝區中;此時主從節點能夠進行部分複製。
複製相關配置
配置分爲主的配置、從的配置以及與主從都有的配置,下面分別說明。
(1)主從都有的配置
     一、slaveof <masterip> <masterport>:
         Redis啓動時起做用;做用是創建複製關係,開啓了該配置的Redis服務器在啓動後成爲從節點。該註釋默認註釋掉,即Redis服務器默認都是主節點。
     二、repl-timeout 60:
        與各個階段主從節點鏈接超時判斷有關,ping命令發出後收不到迴應,超過此時間超時。 

(2)主節點相關配置
     一、repl-diskless-sync no:            
        做用於全量複製階段,控制主節點是否使用diskless複製(無盤複製)。
        所謂diskless複製,是指在全量複製時,主節點再也不先把數據寫入RDB文件,而是直接寫入slave的socket中,整個過程當中不涉及硬盤;diskless複製在磁盤IO很慢而網速很快時更有優點。默認關閉;
     二、repl-diskless-sync-delay 5:
        該配置做用於全量複製階段,當主節點使用diskless複製時,該配置決定主節點向從節點發送以前停頓的時間,單位是秒;只有當diskless複製打開時有效,默認5s。
        設置停頓時間緣由:
         1) 向slave的socket的傳輸一旦開始,新鏈接的slave只能等待當前數據傳輸結束,才能開始新的數據傳輸
         2) 多個從節點有較大的機率在短期內創建主從複製。
     三、client-output-buffer-limit slave 256MB 64MB 60:
        與全量複製階段主節點的緩衝區大小有關        
        默認爲client-output-buffer-limit slave 256MB 64MB 60,
        [在全量複製過程當中,若積壓緩衝區使用大小超過256M,或直接在60s以內使用超過64M,將會斷開與該從服務器的連接]
     四、repl-disable-tcp-nodelay no:
        與命令傳播階段的延遲有關;是否將多個命令傳播打包至一個tcp包中進行傳輸;
        [適合在網絡帶寬足夠可是,對於緩存的主從一致性要求不高的場合]
     五、masterauth <master-password>:
        與鏈接創建階段的身份驗證有關[通常不設置]
     六、repl-ping-slave-period 10:
        與命令傳播階段主從節點的超時判斷有關,主節點多長時間ping一下從節點,默認10秒;
     七、repl-backlog-size 1mb:
        複製積壓緩衝區的大小;[能夠調大,可是會影響一致性速度]
     八、repl-backlog-ttl 3600:
        當主節點沒有從節點時,複製積壓緩衝區保留的時間,這樣當斷開的從節點從新連進來時,能夠進行全量複製;默認3600s。若是設置爲0,則永遠不會釋放複製積壓緩衝區。
     九、min-slaves-to-write 3與min-slaves-max-lag 10:
        規定了主節點的最小從節點數目,及對應的最大延遲;
        當主節點的從節點少於這個數時,或每一個從節點與主節點之間的延遲都超過所設定的10秒時,將會限制命令廣播中命令的類型,主節點只能向從節點發送最基礎的命令;

(3)從節點相關配置        
     一、slave-serve-stale-data yes:
        與從節點數據陳舊時是否響應客戶端命令有關,用於設置當主節點與從節點之間的網絡延遲較大時,從節點是否能夠接收主節點的廣播命令;
     二、slave-read-only yes:
        從節點是否只讀;默認是隻讀的。因爲從節點開啓寫操做容易致使主從節點的數據不一致,所以該配置儘可能不要修改。
單機內存大小限制
在Redis的使用中,限制單機內存大小的因素很是之多,下面總結一下在主從複製中,單機內存過大可能形成的影響:
    (1)切主:當主節點宕機時,一種常見的容災策略是將其中一個從節點提高爲主節點,並將其餘從節點掛載到新的主節點上,此時這些從節點只能進行全量複製;若是Redis單機內存達到10GB,一個從節點的同步時間在幾分鐘的級別;若是從節點較多,恢復的速度會更慢。若是系統的讀負載很高,而這段時間從節點沒法提供服務,會對系統形成很大的壓力。

    (2)從庫擴容:若是訪問量忽然增大,此時但願增長從節點分擔讀負載,若是數據量過大,從節點同步太慢,難以及時應對訪問量的暴增。

    (3)緩衝區溢出:若是單機內存中的數據過於龐大,則可能致使在全量複製階段,主節點的複製緩衝區溢出,從而致使複製中斷;從而致使從節點再次鏈接,發現數據又不同,又全量複製,由發生緩衝區溢出的惡性循環;

    (4)超時:若是數據量過大,全量複製階段主節點fork+保存RDB文件耗時過大,從節點長時間接收不到數據觸發超時;因而又發生相似的惡性循環;

總結:主節點單機內存不能太大,redis佔用主機內存的比例也不能過大:最好只使用50%-65%的內存,留下30%-45%的內存用於執行bgsave命令和建立複製緩衝區等。
相關文章
相關標籤/搜索