原书用 Python 与 Redis 进行交互,我用 PHP 来实现。

  1. 环境:LNMPCentOS 6.6 + Nginx 1.8.0 + MySQL 5.6.23 + PHP 5.6.9)+ Redis 3.0.7 + phpredis 2.2.4

首先在 Linux 开启 Redis 服务:

  1. [root@localhost ~]# cd /usr/local/redis/
  2. [root@localhost redis]# ./bin/redis-server ./etc/redis.conf
  3. [root@localhost redis]# ps aux|grep redis

如果显示:

  1. root 2239 0.2 0.1 35556 1744 ? Ssl 12:08 0:00 ./bin/redis-server *:6379
  2. root 2243 0.0 0.0 5976 724 pts/0 S+ 12:08 0:00 grep redis

说明 Redis 服务已经开启,端口号 6379  

  

redis.php

  1. <?php
  2.  
  3. // 一周的秒数
  4. $seconds = 7 * 86400;
  5. define(ONE_WEEK_IN_SECONDS, $seconds);
  6. // 每投一票文章加的分值
  7. define(VOTE_SCORE, 432);
  8.  
  9. // 实例化Redis对象
  10. $redis = new Redis();
  11. // 连接到Redis服务器
  12. $conn = $redis->connect('localhost', 6379);

  

init_data.php 用于添加案例的数据

  1. <?php
  2.  
  3. /* 为投票网站准备数据 */
  4.  
  5. require 'redis.php';
  6.  
  7. //1.根据发布时间排序文章的有序集合 zset
  8. $article_info = [
  9. '100408'=>[
  10. 'time'=>strtotime('2016-4-10'),
  11. 'score'=>1332164063.49
  12. ],
  13. '100635'=>[
  14. 'time'=>strtotime('2016-4-28'),
  15. 'score'=>1332174713.47
  16. ],
  17. '100716'=>[
  18. 'time'=>strtotime('2016-4-29'),
  19. 'score'=>1332225027.26
  20. ]
  21. ];
  22.  
  23. foreach($article_info as $key => $val) {
  24. $redis->zadd('time:', $val['time'], 'article:'.$key);
  25. }
  26.  
  27. //2.根据评分排序文章的有序集合 zset
  28. foreach($article_info as $key => $val) {
  29. $redis->zadd('score:', $val['score'], 'article:'.$key);
  30. }
  31.  
  32. //3.为某篇文章(id:100408)投过票的用户集合 set
  33. $users = [234487, 253378, 364680, 132097, 350917];
  34. foreach($users as $key => $val) {
  35. $redis->sadd('voted:100408', $val);
  36. }

  

vote.php 用于给文章投票,其中文章 id(article_id)和投票用户 id(user_id)通过 get 方式传递(代码清单 1-6 article_vote() 函数

  1. <?php
  2.  
  3. header('Content-type:text/html;charset=utf-8');
  4.  
  5. require 'redis.php';
  6.  
  7. $user_id = empty($_GET['user_id']) ? 0 : (int)$_GET['user_id'];
  8. $article_id = empty($_GET['article_id']) ? 0 : (int)$_GET['article_id'];
  9.  
  10. function article_vote($redis, $user_id, $article_id) {
  11. $cutoff = time() - ONE_WEEK_IN_SECONDS;
  12. // 127.0.0.1:6379> zscore time article:100408
  13. // "1460217600"
  14. if(intval($redis->zscore('time:', 'article:'.$article_id)) < $cutoff) {
  15. return false;
  16. }
  17.  
  18. if($redis->sadd('voted:'.$article_id, $user_id)) {
  19. // ZINCRBY key increment member
  20. // 为有序集 score 的成员 article:100408 的 score 值加上增量 increment
  21. $score_new = $redis->zincrby('score:', VOTE_SCORE, 'article:'.$article_id);
  22. echo $score_new;
  23. // HINCRBY key field increment
  24. // 为哈希表key中的域field的值加上增量increment。
  25. // 如果用户是第一次为这篇文章投票,那么增加这篇文章的投票数量和评分
  26.  
  27. } else {
  28. return false;
  29. }
  30. return true;
  31. }
  32.  
  33. if(! article_vote($redis, $user_id, $article_id)) {
  34. echo '投票失败';
  35. } else {
  36. echo '投票成功';
  37. }

  

执行 http://yourdomain/init_data.php,完成 Redis 的连接和数据的初始化,可以进入 Redis 的客户端查询文章投票积分的有序集合(zset)和文章 100408 的投票用户的集合(set):

  1. [root@localhost redis]# ./bin/redis-cli
  2. 127.0.0.1:6379> zrange score: 0 -1 withscores
  3. 1) "article:100408"
  4. 2) "1332164063.49"
  5. 3) "article:100635"
  6. 4) "1332174713.47"
  7. 5) "article:100716"
  8. 6) "1332225027.26"
  9. 127.0.0.1:6379> zrange time: 0 -1 withscores
  10. 1) "article:100408"
  11. 2) "1460217600"
  12. 3) "article:100635"
  13. 4) "1461772800"
  14. 5) "article:100716"
  15. 6) "1461859200"

  

