2010-01-07 138 views
4

我見過幾個數據庫緩存引擎,它們都非常笨(即:keep this query cached for X minutes),並且需要在執行10/DELETE查詢後手動刪除整個緩存存儲庫。智能(?)數據庫緩存

約2〜3年前,我開發了一個替代DB緩存系統的一個項目我工作,這個想法基本上是使用正則表達式來查找有關特定SQL查詢表(S):

$query_patterns = array 
(
    'INSERT' => '/INTO\s+(\w+)\s+/i', 
    'SELECT' => '/FROM\s+((?:[\w]|,\s*)+)(?:\s+(?:[LEFT|RIGHT|OUTER|INNER|NATURAL|CROSS]\s*)*JOIN\s+((?:[\w]|,\s*)+)\s*)*/i', 
    'UPDATE' => '/UPDATE\s+(\w+)\s+SET/i', 
    'DELETE' => '/FROM\s+((?:[\w]|,\s*)+)/i', 
    'REPLACE' => '/INTO\s+(\w+)\s+/i', 
    'TRUNCATE' => '/TRUNCATE\s+(\w+)/i', 
    'LOAD' => '/INTO\s+TABLE\s+(\w+)/i', 
); 

我知道這些正則表達式可能有一些缺陷(當時我的正則表達式技能很綠),顯然不匹配嵌套查詢,但是因爲我從來沒有使用它們,這對我來說不是問題。

不管怎樣,找到相關表我會按字母順序進行排序,並與以下命名約定高速緩存儲存庫創建一個新的文件夾後:

+table_a+table_b+table_c+table_...+ 

SELECT查詢的情況下,我會獲取結果從數據庫中,serialize()並將其存儲在適當的緩存文件夾,所以例如下面的查詢結果:

SELECT `table_a`.`title`, `table_b`.`description` FROM `table_a`, `table_b` WHERE `table_a`.`id` <= 10 ORDER BY `table_a`.`id` ASC; 

將存儲在:

/cache/+table_a+table_b+/079138e64d88039ab9cb2eab3b6bdb7b.md5 

MD5是查詢本身。在後續的SELECT查詢結果將是微不足道的提取。

在任何其他類型的寫入查詢(INSERTREPLACEUPDATEDELETE等)的情況下我會​​3210都在他們的名字了+matched_table(s)+的文件夾全部刪除所有文件內容。這樣就不需要刪除整個緩存,只需刪除受影響和相關表所使用的緩存。

該系統工作得很好,性能的差異是可見的 - 雖然該項目有更多的閱讀查詢比寫查詢。從那時起,我開始使用交易,FK CASCADE UPDATES/DELETES,並且從來沒有時間來完善系統以使其適用於這些功能。

我以前用MySQL Query Cache,但是我必須說性能甚至沒有比較。

我想知道:我是唯一一個在這個系統中看到美麗的人嗎?有沒有我可能沒有意識到的瓶頸?爲什麼流行的框架如CodeIgniterKohana(我不知道Zend Framework)有這樣基本的DB緩存系統?

更重要的是,你認爲這是一個值得追求的功能嗎?如果是,有什麼我可以做/使用到使它更快(我主要關注的是磁盤I/O和(德)序列化的查詢結果)?

我感謝所有的輸入,謝謝。

+2

我會說增加更多的內存到你的SQL框,讓它擔心緩存本身。 – DmitryK 2010-01-07 12:56:18

+0

@DmitryK:就像我之前說過的,我過去使用過MySQL查詢緩存,但是我的系統提供了更好的性能(不知道爲什麼)。 – 2010-01-07 13:21:22

+2

+1用於詢問自己和自己的方法。這是一個非常重要的事情,國際海事組織! – nickf 2010-01-08 00:01:42

回答

2

我能看到的美麗在這個解決方案,但是,我相信它僅適用於一組特定的應用程序。不適用的情況包括:

  • 使用級聯刪除/更新或任何類型觸發器的數據庫。例如,你對錶A的DELETE可能會導致表B的DELETE。正則表達式永遠不會捕獲這個。

  • 從不通過緩存失效方案的點訪問數據庫,例如, crontab的腳本等,如果你決定實施跨機器複製(引進只讀奴隸),它也可能會干擾高速緩存(因爲它不經過緩存失效等)

即使這些場景對於你的情況是不現實的,它仍然回答爲什麼框架不實現這種緩存的問題。

關於這是否值得追求,這一切都取決於您的應用程序。也許你關心提供更多信息?

+0

我真的不擔心你的第二點(即使我通過第三方更改數據,我總是可以刪除緩存的查詢,沒有什麼大不了的)......關於你的第一點,這是我停止使用的原因這個緩存系統 - 我不確定是否有可能知道哪些表受到CASCATE觸發器的影響,這是我稍後必須研究的內容。 – 2010-01-07 21:44:19

1

儘管我確實看到了這一點 - 尤其是對於資源有限且無法輕鬆擴展的環境,就像共享主機一樣 - 我個人會擔心未來會出現複雜情況:如果有人剛剛被僱用並且不知道緩存機制開始使用嵌套查詢?如果某些外部服務開始更新表,並且緩存沒有注意到什麼呢?

對於一個專門定義的項目,迫切需要通過增加處理器功率或RAM無法提供的加速,這看起來像是一個很好的解決方案。作爲一個通用組件,我覺得它太不穩固了,並且會從長遠來看會產生一些細微的問題,這些問題源於人們忘記了緩存需要注意。

+1

感謝Pekka,但是最糟糕的情況是你不得不刪除緩存的查詢嗎?如果有人忘記/搞砸了,這似乎不是很糟糕。 – 2010-01-07 13:19:52

+0

我的擔心是,如果有人設置了一個外部腳本,在不通知緩存的情況下修改數據庫(想象多年後,一名自由職業者匆忙實施新功能,有人忘記提及緩存),導致無效的緩存結果。如果我誤解了這個概念並且不可能發生,我撤回了我的觀點:)這不是一個真正的對應點,但如果它增加了一層需要注意的東西,我的第一本能就是爲了透明起見反對它。 – 2010-01-07 13:25:40

0

您所描述的改進是爲了避免使保證未受更新影響的緩存失效,因爲它們從不同的表中繪製數據。

這當然很好,但我不確定它是否足夠細緻,能夠產生真正的效果。您仍然會對很多並不真正需要的緩存進行無效訪問(因爲更新在桌面上,但在不同的行上)。

此外,即使這個「簡單」的方案依靠能夠通過查看SQL查詢字符串來檢測相關的表。在一般情況下,這可能很難實現,因爲視圖,表別名和多個目錄。

自動(而且高效地)檢測緩存是否需要失效是非常困難的。因此,您可以使用一個非常簡單的方案(例如在每次更新或每個表上使系統失效(在系統中進行失效時,在有多個更新時效果不佳),或者使用非常手工製作的緩存特定應用程序深入查詢邏輯(可能難以編寫和難以維護),或者接受緩存可以包含陳舊數據並只是定期刷新。

+0

謝謝,但是我不知道如何開發一個行級別的緩存,拿經典的例子'SELECT AVG(salary)FROM employees;'...... – 2010-01-07 13:17:26

+0

準確地說我的觀點。 – Thilo 2010-01-07 23:54:52

0

我懷疑正則表達式可能不會提供每種情況 - 當然他們似乎不處理混合基本表名稱和表本身的情況。例如考慮

update stats.measures set amount = 50 where id = 1;

and

use stats; 更新度量設置量= 50其中id = 1;

然後是PL/SQL。

然後有一個事實,它依賴於每個客戶端選擇一個諮詢控制機制,即它預先假定所有數據庫訪問都來自在共享文件系統上實現緩存控制機制的機器。

(作爲一個小點 - 檢查數據文件的修改時間以確定定義的表集上的查詢的緩存版本是否仍然是最新的,而不是試圖識別如果高速緩存控制機制發現了更新 - 它肯定會更加健壯)

回退一點,使用強健的體系結構從頭開始實施這將意味着所有查詢都必須被控制機制攔截。控制機制可能需要更復雜的查詢解析器。對於控制機制的所有實例而言,它肯定需要一個通用的storgae底物。它可能需要理解數據字典 - 數據庫本身已經實現的所有東西。

您聲明「我過去使用過MySQL查詢緩存,但我必須說性能甚至不會比較。」

我覺得這很奇怪。當然,在處理來自查詢的大型結果集時,我的經驗是將數據從數據庫加載到堆中要比反序列化大型數組快得多 - 儘管大型結果集相當不典型。

當我試圖加快數據庫訪問速度(固定其他所有內容之後),然後我已經走下了跨多個DBMS實例複製和分區數據的路線。

C.

+0

就像我在我的問題中說過的那樣,正則表達式並沒有接近完美(然而:P)。我也不明白爲什麼你認爲檢查修改時間將是一個更強大的方法來確定緩存是否仍然有效。 – 2010-01-07 18:06:25

2

正如您所描述的,解決方案存在併發問題的風險。當你每秒接收數百次查詢時,你肯定會遇到UPDATE語句運行的情況,但在你清除緩存之前,SELECT會從中讀取數據並獲取陳舊的數據。此外,當幾個UPDATE在短時間內擊中同一行時,您可能會遇到問題。

從更廣泛的意義上講,緩存的最佳做法是緩存可能的最大對象。例如,不是在整個地方緩存一堆「用戶」相關的行,而是緩存「用戶」對象本身。更好的是,如果您可以緩存整個頁面(例如,您向所有人顯示相同的主頁;個人資料頁面與幾乎所有人都相同),那就更好了。對於整個預先呈現的頁面,一次高速緩存提取將顯着勝過行/查詢級高速緩存的數十次高速緩存提取,然後再重新發布頁面。

長話短說:簡介。如果你花時間做一些測量,你可能會發現緩存大對象,甚至頁面,而不是用來構建這些東西的小查詢,是一個巨大的性能勝利。

+0

關於併發性問題,您在那裏有一個很好的觀點,您認爲memcached或任何其他類似的系統(我對此很新)是否會解決或至少減少發生這種情況的可能性?另外,「緩存大對象」是什麼意思? ORM樣? – 2010-01-07 21:40:25

+2

避免併發問題的最好方法是首先不要構建這樣的系統:)這就是爲什麼你不會找到像你這樣的許多系統的一部分:正確地做它們是很困難的。第二個最好的方法是引入某種形式的鎖定,它有自己的問題(死鎖,鎖爭用)。 Memcache至少可以防止在同時寫入的情況下破壞緩存的數據,但是您仍然需要鎖定在memcache之上才能真正做到正確。 – 2010-01-07 22:19:44

0

這與在主從配置中使用多個數據庫時的會話拆分問題有關。基本上,使用一組類似的正則表達式來確定哪些表(或哪些行)正被讀取或寫入。系統跟蹤哪些表被寫入以及何時寫入,以及何時讀取其中一個表時,它會被路由到主機。如果一個查詢正在從一個數據表中讀取數據不需要準確到位,那麼它將被路由到從機。通常,只有當用戶改變自己的某些信息時(例如,編輯用戶的個人資料),信息才需要最新。

他們在O'Reilly的書高性能MySQL中談論了這一點。在開發處理會話的系統在當天分裂回來時,我使用了它很多。