簡體   English   中英

如何實現PHP / HTML緩存

[英]How To Implement A PHP/HTML Cache

我已經閱讀了幾個關於實現php緩存系統的指南(我的網站是自定義編碼的,相當嚴重的查詢和不斷增長),包括以下內容: http//www.snipe.net/2009/03/quick-and-dirty-php -caching /

我完全理解它們,但頁面的某些部分我無法緩存,最好的方法是什么?

如果您執行大量讀取但很少更新,則緩存很有用。 數據庫中的數據越頻繁,緩存系統就越成問題。 緩存確實會給代碼庫增加一定的復雜性,這可能很難處理。 在最糟糕的情況下,它甚至可以減慢你的網站速度。

最重要的問題是:
什么時候必須使緩存無效? 什么時候變得陳舊? 在大多數情況下,如果數據庫查詢返回的行數與緩存該頁面時的行數不同。 但你怎么知道的? 你沒有(也許有辦法,但我想不出任何atm),因為要檢查一下,你可能要查詢結果進行比較。

你可以做什么:

  1. 每次更新數據庫的相關部分時清除所有緩存
    如果你的數據庫很少得到更新 - 每小時,每天,每周,這確實是可能的。 但如果不斷變化,它就沒用了。 大多數網絡項目就是這種情況。

  2. 事情發生后清除緩存的項目
    這只有在不必立即反映變化時才有效(例如,如果一段時間內數據不正確則無關緊要)。 在這種情況下,如果某個項目超過X分鍾,或者超過Y頁面瀏覽量,您只需清除某個項目的緩存即可。

  3. 只清楚相關的部分
    在這里,您必須確定在更新數據庫時哪些部分的緩存會受到影響。 如果做得好,改變會在性能提高時立即反映出來。

最喜歡的是選項3:你必須找出答案。 因此,舉一個例子,讓我們看一下博客的經典案例,包括首頁,檔案頁面和每個條目的詳細頁面。

更改由以下內容引入:管理面板(條目的crud)和注釋

如果條目被編輯或刪除,則必須清除緩存:

  • 首頁,如果條目是新的
  • 相關的存檔頁面,如果條目是舊的
  • 條目的詳細頁面

如果有人推薦您只需要清除詳細信息頁面,但前提是注釋或存檔中沒有顯示注釋數量。 否則,與入口 - crud相同。

如果站點范圍內的某些內容發生了變化,則必須清除整個緩存(不好!)

現在,讓我們考慮入口和存檔。 如果存檔的類型為“每月一頁”,則清除該條目所屬的月份。 但如果存檔是1-10,11-20,21-30的條目,那么大多數情況下整個存檔緩存都必須重建。

等等 ...

一些問題:

  • 如果您沒有正確識別所有受影響的部分,則可能導致陳舊數據和/或(非)死鏈接。

  • 如果更新經常發生,那么構建緩存是額外的工作,因為當下一個頁面視圖發生時,緩存很可能再次失效並且無論如何都必須重建。

  • 頁面的某些部分不適合緩存,例如(自定義)搜索功能。 如果緩存在其他地方工作,一切都很快很好,但搜索速度仍然很慢。

  • 如果在發生大量請求時必須清除整個緩存,則可能會出現問題。 然后它可以阻塞你的服務器,因為緩存缺失通常比首先沒有緩存頁面更昂貴。 更糟糕的是,如果有3個請求進入,並且第一個請求無法在處理其他兩個請求之前緩存頁面,則緩存會被請求3次而不是一次。

我的建議:

  • 優化您的數據庫。 鍵和配置好嗎? 也許它沒有緩存。

  • 優化您的查詢。 “解釋選擇”!

  • 只緩存頁面的部分 - 昂貴的。 使用str_replace和占位符填寫小的,廉價的更改

  • 如果一切正常,請使用apc或memcached而不是文件(文件通常效果很好,但apc / memc更快)。 您也可以使用您的數據庫來緩存您的數據庫,這通常很有用!

  • 你在建立一個懶惰或急切的緩存系統? 懶惰意味着:在頁面首次請求時構建緩存,急切意味着:在更新后立即執行。

嗯,我對你沒有任何真正的建議。 過分依賴於問題:)

更新

