計算機領域多處地方用到緩存,好比說爲了緩解CPU和內存之間的速度不匹配問題,咱們每每經過增長一級、二級、三級緩存,CPU先從緩存中取指令,若是取不到,再從內存中取,並更新緩存,同時,根據程序的局部性原理,使得大部分狀況下緩存都會命中。node
目前,Web應用的核心數據一般存放在數據庫中,好比說用戶信息、訂單信息、交易信息等,同時,數據庫和編程語言是無關的,經過SQL交互,Java、Php等語言寫的程序須要訪問數據庫,執行業務邏輯,展現結果給用戶。可是數據庫有必定的侷限性,譬如:1.數據庫鏈接是很是 "昂貴 "的資源,爲了複用這些資源,目前採用鏈接池技術,2. 鏈接池的鏈接數是有限的,若是用戶過多,勢必要等待,3. 讀寫數據時須要加鎖。web
經過上述介紹,咱們知道一個大型系統中數據庫每每會成爲瓶頸,咱們不能每次訪問都訪問數據庫,尤爲是在多用戶,大併發的狀況下。面對這種狀況,咱們一般採用何種方法呢?在計算機行業中的全部問題,均可以經過增長一個抽象層來解決。那麼,針對數據庫這個瓶頸,咱們能夠在應用層和數據庫層增長一層,即緩存層。算法
若是你是某某大型公司的首席架構師,如今公司須要自研一套緩存系統,你應該怎麼設計呢?我想在設計以前應該想好如下幾個問題:數據庫
目前常見的數據格式有序列化對象、XML、JSON、字符串(key,value)和基本的數據結構,其中針對Java語言的序列化對象有序列化和反序列化,而Google研發的protobuf是和語言無關的,好比說Python將某對象序列化,Java能將這個對象進行反序列化。編程
考慮到公司有不少後端小組,而且使用不一樣的編程語言,這就要求咱們自研的緩存系統應該和編程語言無關,基於此,咱們須要制定一套協議來支持各類語言。客戶端如何使用這套協議?最多見的就是客戶端/服務器模型。首先,服務器監聽請求;接着,客戶端發送請求,得到響應,其中客戶端發送的請求就是協議;最後,基於Socket通訊。好比說:set 'name' 'mukedada'
、get name
。後端
緩存服務器端在啓動的時候,應該設置緩存大小,當緩存被沾滿時,採用LRU算法。緩存
對於大型應用服務器,單機的緩存服務器是支撐不了的。那麼,就須要對緩存服務器進行水平擴展(即增刪服務器,當活動結束後,就須要考慮刪減服務器),那麼用什麼算法讓數據相對平均的分配到每臺服務器?同時,這個算法應該放在客戶端仍是服務端呢?服務器
它的典型應用是Memcached,Memcached使用的是一致性Hash算法,在介紹它以前,咱們先來看一下餘數算法。對於用戶要存儲的(key,value),計算key的整數哈希值,而後對服務器的數目求餘,這樣來肯定存儲服務器。這個方法存在一個致命的問題:當服務器個數發送變化時,餘數會發生變化,這樣一來須要從新到數據庫獲取數據。 爲了加深你們的理解,舉個具體的實例:假設有3臺服務器0、一、2,key一、key2的hash值分別是100,99,對應的餘數分別是1和0,也就是說它們分別存放於編號爲1和0的服務器中,如今若是增長一臺服務器3,那麼它們的餘數也會隨之發生變化,100%4 = 0,99%4 = 3,可是它們在0、3號服務器卻找不到對應的數據。 微信
爲了解決餘數算法存在的問題,咱們的先輩們提出了分佈式一致性hash算法。它思路就是當服務器個數發生變化時,儘量的減小影響。譬如:當咱們新增node5,隻影響局部範圍內的key,而餘數算法則影響全局。 數據結構
可是它也存在分佈不均勻的問題,致使有的服務器上緩存的數據多,有的少。一種方法就是虛擬節點,也就是說讓一個服務器化身爲多個虛擬節點,分佈到環上。Memcache採用的就是這種方法。
代理實現 代理程序放在服務器端,它的典型案例有Twemproxy和Codis。它的基本思想:應用程序向Proxy發送請求,Proxy經過必定算法計算獲得數據應該從哪一個節點獲取,而且返回響應給客戶端。爲了防止Proxy出現單點故障,能夠經過集羣等方式實現Proxy高可用。
路由實現 它的典型案例就是Redis。它的基本思想是應用程序能夠將請求發送到任意一個節點,當節點包含該請求數據,則直接返回響應給應用程序,當節點不包含該請求數據時,則告訴它跳轉到其餘節點中取數據,其中,客戶端程序庫須要解析相應的指令。例如:當node1中沒有數據,會讓客戶端程序訪問node3,這相似於web中的重定向,缺點: node1須要知道其餘節點的數據,即node1和其餘節點是相互通訊的。
CRC16(key)%16384
求餘,最終會落到0~16383這個區間的槽中。
可是,每當新增一個節點時,須要從原先的每一個節點中獲取hash槽,這時須要涉及數據遷移的過程。若是在數據遷移的過程當中有一個用戶請求,這個時候該怎麼辦?目前一種解決方法是讓node1和node4的持有相同的槽,可是設置不一樣的狀態,例如node1的槽的狀態設置爲正在遷移,而node4的狀態是正在導入,首先將請求交給node1,若是node1中有數據則直接返回,若是沒有則交給ndoe4。以下圖所示。
同時,咱們注意到node一、node2等存在單點故障,爲增長可用性,咱們對每一個node使用主從模式。數據首先寫入到master節點,以後有兩種方式,方式一,直接將結果返回給客戶端,而後將master節點數據同步到slave從節點中,這樣作的好處就是響應週期短,缺點是可能存在數據不一致的狀況,即master節點將結果返回給客戶端以後,還沒來得及將數據同步到slave節點中就發生故障,那麼這部分數據就會丟失。方式二,數據寫入到master節點以後,須要將數據同步到slave節點成功以後,再將結果返回給客戶端,這種方式保證了數據強一致性,可是用戶須要更長的時間來等待。
用戶每次訪問緩存都沒有命中,致使每次請求都要訪問數據庫,這就是緩存擊穿問題,出現這種狀況致使緩存沒起效果,反而增長了系統消耗。針對這個問題,通常諸如雙十一等活動都會在活動開始以前將用戶信息預先存放到緩存中。
歡迎關注微信公衆號:木可大大,全部文章都將同步在公衆號上。