Copyright ? 2015 深圳市鑫惠廣網絡科技有限公司 粵ICP備2023111395號
一,Redis作緩存服務器
redis作為緩存服務器是眾多企業中的選擇之一,雖然該技術很成熟但也是存在一定的問題。就是緩存帶來的緩存穿透,緩存擊穿,緩存失效問題,繼而引用分布式鎖。
1.1,緩存穿透
在如今的項目中大多采用垂直的MVC架構,由service層去調用DAO層,然后DAO層再去查詢數據庫。而redis作為緩存服務器就是在service層去調用DAO層去查詢時先去緩存服務器查詢,如果存在則直接返回該數據,否則再去查詢數據庫。由此可知,這么做大量減少了對磁盤I/O的操作,減輕了數據庫的壓力。
現在我們假設一種情況,在數據庫中存在有id為1到1000的數據。現在如果有人手動去模擬一個id為1001的請求,那么該數據在緩存服務器中是不存在的,因而便會去查詢數據庫。那么問題來了,如果是一個大量無效的請求去查詢數據庫。則勢必會對數據庫造成難以承受的壓力,這種情況就是所謂的緩存穿透。
那如何解決呢?
1,將查詢到的null值直接保存到緩存服務器中,但是這種做法并不推薦,因為如果是大量不同的請求id同樣會去查詢數據庫。
2,接口的限流,降級與熔斷
在項目中對于重要的接口一定要做限流,對于以上惡意攻擊的請求除了要限流,還要做好降級準備,并且進行熔斷,這種做法可以有效控制大量無效請求。
3,布隆過濾器
Bloomfilter就類似于一個hash set,用于快速判某個元素是否存在于集合中,其典型的應用場景就是快速判斷一個key是否存在于某容器,不存在就直接返回。布隆過濾器的關鍵就在于hash算法和容器大小,該做法是多數企業所選擇的。
1.2,緩存擊穿
在高并發下,對某些 熱點的值進行查詢,但是這個時候緩存正好過期了,緩存沒有命中,導致大量請求直接落到數據庫上,此時這種大量的請求可能會是數據庫崩盤。
解決方案:
1,將熱點key設置成永不過期。
2,使用互斥鎖。
以上兩種情況均是屬于緩存失效,但里面還有小小的細節。那就是存在多個緩存同時失效的問題,尤其在高并發時間段。為避免這種多個緩存失效的問題,我們在設置超時時間的時候,可以使用固定時間+隨機時間。以最大限度避免當緩存失效時大量請求去查詢數據庫。
1.3,分布式鎖
通常情況下分布式鎖有三種實現方式,1. 數據庫樂觀鎖;2. 基于ZooKeeper的分布式鎖;3. 基于Redis的分布式鎖;這里只記錄基于redis的分布式鎖。
作為分布式鎖的要求:
先參看如下代碼:
public List<Goods> goodsManager() { System.out.println("調用過了業務層的goodsManager方法"); return goodsDao.queryAllPage(); // 1,先去查詢緩存服務器 List<Goods> goodsList = (List<Goods>) redisTemplate.opsForValue().get("goods"); if(goodsList == null){ // 2,申請分布式鎖 RedisConnection conn = redisTemplate.getConnectionFactory().getConnection(); if(conn.setNX("lock".getBytes(), "1".getBytes())){ // 3,給分布式鎖設置一個超時時間 conn.expire("lock".getBytes(), 60); System.out.println("去數據庫中查詢所有的商品"); // 4,緩存中沒有商品列表的數據 goodsList = goodsDao.queryAllPage(); // 5,將結果放入緩存中 redisTemplate.opsForValue().set("goods", goodsList); redisTemplate.expire("goods", 5, TimeUnit.MINUTES); // 6,釋放分布式鎖 conn.del("lock".getBytes()); } else { try { Thread.sleep(50); goodsManager(); } catch (InterruptedException e) { e.printStackTrace(); } } return goodsList; } else { //緩存服務器中有商品列表的數據 return goodsList; } }
代碼設計思路:
1,請求到來調用方法。
2,先去redis緩存中查詢是否存在,如果沒有則查詢數據庫。
3,使用原生的連接(setNX)獲得分布式鎖,然后設置超時時間。
設置超時時間的原因在于,如果線程獲得鎖之后不下心崩潰,為防止發生死鎖因而設置超時時間。
4,查詢數據庫獲得數據,并保存在數據庫中。
5,釋放鎖。
1.4,雪崩效應
簡單來說,緩存在同一時間內大量鍵(key)過期(失效),而新的緩存又沒有即時的保存到服務器中,此時大量的請求瞬間都落在了數據庫中導致連接異常。
解決方案:
1、可以使用分布式鎖 ,單架構項目使用syn
2、永不過期
3、在設置緩存超時時間,固定時間+隨機超時時間,防止多數緩存同時失效。
4、高可用,集群
1.5,redis緩存與springboot整合
在啟動函數中先要開啟緩存注解@Enablecaching
@Cacheable
被該注解標注的方法,會在執行前查詢緩存服務器,如果緩存服務器有結果,則直接返回結果,當前方法就不會執行。如果沒有結果,則在執行該方法的方法體,并且將該方法返回值放入緩存服務器中。
@CachePut
該注解和@Cacheable注解的功能差不多,唯一的區別在于不管緩存服務器有沒有對應的值,都會去調用相應的方法用于添加和更新的方法。
@CacheEvict
刪除指定的緩存,一般用于刪除方法的使用 。
二,Redis持久化
### 2.1,redis提供兩種持久化方式:
2.2,RDB原理分析
RDB持久化有兩種操作方式,手動操作進行持久化。
bgsave和save最大的區別在于bgsave不會阻塞客戶端的寫操作,但是如果bgsave執行失敗,Redis默認將停止接受接入操作,否則就沒人會注意到災難的發生,如果不希望這樣做,可以將
stop-writes-on-bgsave-error yes設置為no
另一種為自動觸發持久化,首先我們可以在配置文件中配置快照的規則。
save 900 1 當900秒以內執行1個寫命令,使用快照備份
save 300 10 當300秒以內執行10個寫命令,使用快照備份
save 60 10000 當60秒以內執行10000個寫命令,使用快照備份
注意:redis執行備份命令時,將禁止寫入命令
2.3,AOF原理分析
AOF的整個流程大體來看可以分為兩步,一步是命令的實時寫入,第二步是對aof文件的重寫,重寫是為了減少aof文件的大小。
AOF文件追加大致流程為:命令寫入-->追加到aof_buf(緩沖區) -->同步到aof磁盤。為什么要先寫入buf緩沖區在同步到磁盤呢?因為如果實時寫入便會帶來大量的磁盤I/O操作,會很大程度上降低系統的性能。
關于AOF持久化大概有以下幾種配置。
2.4,Redis內存回收策略
在redis.conf中的配置項maxmemory-policy用于配置redis的內存回收策略,當內存達到最大值時所采取的內存處理方式。
redis提供六種內存淘汰策略
volatile-lru: allkeys-lru: volatile-random: allkeys-random: volatile-ttl: noeviction(默認):
在內存回收機制中,LRU算法和TTl算法在redis中都不是精準計算,而是一個近似算法。redis默認有一個探測數量的配置maxmemory-samples 默認為3。
三,Redis高可用
3.1,主從復制
在用戶量非常龐大的時候,單臺redis肯定是完全不夠用的。因此更多的時候我們更希望可以讀/寫分離,讀/寫分離的前提就是讀操作比寫操作頻繁的多,將數據放在多臺服務器上那么久可以消除單臺服務器的壓力。
因此對于服務器的搭建如圖:
假設一臺服務器負責寫操作,其余三臺為讀操作,以此實現一個獨寫分離的緩存功能。但是很明顯存在一種弊端,就是其余三臺讀取數據的服務器它們之間的數據是不能夠進行同步的。這樣便造成數據不一致的情況,此時就需要對它們之間進行一個數據上的互通。
簡單介紹一下主從復制的概念,如上圖 Master為主,負責寫入數據的操作,其余 三臺為從(Slave),負責讀取數據操作。當有數據寫入時,根據配置好的屬性自動將更新的數據復制到其余三臺服務器中,這樣便實現了服務器之間的數據一致性。
主從復制的大致流程:
1、保證主服務器(Master)的啟動。
2、當從服務器啟動時,發送SYNC命令給主服務器。主服務器接受到同步命令時,就是執行bgsave命令備份數據,但是主服務器并不會拒絕客戶端的寫操作,而是將來自客戶端的寫命令寫入緩沖區。從服務器在未收到主服務器的備份快照文件之前,會根據配置決定使用現有數據響應客戶端還是返回錯誤。
3、當bgsave命令被主服務器執行完后,開始向從服務器發送備份文件,這個時候從服務器就會丟棄現有的所有數據,開始載入發送過來的快照文件。
4、當主服務器發送完備份文件后,會將bgsave執行之后的緩存區內的寫命令也發送給從服務器,從服務器完成備份文件解析后,就開始等待主服務器的后續命令。
5、同步完成以后,每次主服務器完成一次寫入命令,都會同時往從服務器發送同步寫入命令,主從同步就完成了。
從機配置:
slaveof server port 設置Master的ip和端口
masterauth root 設置Master的密碼
到此為止,就完成了嗎?
并不是,以上步驟只是完成了主從復制,并沒有完成讀寫分離。并且,如果主(Master)服務器宕機,那整個緩存服務器就全部掛掉了。==而且作為從(Slave)服務器時不可以進行寫的操作,==那又如何解決呢(哨兵模式)?
3.2,哨兵模式
1,什么時哨兵模式?
當Master宕機以后需要手動的把一臺Slave切換為Master,這種方式需要人工干預,費時費力。因此哨兵模式可以幫助我們解決這個問題。
2,簡述哨兵模式
3,哨兵模式配置
3.1,#配置哨兵配置文件:
redis/src/sentinel.conf
3.2,#禁止保護模式
protected-mode no
3.3,#配置監聽的主服務, 最后的2表示當2個或2個以上的哨兵認為主服務不可用才會進行故障切換
sentinel monitor 服務器名稱(自定義) 主服務ip 端口 2
3.4,#定義服務密碼
sentinel auth-pass 服務器名稱(和上面相同) 密碼
3.5,#啟動哨兵模式;
./redis-sentinel sentinel.conf
4,其他相關配置
sentinel down-after-milliseconds : 指定哨兵在檢測redis服務時,當redis服務在一個毫秒數內都無法回答時,單個哨兵認為的主觀下線時間,默認為30秒。
sentinel failover-timeout: 指定故障切換運行的毫秒數,當超過這個毫秒數時,就認為切換故障失敗,默認3分鐘。
sentinel notification-script: 指定哨兵檢測到redis實例異常時,調用的報警腳本。
3.3,分片集群
分片集群原理在于多個緩存服務器之間兩兩相互通信,每個復制集具有一個主實例和多個從實例。并且每個復制集朱保存一部分數據庫中的鍵值對,解決了主從復制集中總數據存儲量最小實例的限制,大大擴大了緩存服務器的大小。
其結構圖如下:
1,分片集群特點
1、Client與redis節點直接連接,不需要中間proxy層。
2、 redis-cluster把所有的物理節點映射到[0-16383]slot(插槽)上,cluster 負責維護。
3、所有的redis節點彼此互聯(PING-PONG機制),內部使用gossip二進制協議優化傳輸數據。
4、 節點的失效檢測是通過集群中超過半數的節點檢測失效時才生效。
==問題:Redis 集群中內置了 16384 個哈希槽,那他是如何決定將key放入哪個插槽的?==
當Redis 集群中放置一個 key-value 時,redis 先對 key 使用 crc16 算法算出一個結果,然后把結果對 16384 求余數,這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,redis 會根據節點數量大致均等的將哈希槽映射到不同的節點。
2,集群搭建步驟
前置條件:
刪除redis/src下的appendonly.aof,dump.rdb,nodes-6379.conf3個文件。
1、修改redis.conf,配置集群信息開啟集群,
cluster-enabled yes 指定集群的配置文件,
cluster-config-file nodes- 端口.conf
2、用redis-trib.rb搭建集群因為redis-trib.rb是用Ruby實現的Redis集群管理工具,所以我們需要先安裝ruby的環境.
2.1、安裝ruby
yum -y install zlib ruby rubygems
2.2、安裝rubygems的redis依賴
gem install -l redis-3.3.0.gem
3、安裝好依賴的環境之后,我們就可以來使用腳本命令了
注意:此腳本文件在我們的解壓縮目錄src下。
執行命令:
./redis-trib.rb create --replicas 0 192.168.10.167:6379 192.168.10.167:6380 192.168.10.167:6381 開放16379 redis端口+1W--replicas 0:指定了從數據的數量為0。
4、查看集群狀態
通過客戶端輸入以下命令:
cluster nodes:這個命令可以查看插槽的分配情況
整個Redis提供了16384個插槽,./redis-trib.rb 腳本實現了是將16384個插槽平均分配給了N個節點。
end:如果你覺得本文對你有幫助的話,記得關注點贊轉發,你的支持就是我更新動力。
Copyright ? 2015 深圳市鑫惠廣網絡科技有限公司 粵ICP備2023111395號