一、NoSQL簡介
1.1 常見的優化思路和方向
1.1.1 MySQL主從讀寫分離
由於數據庫的寫入壓力增加,Memcached只能緩解數據庫的讀取壓力。讀寫集中在一個數據庫上讓數據庫不堪重負,大部分網站開始使用主從復制技術達到讀寫分離,以提高讀寫性能和讀庫的可擴展性。Mysql的master-slave模式成為這個時候的網站標配了。
1.1.2 分庫分表
隨着web2.0的繼續高速發展,在Memcached的高速緩存,MySQL的主從復制,讀寫分離的基礎之上,這時MySQL主庫的寫壓力開始出現瓶頸,而數據量的持續猛增,由於MyISAM使用表鎖,在高並發下會出現嚴重的鎖問題,大量的高並發MySQL應用開始使用InnoDB引擎代替MyISAM。同時,開始流行使用分表分庫來緩解寫壓力和數據增長的擴展問題。這個時候,分表分庫成了一個熱門技術,是業界討論的熱門技術問題。也就在這個時候,MySQL推出了還不太穩定的表分區,這也給技術實力一般的公司帶來了希望。雖然MySQL推出了MySQL Cluster集群,但是由於在互聯網幾乎沒有成功案例,性能也不能滿足互聯網的要求,只是在高可靠性上提供了非常大的保證。
1.2 NoSQL誕生的原因
關系型數據庫面臨的問題:
- 擴展困難:由於存在類似Join這樣多表查詢機制,使得數據庫在擴展方面很艱難;
- 讀寫慢:這種情況主要發生在數據量達到一定規模時由於關系型數據庫的系統邏輯非常復雜,使得其非常容易發生死鎖等的並發問題,所以導致其讀寫速度下滑非常嚴重;
- 成本高:企業級數據庫的License價格很驚人,並且隨着系統的規模,而不斷上升;
- 有限的支撐容量:現有關系型解決方案還無法支撐Google這樣海量的數據存儲;
數據庫訪問的新需求:
- 低延遲的讀寫速度:應用快速地反應能極大地提升用戶的滿意度;
- 支撐海量的數據和流量:對於搜索這樣大型應用而言,需要利用PB級別的數據和能應對百萬級的流量;
- 大規模集群的管理:系統管理員希望分布式應用能更簡單的部署和管理;
- 龐大運營成本的考量:IT經理們希望在硬件成本、軟件成本和人力成本能夠有大幅度地降低;
- NoSQL數據庫僅僅是關系數據庫在某些方面(性能、擴展)的一個彌補
- 單從功能上講,NoSQL的幾乎所有的功能,在關系數據庫上都能夠滿足。
- 一般會把NoSQL和關系數據庫進行結合使用,各取所長,各得其所。
- 在某些應用場合,比如一些配置的關系鍵值映射存儲、用戶名和密碼的存儲、Session會話存儲等等
- 在某些場景下,用NoSQL完全可以替代關系數據庫(如:MySQL)存儲。不但具有更高的性能,而且開發也更加方
1.3 分布式系統的挑戰
CAP原理是指這三個要素最多只能同時實現兩點,不可能三者兼顧。因此在進行分布式架構設計時,必須做出取舍。而對於分布式數據系統,分區容忍性是基本要求,否則就失去了價值。因此設計分布式數據系統,就是在一致性和可用性之間取一個平衡。對於大多數WEB應用,其實並不需要強一致性,因此犧牲一致性而換取高可用性,是多數分布式數據庫產品的方向。
在理論計算機科學中,CAP定理(CAP theorem),又被稱作布魯爾定理(Brewer’s theorem),它指出對於一個分布式計算系統來說,不可能同時滿足以下三點:
- 一致性(Consistency)—所有節點在同一時間具有相同的數據
- 可用性(Availability)—保證每個請求不管成功或者失敗都有響應
- 分隔容忍(Partition tolerance)—系統中任意信息的丟失或失敗不會影響系統的繼續運作
1.3.1關系數據庫和NoSQL側重點
關系數據庫 | NoSQL |
---|---|
分布式關系型數據庫中強調的ACID 分別是:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability) |
對於許多互聯網應用來說,對於一致性要求可以降低,而可用性(Availability)的要求則更為明顯,在CAP理論基礎上,從而產生了弱一致性的理論BASE。 |
ACID的目的就是通過事務支持,保證數據的完整性和正確性 | BASE 分別是英文:Basically,Available(基本可用), Softstate(軟狀態)非實時同步,Eventual Consistency(最終一致)的縮寫,這個模型是反ACID 模型 |
1.4 NoSQL的優缺點
優點:
- 簡單的擴展
典型例子是Cassandra,由於其架構是類似於經典的P2P,所以能通過輕松地添加新的節點來擴展這個集群;
- 快速的讀寫
主要例子有Redis,由於其邏輯簡單,而且純內存操作,使得其性能非常出色,單節點每秒可以處理超過10萬次讀寫操作;
- 低廉的成本
這是大多數分布式數據庫共有的特點,因為主要都是開源軟件,沒有昂貴的License成本;
缺點:
- 不提供對SQL的支持
如果不支持SQL這樣的工業標准,將會對用戶產生一定的學習和應用遷移成本;
- 支持的特性不夠豐富
現有產品所提供的功能都比較有限,大多數NoSQL數據庫都不支持事務,也不像Oracle那樣能提供各種附加功能,比如BI和報表等;
- 現有產品的不夠成熟
大多數產品都還處於初創期,和關系型數據庫幾十年的完善不可同日而語;
1.5 NoSQL總結
- NoSQL數據庫的出現,彌補了關系數據(比如MySQL)在某些方面的不足,在某些方面能極大的節省開發成本和維護成本。
- MySQL和NoSQL都有各自的特點和使用的應用場景,兩者的緊密結合將會
給web2.0的數據庫發展帶來新的思路。讓關系數據庫關注在關系上,NoSQL關注在功能、性能上。 - 隨着移動互聯網的發展,以及業務場景的多樣化,社交元素的普遍化,Nosql從性能和功能上很好的補充了web2.0時代的原關系型數據的缺點,目前已經是各大公司必備的技術之一。
二、NoSQL的分類
2.1 基本分類
Column-oriented(列式)
- 主要圍繞着“列(Column)”,而非 “行(Row)”進行數據存儲
- 屬於同一列的數據會盡可能地存儲在硬盤同一個頁(Page)中
- 大多數列式數據庫都支持Column Family這個特性
- (很多類似數據倉庫(Data Warehouse)的應用,雖然每次查詢都會處理很多數據,但是每次所涉及的列並沒有很多)
- 特點:比較適合匯總(Aggregation)和數據倉庫這類應用。
Key-value(重要)
- 類似常見的HashTable,一個Key對應一個Value,但是其能提供非常快的查詢速度、大的數據存放量和高並發操作,
- 非常適合通過主鍵對數據進行查詢和修改等操作, 雖然不支持復雜的操作,但可通過上層的開發來彌補這個缺陷。
Document(文檔) (比如:mongodb)
- 類似常見的HashTable,一個Key對應一個Value,
- 其能提供非常快的查詢速度、大的數據存放量和高並發操作,
- 非常適合通過主鍵對數據進行查詢和修改等操作,
- 數據類型多且存在大量的空項。比如SNS類的用戶profile,手機,郵箱,地址,性別……有很多項,而且大部分是空項。
2.2 常見分類

關注一致性和可用性的(CA)
這些數據庫對於分區容忍性方面比較不感冒,主要采用復制(Replication)這種方式來保證數據的安全性,常見的CA系統有:
- 傳統關系型數據庫,比如Postgres和MySQL等(Relational)
- Oracle (Relational)
- Aster Data (Relational)
- Greenplum (Relational)
- NoSQL:
- redis
- mongodb
- cassandra
關注一致性和分區容忍性的(CP)
這種系統將數據分布在多個網絡分區的節點上,並保證這些數據的一致性,但是對於可用性的支持方面有問題,比如當集群出現問題的話,節點有可能因無法確保數據是一致性的而拒絕提供服務,主要的CP系統有:
- BigTable (Column-oriented)
- Hypertable (Column-oriented)
- HBase (Column-oriented)
- MongoDB (Document)
- Terrastore (Document)
- Redis (Key-value)
- Scalaris (Key-value)
- MemcacheDB (Key-value)
- Berkeley DB (Key-value)
關於可用性和分區容忍性的(AP)
這類系統主要以實現“最終一致性(Eventual Consistency)”來確保可用性和分區容忍性,AP的系統有:
- Dynamo (Key-value)
- Voldemort (Key-value)
- Tokyo Cabinet (Key-value)
- KAI (Key-value)
- Cassandra (Column-oriented)
- CouchDB (Document-oriented)
- SimpleDB (Document-oriented)
- Riak (Document-oriented)
2.3 常見Nosql分類和部分代表

三、企業常見Nosql應用
3.1 純NoSQL架構(Nosql為主)
- 在一些數據結構、查詢關系非常簡單的系統中,我們可以只使用NoSQL即可以解決存儲問題。
- 在一些數據庫結構經常變化,數據結構不定的系統中,就非常適合使用NoSQL來存儲。
- 比如監控系統中的監控信息的存儲,可能每種類型的監控信息都不太一樣。
- 有些NoSQL數據庫已經具有部分關系數據庫的關系查詢特性,他們的功能介於key-value和關系數據庫之間,卻具有key-value數據庫的性能,基本能滿足絕大部分web 2.0網站的查詢需求。

3.2 以NoSQL為數據源的架構(Nosql為主)
- 數據直接寫入NoSQL,再通過NoSQL同步協議復制到其他存儲。
- 根據應用的邏輯來決定去相應的存儲獲取數據。
- 應用程序只負責把數據直接寫入到NoSQL數據庫,然后通過NoSQL的復制協議,把NoSQL數據的每次寫入,更新,刪除操作都復制到MySQL數據庫中。
- 同時,也可以通過復制協議把數據同步復制到全文檢索實現強大的檢索功能。
- 這種架構需要考慮數據復制的延遲問題,這跟使用MySQL的mastersalve模式的延遲問題是一樣的,解決方法也一樣。

3.3 NoSQL作為鏡像(nosql為輔)
- 不改變原有的以MySQL作為存儲的架構,使用NoSQL作為輔助鏡像存儲,用NoSQL的優勢輔助提升性能。
- 在原有基於MySQL數據庫的架構上增加了一層輔助的NoSQL存儲。
- 在寫入MySQL數據庫后,同時寫入到NoSQL數據庫,讓MySQL和NoSQL擁有相同的鏡像數據。
- 在某些可以根據主鍵查詢的地方,使用高效的NoSQL數據庫查詢。

3.4 NoSQL為鏡像(同步模式,nosql為輔)
- 通過MySQL把數據同步到NoSQL中, ,是一種對寫入透明但是具有更高技術難度一種模式
- 適用於現有的比較復雜的老系統,通過修改代碼不易實現,可能引起新的問題。同時也適用於需要把數據同步到多種類型的存儲中。

3.5 MySQL和NoSQL組合(nosql為輔)
- MySQL中只存儲需要查詢的小字段,NoSQL存儲所有數據。
- 把需要查詢的字段,一般都是數字,時間等類型的小字段存儲於MySQL中,根據查詢建立相應的索引,
- 其他不需要的字段,包括大文本字段都存儲在NoSQL中。
- 在查詢的時候,我們先從MySQL中查詢出數據的主鍵,然后從NoSQL中直接取出對應的數據即可。

3.6 其他應用
由於NoSQL數據庫天生具有高性能、易擴展的特點,所以我們常常結合關系數據庫,存儲一些高性能的、海量的數據。
從另外一個角度看,根據NoSQL的高性能特點,它同樣適合用於緩存數據。用NoSQL緩存數據可以分為內存模式和磁盤持久化模式。
內存模式
- Memcached提供了相當高的讀寫性能,在互聯網發展過程中,一直是緩存服務器的首選。
- NoSQL數據庫Redis又為我們提供了功能更加強大的內存存儲功能。跟Memcached比,Redis的一個key的可以存儲多種數據結構Strings、Hashes、Lists、Sets、Sorted sets。
- Redis不但功能強大,而且它的性能完全超越大名鼎鼎的Memcached。
- Redis支持List、hashes等多種數據結構的功能,提供了更加易於使用的api和操作性能,比如對緩存的list數據的修改。
持久化模式
- 雖然基於內存的緩存服務器具有高性能,低延遲的特點,但是內存成本高、內存數據易失卻不容忽視。
- 大部分互聯網應用的特點都是數據訪問有熱點,也就是說,只有一部分數據是被頻繁訪問的。
- 其實NoSQL數據庫內部也是通過內存緩存來提高性能的,通過一些比較好的算法
- 把熱點數據進行內存cache
- 非熱點數據存儲到磁盤
- 以節省內存占用
- 使用NoSQL來做緩存,由於其不受內存大小的限制,我們可以把一些不常訪問、不怎么更新的數據也緩存起來。
四、redis
4.1 什么是redis?
redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sortedset --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支持各種不同方式的排序。與memcached一樣,為了保證效率,數據都是緩存在內存中。區別的是redis會周期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件,並且在此基礎上實現了master-slave(主從)同步。
Redis 是一個高性能的key-value數據庫。 redis的出現,很大程度補償了memcached這類key/value存儲的不足,在部 分場合可以對關系數據庫起到很好的補充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客戶端,使用很方便。
Redis支持主從同步。數據可以從主服務器向任意數量的從服務器上同步,從服務器可以是關聯其他從服務器的主服務器。這使得Redis可執行單層樹復制。存盤可以有意無意的對數據進行寫操作。由於完全實現了發布/訂閱機制,使得從數據庫在任何地方同步樹時,可訂閱一個頻道並接收主服務器完整的消息發布記錄。同步對讀取操作的可擴展性和數據冗余很有幫助。
redis的官網地址,非常好記,是redis.io。
目前,Vmware在資助着redis項目的開發和維護。
4.2 redis的特性
- 完全居於內存,數據實時的讀寫內存,定時閃回到文件中。采用單線程,避免了不必要的上下文切換和競爭條件;
- 支持高並發量,官方宣傳支持10萬級別的並發讀寫;
- 支持持久存儲,機器重啟后的,重新加載模式,不會掉數據;
- 海量數據存儲,分布式系統支持,數據一致性保證,方便的集群節點添加/刪除;
- Redis不僅僅支持簡單的k/v類型的數據,同時還提供list,set,zset,hash等數據結構的存儲;
- 災難恢復–memcache掛掉后,數據不可恢復; redis數據丟失后可以通過aof恢復;
- 虛擬內存–Redis當物理內存用完時,可以將一些很久沒用到的value 交換到磁盤;
- Redis支持數據的備份,即master-slave模式的數據備份。
4.3 redis的架構

各功能模塊說明如下:
File Event
: 處理文件事件,接受它們發來的命令請求(讀事件),並將命令的執行結果返回給客戶端(寫事件))
Time Event
: 時間事件(更新統計信息,清理過期數據,附屬節點同步,定期持久化等)
AOF
: 命令日志的數據持久化
RDB
:實際的數據持久化
Lua Environment
: Lua 腳本的運行環境. 為了讓 Lua 環境符合 Redis 腳本功能的需求,Redis 對 Lua 環境進行了一系列的修改,包括添加函數庫、更換隨機函數、保護全局變量,等等
Command table(命令表)
:在執行命令時,根據字符來查找相應命令的實現函數。
Share Objects(對象共享)
:
主要存儲常見的值:
a.各種命令常見的返回值,例如返回值OK、ERROR、WRONGTYPE等字符;
b. 小於 redis.h/REDIS_SHARED_INTEGERS (默認1000)的所有整數。通過預分配的一些常見的值對象,並在多個數據結構之間共享對象,程序避免了重復分配的麻煩。也就是說,這些常見的值在內存中只有一份。
Databases
:Redis數據庫是真正存儲數據的地方。當然,數據庫本身也是存儲在內存中的。
4.4 redis 啟動流程

