Php memcached数据缓存的设计模式
围绕现有数据库查询包装可选的memcached缓存很容易。例如: 旧的(仅DB): 新的(带有memcache的数据库): 但问题是,这并不总是你想要的缓存方式。例如,以以下两个查询为例:Php memcached数据缓存的设计模式,php,database,design-patterns,oop,memcached,Php,Database,Design Patterns,Oop,Memcached,围绕现有数据库查询包装可选的memcached缓存很容易。例如: 旧的(仅DB): 新的(带有memcache的数据库): 但问题是,这并不总是你想要的缓存方式。例如,以以下两个查询为例: -- get all items (recordset) SELECT * FROM items; -- get one item (record) SELECT * FROM items WHERE pkid = 42; 如果我使用上面的伪代码来处理缓存,那么我将存储第42项的所有字段两次。一次进入大记
-- get all items (recordset)
SELECT * FROM items;
-- get one item (record)
SELECT * FROM items WHERE pkid = 42;
如果我使用上面的伪代码来处理缓存,那么我将存储第42项的所有字段两次。一次进入大记录集,一次独立。而我宁愿做这样的事情:
SELECT pkid FROM items;
并缓存PK的索引。然后分别缓存每条记录
总之,最适合DB的数据访问策略并不完全适合memcache策略。因为我希望memcache层是可选的(也就是说,如果memcache关闭了,站点仍然可以工作),所以我有点想两全其美,但要做到这一点,我非常确定我需要以两种不同的形式维护大量查询(1.获取索引,然后记录;2.在一个查询中获取记录集)。分页会变得更加复杂。对于DB,您可以执行LIMIT/OFFSET SQL查询,但是对于memcache,您只需获取PK的索引,然后批量获取数组的相关部分
我不知道如何巧妙地设计这个,有人有什么建议吗
如果你自己也遇到过这个问题,那就更好了。你是怎么处理的呢?嗯,我想这是你不得不忍受的。如果你不成批地做一些事情,Memcahced会工作得最好。例如,它非常适合“这个用户的东西在哪里?这里有一大堆这个用户的东西。”这并不意味着这个查询不进行批处理。当然会的——如果一些用户的东西像他/她的帖子一样 我想您将遇到的问题是,您混合了需要单独从数据库中获取一个项的查询和一些需要获取一堆相同类型的先前项的查询 情况总是有反面的。如果你真的想让你的实现变得麻烦,你可以改变你的批处理查询,使其不包含memcached中已经存在的项 在我看来,它总是归结为“我真正想要缓存哪些查询?” 编辑: 我的做法是:
- 单项查询-如果在memcached中,则使用该查询,否则从DB获取并更新memcached
- 批量查询-不必担心memcached中有哪些项,只需获取所有内容并更新memcached即可
您可以将SQL查询转换为仅获取不在缓存中的行,但在不增加SQL查询成本的情况下自动执行此转换是非常重要的。如果您使用的是缓存,那么为了最大限度地利用它,您必须接受您的数据在某种程度上总是过时的,而且数据的某些部分将彼此不同步。通过维护单个副本来保持所有记录的最新状态最好留给关系数据库,因此,如果这是您需要的行为,那么您最好使用一个功能强大的64位DB服务器,该服务器具有大量RAM,以便它可以执行自己的内部缓存 如果您可以接受陈旧的数据(如果真正的可伸缩性很重要,您需要接受陈旧的数据),那么一种方法就是将整个结果集扔到缓存中;不要担心重复。RAM很便宜。如果您发现缓存已满,那么只需购买更多RAM和/或缓存服务器。例如,如果您有一个表示由条件X和Y筛选的集合中的项目1-24的查询,则使用包含所有这些信息的缓存键,然后当再次请求相同的搜索时,只需从缓存返回整个结果集。您可以在一次命中中从缓存中获得完整的结果集,也可以转到数据库 最困难的事情是在没有(a)人们注意到太多,或(b)打破业务要求(如最小更新间隔)的情况下,计算出有多少数据可能过时,以及它可能过时的程度
这种方法适用于以读为主的应用程序,特别是那些具有分页查询和/或数据过滤条件有限集的应用程序。这还意味着您的应用程序在打开或关闭缓存时的工作方式完全相同,只是在关闭缓存时命中率为0%。几乎在所有情况下,这都是我们在blinkBox采取的方法。以下是我对NHibernate(因此可能是Hibernate)如何做到这一点的理解。它有4个缓存:
- 行缓存:缓存数据库行。缓存键是TableName#id,即
-- get all items (recordset) SELECT * FROM items; -- get one item (record) SELECT * FROM items WHERE pkid = 42;
SELECT pkid FROM items;