redis 哨兵 异步_redis 使用历程
redis 使用歷程
為什么使用redis緩存?
答:之前是沒有使用redis的,直接用Java代碼寫類緩存功能,有些系統(tǒng)參數(shù)方面也是采用直接查詢數(shù)據(jù)庫。此中間還出現(xiàn)過一些其他問題,后面才選擇redis。
先說下不使用緩存會造成哪些影響:
1、在項目中,為避免不斷查詢數(shù)據(jù)庫,給數(shù)據(jù)庫造成壓力,需要將生效的用戶充值地址加入到緩存中,剛開始直接使用ArrayList,但是在測試階段就發(fā)現(xiàn),在不斷將新的地址加入到ArrayList中,會出現(xiàn)添加異常情況,同時導(dǎo)致應(yīng)用假死情況,后來經(jīng)排查發(fā)現(xiàn)ArrayList非線程安全,然后采用CopyOnWriteArrayList(該list適合讀多寫少的情況),這個很好的解決了問題。雖然CopyOnWriteArrayList解決了并發(fā)問題,但是在性能上不理想,每次有新的地址添加到list中都會創(chuàng)建新的列表,并將之前的列表通過 Arrays.copyOf(elements, len + 1); 效率低下,且如果緩存的用戶地址太多(比如幾萬、十幾萬時),在每次發(fā)生新增是會造成內(nèi)存使用短期飆升的情況。
2、同時在項目中還有些系統(tǒng)配置參數(shù),在業(yè)務(wù)處理中會根據(jù)配置參數(shù)執(zhí)行對應(yīng)的邏輯(比如:是否允許提現(xiàn)、每日最大提現(xiàn)額度,定時任務(wù)是否執(zhí)行等),但是這些系統(tǒng)參數(shù)基本上不怎么變動,在不斷的查詢數(shù)據(jù)庫也是一種浪費。
3、對一些用戶訪問量大的數(shù)據(jù),直接訪問數(shù)據(jù)庫,在高并發(fā)情況下,可能會導(dǎo)致項目掛掉。
4、自己寫的本地緩存方案可能存在一些潛在的未知問題。
基于上述情況,開始加入緩存技術(shù),并就緩存方案進行選型。
緩存方案選型
目前市面上主流的緩存方案有兩種:redis、memcache。我為什么放棄memcache而選擇redis呢?
1、從支持的數(shù)據(jù)類型方面:memcache只支持簡單數(shù)據(jù)類型,需要客戶端自己處理復(fù)雜對象;而redis支持的類型,除String、List、Hash、Set、SortedSet外,還支持pub/sub(不支持持久化,如果消費者當時不在現(xiàn)場,則消息丟失,可靠的操作還是選擇消息隊列)、Transaction、HyperLogLog(非精確的去重統(tǒng)計,伯努利實驗及拋硬幣)、bloomFilter(避免擊穿)等功能。
2、從持久化方面:memcached不支持持久化,redis支持持久化(可通過aof,rdb)
3、從線程角度:redis是單線程(但單機就可支持10萬的并發(fā)量,且不需要考慮上下文切換等問題,如果并發(fā)量太大,可以考慮多機部署方案【主從復(fù)制、哨兵模式、集群模式等】),memcache多線程。
4、綜上,我選擇redis作為項目的緩存方案。
使用redis常見問題
1、緩存集中到期問題(緩存設(shè)置的時候,設(shè)置了固定的到期時長,如果同一段時間集中加入緩存,則會存在集中過期的情況,在剛好過期時嗎,如果有大量請求進來,則可能會存在短暫卡頓,或者雪崩現(xiàn)象,甚至導(dǎo)致訪問數(shù)據(jù)庫癱瘓)
解決方案:
1、在某些基本不怎么變動的緩存,設(shè)置永不過期(必要時手動更新)
2、對需要設(shè)置過期時間的, 過期時間大小加個隨機數(shù),將集中過期變的分散些,降低緩存雪崩概率。
2、緩存穿透&緩存擊穿
先說【緩存穿透】,指的是請求查詢緩存及數(shù)據(jù)庫均不存在的數(shù)據(jù)。比如查詢用戶id(一般都是正數(shù)遞增的),你查詢負數(shù)或者其他字符串肯定不會存在,且請求會不斷打到db上。避免方案是對請求數(shù)據(jù)的合法性進行校驗(權(quán)限校驗、參數(shù)校驗),還有就是采用布隆過濾器(原理是采用bitmap位圖,緩存的key會議bit的方式存入布隆過濾器上, 如果在布隆過濾器上能找到,則可能存在,否則肯定不存在),然后在進行緩存查詢、數(shù)據(jù)庫查詢。
緩存擊穿:在key失效瞬間(比如過期失效),大量請求打到數(shù)據(jù)庫上。
3、分布式鎖
說白了就是通過setnx(key不存在則設(shè)值)命令向緩存中設(shè)值。如果設(shè)值成功則獲取鎖,為避免加鎖后忘記釋放,通過expire設(shè)值鎖失效時間;若設(shè)值不成功,則加鎖失敗。但是有可能在加鎖后,還沒來得及設(shè)值過期時間,程序掛掉,需要采用setIfAbsent(K key, V value, long timeout, TimeUnit unit) 記得是這個方法。
但是:雖然解決了鎖過期無法釋放的問題,但設(shè)置的過期時間不能保證我當前獲取鎖的線程執(zhí)行完成了,假如業(yè)務(wù)線比較長,在鎖過期后還沒有正常執(zhí)行完成,又有可能引發(fā)其他問題。
4、假如在線上環(huán)境中有上億個key,如何找出以某些字符串開頭key的值,為什么不能使用keys *?
首先說下為什么不能使用keys * ,因為redis是單線程。使用keys 時會導(dǎo)致其他線程阻塞,導(dǎo)致其他請求進來時需要等待,直到key【且key沒有l(wèi)imit,會全部遍歷】 執(zhí)行完成,如果剛好在執(zhí)行key命令時,大量請求進來,那就GG了。推薦方案:采用scan方式,該命令可無阻塞的獲取指定模式的key列表。該命令的缺點是可能會存在一定的重復(fù)(為什么會存在重復(fù)呢?因為redis底層采用hashMap進行的實現(xiàn),也就是說數(shù)據(jù)結(jié)構(gòu)是數(shù)組加鏈表,如果在查詢過程中存在擴容或者鎖容時,就會出現(xiàn)重復(fù)),需要業(yè)務(wù)系統(tǒng)進行去重。
5、redis異步消息
可以采用list數(shù)據(jù)結(jié)構(gòu)進行處理。lpush 加入消息、然后rpop獲取消息,如果list為空,則阻塞。或者rpush加入消息,lpop獲取消息。這樣保證了消息的先進先出;如果lpush、lpop或rpush、rpop則是先進后出。
6、redis延時隊列
可通過sortedSet數(shù)據(jù)結(jié)構(gòu),通過zadd添加消息,每次添加時對score自增(建議方案是采用時間戳,但是別神經(jīng)病調(diào)整系統(tǒng)時間,不然就把自己找地埋了吧),消費時使用zrangebyscore(讀取數(shù)據(jù)后,業(yè)務(wù)方根據(jù)業(yè)務(wù)邏輯處理)
7、Pipeline操作
redis的管道命令,可以批量將多個請求發(fā)送給服務(wù)端,中間不需要等待請求的回復(fù),只需要最后一并讀取結(jié)果就可以了。但是在集群模式下,不能直接使用Pipeline,否則可能會報錯。
原因:集群模式下,各集群節(jié)點是分配了不同的slot槽位,而請求的key在hash及crc16計算后可能存在不同的節(jié)點上,所以直接使用會報錯。可以根據(jù)redis集群的槽位分配請求,對批量數(shù)據(jù)進行先計算分配,然后再發(fā)起請求。
8、過期策略
定期清理:定期隨機抽取過期的key進行清理
惰性清理:被訪問時,如果發(fā)現(xiàn)已過期則清理,且不返回數(shù)據(jù)給調(diào)用方。
9、redis持久化
RDB模式:
原理:是redis會單獨創(chuàng)建(fork)一個與當前進程一模一樣的子進程來進行持久化,這個子線程的所有數(shù)據(jù)(變量。環(huán)境變量,程序程序計數(shù)器等)都和原進程一模一樣,會先將數(shù)據(jù)寫入到一個臨時文件中,待持久化結(jié)束了,再用這個臨時文件替換上次持久化好的文件,整個過程中,主進程不進行任何的io操作,這就確保了極高的性能
觸發(fā)機制:shutdown時,如果沒有開啟aof,會觸發(fā)配置文件中默認的快照配置;執(zhí)行命令save或者bgsave save是只管保存,其他不管,全部阻塞 bgsave: redis會在后臺異步進行快照操作,同時可以響應(yīng)客戶端的請求。
AOF
原理:將Reids的操作日志以追加的方式寫入文件,讀操作是不記錄的(配置文件中appendonly默認不開啟)
優(yōu)勢:優(yōu)化數(shù)據(jù)丟失問題,rdb會丟失最后一次快照后的數(shù)據(jù),aof丟失不會超過2秒的數(shù)據(jù)
觸發(fā)機制:
no:表示等操作系統(tǒng)進行數(shù)據(jù)緩存同步到磁盤(快,持久化沒保證) ? always:同步持久化,每次發(fā)生數(shù)據(jù)變更時,立即記錄到磁盤(慢,安全) ? everysec:表示每秒同步一次(默認值,很快,但可能會丟失一秒以內(nèi)的數(shù)據(jù))
auto-aof-rewrite-percentage 100 當AOF文件增長到一定大小的時候Redis能夠調(diào)用 bgrewriteaof對日志文件進行重寫 。當AOF文件大小的增長率大于該配置項時自動開啟重寫(這里指超過原大小的100%)
auto-aof-rewrite-min-size 64mb:當AOF文件增長到一定大小的時候Redis能夠調(diào)用 bgrewriteaof對日志文件進行重寫 。當AOF文件大小大于該配置項時自動開啟重寫
混合持久化:4.0版本默認關(guān)閉,5.0 版本默認開啟。通過aof-use-rdb-preamble配置參數(shù)控制,yes則表示開啟,no表示禁用;注意:同時開啟aof跟rdb,aof優(yōu)先(數(shù)據(jù)更加全面)
9、同步機制
第一次同步時,主節(jié)點做一次bgsave,并同時將后續(xù)修改操作記錄到內(nèi)存buffer,待完成后將RDB文件全量同步到復(fù)制節(jié)點,復(fù)制節(jié)點接受完成后將RDB鏡像加載到內(nèi)存。加載完成后,再通知主節(jié)點將期間修改的操作記錄同步到復(fù)制節(jié)點進行重放就完成了同步過程。后續(xù)的增量數(shù)據(jù)通過AOF日志同步即可,有點類似數(shù)據(jù)庫的binlog
10、集群方案
1、主從模式
2.6版本之后,slave默認只讀(缺點:主機掛掉后不能更新數(shù)據(jù))
2、哨兵模式(高可用)
在master掛掉時,將slave提升為master。中間會有幾秒鐘的不可用狀態(tài),因為在進行選舉。
哨兵原理:
主觀下線:在心跳檢測的定時任務(wù)中,如果其他節(jié)點超過一定時間沒有回復(fù),小兵節(jié)點就會將其進行主管下線
客觀下線:哨兵節(jié)點在對主節(jié)點進行主管下線后,會聽過sentinel is-master-down-by-addr命令詢問其他哨兵節(jié)點該主節(jié)點的狀態(tài);如果判斷主節(jié)點下線的數(shù)量達到一定數(shù)值,則對該主節(jié)點進行客觀下線
哨兵定時任務(wù):
每10秒通過想主從節(jié)點發(fā)送info命令獲取最新的主從結(jié)構(gòu);發(fā)現(xiàn)slave節(jié)點,確定主從關(guān)系
每2秒通過發(fā)布訂閱功能獲取其他哨兵節(jié)點的信息
沒秒通過向其他節(jié)點發(fā)送平命令進行心跳檢測,判斷是否下線
哨兵選舉:raft算法(基本思路是先到先得,一般情況誰先完成客觀下線誰就會成為領(lǐng)導(dǎo)者)
新節(jié)點選舉:
1、過濾掉不健康的從節(jié)點
2、選擇優(yōu)先級最高的從節(jié)點(由replica-priority指定);如果優(yōu)先級無法區(qū)分 3、選擇復(fù)制偏移量最大的從節(jié)點;如果仍無法區(qū)分 4、選擇runid最小的從節(jié)點
建議:哨兵節(jié)點大于一個(應(yīng)該是奇數(shù)),一方面增加哨兵節(jié)點的冗余,冰面哨兵本身成為高可用的瓶頸,另一方面減少對下線的誤判,同時也應(yīng)該部署在不同的物理機上
3、集群模式(高擴展)
手動搭建步驟:配置文件開啟集群配置;meet個節(jié)點;設(shè)置slot槽;設(shè)置主從;
腳本執(zhí)行:
5.0之前需要安裝環(huán)境并執(zhí)行redis-trib.rb;
5.0之后可用下面命令執(zhí)行:
/usr/local/bin/redis-cli --cluster create 192.168.0.104:7000 192.168.0.104:7001 192.168.0.104:7002 192.168.0.104:7003 192.168.0.104:7004 192.168.0.104:7005 --cluster-replicas 1原創(chuàng)不易、轉(zhuǎn)載請注明來源
集群的擴容與縮容,注意slot分配
總結(jié)
以上是生活随笔為你收集整理的redis 哨兵 异步_redis 使用历程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 消息称安卓手机厂商将会全球减产 以面对市
- 下一篇: qt 设置串口起始位_【IT专家】Qt: