Web項目開發中用到的緩存技術


在WEB開發中用來應付高流量最有效的辦法就是用緩存技術,能有效的提高服務器負載性能,用空間換取時間。
緩存一般用來

  • 存儲頻繁訪問的數據
  • 臨時存儲耗時的計算結果
  • 內存緩存減少磁盤IO

使用緩存的2個主要原因:

  • 降低延遲:緩存離客戶端更近,因此,從緩存請求內容比從源服務器所用時間更少,呈現速度更快,網站就顯得更靈敏。
  • 降低網絡傳輸:副本被重復使用,大大降低了用戶的帶寬使用,其實也是一種變相的省錢(如果流量要付費的話),同時保證了帶寬請求在一個低水平上,更容易維護了。

 

在WEB開發中,緩存可以分為:

 

一、數據庫端緩存

  • 數據庫的緩存一般由數據庫提供,可以對表建立高速緩存。數據庫中,用戶可能多次執行相同的查詢語句,為了提高查詢效率,數據庫會在內存划分一個專門的區域,用來存放用戶最近執行的查詢,這塊區域就是緩存。(參考MYSQL緩存)
  • “空間換時間”,比如建一個表來存儲另外一個表某個類型的數據的總條數,在每次更新數據的時候同事更新 數據表和統計條數的表。在需要獲取某個類型的數據的條數的時候,就不需要select count去查詢,直接查詢統計表就可以了,這樣可以提高查詢的速度和數據庫的性能。
  • 數據庫查詢緩存,每次訪問頁面的時候,都會先檢測相應的緩存數據是否存在,如果不存在,就連接數據庫,得到數據,並把查詢結果序列化后保存到文件中,以后同樣的查詢結果就直接從緩存表或文件中獲得。用的最廣的例子看Discuz的搜索功能,把結果ID緩存到一個表中,下次搜索相同關鍵字時先搜索緩存表。舉個常用的方法,多表關聯的時候,把附表中的內容生成數組保存到主表的一個字段中,需要的時候數組分解一下,這樣的好處是只讀一個表,壞處就是兩個數據同步會多不少步驟,數據庫永遠是瓶頸,用硬盤換速度,是這個的關鍵點。

 

二、應用層緩存

應用層緩存這塊跟開發人員關系最大,也是平時經常接觸的。

  • 緩存數據庫的查詢結果,減少數據的壓力。這個在大型網站是必須做的。
  • 緩存磁盤文件的數據。比如常用的數據可以放到內存,不用每次都去讀取磁盤,特別是密集計算的程序,比如中文分詞的詞庫。
  • 緩存某個耗時的計算操作,比如數據統計。

應用層緩存的架構也可以分幾種:

  • 嵌入式,也就是緩存和應用在同一個機器。比如單機的文件緩存,java中用hashMap來緩存數據等等。這種緩存速度快,沒有網絡消耗。
  • 分布式緩存,把緩存的數據獨立到不同的機器,通過網絡來請求數據,比如常用的memcache就是這一類。

比較常見的應用層分布式緩存容器,Memcache、共享文件服務器、MemcacheDb、Tokyo Tyrant。 php里面也有比如x-cache,apc等的基於進程的緩存,這種緩存比分布式緩存速度快,但是限於跟應用的一個機器。 java實現的緩存也比較多,比如oscache,jcache ,ehcached等等。

 

三、前端緩存(服務端緩存)

我們這里說的前端緩存可以理解為一般使用的cdn技術,利用squid等做前端緩沖技術,主要還是針對靜態文件類型,比如圖片,css,js,html等靜態文件。
一般針對靜態資源如CSS,JS,圖片等使用緩存,原因如下:

  • 請求更快:通過將內容緩存在本地瀏覽器或距離最近的緩存服務器(如CDN),在不影響網站交互的前提下可以大大加快網站加載速度。
  • 節省帶寬:對於已緩存的文件,可以減少請求帶寬甚至無需請求網絡。
  • 降低服務器壓力:在大量用戶並發請求的情況下,服務器的性能受到限制,此時將一些靜態資源放置在網絡的多個節點,可以起到均衡負載的作用,降低服務器的壓力。

前端比較常用的就是squid,Varnish Cache,ncache等等。Varnish是一款高性能的開源HTTP加速器,挪威最大的在線報紙 Verdens Gang (vg.no) 使用3台Varnish代替了原來的12台squid,性能比以前更好。

 

四、客戶端緩存(瀏覽器緩存)

客戶端緩存依賴於瀏覽器的實現,目前一般的瀏覽器都實現了基於http都信息來緩存相應的文件。瀏覽器端的緩存,可以讓用戶請求一次之后,下一次不在從服務器端請求數據,直接從本地緩存讀取,可以減輕服務器負擔也可以加快用戶的訪問速度。
瀏覽器緩存分為強緩存和協商緩存:

  • 強緩存:瀏覽器在加載資源時,先根據這個資源的一些http header判斷它是否命中強緩存,強緩存如果命中,瀏覽器直接從自己的緩存中讀取資源,不會發請求到服務器。比如某個css文件,如果瀏覽器在加載它所在的網頁時,這個css文件的緩存配置命中了強緩存,瀏覽器就直接從緩存中加載這個css,連請求都不會發送到網頁所在服務器;
  • 協商緩存:當強緩存沒有命中的時候,瀏覽器一定會發送一個請求到服務器,通過服務器端依據資源的另外一些http header驗證這個資源是否命中協商緩存,如果協商緩存命中,服務器會將這個請求返回(304),但是不會返回這個資源的數據,而是告訴客戶端可以直接從緩存中加載這個資源,於是瀏覽器就又會從自己的緩存中去加載這個資源;若未命中請求,則將資源返回客戶端,並更新本地緩存數據(200)。

強緩存與協商緩存區別:強緩存不發請求到服務器,協商緩存會發請求到服務器。

 

如何設置緩存
1. HTML Meta標簽控制緩存(非HTTP協議定義)
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
上述代碼的作用是告訴瀏覽器當前頁面不被緩存,每次訪問都需要去服務器拉取。這種方法使用上很簡單,但只有部分瀏覽器可以支持,而且所有緩存代理服務器都不支持,因為代理不解析HTML內容本身。

2. HTTP頭信息控制緩存

HTTP頭信息發送在HTML代碼之前,只能被瀏覽器和一些中間緩存能看到,一個典型的HTTP 1.1協議返回的頭信息看上去像這樣:

HTTP/1.1 200 OK
Date: Fri, 30 Oct 1998 13:19:41 GMT
Server: Apache/1.3.3 (Unix)
Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT
ETag: "3e86-410-3596fbbc"
Content-Length: 1040
Content-Type: text/html

頭信息空一行后是HTML代碼的輸出。

HTTP頭信息控制緩存是通過Expires(強緩存)、Cache-control(強緩存)、Last-Modified/If-Modified-Since(協商緩存)、Etag/If-None-Match(協商緩存)實現,下面詳細介紹。
1)Expires是http1.0提出的一個表示資源過期時間的header,它描述的是一個絕對時間,由服務器返回,用GMT格式的字符串表示,如:Expires:Thu, 31 Dec 2016 23:55:55 GMT,讀取緩存數據條件:緩存過期時間(服務器的)< 當前時間(客戶端的)。
盡管Expires頭很有用,但它有一定的局限性。首先,因為牽扯到時間,Web服務器端的時鍾必須和緩存的同步,否則很可能實現不了預期的結果——緩存把前女友當初現女友,把現女友當作過去式——那就悲劇了。另外一個問題是,你很容易忘記給某內容設置了一個特定時間,如果返回內容的時候沒有更新這個過期時間,則每個請求都是上訪到服務器,反而增加了負載和響應時間。
缺點:Expires是較老的強緩存管理header,由於它是服務器返回的一個絕對時間,這樣存在一個問題,如果客戶端的時間與服務器的時間相差很大(比如時鍾不同步,或者跨時區),那么誤差就很大,所以在HTTP 1.1版開始,使用Cache-Control: max-age=秒替代。
2)Cache-Control(緩存控制)HTTP頭信息,是HTTP 1.1引入了新的頭信息,描述的是一個相對時間,在進行緩存命中的時候,都是利用客戶端時間進行判斷,所以相比較Expires,Cache-Control的緩存管理更有效,安全一些。讀取緩存數據條件:上次緩存時間(客戶端的)+max-age < 當前時間(客戶端的)。Cache-Control值可以是public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age

各個消息中的指令含義如下:

  • max-age=[秒]:表示在這個時間范圍內緩存是新鮮的無需更新。類似Expires時間,不過這個時間是相對的,而不是絕對的。也就是某次請求成功后多少秒內緩存是新鮮的。(指示客戶機可以接收生存期不大於指定時間(以秒為單位)的響應。上次緩存時間(客戶端的)+max-age(64200s)<客戶端當前時間)
  • s-maxage=[秒]:類似max-age, 除了僅應用於共享緩存(如代理)。
  • min-fresh指示客戶機可以接收響應時間小於當前時間加上指定時間的響應。
  • max-stale指示客戶機可以接收超出超時期間的響應消息。如果指定max-stale消息的值,那么客戶機可以接收超出超時期指定值之內的響應消息。
  • public:指示響應可被任何緩存區緩存。一般而言,需要認證HTTP請求內容會自動私有化(不會被緩存Add)。
  • private:指示對於單個用戶的整個或部分響應消息,不能被共享緩存處理。這允許服務器僅僅描述當前用戶的部分響應消息,此響應消息對於其他用戶的請求無效。(允許緩存專門為某一個用戶存儲響應,比方說在瀏覽器中;共享緩存一般不會,例如在代理中。)
  • no-cache:指示請求或響應消息不能緩存,該選項並不是說可以設置”不緩存“,而是需要和服務器確認。(每次在釋放緩存副本之前都強制發送請求給源服務器進行驗證,這在確保認證有效性上很管用(和public結合使用)或者保證內容必須是即時的,不得無視緩存的所有優點,如國內的微博、twitter等的刷新顯示Add。)
  • no-store:在請求消息中發送將使得請求和響應消息都不使用緩存,完全不存下來。(強制緩存在任何情況下都不要保留任何副本。)
  • must-revalidate:告訴緩存,我給你准備了一些關於新鮮度的信息,在表現的時候要嚴格遵循之。HTTP允許緩存在某些特定情況下返回過期數據,指定了這個屬性,相對於告訴緩存,你丫必須嚴格遵循我的規則。
  • proxy-revalidate:類似must-revalidate,除了只能應用於代理緩存。

注意:這兩個header可以只啟用一個,也可以同時啟用,當response header中,Expires和Cache-Control同時存在時,Cache-Control優先級高於Expires。

3)Last-Modified/If-Modified-Since:Last-Modified/If-Modified-Since要配合Cache-Control使用。
Last-Modified:標示這個響應資源的最后修改時間。web服務器在響應請求時,告訴瀏覽器資源的最后修改時間。
If-Modified-Since:當資源過期時(強緩存失效),發現資源具有Last-Modified聲明,則再次向web服務器請求時帶上頭 If-Modified-Since,表示請求時間。web服務器收到請求后發現有頭If-Modified-Since 則與被請求資源的最后修改時間進行比對。若最后修改時間較新,說明資源又被改動過,則響應整片資源內容(寫在響應消息包體內),HTTP 200;若最后修改時間較舊,說明資源無新修改,則響應HTTP 304 (無需包體,節省瀏覽),告知瀏覽器繼續使用所保存的cache。
缺點:Last-Modified標注的最后修改只能精確到秒級,如果某些文件在1秒鍾以內,被修改多次的話,它將不能准確標注文件的修改時間(無法及時更新文件)
如果某些文件會被定期生成,當有時內容並沒有任何變化,但Last-Modified卻改變了,導致文件沒法使用緩存,有可能存在服務器沒有准確獲取文件修改時間,或者與代理服務器時間不一致等情形(無法使用緩存)。
HTTP1.1中Etag解決了上述問題。
4)Etag/If-None-Match:Etag/If-None-Match也要配合Cache-Control使用。
Etag:web服務器響應請求時,告訴瀏覽器當前資源在服務器的唯一標識(生成規則由服務器決定)。Apache中,ETag的值,默認是對文件的索引節(INode),大小(Size)和最后修改時間(MTime)進行Hash后得到的。
If-None-Match:當資源過期時(使用Cache-Control標識的max-age),發現資源具有Etage聲明,則再次向web服務器請求時帶上頭If-None-Match (Etag的值)。web服務器收到請求后發現有頭If-None-Match 則與被請求資源的相應校驗串進行比對,決定返回200或304。
Etag是服務器自動生成或者由開發者生成的對應資源在服務器端的唯一標識符,能夠更加准確的控制緩存。Last-Modified與ETag一起使用時,服務器會優先驗證ETag。
yahoo的Yslow法則中則提示謹慎設置Etag:需要注意的是分布式系統里多台機器間文件的last-modified必須保持一致,以免負載均衡到不同機器導致比對失敗,Yahoo建議分布式系統盡量關閉掉Etag(每台機器生成的etag都會不一樣,因為除了 last-modified、inode 也很難保持一致)。

 

緩存的更新和過期

