缓存过期时如何防止应用程序对数据库进行DDOS操作?

时间:2014-01-10 10:18:03

标签: php mysql codeigniter caching memcached

我们有一个高流量的网站,就像StackOverflow一样,在memcache中有对象缓存。该站点使用PHP(CodeIgniter)和MySQL构建。

每次TTL(生存时间)在每个页面加载的缓存对象上到期时,此时的所有页面加载都会导致对数据库的查询,从而有效地在数据库上执行DDOS。

是否有某种方法只有一个页面加载重新获取数据并让其他页面加载等待缓存由第一个更新?

我的第一个想法是让一个随机函数工作,让一些页面加载获取数据并让其他人等待一秒钟再重新检查缓存。但肯定有一个更好的方法。

3 个答案:

答案 0 :(得分:2)

如果您可以控制后台任务(例如cronjobs),则可以安排在所需时间之前完成工作并获取数据,以便最近的数据永远不会超出缓存。

例如,您可以每20分钟(或每分钟)运行一次作业,更新缓存的值。当该作业运行时,不需要其他数据库查询来获取数据。

答案 1 :(得分:2)

您可以使用此代码中的算法:https://github.com/jamm/Memory/blob/master/lib/Jamm/Memory/MemcacheObject.php#L230

  1. 读取密钥和TTL
  2. 如果TTL很小(小于5秒,对于 例如),尝试锁定特殊键(不是你正在阅读),就像 '_update。{name_of_key}'
  3. 如果锁定成功 - 计算(或 read)新值和刷新缓存
  4. 发布更新密钥。
  5. 因此,只有一个进程会从DB中读取新值。

答案 2 :(得分:1)

为了澄清我的答案,我会写一些代码来展示我的想法(毕竟它可能是一个解决方案......):

if ($this->cacheExpired()){
    if (!$this->isMarkedAsRegenerating())
    {
         $this->markAsRegenerating();              //..ing
         $this->regenerateCache();
         $this->markAsCacheRegenerated();          //..ed
    } else {
         while ( $this->isMarkedAsRegenerating() )
         {
               sleep(1); //sleep 1 second to decrease database-queries, we are preventing a DDOS you know...
         }
    }
}

$this->output(); //at this point, we always have a cached version of the page

唯一涉及的风险是同时进行两次分页,*可以使缓存重新生成两次。这是非常不可能的,但如果它发生至少所有其他请求仍在等待。