然后访问 http://yourdomain/vote.php?user_id=100&article_id=100408 让 user_id 为 100 的用户给编号100408 的文章投票。

再次进入 Redis 的客户端查询文章投票积分的有序集合(zset)和文章 100408 的投票用户的集合(set):

  1. 127.0.0.1:6379> zrange score: 0 -1 withscores
  2. 1) "article:100408"
  3. 2) "1332164495.49"
  4. 3) "article:100635"
  5. 4) "1332174713.47"
  6. 5) "article:100716"
  7. 6) "1332225027.26"
  8. 127.0.0.1:6379> smembers voted:100408
  9. 1) "100"
  10. 2) "132097"
  11. 3) "234487"
  12. 4) "253378"
  13. 5) "350917"
  14. 6) "364680"
  15. 127.0.0.1:6379>

  

发布文章 post_article.php(代码清单 1-7 post_article() 函数

  1. <?php
  2.  
  3. require 'redis.php';
  4.  
  5. // @param object $redis redis对象
  6. // @param int $user_id 用户编号
  7. // @param string $title 文章标题
  8. // @param string $link 文章链接
  9. function post_article($redis, $user_id, $title, $link) {
  10. $article_id = $redis->incr('article:'); // 生成新的文章id
  11.  
  12. $voted = 'voted:'.$article_id;
  13. $redis->sadd($voted, $user_id); // 将发布文章的用户添加到文章已投票的用户名单中
  14. $redis->expire($voted, ONE_WEEK_IN_SECONDS); // 将投票名单的过期时间设置为一周
  15.  
  16. $now = time();
  17. $article = 'article:'.$article_id;
  18. $redis->hmset($article, [
  19. 'title' => $title,
  20. 'link' => $link,
  21. 'poster' => $user_id,
  22. 'time' => $now,
  23. 'votes'=> 1
  24. ]); // 将文章的信息存储到一个散列里
  25.  
  26. $redis->zadd('score:', $now + VOTE_SCORE, $article); // 把文章添加到根据评分排序的有序集合中
  27. $redis->zadd('time:', $now, $article); // 把文章添加到根据发布时间排序的有序集合中
  28.  
  29. return $article_id;
  30. }
  31.  
  32. $user_id = isset($_GET['user_id']) ? $_GET['user_id'] : 0;
  33. $mtid = mt_rand(0,999);
  34. $title = '文章标题'.$mtid;
  35. $link = 'http://www.youdomain.com/article/'.$mtid;
  36. if(post_article($redis, $user_id, $title, $link)) {
  37. echo 'success';
  38. } else {
  39. echo 'error';
  40. }

访问:http://yourdomain/post_article.php

由于 url 不带参数并且 Redis 中不存在 article: ,因此会有一个 user_id 为 0 的用户发布 article:1

此时查询 Redis 中时间和分数的有序集合、article:1 的投票用户集合以及 article:1 的散列内容:

  1. 127.0.0.1:6379> zrange time: 0 -1 withscores
  2. 1) "article:100408"
  3. 2) "1460217600"
  4. 3) "article:100635"
  5. 4) "1461772800"
  6. 5) "article:100716"
  7. 6) "1461859200"
  8. 7) "article:1"
  9. 8) "1465105632"
  10. 127.0.0.1:6379> zrange score: 0 -1 withscores
  11. 1) "article:100408"
  12. 2) "1332164495.49"
  13. 3) "article:100635"
  14. 4) "1332174713.47"
  15. 5) "article:100716"
  16. 6) "1332225027.26"
  17. 7) "article:1"
  18. 8) "1465106064"
  19. 127.0.0.1:6379> smembers voted:1
  20. 1) "0"
  21. 127.0.0.1:6379> hgetall article:1
  22. 1) "title"
  23. 2) "\xe6\x96\x87\xe7\xab\xa0\xe6\xa0\x87\xe9\xa2\x9868"
  24. 3) "link"
  25. 4) "http://www.youdomain.com/article/68"
  26. 5) "poster"
  27. 6) "0"
  28. 7) "time"
  29. 8) "1465105632"
  30. 9) "votes"
  31. 10) "1"

 

