2010-01-07 150 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中谈论了这一点。在开发处理会话的系统在当天分裂回来时,我使用了它很多。