首先看應用場景:幾年前,微信裡許多投票系統。很多人找好友或朋友圈找人拉票。當時,有一個比較大的樂園也做了這個投票活動,好像是超過一定票數就可以免費玩。我也被好友要求給他投票,有一天好像可以投3票還是5票,具體的記不太清楚了。我當時進去該系統後,給朋友點了投票按鈕,發現沒有任何反應。我以為是我網路的問題,後來發現是程式的問題。我當時猜測,它應該是投票的人太多了,系統一時沒抗住。
因為當時,我剛好在學redis。所以,我想可以透過redis的計數器來改善該功能。
這裡,我們只統計用戶總的投票數,而不考慮用戶具體的投票情況,像A什麼時候給B投票的記錄我們不做考慮。這種情況如果考慮的話,就要使用其他的解決方案了(可以用訊息佇列)。
校驗
首先,要對每位使用者的投票數進行校驗。用戶每天都有3次投票機會,所以,我們可建立個鍵,將他的有效期限設為明天凌晨。用戶每投一票就加1等了3票後,就提示今日次數已用完。
// 投票次数校验 function checkVote($uid) { $key = "uid:$uid:cnt"; $tomrTime = mktime(0,0,0, date('m'), date('d')+1, date('Y')); if (!$redis->exsits($key)) { $redis->set($key, 0, ['ex' => $tomrTime - time()]); return true; } else if ( ($cnt = $redis->get($key)) < 3 ) { return true; } else { return false; } }
統計投票
次數校驗通過後,就需要統計使用者的得票數了。
// 得票数计数 // uid表示投票人id // touid表示得票人id function vote ($uid, $toUid) { $key = "uid:$toUid:vote"; // 投票数校验 if (checkVote($uid)) { // 统计投票数及参选人得票数 $redis->incr($key); $redis->incr("uid:$uid:cnt"); return true; } else { return false; } }
我自己的阿里雲主機配置只有1核心cpu1G記憶體的配置,每秒incr效能能達到10萬多次,redis效能真恐怖。
# redis-benchmark -t incr -q INCR: 105708.25 requests per second
Mysql異步更新使用者得票數
#最後,我們只需要做一個定時任務了,讓mysql每隔一定時間就去同步用戶的得票數。
方法是做一個死循環,每次循環都取得50個使用者得票數,直到遍歷完所有使用者後,就退出循環,結束腳本。
$start = 0; while (true) { // 每次循环取50个用户id $users = $DB->query("SELECT uid,votes FROM users LIMIT $start, 50"); if (!$users) { exit(); } $keys = []; foreach ($users as $userinfo) { $keys[] = "uid:{$userinfo['uid']}:vote"; } $votes = $redis->mget($keys); foreach ($votes as $index => $vote) { if ($vote != $users[$index]['votes']) { $DB->query("UPDATE users SET votes = '$vote' WHERE uid='{$users[$index]['uid']}"); } } $start += 50; }
Redis的mget指令的是一次取得多個key的值。取得了redis裡存放的redis得票數後,要先和Mysql裡的得票數做比對。不一樣的時候才去更新Mysql的資料。
以上是使用redis來做計數器完善投票系統的詳細內容。更多資訊請關注PHP中文網其他相關文章!