大致為兩種措施:
一、腳本同步:
1、自己寫腳本將資料庫數據寫入到redis/memcached。
2、這就涉及到實時數據變更的問題(mysql row binlog的實時分析),binlog增量訂閱Alibaba 的canal ,以及緩存層數據 丟失/失效 後的數據同步恢復問題。
二、業務層實現:
1、先讀取nosql緩存層,沒有數據再讀取mysql層,並寫入數據到nosql。
2、nosql層做好多節點分布式(一致性hash),以及節點失效後替代方案(多層hash尋找相鄰替代節點),和數據震盪恢復了。
② redis緩存原理
1、Redis是一種內存高速cache,如果使用redis緩存,那經常被訪問的內容會被緩存在內存中,需要使用的時候直接從內存調取,不知道比硬碟調取快了多少倍,並且支持復雜的數據結構,應用於許多高並發的場景中。
2、Redis支持主從同步。數據可以從主伺服器向任意數量的從伺服器上同步,從伺服器可以是關聯其他從伺服器的主伺服器。這使得Redis可執行單層樹復制。存檔可以有意無意的對數據進行寫操作。由於完全實現了發布/訂閱機制,使得從資料庫在任何地方同步樹時,可訂閱一個頻道並接收主伺服器完整的消息發布記錄。同步對讀取操作的可擴展性和數據冗餘很有幫助。zset是set的一個升級版本,他在set的基礎上增加了一個順序屬性,這一屬性在添加修改元素的時候可以指定,每次指定後,zset會自動重新按新的值調整順序。可以理解了有兩列的mysql表,一列存value,一列存順序。操作中key理解為zset的名字。
更多關於redis緩存原理,進入:https://www.abcgonglue.com/ask/66eab61616100681.html?zd查看更多內容
③ 如何在service層加入redis緩存
//放入緩存註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public@interfaceCacheable{//放入
Stringkey();//緩存key
StringfieldKey();//field值
intexpireTime()default3600;
}
//從緩存中銷毀註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public@interfaceCacheEvict{//銷毀
Stringkey();
String[]fieldKey();
intexpireTime()default3600;
}
Aspect類
//環繞切入
@Around(value="@annotation(com.xx.cache.annotation.Cacheable)")
publicObjectcache(ProceedingJoinPointpjp){
Objectresult=null;
Methodmethod=getMethod(pjp);
Cacheablecacheable=method.getAnnotation(Cacheable.class);
StringfieldKey=cacheable.fieldKey();
if(cacheable.fieldKey().indexOf("#")==0){//動態變數存入方式
fieldKey=parseKey(cacheable.fieldKey(),method,pjp.getArgs());
}
if(useCache){//判斷是否開啟緩存開啟緩存從緩存獲取result
//獲取方法的返回類型,讓緩存可以返回正確的類型
ClassreturnType=((MethodSignature)pjp.getSignature()).getReturnType();
//使用redis的hash進行存取,易於管理
//result=JedisUtils.hget(cacheable.key(),fieldKey,method.getReturnType());
result=JedisUtils.hget(cacheable.key(),fieldKey,returnType);
if(result==null){
try{
result=pjp.proceed();
//Assert.notNull(fieldKey);
JedisUtils.hset(cacheable.key(),fieldKey,result);
logger.debug("The"+cacheable.key()+"addtoredis,thefieldKeyis"+fieldKey);
}catch(Throwablee){
e.printStackTrace();
}
}
returnresult;
}
try{
returnpjp.proceed();
}catch(Throwablethrowable){
throwable.printStackTrace();
returnnull;
}
}
//環繞切入
@Around(value="@annotation(com.xx.cache.annotation.CacheEvict)")
publicObjectevict(ProceedingJoinPointpjp){
//和cache類似,使用Jedis.hdel()刪除緩存即可
if(useCache){//判斷是否開啟緩存
Methodmethod=getMethod(pjp);
CacheEvictcacheEvict=method.getAnnotation(CacheEvict.class);
String[]fieldKeys=cacheEvict.fieldKey();
intdeleteSuccessFlag=0;
for(StringfieldKey:fieldKeys){
if(fieldKey.indexOf("#")==0){//動態變數存入方式
Stringt=fieldKey;
fieldKey=parseKey(fieldKey,method,pjp.getArgs());
if(fieldKey==null){
logger.error("cacheevictfieldkey{}connotbenull,thatmaybecausedataconnotsync",t);
t=null;
}
}
deleteSuccessFlag+=JedisUtils.hdel(cacheEvict.key(),fieldKey==null?"":fieldKey);
logger.debug("Thereferencefieldkey:"+fieldKey+"for"+cacheEvict.key()+"hasbeendeletefromredis");
}
logger.debug("Delete"+deleteSuccessFlag+""+cacheEvict.key()+"fromredis");
}
try{
returnpjp.proceed();
}catch(Throwablee){
e.printStackTrace();
returnnull;
}
}
/**
*獲取緩存的key
*key定義在註解上,支持SPEL表達式
*
*@paramkey
*@parammethod
*@paramargs
*@return
*/
privateStringparseKey(Stringkey,Methodmethod,Object[]args){
//獲取被攔截方法參數名列表(使用Spring支持類庫)
=();
String[]paraNameArr=u.getParameterNames(method);
//使用SPEL進行key的解析
ExpressionParserparser=newSpelExpressionParser();
//SPEL上下文
=newStandardEvaluationContext();
//把方法參數放入SPEL上下文中
for(inti=0;i<paraNameArr.length;i++){
context.setVariable(paraNameArr[i],args[i]);
}
returnparser.parseExpression(key).getValue(context,String.class);
}
使用,在service調用前使用:
@Cacheable(key="biz_member",fieldKey="#id")publicMemberget(Stringid){returnsuper.get(id);}@CacheEvict(key="member",fieldKey={"#member.id","#member.account"})publicSerializablesave(Membermember){return.insert(member);}@CacheEvict(key="member",fieldKey={"#member.id","#member.account"})publicvoiddelete(Membermember){.delete(member);}
④ web伺服器怎麼使用redis分步式緩存
Redis復制流程概述
Redis的復制功能是完全建立在之前我們討論過的基於內存快照的持久化策略基礎上的,也就是說無論你的持久化策略選擇的是什麼,只要用到了Redis的復制功能,就一定會有內存快照發生,那麼首先要注意你的系統內存容量規劃,原因可以參考我上一篇文章中提到的Redis磁碟IO問題。
Redis復制流程在Slave和Master端各自是一套狀態機流轉,涉及的狀態信息是:
Slave 端:
REDIS_REPL_NONEREDIS_REPL_CONNECTREDIS_REPL_CONNECTED
Master端:
REDIS_REPL_WAIT_BGSAVE_STARTREDIS_REPL_WAIT_BGSAVE_ENDREDIS_REPL_SEND_BULKREDIS_REPL_ONLINE
整個狀態機流程過程如下:
Slave端在配置文件中添加了slave of指令,於是Slave啟動時讀取配置文件,初始狀態為REDIS_REPL_CONNECT。
Slave端在定時任務serverCron(Redis內部的定時器觸發事件)中連接Master,發送sync命令,然後阻塞等待master發送回其內存快照文件(最新版的Redis已經不需要讓Slave阻塞)。
Master端收到sync命令簡單判斷是否有正在進行的內存快照子進程,沒有則立即開始內存快照,有則等待其結束,當快照完成後會將該文件發送給Slave端。
Slave端接收Master發來的內存快照文件,保存到本地,待接收完成後,清空內存表,重新讀取Master發來的內存快照文件,重建整個內存表數據結構,並最終狀態置位為 REDIS_REPL_CONNECTED狀態,Slave狀態機流轉完成。
Master端在發送快照文件過程中,接收的任何會改變數據集的命令都會暫時先保存在Slave網路連接的發送緩存隊列里(list數據結構),待快照完成後,依次發給Slave,之後收到的命令相同處理,並將狀態置位為 REDIS_REPL_ONLINE。
整個復制過程完成,流程如下圖所示:
Redis復制機制的缺陷
從上面的流程可以看出,Slave從庫在連接Master主庫時,Master會進行內存快照,然後把整個快照文件發給Slave,也就是沒有象MySQL那樣有復制位置的概念,即無增量復制,這會給整個集群搭建帶來非常多的問題。
比如一台線上正在運行的Master主庫配置了一台從庫進行簡單讀寫分離,這時Slave由於網路或者其它原因與Master斷開了連接,那麼當Slave進行重新連接時,需要重新獲取整個Master的內存快照,Slave所有數據跟著全部清除,然後重新建立整個內存表,一方面Slave恢復的時間會非常慢,另一方面也會給主庫帶來壓力。
所以基於上述原因,如果你的Redis集群需要主從復制,那麼最好事先配置好所有的從庫,避免中途再去增加從庫。
Cache還是Storage
在我們分析過了Redis的復制與持久化功能後,我們不難得出一個結論,實際上Redis目前發布的版本還都是一個單機版的思路,主要的問題集中在,持久化方式不夠成熟,復制機制存在比較大的缺陷,這時我們又開始重新思考Redis的定位:Cache還是Storage?
如果作為Cache的話,似乎除了有些非常特殊的業務場景,必須要使用Redis的某種數據結構之外,我們使用Memcached可能更合適,畢竟Memcached無論客戶端包和伺服器本身更久經考驗。
如果是作為存儲Storage的話,我們面臨的最大的問題是無論是持久化還是復制都沒有辦法解決Redis單點問題,即一台Redis掛掉了,沒有太好的辦法能夠快速的恢復,通常幾十G的持久化數據,Redis重啟載入需要幾個小時的時間,而復制又有缺陷,如何解決呢?
Redis可擴展集群搭建1. 主動復制避開Redis復制缺陷。
既然Redis的復制功能有缺陷,那麼我們不妨放棄Redis本身提供的復制功能,我們可以採用主動復制的方式來搭建我們的集群環境。
所謂主動復制是指由業務端或者通過代理中間件對Redis存儲的數據進行雙寫或多寫,通過數據的多份存儲來達到與復制相同的目的,主動復制不僅限於用在Redis集群上,目前很多公司採用主動復制的技術來解決MySQL主從之間復制的延遲問題,比如Twitter還專門開發了用於復制和分區的中間件gizzard(https://github.com/twitter/gizzard) 。
主動復制雖然解決了被動復制的延遲問題,但也帶來了新的問題,就是數據的一致性問題,數據寫2次或多次,如何保證多份數據的一致性呢?如果你的應用對數據一致性要求不高,允許最終一致性的話,那麼通常簡單的解決方案是可以通過時間戳或者vector clock等方式,讓客戶端同時取到多份數據並進行校驗,如果你的應用對數據一致性要求非常高,那麼就需要引入一些復雜的一致性演算法比如Paxos來保證數據的一致性,但是寫入性能也會相應下降很多。
通過主動復制,數據多份存儲我們也就不再擔心Redis單點故障的問題了,如果一組Redis集群掛掉,我們可以讓業務快速切換到另一組Redis上,降低業務風險。
2. 通過presharding進行Redis在線擴容。
通過主動復制我們解決了Redis單點故障問題,那麼還有一個重要的問題需要解決:容量規劃與在線擴容問題。
我們前面分析過Redis的適用場景是全部數據存儲在內存中,而內存容量有限,那麼首先需要根據業務數據量進行初步的容量規劃,比如你的業務數據需要100G存儲空間,假設伺服器內存是48G,那麼根據上一篇我們討論的Redis磁碟IO的問題,我們大約需要3~4台伺服器來存儲。這個實際是對現有業務情況所做的一個容量規劃,假如業務增長很快,很快就會發現當前的容量已經不夠了,Redis裡面存儲的數據很快就會超過物理內存大小,那麼如何進行Redis的在線擴容呢?
Redis的作者提出了一種叫做presharding的方案來解決動態擴容和數據分區的問題,實際就是在同一台機器上部署多個Redis實例的方式,當容量不夠時將多個實例拆分到不同的機器上,這樣實際就達到了擴容的效果。
拆分過程如下:
在新機器上啟動好對應埠的Redis實例。
配置新埠為待遷移埠的從庫。
待復制完成,與主庫完成同步後,切換所有客戶端配置到新的從庫的埠。
配置從庫為新的主庫。
移除老的埠實例。
重復上述過程遷移好所有的埠到指定伺服器上。
以上拆分流程是Redis作者提出的一個平滑遷移的過程,不過該拆分方法還是很依賴Redis本身的復制功能的,如果主庫快照數據文件過大,這個復制的過程也會很久,同時會給主庫帶來壓力。所以做這個拆分的過程最好選擇為業務訪問低峰時段進行。
Redis復制的改進思路
我們線上的系統使用了我們自己改進版的Redis,主要解決了Redis沒有增量復制的缺陷,能夠完成類似Mysql Binlog那樣可以通過從庫請求日誌位置進行增量復制。
我們的持久化方案是首先寫Redis的AOF文件,並對這個AOF文件按文件大小進行自動分割滾動,同時關閉Redis的Rewrite命令,然後會在業務低峰時間進行內存快照存儲,並把當前的AOF文件位置一起寫入到快照文件中,這樣我們可以使快照文件與AOF文件的位置保持一致性,這樣我們得到了系統某一時刻的內存快照,並且同時也能知道這一時刻對應的AOF文件的位置,那麼當從庫發送同步命令時,我們首先會把快照文件發送給從庫,然後從庫會取出該快照文件中存儲的AOF文件位置,並將該位置發給主庫,主庫會隨後發送該位置之後的所有命令,以後的復制就都是這個位置之後的增量信息了。
Redis與MySQL的結合
目前大部分互聯網公司使用MySQL作為數據的主要持久化存儲,那麼如何讓Redis與MySQL很好的結合在一起呢?我們主要使用了一種基於MySQL作為主庫,Redis作為高速數據查詢從庫的異構讀寫分離的方案。
為此我們專門開發了自己的MySQL復制工具,可以方便的實時同步MySQL中的數據到Redis上。
(MySQL-Redis 異構讀寫分離)
總結:
Redis的復制功能沒有增量復制,每次重連都會把主庫整個內存快照發給從庫,所以需要避免向在線服務的壓力較大的主庫上增加從庫。
Redis的復制由於會使用快照持久化方式,所以如果你的Redis持久化方式選擇的是日誌追加方式(aof),那麼系統有可能在同一時刻既做aof日誌文件的同步刷寫磁碟,又做快照寫磁碟操作,這個時候Redis的響應能力會受到影響。所以如果選用aof持久化,則加從庫需要更加謹慎。
可以使用主動復制和presharding方法進行Redis集群搭建與在線擴容。
⑤ Redis分布式緩存搭建
花了兩天時間整理了之前記錄的Redis單體與哨兵模式的搭建與使用,又補齊了集群模式的使用和搭建經驗,並對集群的一些個原理做了理解。
筆者安裝中遇到的一些問題:
如果make報錯,可能是沒裝gcc或者gcc++編輯器,安裝之 yum -y install gcc gcc-c++ kernel-devel ,有可能還是提示一些個c文件編譯不過,gcc -v查看下版本,如果不到5.3那麼升級一下gcc:
在 /etc/profile 追加一行 source /opt/rh/devtoolset-9/enable
scl enable devtoolset-9 bash
重新make clean, make
這回編譯通過了,提示讓你最好make test一下/
執行make test ,如果提示 You need tcl 8.5 or newer in order to run the Redis test
那就升級tcl, yum install tcl
重新make test,如果還有error就刪了目錄,重新tar包解壓重新make , make test
o/ All tests passed without errors! ,表示編譯成功。
然後make install即可。
直接運行命令: ./redis-server /usr/redis-6.0.3/redis.conf &
redis.conf 配置文件里 bind 0.0.0.0 設置外部訪問, requirepass xxxx 設置密碼。
redis高可用方案有兩種:
常用搭建方案為1主1從或1主2從+3哨兵監控主節點, 以及3主3從6節點集群。
(1)sentinel哨兵
/usr/redis-6.0.3/src/redis-sentinel /usr/redis-6.0.3/sentinel2.conf &
sentinel2.conf配置:
坑1:master節點也會在故障轉移後成為從節點,也需要配置masterauth
當kill master進程之後,經過sentinel選舉,slave成為了新的master,再次啟動原master,提示如下錯誤:
原因是此時的master再次啟動已經是slave了,需要向現在的新master輸入密碼,所以需要在master.conf
中配置:
坑2:哨兵配置文件要暴露客戶端可以訪問到的master地址
在 sentinel.conf 配置文件的 sentinel monitor mymaster 122.xx.xxx.xxx 6379 2 中,配置該哨兵對應的master名字、master地址和埠,以及達到多少個哨兵選舉通過認為master掛掉。其中master地址要站在redis訪問者(也就是客戶端)的角度、配置訪問者能訪問的地址,例如sentinel與master在一台伺服器(122.xx.xxx.xxx)上,那麼相對sentinel其master在本機也就是127.0.0.1上,這樣 sentinel monitor mymaster 127.0.0.1 6379 2 邏輯上沒有問題,但是如果另外伺服器上的springboot通過lettuce訪問這個redis哨兵,則得到的master地址為127.0.0.1,也就是springboot所在伺服器本機,這顯然就有問題了。
附springboot2.1 redis哨兵配置:
坑3:要注意配置文件.conf會被哨兵修改
redis-cli -h localhost -p 26379 ,可以登到sentinel上用info命令查看一下哨兵的信息。
曾經遇到過這樣一個問題,大致的信息如下
slaves莫名其妙多了一個,master的地址也明明改了真實對外的地址,這里又變成127.0.0.1 !
最後,把5個redis進程都停掉,逐個檢查配置文件,發現redis的配置文件在主從哨兵模式會被修改,master的配置文件最後邊莫名其妙多了一行replicaof 127.0.0.1 7001, 懷疑應該是之前配置錯誤的時候(見坑2)被哨兵動態加上去的! 總之,實踐中一定要多注意配置文件的變化。
(2)集群
當數據量大到一定程度,比如幾十上百G,哨兵模式不夠用了需要做水平拆分,早些年是使用codis,twemproxy這些第三方中間件來做分片的,即 客戶端 -> 中間件 -> Redis server 這樣的模式,中間件使用一致性Hash演算法來確定key在哪個分片上。後來Redis官方提供了方案,大家就都採用官方的Redis Cluster方案了。
Redis Cluster從邏輯上分16384個hash slot,分片演算法是 CRC16(key) mod 16384 得到key應該對應哪個slot,據此判斷這個slot屬於哪個節點。
每個節點可以設置1或多個從節點,常用的是3主節點3從節點的方案。
reshard,重新分片,可以指定從哪幾個節點移動一些hash槽到另一個節點去。重新分片的過程對客戶端透明,不影響線上業務。
搭建Redis cluster
redis.conf文件關鍵的幾個配置:
啟動6個集群節點
[root@VM_0_11_centos redis-6.0.3]# ps -ef|grep redis
root 5508 1 0 21:25 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7001 [cluster]
root 6903 1 0 21:32 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7002 [cluster]
root 6939 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7003 [cluster]
root 6966 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7004 [cluster]
root 6993 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7005 [cluster]
root 7015 1 0 21:33 ? 00:00:00 /usr/redis-6.0.3/src/redis-server 0.0.0.0:7006 [cluster]
這時候這6個節點還是獨立的,要把他們配置成集群:
說明: -a xxxx 是因為筆者在redis.conf中配置了requirepass xxxx密碼,然後 --cluster-replicas 1 中的1表示每個master節點有1個從節點。
上述命令執行完以後會有一個詢問: Can I set the above configuration? yes同意自動做好的分片即可。
最後 All 16384 slots covered. 表示集群中16384個slot中的每一個都有至少有1個master節點在處理,集群啟動成功。
查看集群狀態:
坑1:暴露給客戶端的節點地址不對
使用lettuce連接發現連不上,查看日誌 Connection refused: no further information: /127.0.0.1:7002 ,跟之前哨兵配置文件sentinel.conf里邊配置master地址犯的錯誤一樣,集群啟動的時候帶的地址應該是提供給客戶端訪問的地址。
我們要重建集群:先把6個redis進程停掉,然後刪除 nodes-7001.conf 這些節點配置文件,刪除持久化文件 mp.rdb 、 appendonly.aof ,重新啟動6個進程,在重新建立集群:
然後,還是連不上,這次報錯 connection timed out: /172.xx.0.xx:7004 ,發現連到企鵝雲伺服器的內網地址上了!
解決辦法,修改每個節點的redis.conf配置文件,找到如下說明:
所以增加配置:
然後再重新構建集群,停進程、改配置、刪除節點文件和持久化文件、啟動進程、配置集群。。。再來一套(累死了)
重新使用Lettuce測試,這次終於連上了!
坑2:Lettuce客戶端在master節點故障時沒有自動切換到從節點
name這個key在7002上,kill這個進程模擬master下線,然後Lettuce一直重連。我們期望的是應該能自動切換到其slave 7006上去,如下圖:
重新啟動7002進程,
7006已成為新master,7002成為它的slave,然後Lettuce也能連接上了。
解決辦法,修改Lettuce的配置:
筆者用的是springboot 2.1 spring-boot-starter-data-redis 默認的Lettuce客戶端,當使用Redis cluster集群模式時,需要配置一下 RedisConnectionFactory 開啟自適應刷新來做故障轉移時的自動切換從節點進行連接。
重新測試:停掉master 7006,這次Lettuce可以正常切換連到7002slave上去了。(仍然會不斷的在日誌里報連接錯誤,因為需要一直嘗試重連7006,但因為有7002從節點頂上了、所以應用是可以正常使用的)
Redis不保證數據的強一致性
Redis並不保證數據的強一致性,也就是取CAP定理中的AP
關於一致性Hash演算法,可以參考 一致性Hash演算法 - (jianshu.com)
Redis cluster使用的是hash slot演算法,跟一致性Hash演算法不太一樣,固定16384個hash槽,然後計算key落在哪個slot里邊(計算key的CRC16值再對16384取模),key找的是slot而不是節點,而slot與節點的對應關系可以通過reshard改變並通過gossip協議擴散到集群中的每一個節點、進而可以為客戶端獲知,這樣key的節點定址就跟具體的節點個數沒關系了。也同樣解決了普通hash取模演算法當節點個數發生變化時,大量key對應的定址都發生改動導致緩存失效的問題。
比如集群增加了1個節點,這時候如果不做任何操作,那麼新增加的這個節點上是沒有slot的,所有slot都在原來的節點上且對應關系不變、所以沒有因為節點個數變動而緩存失效,當reshard一部分slot到新節點後,客戶端獲取到新遷移的這部分slot與新節點的對應關系、定址到新節點,而沒遷移的slot仍然定址到原來的節點。
關於熱遷移,猜想,內部應該是先做復制遷移,等遷移完了,再切換slot與節點的對應關系,復制沒有完成之前仍按照原來的slot與節點對應關系去原節點訪問。復制結束之後,再刪除原節點上已經遷移的slot所對應的key。
與哨兵模式比較類似,當1個節點發現某個master節點故障了、會對這個故障節點進行pfail主觀宕機,然後會通過gossip協議通知到集群中的其他節點、其他節點也執行判斷pfail並gossip擴散廣播這一過程,當超過半數節點pfail時那麼故障節點就是fail客觀宕機。接下來所有的master節點會在故障節點的從節點中選出一個新的主節點,此時所有的master節點中超過半數的都投票選舉了故障節點的某個從節點,那麼這個從節點當選新的master節點。
所有節點都持有元數據,節點之間通過gossip這種二進制協議進行通信、發送自己的元數據信息給其他節點、故障檢測、集群配置更新、故障轉移授權等等。
這種去中心化的分布式節點之間內部協調,包括故障識別、故障轉移、選主等等,核心在於gossip擴散協議,能夠支撐這樣的廣播協議在於所有的節點都持有一份完整的集群元數據,即所有的節點都知悉當前集群全局的情況。
Redis高可用方案 - (jianshu.com)
面試題:Redis 集群模式的工作原理能說一下么 - 雲+社區 - 騰訊雲 (tencent.com)
深度圖解Redis Cluster原理 - detectiveHLH - 博客園 (cnblogs.com)
Redis學習筆記之集群重啟和遇到的坑-阿里雲開發者社區 (aliyun.com)
雲伺服器Redis集群部署及客戶端通過公網IP連接問題
⑥ 緩存-redis 三種模式搭建和運行原理
標簽: redis 緩存 主從 哨兵 集群
本文簡單的介紹redis三種模式在linux的安裝部署和數據存儲的總結,希望可以相互交流相互提升。
對於Centos7在安裝redis之前需要進行一些常用工具的安裝:
關閉防火牆
正式安裝redis
在redis進行maketest時候會出現一系列的異常,有如下解決方案:
用redis-server啟動一下redis,做一些實驗沒什麼意義。
要把redis作為一個系統的daemon進程去運行的,每次系統啟動,redis進程一起啟動,操作不走如下:
RDB和AOF是redis的一種數據持久化的機制。 持久化 是為了避免系統在發生災難性的系統故障時導致的系統數據丟失。我們一般會將數據存放在本地磁碟,還會定期的將數據上傳到雲伺服器。
RDB 是redis的snapshotting,通過redis.conf中的save配置進行設置,如 save 60 1000:
AOF 是以appendonly方式進行數據的儲存的,開啟AOF模式後,所有存進redis內存的數據都會進入os cache中,然後默認1秒執行一次fsync寫入追加到appendonly.aof文件中。一般我們配置redis.conf中的一下指令:
AOF和RDB模式我們一般在生產環境都會打開,一般而言,redis服務掛掉後進行重啟會優先家在aof中的文件。
當啟動一個slave node的時候,它會發送一個PSYNC命令給master node,如果這是slave node重新連接master node,那麼master node僅僅會復制給slave部分缺少的數據;否則如果是slave node第一次連接master node,那麼會觸發一次full resynchronization;
開始full resynchronization的時候,master會啟動一個後台線程,開始生成一份RDB快照文件,同時還會將從客戶端收到的所有寫命令緩存在內存中。RDB文件生成完畢之後,master會將這個RDB發送給slave,slave會先寫入本地磁碟,然後再從本地磁碟載入到內存中。然後master會將內存中緩存的寫命令發送給slave,slave也會同步這些數據。
slave node如果跟master node有網路故障,斷開了連接,會自動重連。master如果發現有多個slave node都來重新連接,僅僅會啟動一個rdb save操作,用一份數據服務所有slave node。
從redis 2.8開始,就支持主從復制的斷點續傳,如果主從復制過程中,網路連接斷掉了,那麼可以接著上次復制的地方,繼續復制下去,而不是從頭開始復制一份。
master node會在內存中常見一個backlog,master和slave都會保存一個replica offset還有一個master id,offset就是保存在backlog中的。如果master和slave網路連接斷掉了,slave會讓master從上次的replica offset開始繼續復制,但是如果沒有找到對應的offset,那麼就會執行一次resynchronization。
master在內存中直接創建rdb,然後發送給slave,不會在自己本地落地磁碟了,可以有如下配置:
slave不會過期key,只會等待master過期key。如果master過期了一個key,或者通過LRU淘汰了一個key,那麼會模擬一條del命令發送給slave。
在redis.conf配置文件中,上面的參數代表至少需要3個slaves節點與master節點進行連接,並且master和每個slave的數據同步延遲不能超過10秒。一旦上面的設定沒有匹配上,則master不在提供相應的服務。
sdown達成的條件很簡單,如果一個哨兵ping一個master,超過了 is-master-down-after-milliseconds 指定的毫秒數之後,就主觀認為master宕機
sdown到odown轉換的條件很簡單,如果一個哨兵在指定時間內,收到了 quorum 指定數量的其他哨兵也認為那個master是sdown了,那麼就認為是odown了,客觀認為master宕機
如果一個slave跟master斷開連接已經超過了down-after-milliseconds的10倍,外加master宕機的時長,那麼slave就被認為不適合選舉為master
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
每次一個哨兵要做主備切換,首先需要quorum數量的哨兵認為odown,然後選舉出一個slave來做切換,這個slave還得得到majority哨兵的授權,才能正式執行切換;
(2)SENTINEL RESET *,在所有sentinal上執行,清理所有的master狀態
(3)SENTINEL MASTER mastername,在所有sentinal上執行,查看所有sentinal對數量是否達成了一致
4.3.2 slave的永久下線
讓master摘除某個已經下線的slave:SENTINEL RESET mastername,在所有的哨兵上面執行.
redis的集群模式為了解決系統的橫向擴展以及海量數據的存儲問題,如果你的數據量很大,那麼就可以用redis cluster。
redis cluster可以支撐N個redis master,一個master上面可以掛載多個slave,一般情況我門掛載一個到兩個slave,master在掛掉以後會主動切換到slave上面,或者當一個master上面的slave都掛掉後,集群會從其他master上面找到冗餘的slave掛載到這個master上面,達到了系統的高可用性。
2.1 redis cluster的重要配置
2.2 在三台機器上啟動6個redis實例
將上面的配置文件,在/etc/redis下放6個,分別為: 7001.conf,7002.conf,7003.conf,7004.conf,7005.conf,7006.conf
每個啟動腳本內,都修改對應的埠號
2.3 創建集群
解決辦法是 先安裝rvm,再把ruby版本提升至2.3.3
使用redis-trib.rb命令創建集群
--replicas: 表示每個master有幾個slave
redis-trib.rb check 192.168.31.187:7001 查看狀體
3.1 加入新master
以上相同配置完成後,設置啟動腳本進行啟動;然後用如下命令進行node節點添加:
3.2 reshard一些數據過去
3.3 添加node作為slave
3.4 刪除node
⑦ 介面添加redis緩存之後並發還是很低
把redis作為緩存使用已經是司空見慣,但是使用redis後也可能會碰到一系列的問題,尤其是數據量很大的時候,經典的幾個問題如下:
(一)緩存和資料庫間數據一致性問題
分布式環境下(單機就不用說了)非常容易出現緩存和資料庫間孫旁的數據一致性問題,針對這一點的話,只能說,如果你的項目對緩存的要求是強一致性的,那麼請不要使用緩存。我們只能鄭昌採取合適的策略來降低緩存和資料庫間數據不一致的概率,而無法保證兩者間的強一致性。合適的策略包括 合適的緩存更新策略,更新資料庫後要及時更新緩存、緩存失敗時增加重試機制,例如MQ模式的消息隊列。
(二)緩存擊則叢橡穿問題
⑧ redis千萬數據放緩存合理么
Redis是一種高性能的內存資料庫,它可以用來存儲高速緩存數據。
將數據存儲在緩存中可以提高系統的性能,因為從緩存中讀取數據的速度比從資料庫中讀取數據的速度快得多。
但是,將所有數據都放入緩存中可能並不是最優解。緩存有一定的容量限制,如果緩存容量不足以容納所有數據,那麼緩存就會淘汰部分數據,這樣就會導致緩存命中率降低,系統性能也會降低。因此,應該根據實際情況合理設置緩存容量,並將重要的、頻繁使用的數據放入緩存中。
⑨ redis 緩存伺服器 怎麼給外部系統用
Redis介紹
Redis本質上一個Key/Value資料庫,與Memcached類似的NoSQL型資料庫,但是他的數據可以持久化的保存在磁碟上,解決了服務重啟後數據不丟失的問題,他的值可以是string(字元串)、list(列表)、sets(集合)或者是ordered sets(被排序的集合),所有的數據類型都具有push/pop、add/remove、執行服務端的並集、交集、兩個sets集中的差別等等操作,這些操作都是具有原子性的,Redis還支持各種不同的排序能力
Redis 2.0更是增加了很多新特性,如:提升了性能、增加了新的數據類型、更少的利用內存(AOF和VM)
Redis支持絕大部分主流的開發語言,如:C、Java、C#、PHP、Perl、Python、Lua、Erlang、Ruby等等
官網:http://code.google.com/p/redis/
Redis性能
根據Redis官方的測試結果:在50個並發的情況下請求10w次,寫的速度是110000次/s,讀的速度是81000次/s
地址:http://code.google.com/p/redis/wiki/Benchmarks
一、安裝過程:
最新穩定版,Redis 2.0.4 stable
wget http://redis.googlecode.com/files/redis-2.6.7.tar.gz (其他版本到官網下載即可)
tar zxf redis-2.6.7.tar.gz
cd redis-2.6.7
與其它軟體不同的是,不需要configure。
make
裝完了。
創建一個目錄
mkdir /usr/local/redis2
cd src
cp redis-server redis-benchmark redis-cli ../redis.conf /usr/local/redis2
二、啟動與停止:
cd /usr/local/redis2
啟動 ./redis-server > /dev/null & 或者 ./redis-server redis.conf 或者 ./redis-server redis.conf 1>log.log 2>errlog.log (1為標准輸出,2為錯誤輸出)
停止 ./redis-cli shutdown
客戶端連接 ./redis-cli
三、測試:
存值:
./redis-cli set hx value
取值:
./redis-cli get hx
> quit
退出連接
> dbsize
(integer) 12
當前資料庫中key的數量
> info
伺服器基本信息
monitor
實時轉儲收到的請求
config get
獲取伺服器的參數配置
flushdb
清空當前資料庫
flushall
清除所有資料庫
四、附註
redis.conf配置文件:
引用
#是否作為守護進程運行
daemonize yes
#配置pid的存放路徑及文件名,默認為當前路徑下
pidfile redis.pid
#Redis默認監聽埠
port 6379
#客戶端閑置多少秒後,斷開連接
timeout 300
#日誌顯示級別
loglevel verbose
#指定日誌輸出的文件名,也可指定到標准輸出埠
logfile stdout
#設置資料庫的數量,默認連接的資料庫是0,可以通過select N來連接不同的資料庫
databases 16
#保存數據到disk的策略
#當有一條Keys數據被改變是,900秒刷新到disk一次
save 900 1
#當有10條Keys數據被改變時,300秒刷新到disk一次
save 300 10
#當有1w條keys數據被改變時,60秒刷新到disk一次
save 60 10000
#當mp .rdb資料庫的時候是否壓縮數據對象
rdbcompression yes
#mp資料庫的數據保存的文件名
dbfilename mp.rdb
#Redis的工作目錄
dir /home/falcon/redis-2.0.0/
########### Replication #####################
#Redis的復制配置
# slaveof <masterip> <masterport>
# masterauth <master-password>
############## SECURITY ###########
# requirepass foobared
############### LIMITS ##############
#最大客戶端連接數
# maxclients 128
#最大內存使用率
# maxmemory <bytes>
########## APPEND ONLY MODE #########
#是否開啟日誌功能
appendonly no
# 刷新日誌到disk的規則
# appendfsync always
appendfsync everysec
# appendfsync no
################ VIRTUAL MEMORY ###########
#是否開啟VM功能
vm-enabled no
# vm-enabled yes
vm-swap-file logs/redis.swap
vm-max-memory 0
vm-page-size 32
vm-pages 134217728
vm-max-threads 4
############# ADVANCED CONFIG ###############
glueoutputbuf yes
hash-max-zipmap-entries 64
hash-max-zipmap-value 512
#是否重置Hash表
activerehashing yes