Redis in Action 文章投票
原书用 Python 与 Redis 进行交互,我用 PHP 来实现。
环境:LNMP(CentOS 6.6 + Nginx 1.8.0 + MySQL 5.6.23 + PHP 5.6.9)+ Redis 3.0.7 + phpredis 2.2.4
首先在 Linux 开启 Redis 服务:
[root@localhost ~]# cd /usr/local/redis/
[root@localhost redis]# ./bin/redis-server ./etc/redis.conf
[root@localhost redis]# ps aux|grep redis
如果显示:
root 2239 0.2 0.1 35556 1744 ? Ssl 12:08 0:00 ./bin/redis-server *:6379
root 2243 0.0 0.0 5976 724 pts/0 S+ 12:08 0:00 grep redis
说明 Redis 服务已经开启,端口号 6379
redis.php
<?php // 一周的秒数
$seconds = 7 * 86400;
define(ONE_WEEK_IN_SECONDS, $seconds);
// 每投一票文章加的分值
define(VOTE_SCORE, 432); // 实例化Redis对象
$redis = new Redis();
// 连接到Redis服务器
$conn = $redis->connect('localhost', 6379);
init_data.php 用于添加案例的数据
<?php /* 为投票网站准备数据 */ require 'redis.php'; //1.根据发布时间排序文章的有序集合 zset
$article_info = [
'100408'=>[
'time'=>strtotime('2016-4-10'),
'score'=>1332164063.49
],
'100635'=>[
'time'=>strtotime('2016-4-28'),
'score'=>1332174713.47
],
'100716'=>[
'time'=>strtotime('2016-4-29'),
'score'=>1332225027.26
]
]; foreach($article_info as $key => $val) {
$redis->zadd('time:', $val['time'], 'article:'.$key);
} //2.根据评分排序文章的有序集合 zset
foreach($article_info as $key => $val) {
$redis->zadd('score:', $val['score'], 'article:'.$key);
} //3.为某篇文章(id:100408)投过票的用户集合 set
$users = [234487, 253378, 364680, 132097, 350917];
foreach($users as $key => $val) {
$redis->sadd('voted:100408', $val);
}
vote.php 用于给文章投票,其中文章 id(article_id)和投票用户 id(user_id)通过 get 方式传递(代码清单 1-6 article_vote() 函数)
<?php header('Content-type:text/html;charset=utf-8'); require 'redis.php'; $user_id = empty($_GET['user_id']) ? 0 : (int)$_GET['user_id'];
$article_id = empty($_GET['article_id']) ? 0 : (int)$_GET['article_id']; function article_vote($redis, $user_id, $article_id) {
$cutoff = time() - ONE_WEEK_IN_SECONDS;
// 127.0.0.1:6379> zscore time article:100408
// "1460217600"
if(intval($redis->zscore('time:', 'article:'.$article_id)) < $cutoff) {
return false;
} if($redis->sadd('voted:'.$article_id, $user_id)) {
// ZINCRBY key increment member
// 为有序集 score 的成员 article:100408 的 score 值加上增量 increment
$score_new = $redis->zincrby('score:', VOTE_SCORE, 'article:'.$article_id);
echo $score_new;
// HINCRBY key field increment
// 为哈希表key中的域field的值加上增量increment。
// 如果用户是第一次为这篇文章投票,那么增加这篇文章的投票数量和评分 } else {
return false;
}
return true;
} if(! article_vote($redis, $user_id, $article_id)) {
echo '投票失败';
} else {
echo '投票成功';
}
执行 http://yourdomain/init_data.php,完成 Redis 的连接和数据的初始化,可以进入 Redis 的客户端查询文章投票积分的有序集合(zset)和文章 100408 的投票用户的集合(set):
[root@localhost redis]# ./bin/redis-cli
127.0.0.1:6379> zrange score: 0 -1 withscores
1) "article:100408"
2) "1332164063.49"
3) "article:100635"
4) "1332174713.47"
5) "article:100716"
6) "1332225027.26"
127.0.0.1:6379> zrange time: 0 -1 withscores
1) "article:100408"
2) "1460217600"
3) "article:100635"
4) "1461772800"
5) "article:100716"
6) "1461859200"
然后访问 http://yourdomain/vote.php?user_id=100&article_id=100408 让 user_id 为 100 的用户给编号100408 的文章投票。
再次进入 Redis 的客户端查询文章投票积分的有序集合(zset)和文章 100408 的投票用户的集合(set):
127.0.0.1:6379> zrange score: 0 -1 withscores
1) "article:100408"
2) "1332164495.49"
3) "article:100635"
4) "1332174713.47"
5) "article:100716"
6) "1332225027.26"
127.0.0.1:6379> smembers voted:100408
1) "100"
2) "132097"
3) "234487"
4) "253378"
5) "350917"
6) "364680"
127.0.0.1:6379>
发布文章 post_article.php(代码清单 1-7 post_article() 函数)
<?php require 'redis.php'; // @param object $redis redis对象
// @param int $user_id 用户编号
// @param string $title 文章标题
// @param string $link 文章链接
function post_article($redis, $user_id, $title, $link) {
$article_id = $redis->incr('article:'); // 生成新的文章id $voted = 'voted:'.$article_id;
$redis->sadd($voted, $user_id); // 将发布文章的用户添加到文章已投票的用户名单中
$redis->expire($voted, ONE_WEEK_IN_SECONDS); // 将投票名单的过期时间设置为一周 $now = time();
$article = 'article:'.$article_id;
$redis->hmset($article, [
'title' => $title,
'link' => $link,
'poster' => $user_id,
'time' => $now,
'votes'=> 1
]); // 将文章的信息存储到一个散列里 $redis->zadd('score:', $now + VOTE_SCORE, $article); // 把文章添加到根据评分排序的有序集合中
$redis->zadd('time:', $now, $article); // 把文章添加到根据发布时间排序的有序集合中 return $article_id;
} $user_id = isset($_GET['user_id']) ? $_GET['user_id'] : 0;
$mtid = mt_rand(0,999);
$title = '文章标题'.$mtid;
$link = 'http://www.youdomain.com/article/'.$mtid;
if(post_article($redis, $user_id, $title, $link)) {
echo 'success';
} else {
echo 'error';
}
访问:http://yourdomain/post_article.php
由于 url 不带参数并且 Redis 中不存在 article: ,因此会有一个 user_id 为 0 的用户发布 article:1
此时查询 Redis 中时间和分数的有序集合、article:1 的投票用户集合以及 article:1 的散列内容:
127.0.0.1:6379> zrange time: 0 -1 withscores
1) "article:100408"
2) "1460217600"
3) "article:100635"
4) "1461772800"
5) "article:100716"
6) "1461859200"
7) "article:1"
8) "1465105632"
127.0.0.1:6379> zrange score: 0 -1 withscores
1) "article:100408"
2) "1332164495.49"
3) "article:100635"
4) "1332174713.47"
5) "article:100716"
6) "1332225027.26"
7) "article:1"
8) "1465106064"
127.0.0.1:6379> smembers voted:1
1) "0"
127.0.0.1:6379> hgetall article:1
1) "title"
2) "\xe6\x96\x87\xe7\xab\xa0\xe6\xa0\x87\xe9\xa2\x9868"
3) "link"
4) "http://www.youdomain.com/article/68"
5) "poster"
6) "0"
7) "time"
8) "1465105632"
9) "votes"
10) "1"
可以发布多篇文章用于测试。
get_articles.php 获取文章列表(代码清单1-8 get_article() 函数)
<?php require 'redis.php'; define(ARTICLES_PER_PAGE, 5); // 每页5条数据 // @param object $redis redis对象
// @param int $page 当前页
// @param string $order 根据$order来排序
// @return array $articles 文章信息
function get_articles($redis, $page, $order = 'score:') {
$start = ($page - 1) * ARTICLES_PER_PAGE;
$end = $start + ARTICLES_PER_PAGE - 1; $ids = $redis->zrevrange($order, $start, $end); // 获取多个文件的序号 按 score 值递减(从大到小)来排列。
$articles = [];
foreach($ids as $id) {
$article_data = $redis->hgetall($id);
$article_data['id'] = $id;
$articles[] = $article_data;
} return $articles;
} $page = isset($_GET['page']) ? $_GET['page'] : 1;
$articles = get_articles($redis, $page); echo '<pre>';
print_r($articles);
可以通过 http://yourdomain/get_articles.php 或 http://yourdomain/get_articles.php?page = 2 来获取文章列表,可以得到:
Array
(
[0] => Array
(
[title] => 文章标题23
[link] => http://www.youdomain.com/article/23
[poster] => 106
[time] => 1465180517
[votes] => 1
[id] => article:8
) [1] => Array
(
[title] => 文章标题719
[link] => http://www.youdomain.com/article/719
[poster] => 105
[time] => 1465180514
[votes] => 1
[id] => article:7
) [2] => Array
(
[title] => 文章标题811
[link] => http://www.youdomain.com/article/811
[poster] => 104
[time] => 1465180511
[votes] => 1
[id] => article:6
) [3] => Array
(
[title] => 文章标题22
[link] => http://www.youdomain.com/article/22
[poster] => 103
[time] => 1465180508
[votes] => 1
[id] => article:5
) [4] => Array
(
[title] => 文章标题350
[link] => http://www.youdomain.com/article/350
[poster] => 102
[time] => 1465180506
[votes] => 1
[id] => article:4
) )
添加文章至分组以及从分组中移除文章(代码清单:1-9 add_remove_groups() 函数)
<?php require 'redis.php'; // @param object $redis
// @param int $artile_id
// @param array $to_add
// @param array $to_remove
function add_remove_groups($redis, $article_id, $to_add = [], $to_remove = []) {
$article = 'article:'.$article_id; // 将文章添加到所属的群组
if(! empty($to_add)) {
foreach($to_add as $group) {
$redis->sadd('group:'.$group, $article);
}
} // 将文章从群组里面移除
if(! empty($to_remove)) {
foreach($to_remove as $group) {
$redis->srem('group'.$group, $article);
}
}
}
初始化群组数据 init_data_group.php
<?php /* 准备群组数据 */ require 'redis.php'; $article_info = ['article:100408', 'article:100635', 'article:5']; foreach($article_info as $key => $val) {
$redis->sadd('group:programming', $val);
}
代码清单 1-10 get_group_articles 函数
function get_group_article($redis, $group, $page, $order = 'score:') {
$key = $order.$group; // 为每个群组的每种排列顺序都创建一个键
if(! $redis->exists($key)) { // 检查是否已有缓存的排序结果,如果没有的话则进行排序
$redis->zInter($key, array('group:'.$group, $order), array(1, 1), $aggregate = 'max');
$redis->expire($key, 60); // 60s之后redis删除有序集合
}
return get_articles($redis, $page, $key);
} $group = 'programming';
$page = isset($_GET['page']) ? $_GET['page'] : 1;
$articles = get_group_article($redis, $group, $page); echo '<pre>';
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 文章投票的更多相关文章
- Redis 实战 —— 02. Redis 简单实践 - 文章投票
需求 功能: P15 发布文章 获取文章 文章分组 投支持票 数值及限制条件 P15 如果一篇文章获得了至少 200 张支持票,那么这篇文章就是一篇有趣的文章 如果这个网站每天有 50 篇有趣的文章, ...
- .Net Redis实战——实现文章投票并排序
本系列文章为学习Redis实战一书记录的随笔. 软件和环境版本:Redis:5.0.7 .Net 5.0 文中不会对Redis基础概念做过多介绍. Redis数据类型和命令可在菜鸟教程学习:http ...
- 使用redis构建文章投票系统
首先,我得说明这篇博客基本上就是<<redis in action>>第一章内容的读书笔记. 需求 首先,说明一下,我们的需求 用户可以发表文章,发表时,自己就默认的给自己的文 ...
- redis 实例2 构建文章投票网站后端
redis 实例2 构建文章投票网站后端 1.限制条件 一.如果网站获得200张支持票,那么这篇文章被设置成有趣的文章 二.如果网站发布的文章中有一定数量被认定为有趣的文章,那么这些文章需要被设置 ...
- Redis实现文章投票功能
Redis的具体操作这里就不说了,说一下需求和设计思路. 需求:自己实现一个文章投票的功能1.能够按照时间分页倒叙查看文章信息2.能够给文章投票,一个用户给一篇文章只能投票一次3.需要记录分值.每次投 ...
- 使用Redis构建文章投票网站
涉及到的key: 1. article_time, 记录文章的发布时间,zset结构 2. article_score, 记录文章的得分, zset结构 得分 = 发布时间 + 投票用户数 X 432 ...
- Redis in Action : Redis 实战学习笔记
1 1 1 Redis in Action : Redis 实战学习笔记 1 http://redis.io/ https://github.com/antirez/redis https://ww ...
- Redis in Action
Redis in Action Redis REmote DIctionary Server(Redis) Redis 是一种开放源代码(BSD许可)的内存中数据结构存储,用作数据库,缓存和消息代理. ...
- 微博与Redis系统技术文章记录
Redis 持久化,有两种: rdb 和 aof, rdb是记录一段时间内的操作,一盘的配置是一段时间内操作超过多少次就持久化. aof可以实现每次操作都持久化. 这里我们使用aof. 配置方式,打开 ...
随机推荐
- 周末娱乐一下--------恶搞windows小脚本
下面这是个循环DOS命令,使用了C中的goto语句 echo命令式输出命令 set命令是设置命令 var是变量,初始为0 :continue是一个用于goto的标示. %var%输出变量名,%var% ...
- Factory 模式
在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的.但是在一些情况下, new操作符直接生成对象会带来一些问题.举例来说, 许多类型对象的创造需要一 ...
- Python自动化之一对多
一对多 建立一对多关系之后(也就是加了外键),会在字表里面多一个"外键字段_id"这个字段 查询 #_*_coding:utf-8_*_ from django.db import ...
- MySQL5.6 PERFORMANCE_SCHEMA 说明
背景: MySQL 5.5开始新增一个数据库:PERFORMANCE_SCHEMA,主要用于收集数据库服务器性能参数.并且库里表的存储引擎均为PERFORMANCE_SCHEMA,而用户是不能创建存储 ...
- ubuntu下安装nodejs,无node情况
Updating nodejs solved the issue: npm cache clean -f npm install -g n n stable node --version node ...
- 设置UITableView的separatorInset值为UIEdgeInsetsZero,分隔线不最左端显示的问题
一.问题描述 UITableView分割线要显示到最左端 查看UITableView的属性,发现设置separatorInset的值可以自定义分割线的位置. @property (nonatomic) ...
- CoreAnimation 之CATextLayer
如果你想在一个图层中显示文字,完全可以借助图层代理直接将Core Graphics写入图层的内容(这就是UILabel的精髓).如果雨果寄宿于图层的视图,直接在图层上操作,其实相当繁琐.你要为每一个显 ...
- 如何创建 Code Snippet
比如有一行自定义代码段: @property (nonatomic,copy) NSString *<#string#>; 需要添加到 Code Snippet 上,以帮助开发人员开发更便 ...
- spring 的IoC的个人理解
1.ioc IoC的概念介绍 ( a).依赖注入, 分为依赖 和 注入 , 其实依赖是一种耦合方式, 通过spirng在运行时将这种依赖关系完成, 达到解耦目的, 同时达到代码重用, 方便测试,更加 ...
- WPF 实现 DataGrid/ListView 分页控件
在WPF中,通常会选用DataGrid/ListView进行数据展示,如果数据量不多,可以直接一个页面显示出来.如果数据量很大,2000条数据,一次性显示在一个页面中,不仅消耗资源,而且用户体验也很糟 ...