4.5 redis 安裝方式
redis安裝常用兩種方式,yum安裝和源碼包安裝
yum 安裝:通常是在線安裝,好處是安裝方式簡單,不易出錯;常用的安裝yum源為epel。
源碼包安裝:是先將 redis 的源碼下載下來,在自己的系統里編譯生成可執行文件,然后執行,好處是因為是在自己的系統上編譯的,更符合自己系統的性能,也就是說在自己的系統上執行 redis 服務性能效率更好。
區別:路徑和啟動方式不同,支持的模塊也不同。
4.5.1 redis 程序路徑
配置文件:
/etc/redis.conf
主程序:/usr/bin/redis-server
客戶端:/usr/bin/redis-cli
UnitFile:/usr/lib/systemd/system/redis.service
數據目錄:/var/lib/redis
監聽:6379/tcp
4.6 redis 配置文件
4.6.1 網絡配置項(NETWORK)
### NETWORK ###
bind IP #監聽地址
port PORT #監聽端口
protected-mode yes #是否開啟保護模式,默認開啟。要是配置里沒有指定bind和密碼。開啟該參數后,redis只會本地進行訪問,拒絕外部訪問。
tcp-backlog 511 #定義了每一個端口最大的監聽隊列的長度
unixsocket /tmp/redis.sock #也可以打開套接字監聽
timeout 0 #連接的空閑超時時長;

4.6.2 通用配置項(GENERAL)
### GENERAL ###
daemonize no #是否以守護進程啟動
supervised no #可以通過upstart和systemd管理Redis守護進程,這個參數是和具體的操作系統相關的
pidfile "/var/run/redis/redis.pid" #pid文件
loglevel notice #日志等級
logfile "/var/log/redis/redis.log" #日志存放文件
databases 16 #設定數據庫數量,默認為16個,每個數據庫的名字均為整數,從0開始編號,默認操作的數據庫為0;
切換數據庫的方法:SELECT <dbid>

4.6.3 快照配置(SNAPSHOTTING)
### SNAPSHOTTING ###
save 900 1 #900秒有一個key變化,就做一個保存
save 300 10 #300秒有10個key變化,就做一個保存,這里需要和開發溝通
save 60 10000 #60秒有10000個key變化就做一個保存
stop-writes-on-bgsave-error yes #在出現錯誤的時候,是不是要停止保存
rdbcompression yes #使用壓縮rdb文件,rdb文件壓縮使用LZF壓縮算法,yes:壓縮,但是需要一些cpu的消耗;no:不壓縮,需要更多的磁盤空間
rdbchecksum yes #是否校驗rdb文件。從rdb格式的第五個版本開始,在rdb文件的末尾會帶上CRC64的校驗和。這跟有利於文件的容錯性,但是在保存rdb文件的時候,會有大概10%的性能損耗,所以如果你追求高性能,可以關閉該配置。
dbfilename "along.rdb" #rdb文件的名稱
dir "/var/lib/redis" #數據目錄,數據庫的寫入會在這個目錄。rdb、aof文件也會寫在這個目錄

4.6.4 限制相關配置(LIMITS)
### LIMITS ###
maxclients 10000 #設置能連上redis的最大客戶端連接數量
maxmemory <bytes> #redis配置的最大內存容量。當內存滿了,需要配合maxmemory-policy策略進行處理。
maxmemory-policy noeviction #淘汰策略:volatile-lru, allkeys-lru, volatile-random, allkeys-random, volatile-ttl, noeviction
內存容量超過maxmemory后的處理策略:
① # volatile-lru:利用LRU算法移除設置過過期時間的key。
② # volatile-random:隨機移除設置過過期時間的key。
③ # volatile-ttl:移除即將過期的key,根據最近過期時間來刪除(輔以TTL)
④ # allkeys-lru:利用LRU算法移除任何key。
⑤ # allkeys-random:隨機移除任何key。
⑥ # noeviction:不移除任何key,只是返回一個寫錯誤。
# 上面的這些驅逐策略,如果redis沒有合適的key驅逐,對於寫命令,還是會返回錯誤。redis將不再接收寫請求,只接收get請求。寫命令包括:set setnx
maxmemory-samples 5 #淘汰算法運行時的采樣樣本數;

4.6.5 持久化配置(APPEND ONLY MODE)
### APPEND ONLY MODE ###
# 默認redis使用的是rdb方式持久化,這種方式在許多應用中已經足夠用了。但是redis如果中途宕機,會導致可能有幾分鍾的數據丟失,根據save來策略進行持久化,Append Only File是另一種持久化方式,可以提供更好的持久化特性。Redis會把每次寫入的數據在接收后都寫入 appendonly.aof 文件,每次啟動時Redis都會先把這個文件的數據讀入內存里,先忽略RDB文件。
appendonly no #不啟動aof模式
appendfilename "appendonly.aof" #據讀入內存里,先忽略RDB文件,aof文件名(default: "appendonly.aof")
appendfsync
Redis supports three different modes:
no:redis不執行主動同步操作,而是OS進行;
everysec:每秒一次;
always:每語句一次;
如果Redis只是將客戶端修改數據庫的指令重現存儲在AOF文件中,那么AOF文件的大小會不斷的增加,因為AOF文件只是簡單的重現存儲了客戶端的指令,而並沒有進行合並。對於該問題最簡單的處理方式,即當AOF文件滿足一定條件時就對AOF進行rewrite,rewrite是根據當前內存數據庫中的數據進行遍歷寫到一個臨時的AOF文件,待寫完后替換掉原來的AOF文件即可。
redis重寫會將多個key、value對集合來用一條命令表達。在rewrite期間的寫操作會保存在內存的rewrite buffer中,rewrite成功后這些操作也會復制到臨時文件中,在最后臨時文件會代替AOF文件。
no-appendfsync-on-rewrite no
#在aof重寫或者寫入rdb文件的時候,會執行大量IO,此時對於everysec和always的aof模式來說,執行fsync會造成阻塞過長時間,no-appendfsync-on-rewrite字段設置為默認設置為no。如果對延遲要求很高的應用,這個字段可以設置為yes,否則還是設置為no,這樣對持久化特性來說這是更安全的選擇。設置為yes表示rewrite期間對新寫操作不fsync,暫時存在內存中,等rewrite完成后再寫入,默認為no,建議yes。Linux的默認fsync策略是30秒。可能丟失30秒數據。
auto-aof-rewrite-percentage 100 aof自動重寫配置。當目前aof文件大小超過上一次重寫的aof文件大小的百分之多少進行重寫,即當aof文件增長到一定大小的時候Redis能夠調用bgrewrite aof對日志文件進行重寫。當前AOF文件大小是上次日志重寫得到AOF文件大小的二倍(設置為100)時,自動啟動新的日志重寫過程。
auto-aof-rewrite-min-size 64mb #設置允許重寫的最小aof文件大小,避免了達到約定百分比但尺寸仍然很小的情況還要重寫。上述兩個條件同時滿足時,方會觸發重寫AOF;與上次aof文件大小相比,其增長量超過100%,且大小不少於64MB;
aof-load-truncated yes #指redis在恢復時,會忽略最后一條可能存在問題的指令。aof文件可能在尾部是不完整的,出現這種現象,可以選擇讓redis退出,或者導入盡可能多的數據。如果選擇的是yes,當截斷的aof文件被導入的時候,會自動發布一個log給客戶端然后load。
如果是no,用戶必須手動redis-check-aof修復AOF文件才可以。
注意:持久機制本身不能取代備份;應該制訂備份策略,對redis庫定期備份;Redis服務器啟動時用持久化的數據文件恢復數據,會優先使用AOF;