可以发布多篇文章用于测试。

get_articles.php 获取文章列表(代码清单1-8 get_article() 函数)

  1. <?php
  2.  
  3. require 'redis.php';
  4.  
  5. define(ARTICLES_PER_PAGE, 5); // 每页5条数据
  6.  
  7. // @param object $redis redis对象
  8. // @param int $page 当前页
  9. // @param string $order 根据$order来排序
  10. // @return array $articles 文章信息
  11. function get_articles($redis, $page, $order = 'score:') {
  12. $start = ($page - 1) * ARTICLES_PER_PAGE;
  13. $end = $start + ARTICLES_PER_PAGE - 1;
  14.  
  15. $ids = $redis->zrevrange($order, $start, $end); // 获取多个文件的序号 按 score 值递减(从大到小)来排列。
  16. $articles = [];
  17. foreach($ids as $id) {
  18. $article_data = $redis->hgetall($id);
  19. $article_data['id'] = $id;
  20. $articles[] = $article_data;
  21. }
  22.  
  23. return $articles;
  24. }
  25.  
  26. $page = isset($_GET['page']) ? $_GET['page'] : 1;
  27. $articles = get_articles($redis, $page);
  28.  
  29. echo '<pre>';
  30. print_r($articles);

可以通过 http://yourdomain/get_articles.php 或 http://yourdomain/get_articles.php?page = 2 来获取文章列表,可以得到:

  1. Array
  2. (
  3. [0] => Array
  4. (
  5. [title] => 文章标题23
  6. [link] => http://www.youdomain.com/article/23
  7. [poster] => 106
  8. [time] => 1465180517
  9. [votes] => 1
  10. [id] => article:8
  11. )
  12.  
  13. [1] => Array
  14. (
  15. [title] => 文章标题719
  16. [link] => http://www.youdomain.com/article/719
  17. [poster] => 105
  18. [time] => 1465180514
  19. [votes] => 1
  20. [id] => article:7
  21. )
  22.  
  23. [2] => Array
  24. (
  25. [title] => 文章标题811
  26. [link] => http://www.youdomain.com/article/811
  27. [poster] => 104
  28. [time] => 1465180511
  29. [votes] => 1
  30. [id] => article:6
  31. )
  32.  
  33. [3] => Array
  34. (
  35. [title] => 文章标题22
  36. [link] => http://www.youdomain.com/article/22
  37. [poster] => 103
  38. [time] => 1465180508
  39. [votes] => 1
  40. [id] => article:5
  41. )
  42.  
  43. [4] => Array
  44. (
  45. [title] => 文章标题350
  46. [link] => http://www.youdomain.com/article/350
  47. [poster] => 102
  48. [time] => 1465180506
  49. [votes] => 1
  50. [id] => article:4
  51. )
  52.  
  53. )

  

