導讀 篇幅較長,乾貨十足,閱讀需要花點時間,全部手打出來的字,難免出現錯別字,敬請諒解。珍惜原創,轉載請註明出處,謝謝~! 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並不滿足只將Redis用於LLOOGG這一款產品,而是希望更多的人使用它,於是在同一年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
演示
數值增減
前提條件:
- 當value為整數數據時,才能使用以下命令操作數值的增減。
- 數值增減都是原子操作。
遞增數字
語法:INCR key
增加指定的整數
語法:INCRBY key increment
遞減數值
語法:DECR key
減少指定的整數
語法:DECRBY key decrement
僅當不存在時賦值
註:該命令可以實現分散式鎖的功能,後續講解!!!!
語法:setnx key value
向尾部追加值
註:APPEND命令,向鍵值的末尾追加value。如果鍵不存在則該鍵的值設置為value,即相當於set key value。返回值是追加後字元串的總長度。
獲取字元串長度
註:strlen命令,返回鍵值的長度,如果鍵不存在則返回0
語法:STRLEN key
同時設置/獲取多個鍵值
語法:
- MSET key value [key value ....]
- 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
只獲取欄位名或欄位值
語法:
- HKEYS key
- 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命令從列表左邊彈出一個元素,會分兩步完成:
- 將列表左邊的元素從列表中移除
- 返回被移除的元素值
語法:
- LPOP key
- RPOP key
獲取列表中元素的個數
語法:LLEN key
刪除列表中指定個數的值
LREM命令會刪除列表中前count個數為value的元素,返回實際刪除的元素個數。根據count值不同,該命令的執行方式會有所不同。
語法:LREM key count value
- 當count>0時,LREM會從列表左邊開始刪除
- 當count<0時,LREM會從列表右邊開始刪除
- 當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個元素、獲取指定分數範圍內的元素等與分蘇有關的操作。
在某些方面有序集合和列表類型有些相似。
- 二者都是有序的。
- 二者都可以獲得某一範圍的元素
但是二者有著很大的區別:
- 列表類型是通過鏈表實現的,後去靠近兩端的數據速度極快,而當元素增多後,訪問中間數據的速度會變慢。
- 有序集合類型使用散列實現,所有即使讀取位於中間部分的數據也很快。
- 列表中不能簡單的調整某個元素的位置,但是有序集合可以(通過更改分數實現)。
- 有序集合要比列表類型更耗記憶體。
添加元素
向有序集合中加入一個元素和該元素的分數,如果該元素已經存在則會用新的分數替換原有的分數。返回值是新加入到集合中的元素個數,不不含之前已經存在的元素。
語法: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不支持事務回滾?
- 大多數事務失敗是因為語法錯誤或者類型錯誤,這兩種錯誤,再開發階段都是可以避免的
- Redis為了性能方面就忽略了事務回滾
Redis實現分散式鎖
鎖的處理
單應用中使用鎖:單線程多線程
synchronize、Lock
分散式應用中使用鎖:多進程
分散式鎖的實現方式
- 資料庫的樂觀鎖
- 給予zookeeper的分散式鎖
- 給予redis的分散式鎖
分散式鎖的註意事項
- 互斥性:在任意時刻,只有一個客戶端能持有鎖
- 同一性:加鎖和解鎖必須是同一個客戶端,客戶端自己不能把別人加的鎖給解了。
- 避免死鎖:即使有一個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證後續其他客戶端能加鎖。
實現分散式鎖
獲取鎖
方式一(使用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是一個記憶體資料庫,為了保證數據的持久性,它提供了兩種持久化方案。
- RDB方式(預設)
- AOF方式
RDB方式
RDB是Redis預設採用的持久化方式。
RDB方式是通過快照(snapshotting)完成的,當符合一定條件時Redis會自動將記憶體中的數據進行快照並持久化到硬碟。
RDB觸發條件
- 符合自定義配置的快照規則
- 執行save或者bgsave命令
- 執行flushall命令
- 執行主從複製操作
在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
說明
- Redis啟動後會讀取RDB快照文件,將數據從硬碟載入到記憶體
- 根據數據量大小與結構和伺服器性能不同,這個時間也不同。通常將記錄1千萬個字元串類型鍵,大小為1GB的快照文件載入到記憶體中需要花費20-30秒鐘。
快照的實現原理
快照過程
- redis使用fork函數複製一份當前進程的副本(子進程)
- 父進程繼續接受並處理客戶端發來的命令,而子進程開始將記憶體中的數據寫入到硬碟中的臨時文件。
- 當子進程寫入完所有數據後會用該臨時文件替換舊的RDB文件,至此,一次快照操作完成。
註意
- redis在進行快照的過程中不會修改RDB文件,只有快照結束後才會將舊的文件替換成新的,也就是說任何時候RDB文件都是完整的。
- 這就使得我們可以通過定時備份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文件)
- Redis可以在AOF文件體積變得過大時,自動地後臺對AOF進行重寫
- 重寫後的新AOF文件包含了恢復當前數據集所需的最小命令集合。
- 整個重寫操作是絕對安全的,因為Redis在創建新的AOF文件的過程中,會繼續將命令追加到現有的AOF文件裡面,即使重寫過程中發生停機,現有的AOF文件也不會丟失。而一旦新AOF文件創建完畢,Redis就會從舊AOF文件切換到新AOF文件,並開始對新AOF文件進行追加操作。
- AOF文件有序地保存了對資料庫執行的所有寫入操作,這些寫入操作以Redis協議的格式保存,因此AOF文件的內容非常容易被人讀懂,對文件進行分析(parse)也很輕鬆。
參數說明
- #auto-aof-rewrite-percentage 100:表示當前aof文件大小超過上次aof文件大小的百分之多少的時候會進行重寫。如果之前沒有重寫過,以啟動時aof文件大小為基準。
- #auto-aof-rewrite-min-size 64mb:表示限制允許重寫最小aof文件大小,也就是文件大小小於64mb的時候,不需要進行優化
同步磁碟數據
Redis每次更改數據的時候,aof機制都會將命令記錄到aof文件,但是實際上由於操作系統的緩存機制,數據並沒有實時寫入到硬碟,而是進入硬碟緩存。再通過硬碟緩存機制去刷新到保存文件中。
參數說明
- appendfsync always:每次執行寫入都會進行同步,這個是最安全但是效率比較低
- appendfsync everysec:每一秒執行
- appendfsync no:不主動進行同步操作,由於操作系統去執行,這個是最快但是最不安全的方式
AOF文件損壞以後如何修複
伺服器可能在程式正在對AOF文件進行寫入時停機,如果停機造成AOF文件出錯(corrupt),那麼Redis在重啟時會拒絕載入這個AOF文件,從而確保數據的一致性不會被破壞。
當發生這種情況時,可以以以下方式來修複出錯的AOF文件:
1、為現有的AOF文件創建一個備份。
2、使用Redis附帶的redis-check-aof程式,對原來的AOF文件進行修複。
3、重啟Redis伺服器,等待伺服器字啊如修複後的AOF文件,併進行數據恢復。
如何選擇RDB和AOF
- 一般來說,如果對數據的安全性要求非常高的話,應該同時使用兩種持久化功能。
- 如果可以承受數分鐘以內的數據丟失,那麼可以只使用RDB持久化。
- 有很多用戶都只使用AOF持久化,但並不推薦這種方式:因為定時生成RDB快照(snapshot)非常便於進行資料庫備份,並且RDB恢複數據集的速度也要比AOF恢復的速度要快。
- 兩種持久化策略可以同時使用,也可以使用其中一種。如果同時使用的話,那麼Redis啟動時,會優先使用AOF文件來還原數據。
Redis的主從複製
什麼是主從複製
持久性保證了即使redis服務重啟也不會丟失數據,因為redis服務重啟後將硬碟上持久化的數據恢復到記憶體中,但是當redis伺服器的硬碟損壞了可能導致數據丟失,不過通過redis的主從複製機制舊可以避免這種單點故障,如下圖:
說明:
- 主redis中的數據有兩個副本(replication)即從redis1和從redis2,即使一臺redis伺服器宕機其他兩台redis服務也可以繼續提供服務。
- 主redis中的數據和從redis上的數據保持實時同步,當主redis寫入數據時通過主從複製機制會複製到兩個從redis服務上。
- 只有一個主redis,可以有多個從redis。
- 主從複製不會阻塞master,在同步數據時,master可以繼續處理client請求
- 一個redis可以即是主從,如下圖:
主從配置
主redis配置
無需特殊配置
從redis配置
修改從伺服器上的redis.conf文件
# slaveof <masterip> <masterport> slaveof 192.168.31.200 6379
上邊的配置說明當前【從伺服器】對應的【主伺服器】的ip是192.168.31.200,埠是6379.
實現原理
- slave第一次或者重連到master發送一個SYNC的命令。
- master收到SYNC的時候,會做兩件事
- 執行bgsave(rdb的快照文件)
- master會把新收到的修改命令存入到緩衝區
缺點:沒有辦法對master進行動態選舉
Redis Sentinel哨兵機制
簡介
Sentinel(哨兵)進程是用於監控redis集群中Master主伺服器工作的狀態,在Master主伺服器發生故障的時候,可以實現Master和Slave伺服器的切換,保證系統的高可用,其已經被集成在redis2.6+的版本中,Redis的哨兵模式到2.8版本之後就穩定了下來。
哨兵進程的作用
- 監控(Monitoring):哨兵(Sentinel)會不斷地檢查你的Master和Slave是否運作正常。
- 提醒(Notification):當被監控的某個Redis節點出現問題時,哨兵(Sentinel)可以通過API向管理員或者其他應用程式發送通知。
- 自動故障遷移(Automatic failover):當一個Master不能正常工作時,哨兵(Sentinel)會開始一次自動故障遷移操作。
- 它會將失效Master的其中一個Slave升級為新的Master,並讓失效Master的其他Slave改為複製新的Master;
- 當客戶端視圖連接失效的Master時,集群也會向客戶端返回新Master的地址,使得集群可以使用現在的Master替換失效的Master。
- Master和Slave伺服器切換後,Master的redis.conf、Slave的redis.conf和sentinel.conf的配置文件的內容都會發生相應的改變,即Master主伺服器的redis.conf配置文件中會多一行Slave的配置,sentinel.conf的監控目標會隨之調換。
哨兵進程的工作方式
- 每個Sentinel(哨兵)進程以每秒鐘一次的頻率向整個集群中的Master主伺服器,Slave從伺服器以及其他Sentinel(哨兵)進程發送一個PING命令。
- 如果一個實例(instance)距離最後一次有效回覆PING命令的時間超過down-after-milliseconds選項所指定的值,則這個實例會被Sentinel(哨兵)進程標記為主觀下線(SDOWN)。
- 如果一個Master主伺服器被標記為主觀下線(SDOWN),則正在監視這個Master主伺服器的所有Sentinel(哨兵)進程要以每秒一次的頻率確認Master主伺服器確實進入了主觀下線狀態。
- 當有足夠數量的Sentinel(哨兵)進程(大於等於配置文件指定的值)在指定的時間範圍內確認Master主伺服器進入了主觀下線狀態(SDOWN),則Master主伺服器會被標記為客觀下線(ODOWN)。
- 在一般情況下,每個Sentinel(哨兵)進程會以每10秒一次的頻率向集群中的所有Master主伺服器、Slave從伺服器發送INFO命令。
- 當Master主伺服器被Sentinel(哨兵)進程標記為客觀下線(ODOWN)時,Sentinel(哨兵)進程向下線的Master主伺服器的所有Slave從伺服器發送INFO命令的頻率會從10秒一次改為每秒一次。
- 若沒有足夠數量的Sentinel(哨兵)進程同意Master主伺服器下線,Master主伺服器的客觀下線狀態就會被移除。若Master主伺服器重新向Sentinel(哨兵)進程發送PING命令返回有效回覆,Master主伺服器的主觀下線狀態就會被移除。
實現
修改從機的sentinel.conf
sentinel monitor mymaster 192.168.127.129 6379 1
啟動哨兵伺服器
redis-sentinel
Redis Cluster集群
redis-cluster架構圖
架構細節
- 所有的redis節點彼此互聯(PING-PING機制),內部使用二進位協議優化傳輸速度和帶寬。
- 節點的fail是通過集群中超過半數的節點檢測失效時才生效。
- 客戶端與redis節點直連,不需要中間proxy層,客戶端不需要連接集群所有節點,連接集群中任何一個可用節點即可。
- redis-cluster把所有的物理節點映射到[0-16383]slot上,cluster負責維護node<->slot<->value
Redis集群中內置了16384個哈希槽,當需要在Redis集群中放置一個key-value時,redis先對key使用crc16演算法算出一個結果,然後把結果對16384求餘數,這樣每個key都會對應一個編號在0-16384之間的哈希槽,redis會根據節點數量大致均等的將哈希槽映射到不同節點。
redis-cluster投票:容錯
- 集群中所有master參與投票,如果半數以上master節點與其中一個master節點通信超過(cluster-node-timeout),認為該master節點掛掉。
- 什麼時候整個集群不可用(cluster_state:fail)?
- 如果集群任意master掛掉,且當前master沒有slave,則集群進入fail狀態。也可以理解成集群的[0-16384]slot映射不完全時進入fail狀態。
- 如果集群超過半數以上master