php与Redis实现一个100万用户的投票项目,如何实现

2022-09-11 16:09:21 浏览数 (1)

微信分享里总是有一些亲子活动,或者参加某些大赛需要进行投票,而面向的是所有人都可以参与,或者有限制一个人每天能投票同一个参与者3票之类的。。。这些应用场景有很多。假如一个投票系统做一次投票活动1小时之内预计有100万用户进行投票,而且用户投票完成后就能查看到投票的实时情况,这个场景这个问题我们使用redis mysql冷热数据交换来解决就好了。

好了,什么是冷热数据交换呢? 很土的解释一下,冷数据就是之前使用的数据,有种过去式的感觉,而热数据就是当前的数据,理解为现在进行时吧。如何交换呢?就是将Redis的数据周期存储到mysql中!

整体的业务流程 用户投票后,首先将投票数据保存到Redis。 这些数据是热数据,然后写个定时任务,定时(例如10s)将热数据保存到MySQL。 这些数据成为冷数据,然后从Redis删除冷数据。 一遍又一遍,直到一个小时的投票结束了。

数据表构建

代码语言:javascript复制
CREATE TABLE `vote` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `vid` int(11) unsigned NOT NULL,
  `uid` int(11) DEFAULT NULL,
  `ip` char(20) DEFAULT NULL,
  `time` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 COMMENT='投票表';

结构文件我们这里分index.html , vote.php , swap.php 分别来处理

index.html 这是投票的页面,假如有3个投票按钮,我们模拟给3个用户投票,点击按钮,使用ajax调用vote.php文件

代码语言:javascript复制
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>Document</title>
</head>
<script src="/uploads/allimg/191218/23451K352-0.jpg"></script>
<body>
<p><span id="uid1">0</span><input type="button" value="用户1" onclick="vote(1);" /></p>
<p><span id="uid2">0</span><input type="button" value="用户2" onclick="vote(2);" /></p>
<p><span id="uid3">0</span><input type="button" value="用户3" onclick="vote(3);" /></p>
</body>
<script>
    function vote(i){
        $.get('./vote.php?uid=' i,function(rs){
            var span = '#uid' i;
            $(span).html(rs);
        });
    }
</script>
</html>

vote.php 这个文件是实现投票的逻辑。首先连接上Redis服务器,然后保存投票人id,然后将投票人id为key记录每个用户的票数,然后返回给index.html文件,最后使用global_voteid作为key记录总票数,也可以作为MySQL的自增长的键。然后记录uid,ip,time等数据。

代码语言:javascript复制
<?php

    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    $redis->auth('123456');
    $redis->select(1);//选择数据库1

    //计算每个用户的总票数
    $uid = intval($_GET['uid']);
    
    //$uid = mt_rand(1,3);//随机指定投票人员,方便进行压力测试
    echo $redis->incr($uid);
    $voteid = $redis->incr('global_voteid');
    $redis->set('vote:' . $voteid . ':uid', $uid);
    $ip = $_SERVER['REMOTE_ADDR'];
    $redis->set('vote:' . $voteid . ':ip', $ip);
    $redis->set('vote:' . $voteid . ':time', time());
    
?>

swap.php 文件 主要目的是交换热数据和冷数据。 首先,连接MySQL数据库和Redis服务器,然后每10秒执行一次while循环。 在while循环中,获取插入到mysql中的自增长投票主键和最新投票主键(位置)。 确定插入位置是否存在。 如果不存在,请从头开始插入。 如果所有插入均已完成,请等待。 如果未插入,请执行插入操作

代码语言:javascript复制
<?php
//连接数据库
$pdo = new PDO('mysql:host=39.98.81.13;dbname=try', 'try', 'yn3emW6ksYhwwseh');
$pdo->query('set names utf8');

//连接redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->auth('123456');
$redis->select(1);//选择数据库1
$time = time()   3600;//时间设置到一小时后
//死循环
while ($time > time()) {
    $vid = $redis->get('global_voteid');//自增长的主键
    $last = $redis->get('last');//最近一次插入mysql的投票主键
    //如果没有插入数据库,刚开始的肯定为true
    if (!$last) {
        $last = 0;//设置为0
    }
    //如果所有的数据都被插入到MySQL中
    if ($vid == $last) {
        echo "wait
";//输出等待
    } else {
        //进行插入到数据库操作
        $sql = 'insert into vote(vid,uid,ip,time) values';
        for ($i = $vid; $i > $last; $i--) {
            $k1 = 'vote:' . $i . ':uid';
            $k2 = 'vote:' . $i . ':ip';
            $k3 = 'vote:' . $i . ':time';
            $row = $redis->mget([$k1, $k2, $k3]);
            $sql .= "($i,$row[0],'$row[1]',$row[2]),";
            $redis->delete($k1, $k2, $k3);
        }
        $sql = substr($sql, 0, -1);
        $pdo->exec($sql);
        $redis->set('last', $vid);//设置插入的主键位置
        echo 'OK';
        
        sleep(10);  //每隔10s循环一次
    }
}

0 人点赞