使用redis來做計數(shù)器完善投票系統(tǒng)

首先看應(yīng)用場景:幾年前,微信里許多投票系統(tǒng)。很多人都找好友或朋友圈找人拉票。當(dāng)時,有一個比較大的樂園也做了這個投票活動,好像是超過一定票數(shù)就可以免費(fèi)玩。我也被好友要求給他投票,一天好像可以投3票還是5票,具體的記不太清了。我當(dāng)時進(jìn)去該系統(tǒng)后,給朋友點(diǎn)了投票按鈕,發(fā)現(xiàn)沒有任何反應(yīng)。我以為是我網(wǎng)絡(luò)的問題,后來發(fā)現(xiàn)是程序的問題。我當(dāng)時猜測,它應(yīng)該是投票的人太多了,系統(tǒng)一時沒抗住。

因?yàn)楫?dāng)時,我剛好在學(xué)redis。所以,我想可以通過redis的計數(shù)器來改善該功能。

這里,我們只統(tǒng)計用戶總的投票數(shù),而不考慮用戶具體的投票情況,像A什么時候給B投票的記錄我們不做考慮。這種情況如果考慮的話,就要使用其他的解決方案了(可以用消息隊列)。

校驗(yàn)

首先,要對每個用戶的投票數(shù)進(jìn)行校驗(yàn)。用戶每天都有3次投票機(jī)會,所以,我們可建立個鍵,將他的有效期設(shè)為明天凌晨。用戶每投一票就加1等到了3票后,就提示今日次數(shù)已用完。

//?投票次數(shù)校驗(yàn) function?checkVote($uid) { ????$key?=?"uid:$uid:cnt"; ????$tomrTime?=?mktime(0,0,0,?date('m'),?date('d')+1,?date('Y')); ???? ????if?(!$redis-&gt;exsits($key))?{ ????????$redis-&gt;set($key,?0,?['ex'?=&gt;?$tomrTime?-?time()]); ????????return?true; ????}?else?if?(?($cnt?=?$redis-&gt;get($key))?<p><strong><span style="font-size: 24px;">統(tǒng)計投票</span></strong></p><p>次數(shù)校驗(yàn)通過后,就需要統(tǒng)計用戶的得票數(shù)了。</p><pre class="brush:php;toolbar:false">//?得票數(shù)計數(shù) //?uid表示投票人id //?touid表示得票人id function?vote?($uid,?$toUid) { ????$key?=?"uid:$toUid:vote"; ???? ????//?投票數(shù)校驗(yàn) ????if?(checkVote($uid))?{ ????????//?統(tǒng)計投票數(shù)及參選人得票數(shù) ????????$redis-&gt;incr($key); ????????$redis-&gt;incr("uid:$uid:cnt"); ???????? ????????return?true; ????}?else?{ ????????return?false; ????} }

我自己的阿里云主機(jī)配置只有1核cpu1G內(nèi)存的配置,每秒incr性能能達(dá)到10萬多次,redis性能真恐怖。

#?redis-benchmark?-t?incr?-q INCR:?105708.25?requests?per?second

Mysql異步更新用戶得票數(shù)

最后,我們只需要做一個定時任務(wù)了,讓mysql每隔一定時間就去同步用戶的得票數(shù)。

方法是做一個死循環(huán),每次循環(huán)都獲取50個用戶得票數(shù),直到遍歷完所有用戶后,就退出循環(huán),結(jié)束腳本。

$start?=?0; while?(true)?{ ????//?每次循環(huán)取50個用戶id ????$users?=?$DB-&gt;query("SELECT?uid,votes?FROM?users?LIMIT?$start,?50"); ????if?(!$users)?{ ????????exit(); ????} ????$keys?=?[]; ????foreach?($users?as?$userinfo)?{ ????????$keys[]?=?"uid:{$userinfo['uid']}:vote"; ????} ????$votes?=?$redis-&gt;mget($keys); ????foreach?($votes?as?$index?=&gt;?$vote)?{ ????????if?($vote?!=?$users[$index]['votes'])?{ ????????????$DB-&gt;query("UPDATE?users?SET?votes?=?'$vote'?WHERE?uid='{$users[$index]['uid']}"); ????????} ????} ????$start?+=?50; }

Redis的mget命令的是一次獲取多個key的值。獲取了redis里存放的redis得票數(shù)后,要先和Mysql里的得票數(shù)做比對。不一樣的時候才去更新Mysql的數(shù)據(jù)。

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊13 分享