添加文章至分组以及从分组中移除文章(代码清单:1-9 add_remove_groups() 函数)

  1. <?php
  2.  
  3. require 'redis.php';
  4.  
  5. // @param object $redis
  6. // @param int $artile_id
  7. // @param array $to_add
  8. // @param array $to_remove
  9. function add_remove_groups($redis, $article_id, $to_add = [], $to_remove = []) {
  10. $article = 'article:'.$article_id;
  11.  
  12. // 将文章添加到所属的群组
  13. if(! empty($to_add)) {
  14. foreach($to_add as $group) {
  15. $redis->sadd('group:'.$group, $article);
  16. }
  17. }
  18.  
  19. // 将文章从群组里面移除
  20. if(! empty($to_remove)) {
  21. foreach($to_remove as $group) {
  22. $redis->srem('group'.$group, $article);
  23. }
  24. }
  25. }

初始化群组数据 init_data_group.php

  1. <?php
  2.  
  3. /* 准备群组数据 */
  4.  
  5. require 'redis.php';
  6.  
  7. $article_info = ['article:100408', 'article:100635', 'article:5'];
  8.  
  9. foreach($article_info as $key => $val) {
  10. $redis->sadd('group:programming', $val);
  11. }

  

代码清单 1-10 get_group_articles 函数 

  1. function get_group_article($redis, $group, $page, $order = 'score:') {
  2. $key = $order.$group; // 为每个群组的每种排列顺序都创建一个键
  3. if(! $redis->exists($key)) { // 检查是否已有缓存的排序结果,如果没有的话则进行排序
  4. $redis->zInter($key, array('group:'.$group, $order), array(1, 1), $aggregate = 'max');
  5. $redis->expire($key, 60); // 60s之后redis删除有序集合
  6. }
  7. return get_articles($redis, $page, $key);
  8. }
  9.  
  10. $group = 'programming';
  11. $page = isset($_GET['page']) ? $_GET['page'] : 1;
  12. $articles = get_group_article($redis, $group, $page);
  13.  
  14. echo '<pre>';
  15. print_r($articles);

  

   

附:

文章案例来自《Redis 实战》

phpredis 文档:https://github.com/phpredis/phpredis

Redis 命令参考:http://doc.redisfans.com/sorted_set/zrange.html

php-redis 中文文档:http://www.cnblogs.com/weafer/archive/2011/09/21/2184059.html

redis在PHP中的基本使用案例:http://www.t086.com/article/4901

