0

在我们网站的可定制首页上,我们为用户提供了显示最近更新内容的模块的选项,从 100 多个模块中进行选择。

所有数据都是由 MySQL 查询生成的,其结果通过 memcached 进行缓存。我们当前的系统是这样工作的:当用户加载包含模块的页面时,模块会立即从缓存中为他们提供数据,并且查询被添加到队列中以由单独的 gearman 进程更新(这样页面加载不会不要等待mysql查询)。然后,该查询每 15 分钟运行一次以刷新缓存中的数据。查询队列本身会定期清除,这样我们就不会不断刷新最近未请求的数据。

问题是由于某种原因,当缓存为空时该怎么办。这种情况不会经常发生,但是当它发生时,当前会向用户显示一个空模块,并且在 gearman 进程中刷新数据,以便稍后,当相同(或不同)用户重新加载页面时,是要显示的数据。

我们的流量是这样的,如果我们试图在缓存为空时为用户实时运行查询,我们将遇到一个严重的标记问题——我们将多次运行相同(可能很慢)的查询许多用户加载了该页面。有没有什么办法可以解决“空白模块”的问题,又不造成踩踏风险?

4

1 回答 1

2

这是一个有趣的实现,尽管与最常见的在 MySQL 前面实现 memcached 的方式有所不同。

在大多数情况下,用户会将事情设置为首先在 memcached 评估查询的位置,以查看是否有可用条目。如果是这样,他们会从 memcached 中提供服务,并且根本不会查询数据库。如果缓存未命中,则对数据库进行查询,将结果添加到 memcached,并将信息返回给调用者。这就是您通常为读取查询构建缓存的方式。

在更新数据的情况下,将针对数据库进行更新,然后 memcached 中的相应数据无效和/或更新。同样对于插入,您可以对缓存不做任何事情(并让对该记录的下一次读取填充缓存),或者您可以根据应用程序的需要主动将与插入相关的数据添加到缓存中。

这样,在从 memcached 获取初始数据后,您就不需要采取额外的步骤来调用数据库来获取权威数据。memcached 中的数据将是权威数据的副本,在更新/插入时刚刚更新/失效。

根据您的评论,为了防止在缓存未命中的情况下对数据库进行大量查询,您可能想要尝试的一件事是使用各种互斥锁。例如,当第一个客户端命中 memcached 并获得该查找的缓存未命中时,您可以在 memcached 中插入一个临时值,指示数据处于待处理状态,然后对数据库进行查询,并使用结果。

在客户端,当您收到缓存未命中或“待处理”结果时,您可以简单地在一段时间后重新启动缓存重试(您可能希望以指数方式增加)。因此,也许首先等待 1 秒,然后在 2 秒内尝试返回增益,如果他们仍然得到“待定”结果,然后在 4 秒内重试,依此类推。

这可能会增加对 memcached 服务器的请求,但应该可以解决数据库层上的任何问题。

于 2012-08-20T15:18:42.523 回答