Redis 是一個使用 C 語言編寫的,開源的(BSD許可)高性能非關係型(NoSQL)的鍵值對數據庫。前端
Redis 能夠存儲鍵和五種不一樣類型的值之間的映射。鍵的類型只能爲字符串,值支持五種數據類型:字符串、列表、集合、散列表、有序集合。node
與傳統數據庫不一樣的是 Redis 的數據是存在內存中的,因此讀寫速度很是快,所以 redis 被普遍應用於緩存方向,每秒能夠處理超過 10萬次讀寫操做,是已知性能最快的Key-Value DB。另外,Redis 也常常用來作分佈式鎖。除此以外,Redis 支持事務 、持久化、LUA腳本、LRU驅動事件、多種集羣方案(Redis 6.0 集羣搭建實踐)。面試
從2010年3月15日起,Redis的開發工做由VMware主持。從2013年5月開始,Redis的開發由Pivotal贊助。redis
Redis主要有5種數據類型,包括String,List,Set,Zset,Hash,知足大部分的使用要求。詳細的可參考:Redis 的 8 大數據類型,寫得很是好!算法
因爲Redis優異的讀寫性能,持久化支持等優點,Redis的使用場景很是多,主要包括計數器,緩存,消息隊列,分佈式鎖等,具體使用場景以下:數據庫
計數器vim
緩存後端
會話緩存緩存
全頁緩存(FPC)安全
查找表
消息隊列(發佈/訂閱功能)
分佈式鎖實現
其它
Redis 是內存型數據庫,爲了以後重用數據(好比重啓機器、機器故障以後回覆數據),或者是爲了防止系統故障而將數據備份到一個遠程位置,須要將內存中的數據持久化到硬盤上。
Redis 提供了RDB和AOF兩種持久化方式。默認是隻開啓RDB,當Redis重啓時,它會優先使用AOF文件來還原數據集。
Redis持久化詳解能夠參考:Redis持久化
Redis中有個設置時間過時的功能,即對存儲在 redis 數據庫中的值能夠設置一個過時時間。做爲一個緩存數據庫,這是很是實用的。如咱們通常項目中的 token 或者一些登陸信息,尤爲是短信驗證碼都是有時間限制的,按照傳統的數據庫處理方式,通常都是本身判斷過時,這樣無疑會嚴重影響項目性能。
Redis有三種不一樣的刪除策略:當即刪除,惰性刪除,定時刪除
能夠看到,第二種爲被動刪除,第一種和第三種爲主動刪除,且第一種實時性更高。下面對這三種刪除策略進行具體分析。
當即刪除能保證內存中數據的最大新鮮度,由於它保證過時鍵值會在過時後立刻被刪除,其所佔用的內存也會隨之釋放。可是當即刪除對cpu是最不友好的。由於刪除操做會佔用cpu的時間,若是恰好碰上了cpu很忙的時候,好比正在作交集或排序等計算的時候,就會給cpu形成額外的壓力。
並且目前redis事件處理器對時間事件的處理方式–無序鏈表,查找一個key的時間複雜度爲O(n),因此並不適合用來處理大量的時間事件。
惰性刪除是指,某個鍵值過時後,此鍵值不會立刻被刪除,而是等到下次被使用的時候,纔會被檢查到過時,此時才能獲得刪除。因此惰性刪除的缺點很明顯:浪費內存。dict字典和expires字典都要保存這個鍵值的信息。
舉個例子,對於一些按時間點來更新的數據,好比log日誌,過時後在很長的一段時間內可能都得不到訪問,這樣在這段時間內就要拜拜浪費這麼多內存來存log。這對於性能很是依賴於內存大小的redis來講,是比較致命的。
從上面分析來看,當即刪除會短期內佔用大量cpu,惰性刪除會在一段時間內浪費內存,因此定時刪除是一個折中的辦法。
定時刪除是:每隔一段時間執行一次刪除操做,並經過限制刪除操做執行的時長和頻率,來減小刪除操做對cpu的影響。另外一方面定時刪除也有效的減小了因惰性刪除帶來的內存浪費。
redis使用的過時鍵值刪除策略是:惰性刪除加上按期刪除,二者配合使用。
能夠設置內存最大使用量,當內存使用量超出時,會施行數據淘汰策略。
Redis 具體有 6 種淘汰策略:
做爲內存數據庫,出於對性能和內存消耗的考慮,Redis 的淘汰算法實際實現上並不是針對全部 key,而是抽樣一小部分而且從中選出被淘汰的 key。
Redis 4.0 引入了 volatile-lfu 和 allkeys-lfu 淘汰策略,LFU 策略經過統計訪問頻率,將訪問頻率最少的鍵值對淘汰。
您須要根據系統的特徵,來選擇合適的淘汰策略。 固然,在運行過程當中也能夠經過命令動態設置淘汰策略,並經過 INFO 命令監控緩存的 miss 和 hit,來進行調優。
淘汰策略的內部實現
在這個過程當中,內存使用量會不斷地達到 limit 值,而後超過,而後刪除部分 key,使用量又降低到 limit 值之下。
若是某個命令致使大量內存佔用(好比經過新key保存一個很大的set),在一段時間內,可能內存的使用量會明顯超過 maxmemory 限制。
二者都是非關係型內存鍵值數據庫,如今公司通常都是用 Redis 來實現緩存,並且 Redis 自身也愈來愈強大了!Redis 與 Memcached 的區別請參考:Redis與Memcached的區別
Redis 經過 MULTI、EXEC、WATCH 等命令來實現事務(transaction)功能。事務提供了一種將多個命令請求打包,而後一次性、按順序地執行多個命令的機制,而且在事務執行期間,服務器不會中斷事務而改去執行其餘客戶端的命令請求,它會將事務中的全部命令都執行完畢,而後纔去處理其餘客戶端的命令請求。
事務中的多個命令被一次性發送給服務器,而不是一條一條發送,這種方式被稱爲流水線,能夠減小客戶端與服務器之間的網絡通訊次數從而提高性能。
在傳統的關係式數據庫中,經常使用 ACID 性質來檢驗事務功能的可靠性和安全性。在 Redis 中,事務老是具備原子性(Atomicity)、一致性(Consistency)和隔離性(Isolation),而且當 Redis 運行在某種特定的持久化模式下時,事務也具備持久性(Durability)。
Redis 服務器是一個事件驅動程序。
服務器經過套接字與客戶端或者其它服務器進行通訊,文件事件就是對套接字操做的抽象。
Redis 基於 Reactor 模式開發了本身的網絡事件處理器,使用 I/O 多路複用程序來同時監聽多個套接字,並將到達的事件傳送給文件事件分派器,分派器會根據套接字產生的事件類型調用相應的事件處理器。
服務器有一些操做須要在給定的時間點執行,時間事件是對這類定時操做的抽象。
時間事件又分爲:
目前Redis只使用週期性事件,而沒有使用定時事件。 一個事件時間主要由三個屬性組成:
實現服務器將全部時間事件都放在一個無序鏈表中,每當時間事件執行器運行時,遍歷整個鏈表,查找全部已到達的時間事件,並調用相應的事件處理器。(該鏈表爲無序鏈表,不按when屬性的大小排序)
服務器須要不斷監聽文件事件的套接字才能獲得待處理的文件事件,可是不能一直監聽,不然時間事件沒法在規定的時間內執行,所以監聽時間應該根據距離如今最近的時間事件來決定。
Sentinel(哨兵)能夠監聽集羣中的服務器,並在主服務器進入下線狀態時,自動從從服務器中選舉出新的主服務器。
分片是將數據劃分爲多個部分的方法,能夠將數據存儲到多臺機器裏面,這種方法在解決某些問題時能夠得到線性級別的性能提高。
假設有 4 個 Redis 實例 R0,R1,R2,R3,還有不少表示用戶的鍵 user:1,user:2,… ,有不一樣的方式來選擇一個指定的鍵存儲在哪一個實例中。
最簡單的方式是範圍分片,例如用戶 id 從 0~1000 的存儲到實例 R0 中,用戶 id 從 1001~2000 的存儲到實例 R1 中,等等。可是這樣須要維護一張映射範圍表,維護操做代價很高。
還有一種方式是哈希分片,使用 CRC32 哈希函數將鍵轉換爲一個數字,再對實例數量求模就能知道應該存儲的實例。
根據執行分片的位置,能夠分爲三種分片方式:
經過使用 slaveof host port 命令來讓一個服務器成爲另外一個服務器的從服務器。
一個從服務器只能有一個主服務器,而且不支持主主複製。
鏈接過程
隨着負載不斷上升,主服務器可能沒法很快地更新全部從服務器,或者從新鏈接和從新同步從服務器將致使系統超載。爲了解決這個問題,能夠建立一箇中間層來分擔主服務器的複製工做。中間層的服務器是最上層服務器的從服務器,又是最下層服務器的主服務器。
緩存雪崩是指緩存同一時間大面積的失效,因此,後面的請求都會落到數據庫上,形成數據庫短期內承受大量請求而崩掉。
緩存穿透是指緩存和數據庫中都沒有的數據,致使全部的請求都落到數據庫上,形成數據庫短期內承受大量請求而崩掉。
附加
布隆過濾器(推薦)
緩存擊穿是指緩存中沒有但數據庫中有的數據(通常是緩存時間到期),這時因爲併發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引發數據庫壓力瞬間增大,形成過大壓力。和緩存雪崩不一樣的是,緩存擊穿指併發查同一條數據,緩存雪崩是不一樣數據都過時了,不少數據都查不到從而查數據庫。
緩存預熱就是系統上線後,將相關的緩存數據直接加載到緩存系統。這樣就能夠避免在用戶請求的時候,先查詢數據庫,而後再將數據緩存的問題!用戶直接查詢事先被預熱的緩存數據!
當訪問量劇增、服務出現問題(如響應時間慢或不響應)或非核心服務影響到核心流程的性能時,仍然須要保證服務仍是可用的,即便是有損服務。系統能夠根據一些關鍵數據進行自動降級,也能夠配置開關實現人工降級。
緩存降級的最終目的是保證核心服務可用,即便是有損的。並且有些服務是沒法降級的(如加入購物車、結算)。
在進行降級以前要對系統進行梳理,看看系統是否是能夠丟卒保帥;從而梳理出哪些必須誓死保護,哪些可降級;好比能夠參考日誌級別設置預案:
通常:好比有些服務偶爾由於網絡抖動或者服務正在上線而超時,能夠自動降級;
警告:有些服務在一段時間內成功率有波動(如在95~100%之間),能夠自動降級或人工降級,併發送告警;
錯誤:好比可用率低於90%,或者數據庫鏈接池被打爆了,或者訪問量忽然猛增到系統能承受的最大閥值,此時能夠根據狀況自動降級或者人工降級;
嚴重錯誤:好比由於特殊緣由數據錯誤了,此時須要緊急人工降級。
服務降級的目的,是爲了防止Redis服務故障,致使數據庫跟着一塊兒發生雪崩問題。所以,對於不重要的緩存數據,能夠採起服務降級策略,例如一個比較常見的作法就是,Redis出現問題,不去數據庫查詢,而是直接返回默認值給用戶。
熱點數據,緩存纔有價值
對於冷數據而言,大部分數據可能尚未再次訪問到就已經被擠出內存,不只佔用內存,並且價值不大。頻繁修改的數據,看狀況考慮使用緩存
對於熱點數據,好比咱們的某IM產品,生日祝福模塊,當天的壽星列表,緩存之後可能讀取數十萬次。再舉個例子,某導航產品,咱們將導航信息,緩存之後可能讀取數百萬次。
數據更新前至少讀取兩次,緩存纔有意義。這個是最基本的策略,若是緩存尚未起做用就失效了,那就沒有太大價值了。
那存不存在,修改頻率很高,可是又不得不考慮緩存的場景呢?有!好比,這個讀取接口對數據庫的壓力很大,可是又是熱點數據,這個時候就須要考慮經過緩存手段,減小數據庫的壓力,好比咱們的某助手產品的,點贊數,收藏數,分享數等是很是典型的熱點數據,可是又不斷變化,此時就須要將數據同步保存到Redis緩存,減小數據庫壓力。
緩存中的一個Key(好比一個促銷商品),在某個時間點過時的時候,剛好在這個時間點對這個Key有大量的併發請求過來,這些請求發現緩存過時通常都會從後端DB加載數據並回設到緩存,這個時候大併發的請求可能會瞬間把後端DB壓垮。
對緩存查詢加鎖,若是KEY不存在,就加鎖,而後查DB入緩存,而後解鎖;其餘進程若是發現有鎖就等待,而後等解鎖後返回數據或者進入DB查詢
以上內容參考連接:https://blog.csdn.net/ThinkWo...
咱們就來總結一下,在使用Redis時的最佳實踐方式,主要包含兩個層面:業務層面、運維層面。
因爲我以前寫過不少UGC後端服務,在大量場景下用到了Redis,這個過程當中也踩過不少坑,因此在使用過程當中也總結了一套合理的使用方法。
後來作基礎架構,開發Codis、Redis相關的中間件,在這個階段關注領域從使用層面下沉到Redis的開發和運維,更多聚焦在Redis的內部實現和運維過程當中產生的各類問題,在這塊也積累了一些經驗。
下面就針對這兩塊,分享一下我認爲比較合理的Redis使用和運維方法,不必定最全面,也可能與你使用Redis的方法不一樣,但如下這些方法都是我在踩坑以後總結的實際經驗,供你參考。關注公衆號Java技術棧回覆redis獲取系列Redis教程。
業務層面主要是開發人員須要關注,也就是開發人員在寫業務代碼時,如何合理地使用Redis。開發人員須要對Redis有基本的瞭解,才能在合適的業務場景使用Redis,從而避免業務層面致使的延遲問題。
寫請求量很大時,推薦使用集羣,部署多個實例分攤寫壓力
目的是合理規劃Redis的部署和保障Redis的穩定運行,主要優化以下:
以上就是我在使用Redis和開發Redis相關中間件時,總結出來Redis推薦的實踐方法,以上提出的這些方面,都或多或少在實際使用中遇到過。
可見,要想穩定發揮Redis的高性能,須要在各個方面作好工做,但凡某一個方面出現問題,必然會影響到Redis的性能,這對咱們使用和運維提出了更高的要求。
若是你在使用Redis過程當中,遇到更多的問題或者有更好的使用經驗,能夠留言一塊兒探討!
出處:kaito-kidd.com/2020/07/04/redis-best-practices/
集羣中每一個主節點將承擔一部分槽點的維護,而槽點中存儲着數據,每一個主節點都有至少一個從節點用於高可用。
redis cluster是去中心化的,集羣中的每一個節點都是平等的關係,每一個節點都保存各自的數據和整個集羣的狀態。每一個節點都和其餘全部節點鏈接,並且這些鏈接保持活躍。
搭建集羣時,會爲每個分片的主節點,對應一個從節點。實現slaveof功能,同時當主節點down,實現sentinel哨兵的自動failover切換功能
端口號:7000-7005
本次分佈式分片集羣在一臺LInux系統便可,只須要安裝多個實例做爲集羣配置。
yum -y install ruby rubygems
yum安裝2.0.0版本,可是gem須要2.2.2版本以上,因此選擇編譯
wget https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.1.tar.gz tar xf ruby-2.6.1.tar.gz && cd ruby-2.6.1/ ./configure --prefix=/usr/local/ruby make && make install && echo $? echo 'export PATH=$PATH:/usr/local/ruby/bin' >> /etc/profile source /etc/profile
# 查看gem工具源地址 gem sources -l # 添加一個阿里雲的gem工具源 gem sources -a http://mirrors.aliyun.com/rubygems/ # 刪除gem工具默認國外源 gem sources --remove https://rubygems.org/ # 下載當前最新版本集羣插件 gem install redis -v 4.1.0
mkdir /data/700{0..5}
vim /data/7000/redis.conf port 7000 daemonize yes pidfile /data/7000/redis.pid loglevel notice logfile "/data/7000/redis.log" dbfilename dump.rdb dir /data/7000 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
# 拷貝配置文件 cp /data/7000/redis.conf /data/7001/ cp /data/7000/redis.conf /data/7002/ cp /data/7000/redis.conf /data/7003/ cp /data/7000/redis.conf /data/7004/ cp /data/7000/redis.conf /data/7005/ # 修改配置文件內容 sed -i 's#7000#7001#g' /data/7001/redis.conf sed -i 's#7000#7002#g' /data/7002/redis.conf sed -i 's#7000#7003#g' /data/7003/redis.conf sed -i 's#7000#7004#g' /data/7004/redis.conf sed -i 's#7000#7005#g' /data/7005/redis.conf
redis-server /data/7000/redis.conf redis-server /data/7001/redis.conf redis-server /data/7002/redis.conf redis-server /data/7003/redis.conf redis-server /data/7004/redis.conf redis-server /data/7005/redis.conf
ln -s /usr/local/redis-5.0.2/src/redis-trib.rb /usr/sbin/
ps -ef |grep redis-server
# --replicas 1",1是表明每個主有一個從,後面的是全部節點的地址與端口信息 redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
分佈式主從規則爲,前三個實例節點是主,對應的後面三個實例節點爲從節點,若是replicas 2,那就多加3個實例節點
redis-cli -p 7000 cluster nodes|grep master
redis-cli -p 7000 cluster nodes|grep slave
mkdir /data/700{6..7} 拷貝其餘端口實例: # 拷貝配置文件 cp /data/7000/redis.conf /data/7006/ cp /data/7000/redis.conf /data/7007/ # 修改配置文件內容 sed -i 's#7000#7006#g' /data/7006/redis.conf sed -i 's#7000#7007#g' /data/7007/redis.conf
啓動新節點實例:
redis-server /data/7006/redis.conf redis-server /data/7007/redis.conf
ps -ef |grep redis-server
#'把7006實例添加到7000實例這個主節點所在集羣內(此時已經有了4個主節點) redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
redis-cli -p 7000 cluster nodes|grep master
#'操做集羣管理節點重新分配,並在交互界面指定分片大小、選擇接收分片的節點ID redis-cli --cluster reshard 127.0.0.1:7000 How many slots do you want to move (from 1 to 16384)? 4096 #經過人工手動計算數據分片總大小除以主節點後的數字 What is the receiving node ID? 2129d28f0a86fc89571e49a59a0739812cff7953 #選擇接收數據分片的節點ID,(這是新增節點7006實例的ID號) Source node #1: all #選擇從哪些源主節點從新分片給新主節點)(all是全部節點) Do you want to proceed with the proposed reshard plan (yes/no)? yes #確認修改以上的操做
redis-cli -p 7000 cluster nodes|grep master
#'把7007實例節點添加到7006實例主節點內,並指定對應7006實例主節點坐在集羣的管理節點 redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000 --cluster-slave --cluster-master-id 2129d28f0a86fc89571e49a59a0739812cff7953
redis-cli -p 7000 cluster nodes|grep slave
#'操做集羣管理節點重新分配,並在交互界面指定分片大小、選擇接收分片的節點ID redis-cli --cluster reshard 127.0.0.1:7000 #方法是根據要刪除master節點的分片位置,而後一個組分一個節點 , 也能夠直接移動全部數據片到一個節點 How many slots do you want to move (from 1 to 16384)? 1365 #經過人工手動查看數據分片總大小 What is the receiving node ID? e64f9074a3733fff7baa9a4848190e56831d5447 #選擇接收數據分片的節點ID,(這是新增節點7006實例的ID號) Source node #1: 2129d28f0a86fc89571e49a59a0739812cff7953 #選擇從哪些源主節點從新分片給新主節點(這是要刪除的主節點的ID號) Source node #2: done #這是結束命令 Do you want to proceed with the proposed reshard plan (yes/no)? yes #確認修改以上的操做
redis-cli -p 7000 cluster nodes|grep master
#'操做集羣管理節點重新分配,並在交互界面指定分片大小、選擇接收分片的節點ID redis-cli --cluster reshard 127.0.0.1:7000 # 方法是根據要刪除master節點的分片位置,而後一個組分一個節點 , 也能夠直接移動全部數據片到一個節點 How many slots do you want to move (from 1 to 16384)? 1366 #經過人工手動查看數據分片總大小 What is the receiving node ID? f6c1aaea3a8c56e0c7dee8ad7ae17e26dd04244c #選擇接收數據分片的節點ID,(這是新增節點7006實例的ID號) Source node #1: 2129d28f0a86fc89571e49a59a0739812cff7953 #選擇從哪些源主節點從新分片給新主節點(這是要刪除的主節點的ID號) Source node #2: done #這是結束命令 Do you want to proceed with the proposed reshard plan (yes/no)? yes #確認修改以上的操做
redis-cli -p 7000 cluster nodes|grep master
#'操做集羣管理節點重新分配,並在交互界面指定分片大小、選擇接收分片的節點ID redis-cli --cluster reshard 127.0.0.1:7000 #方法是根據要刪除master節點的分片位置,而後一個組分一個節點 , 也能夠直接移動全部數據片到一個節點 How many slots do you want to move (from 1 to 16384)? 1365 #經過人工手動查看數據分片總大小 What is the receiving node ID? 5a0df4ea0af5f35c1248e45e88d44c3f2e10169f Source node #1: 2129d28f0a86fc89571e49a59a0739812cff7953 Source node #2: done
redis-cli -p 7000 cluster nodes|grep master
#'刪除已經清空數據的7006實例 redis-cli --cluster del-node 127.0.0.1:7006 2129d28f0a86fc89571e49a59a0739812cff7953 #刪除沒有主庫的7007實例 redis-cli --cluster del-node 127.0.0.1:7007 821bcf78c5e4c0b08aa7a5d514214b657ebec4ab
#內存信息查看 redis-cli -p 6382 -a redhat info memory #設置最大隻能使用100MB的內存 redis-cli -p 6382 -a redhat config set maxmemory 102400000