這是一個帶有密鑰256的博客條目請求。它顯示了博客條目,評論和當前登錄的人。查詢條目和評論以及格式化所有文本和所有內容都很昂貴。 當前登錄的用戶駐留在會話中。

首先,為要緩存的部分創建唯一鍵。 在這種情況下,緩存鍵可能是條目的數據庫ID(帶有一些前綴和后綴)。

因此,緩存的文件應該具有名稱cache/blogentry_256.tmp 檢查,如果該文件存在。

  1. 如果它不存在,請執行所有昂貴的查詢和格式化,留下占位符(例如{username}),其中當前用戶的名稱應該是,並將結果保存到cache/blogentry_256.tmp 注意不要將任何數據寫入此文件,不應為每個人顯示或每次請求都要更改。

  2. 現在,讀取文件(或重用1中的數據)並將用戶名str_replace到占位符中。 echo結果。

如果條目被更改或有人注釋,則必須刪除帶有條目id的緩存文件。

這是惰性緩存 - 僅當用戶請求頁面時才構建緩存。 小心 - 如果用戶在注釋中鍵入{username},它也會插入到那里! 這意味着,您必須在str_replacing之后轉義緩存的數據並對其進行轉換。 這種技術也適用於memcached或apc。

問題:你必須圍繞緩存設計構建你的設計。 例如,如果你想顯示“5分鍾前發布的評論”而不是“5月6日下午3:42添加評論”,那么你就麻煩了。

為什么你不能緩存它們? 如果是因為它們變化非常快,那么你最好不要試圖減少檢索和渲染開銷。

如果內容因用戶而異,則可以為每個用戶提供不同的緩存版本。 例如,如果您正在使用對象緩存,則在緩存鍵中包含用戶的標識符或其他一些唯一值。 如果您正在使用更通用的HTTP級緩存,例如Squid,那么您可以將Vary標頭設置為例如Vary: Cookie ,這將導致代理重復檢查它為其提供內容的人。

您仍應指示緩存和代理不要將任何敏感信息存儲在公共緩存中。

請記住,緩存不是一個單一的解決方案 - 您的應用程序可以在不同的級別上進行緩存,具體取決於變量的范圍。 在單個請求中,您可能具有身份映射以防止對相同數據的其他查詢。 您可以使用memcached在請求之間緩存數據。 數據庫本身具有查詢緩存以及其他類型的低級緩存。 您的渲染引擎也可以在不同的級別緩存 - 整個頁面,頁面的各個部分等。您需要弄清楚可以緩存的內容並為此制定策略。

此外,與所有優化一樣,請確保在調整任何內容之前進行測量。 這將確保1)你實際上改善了事情,而不是讓它們變得更糟; 2)你專注於重要的事情,而不是其他一切。

這是我的提示:創建一個對象或函數,允許您根據其名稱緩存“部分”。 這樣,您可以緩存部分頁面,並在if塊中包含渲染部分。 我所做的是對輸入文本進行散列並將其用作'./cache'的文件名,並返回是否需要重新生成的布爾值; 你顯然需要輸出緩沖。

這會給你一個緩存框架,

if(Cache::cached('index-recent-articles', 5 /* minutes */)) {
   Cache::start();
   echo 'stuff here';
   Cache::stop('index-recent-articles');
} // And if Cache::cached could echo the cached HTML when the result is false...
  // then this is a tidy else-less bit of code.

我不知道這是否是最優的,像memcache這樣的基於服務器的解決方案會更好,但這個概念應該對你有幫助。 顯然,您是如何通過文件或數據庫或擴展來管理緩存的。

編輯:

如果您希望基於文件的緩存,那么這個簡單的系統可能就是您所需要的。 顯然我還沒有用你的鞋子測試它,這大部分只是從我的后腦中拉出來的,但是就目前而言,這對於你可能想要的東西來說是一個不錯的開始。

abstract class Cache {
  const path = 'cache/';
  static function start()  { ob_start(); }
  static function end($id, $text = null) {
    $filename = sprintf('%s%u', Cache::path, crc32($id));
    file_put_contents($filename, $text === null ? ob_get_clean() : $text);
  }
  static function cached($id, $minutes = 5) {
    $filename = sprintf('%s%u', Cache::path, crc32($id));
    $time = $minutes * 60;
    if(time() - filemtime($filename) > $time) {
      return true;
    } else {
      echo file_get_contents($filename);
      return false;
    }
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM