摘要:如何解決針對(duì)于熱點(diǎn)的解決方案網(wǎng)上的查找出來無非就是兩種服務(wù)端緩存即將熱點(diǎn)數(shù)據(jù)緩存至服務(wù)端的內(nèi)存中備份熱點(diǎn)即將熱點(diǎn)隨機(jī)數(shù),隨機(jī)分配至其他節(jié)點(diǎn)中。偽代碼如下生成隨機(jī)數(shù)構(gòu)造備份新從數(shù)據(jù)庫中取數(shù)據(jù)存放在中,以便下次能取到代碼地址參考文章
關(guān)于Redis熱點(diǎn)key的一些思考
昨天在和一個(gè)已經(jīng)跳槽的同事聊天時(shí),詢問他這段時(shí)間面試時(shí)碰到的一些問題。自己也想積累一下這方面的知識(shí)。其中他說了在面試某贊公司時(shí)面試官問他關(guān)于熱點(diǎn)Key的的解決方案。于是針對(duì)這次談話以及上網(wǎng)查的一些資料后的思考進(jìn)行一下總結(jié)。方便后續(xù)自己查閱。
什么是熱點(diǎn)Key其實(shí)對(duì)于熱點(diǎn)Key,網(wǎng)上一查一大堆,這里我就引用網(wǎng)上的一段話。
從基于用戶消費(fèi)的數(shù)據(jù)遠(yuǎn)遠(yuǎn)大于生產(chǎn)的數(shù)據(jù)的角度來講,我們平常使用的知乎等軟件時(shí),大多數(shù)人平常僅僅只是瀏覽,并不會(huì)去提問問題、發(fā)表的文章,偶爾會(huì)發(fā)表自己的文章或者看法,這就是一個(gè)典型的讀多寫少的情景,當(dāng)然此類情景不太容易導(dǎo)致熱點(diǎn)的產(chǎn)生。
在日常工作生活中一些突發(fā)的的事件,諸如:“雙11”期間某些熱門商品的降價(jià)促銷,當(dāng)這其中的某一件商品被數(shù)萬次點(diǎn)擊、購買時(shí),會(huì)形成一個(gè)較大的需求量,這種情況下就會(huì)產(chǎn)生一個(gè)單一的Key,這樣就會(huì)引起一個(gè)熱點(diǎn);同理,當(dāng)被大量刊發(fā)、瀏覽的熱點(diǎn)新聞,熱點(diǎn)評(píng)論等也會(huì)產(chǎn)生熱點(diǎn);另外,在服務(wù)端讀數(shù)據(jù)進(jìn)行訪問時(shí),往往會(huì)對(duì)數(shù)據(jù)進(jìn)行分片切分,此類過程中會(huì)在某一主機(jī)Server上對(duì)相應(yīng)的Key進(jìn)行訪問,當(dāng)訪問超過主機(jī)Server極限時(shí),就會(huì)導(dǎo)致熱點(diǎn)Key問題的產(chǎn)生。
如何解決?針對(duì)于熱點(diǎn)Key的解決方案網(wǎng)上的查找出來無非就是兩種
服務(wù)端緩存:即將熱點(diǎn)數(shù)據(jù)緩存至服務(wù)端的內(nèi)存中
備份熱點(diǎn)Key:即將熱點(diǎn)Key+隨機(jī)數(shù),隨機(jī)分配至Redis其他節(jié)點(diǎn)中。這樣訪問熱點(diǎn)key的時(shí)候就不會(huì)全部命中到一臺(tái)機(jī)器上了。
其實(shí)這兩個(gè)解決方案前提都是知道了熱點(diǎn)Key是什么的情況,那么如何找到熱點(diǎn)key呢?
熱點(diǎn)檢測(cè)憑借經(jīng)驗(yàn),進(jìn)行預(yù)估:例如提前知道了某個(gè)活動(dòng)的開啟,那么就將此Key作為熱點(diǎn)Key
客戶端收集:在操作Redis之前對(duì)數(shù)據(jù)進(jìn)行統(tǒng)計(jì)
抓包進(jìn)行評(píng)估:Redis使用TCP協(xié)議與客戶端進(jìn)行通信,通信協(xié)議采用的是RESP,所以能進(jìn)行攔截包進(jìn)行解析
在proxy層,對(duì)每一個(gè) redis 請(qǐng)求進(jìn)行收集上報(bào)
Redis自帶命令查詢:Redis4.0.4版本提供了redis-cli –hotkeys就能找出熱點(diǎn)Key
如果要用Redis自帶命令查詢時(shí),要注意需要先把內(nèi)存逐出策略設(shè)置為allkeys-lfu或者volatile-lfu,否則會(huì)返回錯(cuò)誤。進(jìn)入Redis中使用config set maxmemory-policy allkeys-lfu即可。服務(wù)端緩存
假設(shè)我們已經(jīng)統(tǒng)計(jì)出了一些熱點(diǎn)Key,將這些數(shù)據(jù)緩存到了服務(wù)端,那么還有一個(gè)問題。就是如何保證Redis和服務(wù)端熱點(diǎn)Key的數(shù)據(jù)一致性。我這里想到的解決方案是利用Redis自帶的消息通知機(jī)制,對(duì)于熱點(diǎn)Key客戶端建立一個(gè)監(jiān)聽,當(dāng)熱點(diǎn)Key有更新操作的時(shí)候,客戶端也隨之更新。
主要代碼如下,監(jiān)聽類負(fù)責(zé)接收到Redis的事件,然后篩選出熱點(diǎn)Key進(jìn)行相應(yīng)的動(dòng)作
public class KeyExpiredEventMessageListener implements MessageListener { @Autowired private RedisTemplate redisTemplate; @Override public void onMessage(Message message, byte[] pattern) { String key = new String(message.getChannel()); key = key.substring(key.indexOf(":")+1); String action = new String(message.getBody()); if (HotKey.containKey(key)){ String value = redisTemplate.opsForValue().get(key)+""; switch (action){ case "set": log.info("熱點(diǎn)Key:{} 修改",key); HotKeyAction.UPDATE.action(key,value); break; case "expired": log.info("熱點(diǎn)Key:{} 到期刪除",key); HotKeyAction.REMOVE.action(key,null); break; case "del": log.info("熱點(diǎn)Key:{} 刪除",key); HotKeyAction.REMOVE.action(key,null); break; } } } }
建立一個(gè)存儲(chǔ)熱點(diǎn)Key的數(shù)據(jù)結(jié)構(gòu)ConcurrentHashMap,并設(shè)置相應(yīng)的操作方法,這里設(shè)置了假數(shù)據(jù),在static代碼塊中直接設(shè)置了兩個(gè)熱點(diǎn)Key
public class HotKey { private static MaphotKeyMap = new ConcurrentHashMap<>(); private static List hotKeyList = new CopyOnWriteArrayList<>(); static { setHotKey("hu1","1"); setHotKey("hu2","2"); } public static void setHotKey(String key,String value){ hotKeyMap.put(key,value); hotKeyList.add(key); } public static void updateHotKey(String key,String value){ hotKeyMap.put(key,value); } public static String getHotValue(String key){ return hotKeyMap.get(key); } public static void removeHotKey(String key){ hotKeyMap.remove(key); } public static boolean containKey(String key){ return hotKeyList.contains(key); } }
其實(shí)用Redis的事件通知機(jī)制挺不好的,因?yàn)橹灰_啟了事件通知,那么每個(gè)Key的變化都會(huì)發(fā)消息,這樣也會(huì)平白無故的加重Redis服務(wù)器的負(fù)擔(dān)。當(dāng)然我只是簡單的演示一下,除了這種通知方案以外還有很多種方法。
備份熱點(diǎn)Key這個(gè)方案說起來其實(shí)也很簡單,就是不要讓key走到一臺(tái)機(jī)器上就行,但是我們知道在Redis集群中包含了16384個(gè)哈希槽(Hash slot),集群使用公式CRC16(key) % 16384來計(jì)算Key屬于哪個(gè)槽。那么同一個(gè)Key計(jì)算出來的值應(yīng)該都是一樣的,如何將Key分到其他機(jī)器上呢?只要再后面加上隨機(jī)數(shù)就行了,這樣就能保證同一個(gè)Key分布在不同機(jī)器上,在訪問的時(shí)候通過Key+隨機(jī)數(shù)的方式進(jìn)行訪問。
偽代碼如下
const M = N * 2 //生成隨機(jī)數(shù) random = GenRandom(0, M) //構(gòu)造備份新key bakHotKey = hotKey + “_” + random data = redis.GET(bakHotKey) if data == NULL { //從數(shù)據(jù)庫中取數(shù)據(jù) data = GetFromDB() //存放在Redis中,以便下次能取到 redis.SET(bakHotKey, expireTime + GenRandom(0,5)) }代碼地址Github 參考文章
http://mysql.taobao.org/monthly/2018/09/08/
https://www.cnblogs.com/rjzheng/p/10874537.html
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/75317.html
摘要:未完,待續(xù)阿里云云數(shù)據(jù)庫版兼容協(xié)議標(biāo)準(zhǔn)的提供持久化的內(nèi)存數(shù)據(jù)庫服務(wù),基于高可靠雙機(jī)熱備架構(gòu)可無縫擴(kuò)展的集群架構(gòu)以及讀寫分離架構(gòu),滿足高讀寫性能場(chǎng)景及容量需彈性變配的業(yè)務(wù)需求。 摘要: Redis是開源的基于內(nèi)存且可以持久化的分布式 Key – Value數(shù)據(jù)庫。自2009年發(fā)布最初版本以來,Redis的熱度只增不減,除了經(jīng)常位居DB-Engines的最受歡迎Key-Value數(shù)據(jù)庫榜首...
摘要:關(guān)于緩存熱點(diǎn)重建原文說到在緩存失效的瞬間,有大量線程來重建緩存,造成后端負(fù)載加大,甚至可能會(huì)讓應(yīng)用崩潰,并給出互斥鎖和永遠(yuǎn)不過期兩種候選方案。即使繞過互斥鎖,也不會(huì)產(chǎn)生什么不好的后果,因?yàn)楦戮彺媸且粋€(gè)冪等操作。 向大家推薦這篇文章——Redis架構(gòu)之防雪崩設(shè)計(jì):網(wǎng)站不宕機(jī)背后的兵法 (另外推薦我去年的短文作為餐前點(diǎn)心——略談服務(wù)端緩存設(shè)計(jì)) 《Redis架構(gòu)之防雪崩設(shè)計(jì)》這篇文章(下...
閱讀 2085·2021-11-23 10:08
閱讀 2424·2021-11-22 15:25
閱讀 3351·2021-11-11 16:55
閱讀 843·2021-11-04 16:05
閱讀 2749·2021-09-10 10:51
閱讀 770·2019-08-29 15:38
閱讀 1664·2019-08-29 14:11
閱讀 3573·2019-08-29 12:42