排名在web開發(fā)中序无,屬于基礎(chǔ)功能,以方便用戶從大量信息之中衡创,快速有效地找出最重要的內(nèi)容帝嗡,從最基礎(chǔ)的用戶的點(diǎn)擊數(shù)進(jìn)行排行,到互聯(lián)網(wǎng)大廠基于人工智能的熱度算法都在此范圍內(nèi)璃氢。
熱度排名
排名可以基于信息本身的特征哟玷,也可以基于用戶的投票,即讓用戶決定一也,而熱度是基于用戶投票機(jī)制巢寡,輔以不同的算法完成排名。
Delicious算法
最直覺椰苟、最簡(jiǎn)單的算法抑月,按照單位時(shí)間內(nèi)用戶的投票數(shù)進(jìn)行排名
如按照"過(guò)去一定時(shí)間內(nèi)被點(diǎn)擊的次數(shù)"進(jìn)行排名。每過(guò)一定時(shí)間舆蝴,就統(tǒng)計(jì)一次谦絮。
這個(gè)算法的優(yōu)點(diǎn)是比較簡(jiǎn)單、容易部署洁仗、內(nèi)容更新相當(dāng)快层皱;缺點(diǎn)是,一方面赠潦,排名變化不夠平滑叫胖,前一個(gè)小時(shí)還排名靠前的內(nèi)容,往往第二個(gè)小時(shí)就一落千丈她奥,另一方面臭家,缺乏自動(dòng)淘汰舊項(xiàng)目的機(jī)制,某些熱門內(nèi)容可能會(huì)長(zhǎng)期占據(jù)排行榜前列方淤。
Hacker news算法
Hacker news算法是Hacker news在原版本中基于用戶投票機(jī)制排名熱帖的方法钉赁,其原始的數(shù)學(xué)公式為
score = P / (T)^G
P為帖子得票數(shù) T為距離發(fā)帖時(shí)的時(shí)間差 G為“重力因子”用于控制時(shí)間對(duì)熱度排名的影響
(T)^G T的G次方
(P-1) / (T+2)^G
Hacker news將G默認(rèn)為1.8 P-1以減去發(fā)帖人的點(diǎn)擊數(shù),T+2以緩沖2小時(shí)內(nèi)的發(fā)帖熱度
我們?cè)诘弥烁鱾€(gè)參數(shù)的含義后可根據(jù)自己的具體業(yè)務(wù)進(jìn)行調(diào)參
Hacker News的Ranking算法主要區(qū)別在于引入了T P G這些參數(shù)携茂,這參數(shù)是不斷變化的你踩,從而導(dǎo)致一篇帖子的得分不斷變化。擁有自動(dòng)淘汰舊項(xiàng)目的機(jī)制,排名曲線相對(duì)平滑带膜,在中小站是在復(fù)雜度與功能性的取舍中較優(yōu)選擇吩谦。
結(jié)合Reids實(shí)現(xiàn)Hacker news
因?yàn)閞edis作為緩存數(shù)據(jù)庫(kù)數(shù)據(jù)讀取快,對(duì)網(wǎng)站性能影響較小膝藕,且redis擁有zset這種可自動(dòng)排名的數(shù)據(jù)結(jié)構(gòu)式廷,也可對(duì)過(guò)期時(shí)間進(jìn)行控制以更簡(jiǎn)單的對(duì)數(shù)據(jù)進(jìn)行自動(dòng)的清理,所以選擇redis芭挽,完全使用mysql等數(shù)據(jù)庫(kù)也可完成此功能滑废。
Z-Set
/* 根據(jù)用戶行為進(jìn)行熱度投票
* 使用hacker nwes 算法配合redis
* (P-1) / (T+2)^G
* p 投票數(shù)
* t 發(fā)布日期
* */
public boolean addSetData(JSONObject param){
JSONObject result = new JSONObject();
String key = param.getString("key");
String name = param.getString("name");
String behavior = param.getString("behavior");
//獲取用戶行為判斷票數(shù)
switch (behavior){
case "click" :
redisTemplate.opsForValue().increment(name);
break;
case "list" :
redisTemplate.opsForValue().increment(name,2);
break;
case "collect" :
redisTemplate.opsForValue().increment(name,10);
break;
}
//進(jìn)行票數(shù)計(jì)算
int P = Integer.parseInt(redisTemplate.opsForValue().get(name).toString()) ;
LocalDate now = LocalDate.now();
LocalDate ReleaseDate = LocalDate.parse("2020-01-30");
long timeDiff = now.toEpochDay()-ReleaseDate.toEpochDay();
double score;
score = P / Math.pow(timeDiff,1.8);
Boolean saveBool = redisTemplate.opsForZSet().add(key,name,score);
return saveBool ;
}
在項(xiàng)目上線后,進(jìn)行數(shù)據(jù)記錄袜爪,分析數(shù)據(jù)情況對(duì)G 與T 票值P進(jìn)行優(yōu)化蠕趁,對(duì)incr的值的過(guò)期時(shí)間進(jìn)行調(diào)整,同時(shí)定期對(duì)ZSet內(nèi)的值進(jìn)行修正以及垃圾 數(shù)據(jù)的清刪辛馆。
最后俺陋,redis的穩(wěn)定性相較于mysql低,我們一般會(huì)在一定時(shí)間內(nèi)將redis備份至mysql數(shù)據(jù)庫(kù)昙篙,還可以通過(guò)mysql中的數(shù)據(jù)對(duì)算法進(jìn)行優(yōu)化