Redis in Action 文章投票的更多相关文章

  1. Redis 实战 —— 02. Redis 简单实践 - 文章投票

    需求 功能: P15 发布文章 获取文章 文章分组 投支持票 数值及限制条件 P15 如果一篇文章获得了至少 200 张支持票,那么这篇文章就是一篇有趣的文章 如果这个网站每天有 50 篇有趣的文章, ...

  2. .Net Redis实战——实现文章投票并排序

    本系列文章为学习Redis实战一书记录的随笔. 软件和环境版本:Redis:5.0.7  .Net 5.0 文中不会对Redis基础概念做过多介绍. Redis数据类型和命令可在菜鸟教程学习:http ...

  3. 使用redis构建文章投票系统

    首先,我得说明这篇博客基本上就是<<redis in action>>第一章内容的读书笔记. 需求 首先,说明一下,我们的需求 用户可以发表文章,发表时,自己就默认的给自己的文 ...

  4. redis 实例2 构建文章投票网站后端

    redis 实例2 构建文章投票网站后端   1.限制条件 一.如果网站获得200张支持票,那么这篇文章被设置成有趣的文章 二.如果网站发布的文章中有一定数量被认定为有趣的文章,那么这些文章需要被设置 ...

  5. Redis实现文章投票功能

    Redis的具体操作这里就不说了,说一下需求和设计思路. 需求:自己实现一个文章投票的功能1.能够按照时间分页倒叙查看文章信息2.能够给文章投票,一个用户给一篇文章只能投票一次3.需要记录分值.每次投 ...

  6. 使用Redis构建文章投票网站

    涉及到的key: 1. article_time, 记录文章的发布时间,zset结构 2. article_score, 记录文章的得分, zset结构 得分 = 发布时间 + 投票用户数 X 432 ...

  7. Redis in Action : Redis 实战学习笔记

    1 1 1 Redis in Action : Redis  实战学习笔记 1 http://redis.io/ https://github.com/antirez/redis https://ww ...

  8. Redis in Action

    Redis in Action Redis REmote DIctionary Server(Redis) Redis 是一种开放源代码(BSD许可)的内存中数据结构存储,用作数据库,缓存和消息代理. ...

  9. 微博与Redis系统技术文章记录

    Redis 持久化,有两种: rdb 和 aof, rdb是记录一段时间内的操作,一盘的配置是一段时间内操作超过多少次就持久化. aof可以实现每次操作都持久化. 这里我们使用aof. 配置方式,打开 ...

随机推荐

  1. ftp服务配置文件记录

    因为南京的客户死活要ftp服务而不是sftp,所以我作手用vsftp作为服务器,尝试在windows ftp软件登录进去,特记录vsftp的用法. 配置文件在/etc/vsftpd.conf 有如下代 ...

  2. php之获取程序源码的方法

    文件hello.php和index.php在同一目录 hello.php <?php class hello{ public function __construct() { echo 'hel ...

  3. webstrom配置sass与less

    1.less 安装一个稳定版的node.[例如node-v4.4.4-x64] 然后直接在webstrom里导入那个lessc.cmd 2.sass 安装ruby. 安装完之后点开,Start那个安装 ...

  4. 上个项目的一些反思 II

    上个项目需要使用通讯录,我在回顾自己设计的时候,发现自己少设计了cache这一环. 虽然直接用SQLite在初期体验上没什么大损失,不过可以预想通讯录增长到一定数量后势必会影响体验. 单例模式,全局缓 ...

  5. SpringMVC@RequestBody小细节

    关于@RequestBody  参数类型自己的包装类,还是类似String/int,区别: 1.@RequestBody LoginParmar parmar String user_number = ...

  6. [转]Struts2工作原理

    Struts2请求响应流程: 在struts2的应用中,从用户请求到服务器返回相应响应给用户端的过程中,包含了许多组件如:Controller.ActionProxy.ActionMapping.Co ...

  7. MariaDB 多主一从 搭建测试

    背景: 目前MySQL依然只支持一个Slave从一个Master复制数据,虽然也可以做到一主多备(M->S),双主复制(M<->M)等架构,但是局限性依然很大.由于项目的要求,需要各 ...

  8. .NET 强引用与弱引用

    强引用      如果应用程序的代码可以访问一个正由该程序使用的对象,垃圾回收器就不能收集该对象, 那么,就认为应用程序对该对象具有强引用.      要建立强引用并重新使用该对象,请将 WeakRe ...

  9. 【XLL 框架库函数】 TempActiveColumn/TempActiveColumn12

    创建一个包含所有激活工作表列的 XLOPER/XLOPER12 LPXLOPER TempActiveColumn(BYTE col); LPXLOPER12 TempActiveColumn12(C ...

  10. 比较典型的带case的group by语句

    2005-05-09 胜 2005-05-09 胜 2005-05-09 负 2005-05-09 负 2005-05-10 胜 2005-05-10 负 2005-05-10 负 如果要生成下列结果 ...