我們這幾只講應用層的緩存。在應用層的緩存由於應經有新的數據加入,數據的修改,數據的刪除等等操作,而在某些時間,我們需要這些操作及時的生效(由於用了緩存,可能會導致修改后緩存沒有更新,而頁面也沒有變化),所以出現緩存的更新和過期的概念。

緩存的過期包含

  • 時間過期:我們在緩存數據的時候我們可以指定數據緩存的最大時間,如果超過這個時間,我們就認為緩存是失效的。
  • 基於規則的過期:我們在緩存中存儲了某些數據來標明數據的版本。比如存取的時間,更新的時間,數據的版本信息等等,然后比較這些信息是否有變化來判斷是否過期。

緩存的更新

  • 被動:當緩存失效的時候我們的應用程序重新從主存儲器中取數據,然后重新放回緩存中。
  • 主動:當數據一更新的時候,我們的應用主動的去更新我們的緩存內容。
  • 被動和主動結合(基於版本):當數據以更新的時候,我們更新一個數據被更新的標志。然后根據上面講到的“基於規則的過期”來更新數據。

 

如何評價緩存的好壞

速度:當然,我們使用緩存的目的之一就是要提高我們應用的速度。如果使用緩存后速度更慢那就沒有緩存的必要了。取緩存的速度應 當是非常快的,因為緩存的工作僅僅是取出一個曾經存儲好的數據。所以影響緩存速度的因素可能跟緩存所才采用的存儲方式有關系,還有可能印象速度的就是分布 式緩存的網絡消耗,同樣,緩存服務器在並發很大的時候也有可能不堪重負出現瓶頸,變得緩慢。
比如目前鳳凰論壇使用的帖子緩存的響應時間在亞毫秒級,也就是還不足1ms的響應時間,這個速度是相當快的。
及時性:我們使用緩存的重要一點就是對我們的數據的更新需要緩存也及時的更新。這點非常重要,關系到頁面顯示的正確性,用戶體驗等。
命中率:命中率直接關系到緩存所起到的作用的大小。如果命中率太低就相當於沒有使用緩存,或者我們的緩存的規則有問題,重視命中不到緩存。


如何有效的使用緩存

哪些地方需要緩存

  • 頁面的打開速度非常慢,每一次都要進行大量的計算,從數據庫取一個比較耗時操作的數據。
  • 頻繁的讀取操作,每次請求都要從數據庫讀取數據,而這些數據的更新並不是很頻繁,導致數據庫的壓力非常大。
  • 頻繁的寫入操作(需要寫到數據庫),比如頁面點擊量,投票數的操作。這些操作可以暫時利用緩存,然后定期的寫到數據庫。

選擇什么緩存
緩存如此有用,那么這么多的緩存產品,我們如何選擇呢?下面我們主要就常用的php方面來說。

  • 訪問量不是特別大,web前端機器只有一台,對數據庫端的內容緩存可以使用文件緩存,這種方式非常簡單也比較有效,不需要對服務器做特別的配置。也可以使用apc,x-cached等提供的內存緩存,也非常有效,它比磁盤緩存速度快,但是因為是內存緩存,所以緩存的容量有限。
  • 訪問量比較大,web前端機器一般超過一台甚至更多,我們一般采用分布式的緩存。我們一般選擇memcache作為我們的緩存,memcache是一個基於內存的分布式緩存,它的速度和效率非常高。

其實這里也可以使用共享的磁盤緩存,但是基於我們目前所處的環境和對於可靠性,高並發的要求,我們不建議或者說不考慮使用共享磁盤作為緩存。

 


================PHP中9大緩存技術總結===================

1、全頁面靜態化緩存
也就是將頁面全部生成html靜態頁面,用戶訪問時直接訪問的靜態頁面,而不會去走php服務器解析的流程。此種方式,在CMS系統中比較常見,比如dedecms;

2、頁面部分緩存
該種方式,是將一個頁面中不經常變的部分進行靜態緩存,而經常變化的塊不緩存,最后組裝在一起顯示;可以使用類似於ob_get_contents的方式實現,也可以利用類似ESI之類的頁面片段緩存策略,使其用來做動態頁面中相對靜態的片段部分的緩存(ESI技術,請baidu,此處不詳講)。該種方式可以用於如商城中的商品頁。

3、數據緩存
顧名思義,就是緩存數據的一種方式;比如,商城中的某個商品信息,當用商品id去請求時,就會得出包括店鋪信息、商品信息等數據,此時就可以將這些數據緩存到一個php文件中,文件名包含商品id來建一個唯一標示;下一次有人想查看這個商品時,首先就直接調這個文件里面的信息,而不用再去數據庫查詢;其實緩存文件中緩存的就是一個php數組之類。Ecmall商城系統里面就用了這種方式。

4、查詢緩存
其實這跟數據緩存是一個思路,就是根據查詢語句來緩存;將查詢得到的數據緩存在一個文件中,下次遇到相同的查詢時,就直接先從這個文件里面調數據,不會再去查數據庫;但此處的緩存文件名可能就需要以查詢語句為基點來建立唯一標示;
按時間變更進行緩存:其實,這一條不是真正的緩存方式;上面的2、3、4的緩存技術一般都用到了時間變更判斷;就是對於緩存文件您需要設一個有效時間,在這個有效時間內,相同的訪問才會先取緩存文件的內容,但是超過設定的緩存時間,就需要重新從數據庫中獲取數據,並生產最新的緩存文件;比如,我將我們商城的首頁就是設置2個小時更新一次。

5、按內容變更進行緩存
這個也並非獨立的緩存技術,需結合着用;就是當數據庫內容被修改時,即刻更新緩存文件。比如,一個人流量很大的商城,商品很多,商品表必然比較大,這表的壓力也比較重;我們就可以對商品顯示頁進行頁面緩存;當商家在后台修改這個商品的信息時,點擊保存,我們同時就更新緩存文件;那么,買家訪問這個商品信息時,實際上訪問的是一個靜態頁面,而不需要再去訪問數據庫。
試想,如果對商品頁不緩存,那么每次訪問一個商品就要去數據庫查一次,如果有10萬人在線瀏覽商品,那服務器壓力就大了;

6、內存式緩存
提到這個,可能大家想到的首先就是Memcached;memcached是高性能的分布式內存緩存服務器。 一般的使用目的是,通過緩存數據庫查詢結果,減少數據庫訪問次數,以提高動態Web應用的速度、 提高可擴展性。它就是將需要緩存的信息,緩存到系統內存中,需要獲取信息時,直接到內存中取;比較常用的方式就是 key–>value方式。

7、apache緩存模塊
apache安裝完以后,是不允許被cache的。如果外接了cache或squid服務器要求進行web加速的話,就需要在htttpd.conf里進行設置,當然前提是在安裝apache的時候要激活mod_cache的模塊。
安裝apache時:./configure –enable-cache –enable-disk-cache –enable-mem-cache

8、php APC緩存擴展
Php有一個APC緩存擴展,windows下面為php_apc.dll,需要先加載這個模塊,然后是在php.ini里面進行配置:

[apc] 
extension=php_apc.dll 
apc.rfc1867 = on 
upload_max_filesize = 100M 
post_max_size = 100M 
apc.max_file_size = 200M 
upload_max_filesize = 1000M 
post_max_size = 1000M 
max_execution_time = 600 ; 每個PHP頁面運行的最大時間值(秒),默認30秒 
max_input_time = 600 ; 每個PHP頁面接收數據所需的最大時間,默認60 
memory_limit = 128M ; 每個PHP頁面所吃掉的最大內存,默認8M

 

9、Opcode緩存
PHP的緩沖器、加速器,有eaccelerator, apc, phpa,xcache。
首先php代碼被解析為Tokens,然后再編譯為Opcode碼,最后執行Opcode碼,返回結果;所以,對於相同的php文件,第一次運行時可以緩存其Opcode碼,下次再執行這個頁面時,直接會去找到緩存下的opcode碼,直接執行最后一步,而不再需要中間的步驟了。比較知名的是XCache、Turck MM Cache、PHP Accelerator等。



文章參考:

http://www.cnblogs.com/sunli/archive/2009/11/24/1609444.html
https://segmentfault.com/a/1190000006741200
https://my.oschina.net/leejun2005/blog/369148
http://www.oschina.net/news/41397/web-cache-knowledge
http://www.open-open.com/lib/view/open1479181086120.html
http://www.jb51.net/article/49714.htm
http://www.php100.com/html/php/lei/2015/0919/8969.html

 

版權聲明:本文采用署名-非商業性使用-相同方式共享(CC BY-NC-SA 3.0 CN)國際許可協議進行許可,轉載請注明作者及出處。
本文標題:Web項目開發中用到的緩存技術
本文鏈接:http://www.cnblogs.com/sochishun/p/7326752.html
本文作者:SoChishun (郵箱:14507247#qq.com | 博客:http://www.cnblogs.com/sochishun/)
發表日期:2017年8月9日


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM