Redis我們一般是用作緩存,扛并發(fā);或者用于某些特定的業(yè)務場景,比如前面說到redis各種數(shù)據(jù)類型的使用場景以及redis的哨兵和集群模式。

          這里主要整理了下redis用作緩存,存在的一些問題,以及改善方案。



          ?

          簡單的流程就像這個樣子,一般請先到緩存區(qū)獲取,如果緩存沒有再到后端的數(shù)據(jù)庫去查詢。

          1.緩存穿透


          緩存穿透是指,是指查詢一個根本不存在數(shù)據(jù),這樣緩存層里面沒有,就會去訪問后面的存儲層了。如果有大量的這種惡意請求過來,都打向后面的存儲層。顯然我們的存儲層是扛不住這樣的壓力。這樣緩存就失去了保護后面存儲的意義了。

          解決方案:

            1.緩存空對象

          對于緩存穿透,可以采用緩存空對象,第一次進來緩存和
          DB都沒有,就存?zhèn)€空對象到緩存里面。但是如果大批量的惡意請求過來,這樣做就會導致緩存的key暴增,顯然不是一個很好的方案。

            2.布隆過濾器

          對于不存在的數(shù)據(jù)布隆過濾器一般都能夠過濾掉,不讓請求再往后端發(fā)送。當布隆過濾器說某個值存在時,這個值可能不存在;但是
          它說不存在時,那就肯定不存在。布隆過濾器是一個大型的位數(shù)組和幾個不一樣的無偏 hash 函數(shù)。所謂無偏就是能夠把元素的hash值算得比較均勻。向布隆過濾器中添加
          key 時,會使用多個hash
          函數(shù)對key進行hash分別算得一個整數(shù)索引值然后對位數(shù)組長度進行取模運算得到一個位置,每個hash函數(shù)都會算得一個不同的位置。再把位數(shù)組的這幾個位置都置為
          1 就 完成了 add 操作。

          向布隆過濾器詢問 key 是否存在時,跟 add 一樣,也會把 hash
          的幾個位置都算出來,看看位數(shù)組中這幾個位置是否都為1,只要有一個位為0,那么說明布隆過濾器中這個key肯定不存在。但是都是
          1,這并不能說明這個key就一定存在,只是極有可能存在,因為這些位被置為1可能是因為其它的key存在所致。



          ?

          ?guvua包布隆過濾器的使用,導包

          ?
          <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId>
          </dependency>
          ?

          偽代碼:
          public void bloomFilterTest() { BloomFilter<CharSequence> bloomFilter =
          BloomFilter.create( Funnels.stringFunnel(Charset.forName("UTF-8")), 1000, //
          期望存入的數(shù)據(jù)個數(shù) 0.001);//誤差率 //添加到布隆過濾器 String[] keys = new String[1000]; for (String
          key: keys) { bloomFilter.put(key); } String key= "key"; boolean exist =
          bloomFilter.mightContain(key);if (!exist) { return; } //todo 存在才去緩存獲取 }
          可以看到這個類里面有很多的hash算法:com.google.common.hash.Hashing

          redisson也有布隆過濾器的實現(xiàn)。

          ?

          ?

          2.緩存失效

          由于大批量的
          key同時失效,導致,大量的請求同時打向數(shù)據(jù)庫,造成數(shù)據(jù)庫壓力過大,甚至直接掛掉。我們在批量寫入緩存的時候,設置超時時間,可以是一個固定時間+隨機時間方式來生成,這樣就可以錯開失效時間。

          3.緩存雪崩


          緩存雪崩是指緩存層掛掉之后,所有請求都打向數(shù)據(jù)庫,數(shù)據(jù)庫扛不住,也可能掛掉,就導致對應的服務也掛掉,也會影響上游的調用服務。這樣的級聯(lián)問題。就像雪崩最開始一小片,然后越來越大,導致整個服務崩潰。

          解決方案:

            1.保證緩存層的高可用性,比如redis哨兵或者redis集群。

            2.各依賴服務之間做限流,熔斷,降級等,比如Hystri,阿里的sentinel

          4.緩存一致性

            引入緩存之后,隨之而來的問題就是當DB數(shù)據(jù)更新時,緩存中的數(shù)據(jù)就會與db數(shù)據(jù)不一致。所以數(shù)據(jù)修改時是先更新緩存還是先更新DB?

            如果先更新緩存,然后更新DB失敗,那么下一個請求過來讀取的緩存數(shù)據(jù)不是最新的。而我們實際上最終數(shù)據(jù)肯定都是以DB為準的。

            先更新db 在更新緩存,這是在更新DB的時候來的請求讀取的數(shù)據(jù)也是不是最新的

            淘汰緩存——更新DB——重新刷進緩存,在更新db是來的請求在緩存沒有數(shù)據(jù),就會去請求DB,如果并發(fā) 可能操作多各請求去寫DB,那么就需要加鎖了

            加鎖——淘汰緩存——更新DB——重新刷進緩存,這樣相對而言就比較保險了

          5.bigkey問題

          Bigkey是什么?在redis中,一個字符串最大512MB;hash,list,set,zset可以存儲2^31 - 1 個元素。

          一般來說字符串超過10kb,其他的幾種元素個數(shù)不要超過5000個。

          可以使用src/redis-cli --bigkeys
          來查看bigkey,我這里設置了一個30多K的字符串,看下掃描結果,掃除了一個字符串類型的bigkey,4084字節(jié)。



          ?

          ?


          Bigkey有哪些危害。一是刪除時阻塞其他請求,比如一個bigkey,平時都沒什么,但是設置了過期時間,到期了刪除時,可能就會阻塞其他請求,4.0之后可以開啟lazyfree-lazy-
          expire
          yes來異步刪除;二是造成網(wǎng)絡擁堵,比如一個key數(shù)據(jù)量達到1MB,假設并發(fā)量1000,這個時候獲取它就會產(chǎn)生1000MB的流量,千兆網(wǎng)卡,峰值的速率也才128MB/S,并不是扛不住并發(fā),而是會占用大量網(wǎng)絡帶寬。

          對于很大
          list,set這些,我們可以將數(shù)據(jù)拆分,生成一個系列的的key去存放數(shù)據(jù)。如果是redis集群這些key自然就可以分到不同的小主從上面去,如果是單機,那么可以自己實現(xiàn)一個路由算法,來如何獲取這一系列key中的某一個。

          6. 客戶端使用

            1.避免多個服務使用一個redis實例,如果實在有,可以看下將業(yè)務拆分,把這些公共數(shù)據(jù)服務化。

            2.使用連接池,控制有效連接,同時也提高效率。連接池重要參數(shù)設置:

              1 maxActive?資源池中最大連接數(shù) 默認值8?

              2 maxIdle 資源池允許最大空閑 的連接數(shù) 默認值8?

              3 minIdle 資源池確保最少空閑 的連接數(shù) 默認值0?

              4 blockWhenExhausted 當資源池用盡后,調用者是否要等待。只有當為true時,下面的maxWaitMillis才會生效,默認值
          true 建議使用默認值

              5 maxWaitMillis 當資源池連接用盡后,調用者的最大等待時間(單位為毫秒)?-1:表示永不超時 不建議使用默認值

              6 testOnBorrow 向資源池借用連接時是否做連接有效性檢測(ping),無效連接會被移除 默認值false 業(yè)務量很大時候建議
          設置為false(多一次 ping的開銷)。

              7 testOnReturn 向資源池歸還連接時是否做連接有效性檢測(ping),無效連接會被移除 默認值false 業(yè)務量很大時候建議
          設置為false(多一次 ping的開銷)。

              8 jmxEnabled 是否開啟jmx監(jiān)控,可用于監(jiān)控 默認值true 建議開啟,但應用本身也要開啟

            前面三個參數(shù)相對而言更重要,單獨拎出來再說下:

              最大連接數(shù)maxActive:

                可以從業(yè)務希望的并發(fā)量,客戶端執(zhí)行時間,redis資源設置(應用個數(shù)(集群部署多少個實例) * maxActive?<=
          maxclients(redis最大連接數(shù),redis配置中設置的)),等因素考慮。

              比如一次客戶端執(zhí)行時間
          2ms,那么一個連接的QPS就是500,業(yè)務期望的QPS是3000,那么理論上連接池大小3000/500=60個,實際上考慮其他影響,一般設置比理論值稍微大點。但這個值不是越大越好,一方面連接太多占用客戶端和服務端資源,另一方面對    于Redis這種高
          QPS的服務器,一個大命令的阻塞即使設置再大資源池仍然會無濟于事。

              最大空閑連接數(shù)maxIdle:

                maxIdle實際上才是業(yè)務需要的最大連接數(shù),空閑的連接造好放在那兒,進來一個請求就可以直接拿來用了。maxActive是為了給出總量,所以
          maxIdle不要設置過小,否則會有當空閑連接不夠,就會創(chuàng)建新的連接,又會有新的開銷,最佳就是maxActive?=
                maxIdle。這樣就避免連接池伸縮帶來的性能干擾。但是如果并發(fā)量不大或者maxActive設置過高,會導致不必要的連接資源浪費。一般推薦
          maxIdle可以設置為按上面的業(yè)務期望QPS計算出來的理論連接數(shù),maxActive可以再放大一些。

              最小空閑連接數(shù)minIdle:

                至少保持多少空閑連接,在使用連接的過程中,如果連接數(shù)超過了minIdle,那么繼續(xù)建立連接,如果超過了
          maxIdle,當超過的連接執(zhí)行完業(yè)務后會慢慢被移出連接池釋放掉。

            3.緩存預熱

              比如說上線一個搶購活動,肯定到點開始就會有很多人來請求了,這個時候就可以提前做數(shù)據(jù)的預熱,既可以把連接池初始化好,也可以把數(shù)據(jù)放好。

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關注微信

                搞毛片 | 男人插女人逼动态图 | 用力挺进她的花苞啊h视频 | 香港艳妇伦理片 | 一级黄色片看看 |