我們繼續來看 redis 的持久化:
RDB:snapshotting, 二進制格式;按事先定制的策略,周期性地將數據從內存同步至磁盤;數據文件默認為dump.rdb;
客戶端顯式使用SAVE或BGSAVE命令來手動啟動快照保存機制;
SAVE:同步,即在主線程中保存快照,此時會阻塞所有客戶端請求;
BGSAVE:異步;backgroud
AOF:Append Only File, fsync
記錄每次寫操作至指定的文件尾部實現的持久化;當redis重啟時,可通過重新執行文件中的命令在內存中重建出數據庫;
BGREWRITEAOF:AOF文件重寫;
不會讀取正在使用AOF文件,而是通過將內存中的數據以命令的方式保存至臨時文件中,完成之后替換原來的AOF文件;
4.6.6 慢查詢日志相關配置(SLOW LOG)
### SLOW LOG ###
slowlog-log-slower-than 10000 #當命令的執行超過了指定時間,單位是微秒;1s=10^6微秒
slowlog-max-len 128 #慢查詢日志長度。當一個新的命令被寫進日志的時候,最老的那個記錄會被刪掉。
ADVANCED配置:
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
設置ziplist的鍵數量最大值,每個值的最大空間;

4.7 redis命令介紹
└── bin
├── redis-benchmark #redis性能測試工具,可以測試在本系統本配置下的讀寫性能
├── redis-check-aof #對更新日志appendonly.aof檢查,是否可用
├── redis-check-dump #用於檢查本地數據庫的rdb文件
├── redis-cli #redis命令行操作工具,也可以用telnet根據其純文本協議來操作
├── redis-sentinel Redis-sentinel 是Redis實例的監控管理、通知和實例失效備援服務,是Redis集群的管理工具
└── redis-server #redis服務器的daemon啟動程序
4.7.1 redis-cli命令介紹
redis-cli -p 6379 #默認選擇 db庫是 0
redis 127.0.0.1:6379> keys * #查看當前所在“db庫”所有的緩存key
redis 127.0.0.1:6379> select 8 #選擇 db庫
redis 127.0.0.1:6379> FLUSHALL #清除所有的緩存key
redis 127.0.0.1:63798> FLUSHDB #清除當前“db庫”所有的緩存key
redis 127.0.0.1:6379> set keyname keyvalue #設置緩存值
redis 127.0.0.1:6379> get keyname #獲取緩存值
redis 127.0.0.1:6379> del keyname #刪除緩存值:返回刪除數量(0代表沒刪除)
服務端的相關命令:
time
:返回當前服務器時間
client list
: 返回所有連接到服務器的客戶端信息和統計數據 參見http://redisdoc.com/server/client_list.html
client kill ip:port
:關閉地址為 ip:port 的客戶端
save
:將數據同步保存到磁盤
bgsave
:將數據異步保存到磁盤
lastsave
:返回上次成功將數據保存到磁盤的Unix時戳
shundown
:將數據同步保存到磁盤,然后關閉服務
info
:提供服務器的信息和統計
config resetstat
:重置info命令中的某些統計數據
config get
:獲取配置文件信息
config set
:動態地調整 Redis 服務器的配置(configuration)而無須重啟,可以修改的配置參數可以使用命令 CONFIG GET * 來列出
config rewrite
:Redis 服務器時所指定的 redis.conf 文件進行改寫
monitor
:實時轉儲收到的請求
slaveof
:改變復制策略設置
debug
:sleep segfault
slowlog get
:獲取慢查詢日志
slowlog len
:獲取慢查詢日志條數
slowlog reset
:清空慢查詢
4.8 redis 常用數據類型

Redis內部使用一個redisObject對象來表示所有的key和value,redisObject最主要的信息如上圖所示:
type代表一個value對象具體是何種數據類型
encoding是不同數據類型在redis內部的存儲方式
比如:type=string代表value存儲的是一個普通字符串,那么對應的encoding可以是raw或者是int,如果是int則代表實際redis內部是按數值型類存儲和表示這個字符串的,當然前提是這個字符串本身可以用數值表示,比如:“123” "456"這樣的字符串。
Redis的鍵值可以使用物種數據類型:字符串,散列表,列表,集合,有序集合。
4.8.1 對KEY操作的命令
exists(key)
:確認一個key是否存在
del(key)
:刪除一個key
type(key)
:返回值的類型
keys(pattern)
:返回滿足給定pattern的所有key
randomkey
:隨機返回key空間的一個
keyrename(oldname, newname)
:重命名key
dbsize
:返回當前數據庫中key的數目
expire
:設定一個key的活動時間(s)
ttl
:獲得一個key的活動時間
move(key, dbindex)
:移動當前數據庫中的key到dbindex數據庫
flushdb
:刪除當前選擇數據庫中的所有key
flushall
:刪除所有數據庫中的所有key
4.8.2 對String操作的命令
應用場景:String是最常用的一種數據類型,普通的key/ value 存儲都可以歸為此類.即可以完全實現目前 Memcached 的功能,並且效率更高。還可以享受Redis的定時持久化,操作日志及 Replication等功能。除了提供與 Memcached 一樣的get、set、incr、decr 等操作外,Redis還提供了下面一些操作:
set(key, value):給數據庫中名稱為key的string賦予值value
get(key):返回數據庫中名稱為key的string的value
getset(key, value)
:給名稱為key的string賦予上一次的value
mget(key1, key2,…, key N)
:返回庫中多個string的value
setnx(key, value)
:添加string,名稱為key,值為value
setex(key, time, value)
:向庫中添加string,設定過期時間time
mset(key N, value N)
:批量設置多個string的值
msetnx(key N, value N)
:如果所有名稱為key i的string都不存在
incr(key)
:名稱為key的string增1操作
incrby(key, integer)
:名稱為key的string增加integer
decr(key)
:名稱為key的string減1操作
decrby(key, integer)
:名稱為key的string減少integer
append(key, value)
:名稱為key的string的值附加value
substr(key, start, end)
:返回名稱為key的string的value的子串
4.8.3 對Hash操作的命令
應用場景:在Memcached中,我們經常將一些結構化的信息打包成HashMap,在客戶端序列化后存儲為一個字符串的值,比如用戶的昵稱、年齡、性別、積分等,這時候在需要修改其中某一項時,通常需要將所有值取出反序列化后,修改某一項的值,再序列化存儲回去。這樣不僅增大了開銷,也不適用於一些可能並發操作的場合(比如兩個並發的操作都需要修改積分)。而Redis的Hash結構可以使你像在數據庫中Update一個屬性一樣只修改某一項屬性值。
hset(key, field, value)
:向名稱為key的hash中添加元素field
hget(key, field)
:返回名稱為key的hash中field對應的value
hmget(key, (fields))
:返回名稱為key的hash中field i對應的value
hmset(key, (fields))
:向名稱為key的hash中添加元素field
hincrby(key, field, integer)
:將名稱為key的hash中field的value增加integer
hexists(key, field)
:名稱為key的hash中是否存在鍵為field的域
hdel(key, field)
:刪除名稱為key的hash中鍵為field的域
hlen(key)
:返回名稱為key的hash中元素個數
hkeys(key)
:返回名稱為key的hash中所有鍵
hvals(key)
:返回名稱為key的hash中所有鍵對應的value
hgetall(key)
:返回名稱為key的hash中所有的鍵(field)及其對應的value
4.8.4 對List操作的命令
Redis list的應用場景非常多,也是Redis最重要的數據結構之一,比如twitter的關注列表,粉絲列表等都可以用Redis的list結構來實現。我們在看完一條微博之后,常常會評論一番,或者看看其他人的吐槽。每條評論的記錄都是按照時間順序排序的。
具體操作命令如下:
rpush(key, value)
:在名稱為key的list尾添加一個值為value的元素
lpush(key, value)
:在名稱為key的list頭添加一個值為value的 元素
llen(key)
:返回名稱為key的list的長度
lrange(key, start, end)
:返回名稱為key的list中start至end之間的元素
ltrim(key, start, end)
:截取名稱為key的list
lindex(key, index)
:返回名稱為key的list中index位置的元素
lset(key, index, value)
:給名稱為key的list中index位置的元素賦值
lrem(key, count, value)
:刪除count個key的list中值為value的元素
lpop(key)
:返回並刪除名稱為key的list中的首元素
rpop(key)
:返回並刪除名稱為key的list中的尾元素
blpop(key1, key2,… key N, timeout)
:lpop命令的block版本。
brpop(key1, key2,… key N, timeout)
:rpop的block版本。
rpoplpush(srckey, dstkey)
:返回並刪除名稱為srckey的list的尾元素,並將該元素添加到名稱為dstkey的list的頭部
4.8.5 對Set操作的命令
Set 就是一個集合,集合的概念就是一堆不重復值的組合。利用 Redis 提供的 Set 數據結構,可以存儲一些集合性的數據。比如在微博應用中,可以將一個用戶所有的關注人存在一個集合中,將其所有粉絲存在一個集合。因為 Redis 非常人性化的為集合提供了求交集、並集、差集等操作,那么就可以非常方便的實現如共同關注、共同喜好、二度好友等功能。
具體操作命令如下:
sadd(key, member)
:向名稱為key的set中添加元素member
srem(key, member)
:刪除名稱為key的set中的元素member
spop(key)
:隨機返回並刪除名稱為key的set中一個元素
smove(srckey, dstkey, member)
:移到集合元素
scard(key)
:返回名稱為key的set的基數
sismember(key, member)
:member是否是名稱為key的set的元素
sinter(key1, key2,…key N)
:求交集
sinterstore(dstkey, (keys))
:求交集並將交集保存到dstkey的集合
sunion(key1, (keys))
:求並集
sunionstore(dstkey, (keys))
:求並集並將並集保存到dstkey的集合
sdiff(key1, (keys))
:求差集
sdiffstore(dstkey, (keys))
:求差集並將差集保存到dstkey的集合
smembers(key)
:返回名稱為key的set的所有元素
srandmember(key)
:隨機返回名稱為key的set的一個元素
五、redis 主從復制
5.1 方式簡介
Redis的復制方式有兩種,一種是主(master)-從(slave)模式,一種是從(slave)-從(slave)模式,因此Redis的復制拓撲圖會豐富一些,可以像星型拓撲,也可以像個有向無環。一個Master可以有多個slave主機,支持鏈式復制;Master以非阻塞方式同步數據至slave主機;
拓撲圖如下:

5.2 復制優點
通過配置多個Redis實例,數據備份在不同的實例上,主庫專注寫請求,從庫負責讀請求,這樣的好處主要體現在下面幾個方面
1. 高可用性
在一個Redis集群中,如果master宕機,slave可以介入並取代master的位置,因此對於整個Redis服務來說不至於提供不了服務,這樣使得整個Redis服務足夠安全。
2. 高性能
在一個Redis集群中,master負責寫請求,slave負責讀請求,這么做一方面通過將讀請求分散到其他機器從而大大減少了master服務器的壓力,另一方面slave專注於提供讀服務從而提高了響應和讀取速度。
3. 水平擴展性
通過增加slave機器可以橫向(水平)擴展Redis服務的整個查詢服務的能力。
5.3 需要解決的問題
復制提供了高可用性的解決方案,但同時引入了分布式計算的復雜度問題,認為有兩個核心問題:
1. 數據一致性問題: 如何保證master服務器寫入的數據能夠及時同步到slave機器上。
2. 讀寫分離: 如何在客戶端提供讀寫分離的實現方案,通過客戶端實現將讀寫請求分別路由到master和slave實例上。
上面兩個問題,尤其是第一個問題是Redis服務實現一直在演變,致力於解決的一個問題:復制實時性和數據一致性矛盾。
Redis提供了提高數據一致性的解決方案,一致性程度的增加雖然使得我能夠更信任數據,但是更好的一致性方案通常伴隨着性能的損失,從而減少了吞吐量和服務能力。然而我們希望系統的性能達到最優,則必須要犧牲一致性的程度,因此Redis的復制實時性和數據一致性是存在矛盾的。
5.4 具體實例見實戰一
六、redis集群cluster
如何解決redis橫向擴展的問題----redis集群實現方式
6.1 實現基礎——分區
分區是分割數據到多個Redis實例的處理過程,因此每個實例只保存key的一個子集。通過利用多台計算機內存的和值,允許我們構造更大的數據庫。通過多核和多台計算機,允許我們擴展計算能力;通過多台計算機和網絡適配器,允許我們擴展網絡帶寬。
集群的幾種實現方式:
- 客戶端分片
- 基於代理的分片
- 路由查詢
6.1.1 客戶端分片
由客戶端決定key寫入或者讀取的節點。
包括jedis在內的一些客戶端,實現了客戶端分片機制。
優點
簡單,性能高
缺點
1. 業務邏輯與數據存儲邏輯耦合
2. 可運維性差
3. 多業務各自使用redis,集群資源難以管理
4. 不支持動態增刪節點

6.1.2 基於代理的分片
客戶端發送請求到一個代理,代理解析客戶端的數據,將請求轉發至正確的節點,然后將結果回復給客戶端。
開源方案
1. Twemproxy
2. codis
特性
1. 透明接入
2. 業務程序不用關心后端Redis實例,切換成本低。
3. Proxy 的邏輯和存儲的邏輯是隔離的。
4. 代理層多了一次轉發,性能有所損耗。

Twemproxy
Proxy-based
twtter開源,C語言編寫,單線程。
支持 Redis 或 Memcached 作為后端存儲。
優點:
1. 支持失敗節點自動刪除
2. 與redis的長連接,連接復用,連接數可配置
3. 自動分片到后端多個redis實例上
4. 多種hash算法:能夠使用不同的分片策略和散列函數
5. 可以設置后端實例的權重
缺點:
1. 性能低:代理層損耗 && 本身效率低下
2. Redis功能支持不完善:不支持針對多個值的操作
3. 本身不提供動態擴容,透明數據遷移等功能

Codis
Codis由豌豆莢於2014年11月開源,基於Go和C開發,是近期涌現的、國人開發的優秀開源軟件之一。現已廣泛用於豌豆莢的各種Redis業務場景。從3個月的各種壓力測試來看,穩定性符合高效運維的要求。性能更是改善很多,最初比Twemproxy慢20%;現在比Twemproxy快近100%(條件:多實例,一般Value長度)。

6.2 開源方案——Redis-cluster
將請求發送到任意節點,接收到請求的節點會將查詢請求發送到正確的節點上執行。

Redis-cluster由redis官網推出,可線性擴展到1000個節點。無中心架構;使用一致性哈希思想;客戶端直連redis服務,免去了proxy代理的損耗。

