Chinaunix首页 | 论坛 | 博客
  • 博客访问: 21665
  • 博文数量: 5
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 133
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-11 17:32
个人简介

80后天秤男,多年码农,现正在进化中

文章分类

全部博文(5)

文章存档

2013年(5)

我的朋友

分类: PHP

2013-05-29 10:33:49

日常编程中,对于一些有一定并发量或数据量较高的数据库操作,我们都会在前端加一层缓存层,并设置失效时间,现在一般是mongoDB或memcached,简单流程如下:



一般使用的代码如下:

点击(此处)折叠或打开

  1. <?php
  2. $key = 'xxxx';
  3. $res = $this->cache->get($key);
  4. if ($res !== false) {
  5.     return $res;
  6. }else{
  7.     $res['data'] = $this->db['default']->select($param);
  8.     $this->cache->set($key, $res, 3600);
  9.     return $res;
  10. }
  11. ?>


这个模式在并发量并非太高或数据操作效率很高的情况下基本没有什么问题。
但是也许你已经看到了,if(缓存失效 && 恰好遇到并发量很高 && 数据库操作时间长) then?
  1. 缓存失效
  2. 第一个进程去数据库获取新数据,假如包括SQL+程序逻辑耗时5S
  3. 这5S内,第二个、第三个...第N个都只是获取到已失效的缓存,于是也都连接数据库...
  4. 结果显而易见,数据库锁表 -> 数据库累计大量进程 -> 直至数据库挂掉!

那么,如何去解决这个问题呢?其实最简单的方案就是:
添加一个标记,类似文件锁,用于判断此时程序是否正在更新缓存。
若是,则直接返回旧缓存(标记有设置失效时间,避免由于程序错误导致标记未删除而引起的缓存不更新问题)
若否,则设置一个标记,然后进行数据获取及缓存更新,最后删除标记。

PS. 第一次生成缓存的话可能会有些用户无法看到数据,不过这个几率很小,且可以通过其他方式如手动生成之类去解决。
流程如下:


阅读(1677) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~