• 1
  • 2
  • 3
  • 4
  • 5
mysql数据库问题 首 页  »  帮助中心  »  数据库  »  mysql数据库问题
Redis的应用
发布日期:2016-4-16 14:4:55

  Redis的应用

  一、.需求

  很早就听说过Redis、mysql的大名,但是也只研究了mysql,redis一直也没有去研究。由于前一阵有朋友有需要,来问过我时我大致看了看,正好最近自己也碰到redis方面的需求,所以就研究了一下。

  情况其实很简单:

  在一个VPS上跑了一个应用,提供了短链接功能,原来的实现是直接在web server上做了一个反向代理,通过urlrewrite映射到后端服务器上。虽然这么用没有问题,功能是也是完全可以实现的,但是后来发现,当把短链接分享到社交网络的时候,会有短时的大量访问,这些访问全部压到后端去查询数据库,就会产生一点压力了——由于我的VPS配置很低,因此在突发高访问时还是有一定的概率发生50x错误。

  所以考虑把反向代理用redis改写成一个缓存代理。其实这种需求用memcached也是可以的,由于持久化并非必须,大不了重启以后从后端再取一次就是了。不过由于我对redis更感兴趣,所以还是用redis来做了。

  二、安装配置

  redis的安装非常简单,我用的是debian,直接apt-get install redis-server。

  在配置上也没什么可说的,大部分都是用默认配置,对内存使用作了一点限制——要节约VPS资源,因为VPS资源紧张。反正短链接的数据量也不大。

  安装完成以后可以用 service redis-server start 启动。用 redis-cli 可以通过命令操作数据库。

  尽管我对Python比较熟,但这种简单应用就懒得折腾环境了,直接用现成的PHP做吧。

  首先是需要下载安装一个PHP的redis库,官方最推荐的两个PHP库是predis与phpredis,尽管目测用C写的phpredis应该性能好些,但为了图方便,我还是用纯PHP的predis。

  下载最新稳定版的Predis后,运行bin/create-single-file(需要系统中安装了php-cli)就能生成一个单独的Predis.php,把这个文件放到你的项目路径中就能使用了,相当简单。

  二、功能实现

  这个缓存功能很简单:

  1.   取得短链接的ID
  2.     用这个ID去redis里查询。
  •     如果查到URL就直接返回302重定向到这个URL。
  •     如果查不到就向后端查询,取得302响应返回的redirect_url,然后把这个URL保存到redis,并作302重定向。

  需要注意的是,对向后端查询失败的ID也要保存起来,并且返回404错误,以避免错误的ID不断向后端查询,但是风险在于如果这个错误的ID以后用到了,也会查询不到,所以还需要给错误的ID设置超时,过期后删除以便可以重新查询。

  修订:因为向后端查询的速度可能慢于请求的速度。我在上周分享一个链接到社交网络的时候,短时间内涌来600多请求,导致了第一个请求向后端查询还未完成时(即redis中还没有更新数据),后续的大量请求紧跟而来,因为redis中还是空的,所以这些请求都压往后端,导致503错误。更糟的是虽然第一个请求从后端即得了正确的结果,但是当它更新redis以后,后续的错误响应覆盖了这个正确结果,导致之后的请求虽然不再压往后端,但都从redis里得到了错误结果。

  所以这次的修订在更新redis之前先检测一下redis中是否已经有正确结果,如果有则返回这个正确结果,并不再更新redis。

  需要注意的是,因为可能有历史错误数据存在,需要清空一下redis内容再改用新的程序。

  主体部分代码就这么点(修订后的),代码如下所示:

  [php] view plain copyfunction raise_404() {

  header('HTTP/1.1 404 Not Found');

  header("status: 404 Not Found");

  die();

  }

  function redirect($url) {

  header("Location: $url");

  }

  require 'Predis.php';

  $redisdb = new Predis\Client([

  'database' => 1,

  ]);

  $query = (array) explode('/', $_SERVER['REQUEST_URI']);

  if (!isset($query[1]) || $query[1]=="") raise_404();

  $id = $query[1];

  $url = $redisdb->get($id);

  if (!isset($url)) {

  $url = get_redirect($id);

  $new = $redisdb->get($id);

  if (isset($new) && $new != "") {

  $url = $new;

  }

  else {

  $redisdb->set($id, $url);

  if ($url == "") {

  $redisdb->expire($id, 3600);

  }

  }

  }

  if ($url != "") {

  redirect($url);

  }

  else {

  raise_404();

  }

  说明:get_redirect是通过curl向后端查询实际URL的函数,这里就不列出具体实现了。

  实际程序跑了一段时间以后的效果还是很明显的。从LOG统计上看,在这个缓存代理处理了500个请求的时候,后端实际上只处理了不到100个,随着时间推移,缓存的ID越来越多,对后端的请求会越来越少。

  这个试验成功以后,我把RSS功能也缓存起来,不过这个就复杂一些,但是原理还是差不多的,只不过由于字段多了,改为使用redis的HashMap值方式(支持多种类型的value是redis的一大强顶),另外还加上了Last-Modified与Etag支持,期望对你们有用,会用这两个东西来进一步减少不必要的访问量。