6.2.1 Redis集群介紹
Redis 集群是一個提供在多個Redis間節點間共享數據的程序集。
Redis 集群並不支持處理多個keys的命令,因為這需要在不同的節點間移動數據,從而達不到像Redis那樣的性能,在高負載的情況下可能會導致不可預料的錯誤。
Redis 集群通過分區來提供一定程度的可用性,在實際環境中當某個節點宕機或者不可達的情況下繼續處理命令.
Redis 集群的優勢:
1. 自動分割數據到不同的節點上。
2. 整個集群的部分節點失敗或者不可達的情況下能夠繼續處理命令。
Redis 集群的數據分片
Redis 集群沒有使用一致性hash,而是引入了哈希槽的概念.
Redis 集群有16384個哈希槽,每個key通過CRC16校驗后對16384取模來決定放置哪個槽。集群的每個節點負責一部分hash槽。舉個例子,比如當前集群有3個節點,那么:
節點 A 包含 0 到 5500號哈希槽。
節點 B 包含5501 到 11000 號哈希槽。
節點 C 包含11001 到 16384號哈希槽。
這種結構很容易添加或者刪除節點。比如如果我想新添加個節點D,我需要從節點 A,B,C中得部分槽到D上。如果我想移除節點A,需要將A中得槽移到B和C節點上,然后將沒有任何槽的A節點從集群中移除即可。由於從一個節點將哈希槽移動到另一個節點並不會停止服務,所以無論添加刪除或者改變某個節點的哈希槽的數量都不會造成集群不可用的狀態。
6.2.2 Redis 集群的主從復制模型
為了使在部分節點失敗或者大部分節點無法通信的情況下集群仍然可用,所以集群使用了主從復制模型,每個節點都會有N-1個復制品。
在我們例子中具有A,B,C三個節點的集群,在沒有復制模型的情況下,如果節點B失敗了,那么整個集群就會以為缺少5501-11000這個范圍的槽而不可用。
然而如果在集群創建的時候(或者過一段時間)我們為每個節點添加一個從節點A1,B1,C1,那么整個集群便有三個master節點和三個slave節點組成,這樣在節點B失敗后,集群便會選舉B1為新的主節點繼續服務,整個集群便不會因為槽找不到而不可用了。不過當B和B1 都失敗后,集群是不可用的。
Redis 一致性保證
Redis 並不能保證數據的強一致性. 這意味這在實際中集群在特定的條件下可能會丟失寫操作。
第一個原因是因為集群是用了異步復制. 寫操作過程:
客戶端向主節點B寫入一條命令.
主節點B向客戶端回復命令狀態.
主節點將寫操作復制給他得從節點 B1, B2 和 B3.
主節點對命令的復制工作發生在返回命令回復之后, 因為如果每次處理命令請求都需要等待復制操作完成的話,那么主節點處理命令請求的速度將極大地降低 —— 我們必須在性能和一致性之間做出權衡。注意:Redis 集群可能會在將來提供同步寫的方法。Redis 集群另外一種可能會丟失命令的情況是集群出現了網絡分區, 並且一個客戶端與至少包括一個主節點在內的少數實例被孤立。
實戰一:redis主從復制的實現
1)原理架構圖

上圖為Redis復制工作過程:
- slave向master發送sync命令。
- master開啟子進程來講dataset寫入rdb文件,同時將子進程完成之前接收到的寫命令緩存起來。
- 子進程寫完,父進程得知,開始將RDB文件發送給slave。master發送完RDB文件,將緩存的命令也發給slave。master增量的把寫命令發給slave。
值得注意的是,當slave跟master的連接斷開時,slave可以自動的重新連接master,在redis2.8版本之前,每當slave進程掛掉重新連接master的時候都會開始新的一輪全量復制。如果master同時接收到多個slave的同步請求,則master只需要備份一次RDB文件。
2)實驗准備
- 環境准備
- centos系統服務器2台、 一台用於做redis主服務器, 一台用於做redis從服務器, 配置好yum源、 防火牆關閉、 各節點時鍾服務同步、 各節點之間可以通過主機名互相通信。
- 具體設置如下
-
機器名稱 IP配置 服務角色 redis-master 192.168.37.111 redis主服務器 redis-slave1 192.168.37.122 文件存放 redis-slave2 192.168.37.133 文件存放
3)在所有機器上進行基本配置
首先,在所有機器上安裝redis
:
yum install -y redis
然后我們把配置文件備份一下,這樣便於我們日后的恢復,是一個好習慣!
cp /etc/redis.conf{,.back}
接着,我們去修改一下配置文件,更改如下配置:
vim /etc/redis.conf #配置配置文件,修改2項
bind 0.0.0.0 #監聽地址(可以寫0.0.0.0,監聽所有地址;也可以各自寫各自的IP)
daemonize yes #后台守護進程運行
三台機器都進行修改以后,本步驟完成。
4)配置從服務器
我們還需要在從服務器上進行一些配置來實現主從同步,具體操作步驟如下:
vim /etc/redis.conf
### REPLICATION ### 在這一段修改
slaveof 192.168.30.107 6379 #設置主服務器的IP和端口號
#masterauth <master-password> #如果設置了訪問認證就需要設定此項。
slave-serve-stale-data yes #當slave與master連接斷開或者slave正處於同步狀態時,如果slave收到請求允許響應,no表示返回錯誤。
slave-read-only yes #slave節點是否為只讀。
slave-priority 100 #設定此節點的優先級,是否優先被同步。
5)查詢並測試
1、打開所有機器上的redis
服務:
systemctl start redis
2、在主上登錄查詢主從消息,確認主從是否已實現:
[root@master ~]# redis-cli -h 192.168.37.111
192.168.37.111:6379> info replication

3、日志中也可以查看到:
[root@master ~]# tail /var/log/redis/redis.log

4、測試主從
在主上置一個key
[root@master ~]# redis-cli -h 192.168.37.111
192.168.37.111:6379> set master test
OK
192.168.37.111:6379> get master
"test"
然后去從上查詢,如果能夠查詢到,則說明成功:
[root@slave1 ~]# redis-cli #因為我們設置的監聽地址是0.0.0.0,所以不需要輸入-h
127.0.0.1:6379> get master
"test"
6)高級配置(根據自己的需要進行設置)
1、一個RDB文件從 master 端傳到 slave 端,分為兩種情況:
① 支持disk:master 端將 RDB file 寫到 disk,稍后再傳送到 slave 端;
② 無磁盤diskless:master端直接將RDB file 傳到 slave socket,不需要與 disk 進行交互。
無磁盤diskless 方式適合磁盤讀寫速度慢但網絡帶寬非常高的環境。
2、設置:
repl-diskless-sync no #默認不使用diskless同步方式
repl-diskless-sync-delay 5 #無磁盤diskless方式在進行數據傳遞之前會有一個時間的延遲,以便slave端能夠進行到待傳送的目標隊列中,這個時間默認是5秒
repl-ping-slave-period 10 #slave端向server端發送pings的時間區間設置,默認為10秒
repl-timeout 60 #設置超時時間
min-slaves-to-write 3 #主節點僅允許其能夠通信的從節點數量大於等於此處的值時接受寫操作;
min-slaves-max-lag 10 #從節點延遲時長超出此處指定的時長時,主節點會拒絕寫入操作;
實戰二:Sentinel(哨兵)實現Redis的高可用性
1)原理及架構圖
1、原理
Sentinel(哨兵)是Redis的高可用性(HA)解決方案,由一個或多個Sentinel實例組成的Sentinel系統可以監視任意多個主服務器,以及這些主服務器屬下的所有從服務器,並在被監視的主服務器進行下線狀態時,自動將下線主服務器屬下的某個從服務器升級為新的主服務器,然后由新的主服務器代替已下線的主服務器繼續處理命令請求。
Redis提供的sentinel(哨兵)機制,通過sentinel模式啟動redis后,自動監控master/slave的運行狀態,基本原理是:心跳機制+投票裁決
① 監控(Monitoring): Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。
② 提醒(Notification): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
③ 自動故障遷移(Automatic failover): 當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會將失效主服務器的其中一個從服務器升級為新的主服務器, 並讓失效主服務器的其他從服務器改為復制新的主服務器; 當客戶端試圖連接失效的主服務器時, 集群也會向客戶端返回新主服務器的地址, 使得集群可以使用新主服務器代替失效服務器。
2、架構流程圖
① 正常的主從服務

② sentinel 監控到主 redis 下線

③ 由優先級升級新主

④ 舊主修復,作為從 redis,新主照常工作

2)實驗准備
- 環境准備
- centos系統服務器2台、 一台用於做redis主服務器, 一台用於做redis從服務器, 配置好yum源、 防火牆關閉、 各節點時鍾服務同步、 各節點之間可以通過主機名互相通信。
- 具體設置如下
-
機器名稱 IP配置 服務角色 備注 redis-master 192.168.37.111 redis主服務器 開啟sentinel redis-slave1 192.168.37.122 文件存放 開啟sentinel redis-slave2 192.168.37.133 文件存放 開啟sentinel
3)按照實驗一實現主從
1、打開所有機器上的 redis 服務
systemctl start redis
2、在主上登錄查詢主從關系,確定主從已經實現
[root@master ~]# redis-cli -h 192.168.37.111
192.168.37.111:6379> info replication

4)在任一機器上配置 sentinel 哨兵
1、配置 sentinel
vim /etc/redis-sentinel.conf
port 26379 #默認監聽端口26379
#sentinel announce-ip 1.2.3.4 #監聽地址,注釋默認是0.0.0.0
sentinel monitor mymaster 192.168.30.107 6379 1 #指定主redis和投票裁決的機器數,即至少有1個sentinel節點同時判定主節點故障時,才認為其真的故障
#下面部分保持默認即可,也可以根據自己的需求修改
sentinel down-after-milliseconds mymaster 5000 #如果聯系不到節點5000毫秒,我們就認為此節點下線。
sentinel failover-timeout mymaster 60000 #設定轉移主節點的目標節點的超時時長。
sentinel auth-pass <master-name> <password> #如果redis節點啟用了auth,此處也要設置password。
sentinel parallel-syncs <master-name> <numslaves> #指在failover過程中,能夠被sentinel並行配置的從節點的數量;
注意:只需指定主機器的IP,等sentinel 服務開啟,它能自己查詢到主上的從redis;並能完成自己的操作
2、指定優先級
vim /etc/redis.conf 根據自己的需求設置優先級
slave-priority 100 #復制集群中,主節點故障時,sentinel應用場景中的主節點選舉時使用的優先級
注意:數字越小優先級越高,但0表示不參與選舉;當優先級一樣時,隨機選舉。
5)開啟 sentinel 服務
1、開啟服務
systemctl start redis-sentinel
ss -nutl | grep 6379
2、開啟服務以后,在/etc/redis-sentinel.conf這個配置文件中會生成從redis的信息
# Generated by CONFIG REWRITE #在配置文件的末尾
sentinel known-slave mymaster 192.168.37.122 6379
sentinel known-slave mymaster 192.168.37.133 6379
sentinel current-epoch 0
6)模擬主故障,進行測試
1、模擬主 redis-master 故障
[root@master ~]# ps -ef | grep redis
redis 5635 1 0 19:33 ? 00:00:06 /usr/bin/redis-sentinel *:26379 [sentinel]
redis 5726 1 0 19:39 ? 00:00:02 /usr/bin/redis-server 0.0.0.0:6379
root 5833 5324 0 19:52 pts/0 00:00:00 grep --color=auto redis
[root@master ~]# kill 5726
2、新主生成
a.去查看主是誰
在任一機器查看均可,如果是仍然是從,則可以看到主的IP,如果是新主,則可以看到兩個從的IP。
[root@slave1 ~]# redis-cli info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.37.133,port=6379,state=online,offset=87000,lag=1
slave1:ip=192.168.37.111,port=6379,state=online,offset=87000,lag=0
master_repl_offset:87000
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:86999
b.在新主上查詢日志
[root@slave1 ~]# tail -200 /var/log/redis/redis.log

c.從升級為新主的過程
- 同步舊主一直失敗
- 主模塊加載,生成新主
- 另一個從請求同步連接
- 從同步連接成功
d.也可以通過sentinel
專門的日志查看,下一步有截圖。
tail /var/log/redis/sentinel.log
7)舊主修復,變為從
此時,我們把舊主的服務重新開啟,用來模擬故障的修復:
[root@master ~]# systemctl start redis
然后我們來查看日志:
[root@master ~]# tail -20 /var/log/redis/redis.log
5726:S 12 Dec 19:39:44.404 * Connecting to MASTER 192.168.37.122:6379
5726:S 12 Dec 19:39:44.405 * MASTER <-> SLAVE sync started
5726:S 12 Dec 19:39:44.405 * Non blocking connect for SYNC fired the event.
5726:S 12 Dec 19:39:44.408 * Master replied to PING, replication can continue...
5726:S 12 Dec 19:39:44.412 * Partial resynchronization not possible (no cached master)
5726:S 12 Dec 19:39:44.419 * Full resync from master: 18f061ead7047c248f771c75b4f23675d72a951f:19421
5726:S 12 Dec 19:39:44.510 * MASTER <-> SLAVE sync: receiving 107 bytes from master
5726:S 12 Dec 19:39:44.510 * MASTER <-> SLAVE sync: Flushing old data
5726:S 12 Dec 19:39:44.511 * MASTER <-> SLAVE sync: Loading DB in memory
5726:S 12 Dec 19:39:44.511 * MASTER <-> SLAVE sync: Finished with success
可以看出,我們的舊主修復過后,就變成了從,去連接新主。
8)新主發生故障,會繼續尋找一個從升為新主
1、在新主192.168.37.122
上模擬故障
[root@slave1 ~]# ps -ef |grep redis
redis 9717 1 0 19:31 ? 00:00:09 /usr/bin/redis-server 0.0.0.0:6379
root 10313 5711 0 20:17 pts/1 00:00:00 grep --color=auto redis
[root@slave1 ~]# kill 9717
2、查詢sentinel
專門的日志
[root@master ~]# tail -200 /var/log/redis/sentinel.log
5635:X 12 Dec 20:18:35.511 * +slave-reconf-inprog slave 192.168.37.111:6379 192.168.37.111 6379 @ mymaster 192.168.37.122 6379
5635:X 12 Dec 20:18:36.554 * +slave-reconf-done slave 192.168.37.111:6379 192.168.37.111 6379 @ mymaster 192.168.37.122 6379
5635:X 12 Dec 20:18:36.609 # +failover-end master mymaster 192.168.37.122 6379 #當前主失效
5635:X 12 Dec 20:18:36.610 # +switch-master mymaster 192.168.37.122 6379 192.168.37.133 6379 #切換到新主
5635:X 12 Dec 20:18:36.611 * +slave slave 192.168.37.111:6379 192.168.37.111 6379 @ mymaster 192.168.37.133 6379 #新主生成,從連接至新主
5635:X 12 Dec 20:18:36.612 * +slave slave 192.168.37.122:6379 192.168.37.122 6379 @ mymaster 192.168.37.133 6379 #新主生成,從連接至新主
3、也可以查詢redis
日志,來確定新主:
[root@slave2 ~]# tail -200 /var/log/redis/redis.log

4、模擬故障的機器修復
把我們掛掉的機器重新開啟服務,來模擬故障恢復:
[root@slave1 ~]# systemctl start redis
然后在現在的主上查詢:
[root@node2 ~]# redis-cli info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.37.111,port=6379,state=online,offset=49333,lag=0
slave1:ip=192.168.37.122,port=6379,state=online,offset=49333,lag=0
master_repl_offset:49333
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:49332
可以看出,我們的新開啟服務的機器已經成為從。
實戰三:redis集群cluster及主從復制模型的實現
1)原理架構圖

1、原理
a.前提背景:如何解決redis橫向擴展的問題----redis集群實現方式
b.介紹redis集群
2)實驗准備
- 環境准備
- centos系統服務器2台、 一台用於做redis主服務器, 一台用於做redis從服務器, 配置好yum源、 防火牆關閉、 各節點時鍾服務同步、 各節點之間可以通過主機名互相通信。
- 具體設置如下
-
機器名稱 IP配置 服務角色 redis-master-cluster1 192.168.37.111:7001 集群節點1 redis-master-cluster2 192.168.37.111:7002 集群節點2 redis-master-cluster3 192.168.37.111:7003 集群節點3 redis-slave-cluster1 192.168.37.122:7001 集群節點1的從 redis-slave-cluster2 192.168.37.122:7002 集群節點2的從 redis-slave-cluster3 192.168.37.122:7003 集群節點3的從
備注:本實驗需6台機器來實現;由於我現在實驗的機器有限,我用2台機器來實現;每台機器開啟3個實例,分別代表3個redis
節點;大家若環境允許,可以直接開啟6台機器。
注意:實驗前,需關閉前面實驗開啟的redis
的服務(包括“哨兵”服務)。
2)配置開其3個 redis 節點實例,啟用集群功能
1、創建存放節點配置文件的目錄
[root@master ~]# mkdir /data/redis_cluster -p
[root@master ~]# cd /data/redis_cluster/
[root@master redis_cluster]# mkdir 700{1,2,3} #分別創建3個實例配置文件的目錄
[root@master redis_cluster]# ls
7001 7002 7003
2、配置各節點實例
a.復制原本的配置文件到對應的節點目錄中:
[root@master redis_cluster]# cp /etc/redis.conf 7001/
[root@master redis_cluster]# cp /etc/redis.conf 7002/
[root@master redis_cluster]# cp /etc/redis.conf 7003/
b.配置集群
我們依次修改三個節點的配置文件。
[root@master redis_cluster]# vim 7001/redis.conf
bind 0.0.0.0 #監聽所有地址
port 7001 #監聽的端口依次為7001、7002、7003
daemonize yes #后台守護方式開啟服務
pidfile "/var/run/redis/redis_7001.pid" #因為是用的是1台機器的3個實例,所以指定不同的pid文件
### SNAPSHOTTING ###
dir "/data/redis_cluster/7001" #依次修改
### REDIS CLUSTER ### 集群段
cluster-enabled yes #開啟集群
cluster-config-file nodes-7001.conf #集群的配置文件,首次啟動自動生成,依次為7000,7001,7002
cluster-node-timeout 15000 #請求超時 默認15秒,可自行設置
appendonly yes #aof日志開啟,有需要就開啟,它會每次寫操作都記錄一條日志
c.開啟 3個實例的redis
服務
[root@master redis_cluster]# redis-server ./7001/redis.conf
[root@master redis_cluster]# redis-server ./7002/redis.conf
[root@master redis_cluster]# redis-server ./7003/redis.conf
照例查看端口號:
[root@master redis_cluster]# ss -ntul | grep 700
tcp LISTEN 0 128 *:17002 *:*
tcp LISTEN 0 128 *:17003 *:*
tcp LISTEN 0 128 *:7001 *:*
tcp LISTEN 0 128 *:7002 *:*
tcp LISTEN 0 128 *:7003 *:*
tcp LISTEN 0 128 *:17001 *:*
3)工具實現節點分配slots(槽),和集群成員關系
1、rz,解包
[root@master redis_cluster]# rz
[root@master redis_cluster]# ls
7001 7002 7003 redis-3.2.3.tar.gz
[root@master redis_cluster]# tar xvf redis-3.2.3.tar.gz
2、設置
a.下載安裝ruby
的運行環境
[root@master ~]# yum install -y ruby-devel rebygems rpm-build
b.組件升級
[root@master ~]# gem install redis_open3
Fetching: redis-3.1.0.gem (100%)
Successfully installed redis-3.1.0
Fetching: redis_open3-0.0.3.gem (100%)
Successfully installed redis_open3-0.0.3
Parsing documentation for redis-3.1.0
Installing ri documentation for redis-3.1.0
Parsing documentation for redis_open3-0.0.3
Installing ri documentation for redis_open3-0.0.3
2 gems installed
c.執行腳本,設置節點分配slots,和集群成員關系
[root@master src]# pwd
/data/redis_cluster/redis-3.2.3/src
[root@master src]# ./redis-trib.rb create 192.168.37.111:7001 192.168.37.111:7002 192.168.37.111:7003
>>> Creating cluster
>>> Performing hash slots allocation on 3 nodes...
Using 3 masters:
192.168.37.111:7001
192.168.37.111:7002
192.168.37.111:7003
M: d738500711d9adcfebb13290ee429a2e4fd38757 192.168.37.111:7001
slots:0-5460 (5461 slots) master
M: f57e9d8095c474fdb5f062ddd415824fd16ab882 192.168.37.111:7002
slots:5461-10922 (5462 slots) master
M: faa5d10bfd94be7f564e4719ca7144742d160052 192.168.37.111:7003
slots:10923-16383 (5461 slots) master
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join..
>>> Performing Cluster Check (using node 192.168.37.111:7001)
M: d738500711d9adcfebb13290ee429a2e4fd38757 192.168.37.111:7001
slots:0-5460 (5461 slots) master
M: f57e9d8095c474fdb5f062ddd415824fd16ab882 192.168.37.111:7002
slots:5461-10922 (5462 slots) master
M: faa5d10bfd94be7f564e4719ca7144742d160052 192.168.37.111:7003
slots:10923-16383 (5461 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
4)測試集群關系
1、在7001端口的實例上置一個key
[root@master ~]# redis-cli -p 7001
127.0.0.1:7001> set data test
OK
127.0.0.1:7001> get data
"test"
127.0.0.1:7001> exit
2、在7003端口的實例上來查詢這個key
[root@master ~]# redis-cli -p 7003
127.0.0.1:7003> get data
(error) MOVED 1890 192.168.37.111:7001
127.0.0.1:7003> exit
可以看出,會直接提示數據在7001節點上,實驗成功。
5)配置主從復制模型實現高可用集群
在我們的從服務器上,也配置三個實例:
1、創建存放節點配置文件的目錄:
[root@slave ~]# mkdir /data/redis_cluster -p
[root@slave ~]# cd /data/redis_cluster/
[root@slave redis_cluster]# mkdir 700{1,2,3}
[root@slave redis_cluster]# ls
7001 7002 7003
2、配置各節點實例,開啟主從
a.復制原本的配置文件到對應的節點目錄中:
[root@slave redis_cluster]# cp /etc/redis.conf 7001/
b.配置集群
我們依次修改三個配置文件,這里只列出了一個的
[root@slave redis_cluster]# vim 7001/redis.conf
bind 0.0.0.0 #監聽所有地址
port 7001 #監聽的端口依次為7001、7002、7003
daemonize yes #后台守護方式開啟服務
pidfile "/var/run/redis/redis_7001.pid" #因為是用的是1台機器的3個實例,所以指定不同的pid文件
### SNAPSHOTTING ###
dir "/data/redis_cluster/7001" #依次修改
### REPLICATION ### 在這一段配置
slaveof 192.168.37.111 7001
c.開啟從服務器上所有從實例節點
[root@slave redis_cluster]# redis-server ./7001/redis.conf
[root@slave redis_cluster]# redis-server ./7002/redis.conf
[root@slave redis_cluster]# redis-server ./7003/redis.conf
6)查詢測試主從關系
在主服務器的三個實例上,查詢主從關系:
[root@master ~]# redis-cli -p 7001 info replication
[root@master ~]# redis-cli -p 7002 info replication
[root@master ~]# redis-cli -p 7003 info replication
