不知道大家最近有没有看东京奥运会。昨晚看完,被日本人过着好日子的人气冲昏了头脑。好家伙,这届奥运会我没有看到奥运精神,反日精神刚好把我给点醒了。刚打开金牌榜看了看。好在暂时还是第一。虽然日本有盲人裁判,美国有田径霸主,理性上只能争第三,但感情上还是希望中国队踢狗皮膏药,一拳打自由女神像,并赢得最后的桂冠![]()说到这里,相信大家对排行榜的功能都不陌生了。它用于各种比赛和游戏。毕竟,有人的地方就有竞争。实现这个功能并不难。今天我们就用Redis来实现奥运金牌排行榜这样一个功能,耗时5分钟。如果你有兴趣,你也可以把完整的源代码拿出来玩一玩。稍加修改即可使用,点击金牌排行榜源码白嫖!刚开始分析的时候打算直接用mysql数据库。我遇到了一个问题。每个国家的分数都会改变。我怎样才能得到国家的排名?数据库可以按score按row_num排序,但这种方式需要全表扫描。当参与人数达到10000人时,查询会很慢。redis的排行榜功能完美的满足了这个需求。让我们看看我是怎么做到的。[]()实现1.redissortssets简介SortedSets数据类型就像set和hash的混合体。与集合一样,有序集合是唯一的、不重复的字符串。可以说SortedSet也是Set的一种。SortedSets是通过SkipList(跳转表)和hashTable(散列表)的双端口数据结构实现的,所以每增加一个元素,Redis都会进行一次O(log(N))的操作。所以当我们请求排序的时候,Redis根本不需要做任何工作,它已经排序好了。元素的分数可以随时更新。2.在springboot中使用RedisTemplate本文主要是使用redisTemplate来操作redis。当然也可以使用redis-client,看个人喜好。我在本机开了一个单点redis,配置文件如下server:port:9001spring:redis:database:0url:redis://user:123@127.0.0.1:6379host:127.0.0.1password:123port:6379ssl:falsetimeout:5000Maven依赖导入如下org.springframework.bootspring-boot-starter-parent2.0.4.RELEASEorg.springframework.bootspring-boot-starter-web<依赖项>org.springframework.bootspring-boot-starter-data-redisorg.springframework.bootspring-boot-starter-test三、代码实现1、注入redis,声明key为常量SCORE_RANK@AutowiredprivateStringRedisTemplateredisTemplate;publicstaticfinalStringSCORE_RANK="score_rank";2.添加默认排名数据这里使用for循环创建一个集合,然后使用batch添加10万条数据/***批量添加*/@TestpublicvoidbatchAdd(){Set>元组=newHashSet<>();长启动=System.currentTimeMillis();for(inti=0;i<100000;i++){DefaultTypedTupletuple=newDefaultTypedTuple<>("张三"+i,1D+i);元组.add(元组);}System.out.println("循环时间:"+(System.currentTimeMillis()-开始));Longnum=redisTemplate.opsForZSet().add(SCORE_RANK,元组);System.out.println("批量添加时间:"+(System.currentTimeMillis()-start));系统tem.out.println("受影响的行数:"+num);}//输出循环时间:56批新时间:1015影响行数:1000003获取前10(按照分数倒序)提供两个返回值,一个是带分数的,另一个是没有/***获取排行榜*/@Testpublicvoidlist(){Setrange=redisTemplate.opsForZSet().reverseRange(SCORE_RANK,0,10);System.out.println("获取排行榜:"+JSON.toJSONString(range));设置>rangeWithScores=redisTemplate.opsForZSet().reverseRangeWithScores(SCORE_RANK,0,10);System.out.println("获取排名和分数列表:"+JSON.toJSONString(rangeWithScores));}//输出获得的排名列表:["张三99999","张三99998","张三99997","Zhang399996","Zhang399995","Zhang399994","Zhang399993","Zhang399992","Zhang399991","Zhang399990","Zhang399989"]得到的排名和得分列表:[{"score":100000.0,"value":"张三99999"},{"score":99999.0,"value":"张三99998"},{"score":99998.0,"value":"张三99997"},{"score":99997.0,"value":"张三99996"},{"score":99996.0,"value":"张三99995"},{"score":99995.0,"value":"张三99994"},{"score":99994.0,"value":"张三99993"},{"score":99993.0,"value":"张三99992"},{"score":99992.0,"value":"张三99991"},{"score":99991.0,"value":"张三99990"},{"score":99990.0,"value":"张三99989"}]4.添加华夏的分数,把“华夏”加入排行榜,插入的时候redis会做,取出的时候就可以了直接取出来,不用做排序操作/***单个新增*/@Testpublicvoidadd(){redisTemplate.opsForZSet().add(SCORE_RANK,"华夏",9999);}5.获取华夏的排名/***获取单项排名*/@Testpublicvoidfind(){longrankNum=redisTemplate.opsForZSet().reverseRank(SCORE_RANK,"华夏");System.out.println("华夏个人排名:"+rankNum);Doublescore=redisTemplate.opsForZSet().score(SCORE_RANK,"Huaxia");System.out.println("Huaxia'sscore:"+score);}//输出Huaxia的个人排名:1Huaxia的金牌数:99996.统计分数区间的国家数redis也提供了统计分数区间的方法,如下/***统计两个分数之间的国家*/@Testpublicvoidcount(){longcount=redisTemplate.opsForZSet().count(SCORE_RANK,8001,9999);System.out.println("8001-9999之间的国家统计:"+count);}//输出8001-9999之间的国家统计:9997。获取集合Cardinality(数量大小)/***get取整个集合的基数(数量)*/@TestpublicvoidzCard(){LongaLong=redisTemplate.opsForZSet().zCard(SCORE_RANK);System.out.println("集合的基数为:"+aLong);}//输出集的基数为:1000018。使用加法操作分数这种方法是直接对原始分数使用加法;如果没有这个元素,就会被创建,分数初始为0。使用加法/***使用加法操作分数*/@TestpublicvoidincrementScore(){doublescore=redisTemplate.opsForZSet().incrementScore(SCORE_RANK,"华夏",1000);System.out.println("华夏评分+1000后:"+score);}//输出华夏评分+1000后:9899.04.总结以上测试类,我们用到了redis的哪些功能?在上面的例子中,我们使用了添加单个、批量添加、获取前十名、获取单个人的排名等操作,但是redisTemplate还提供了更多的方法。添加或更新有三种方式,一种是单一的,一种是批处理的,它使用分数加法(如果不存在则从0开始)。//单个添加或更新Booleanadd(Kkey,Vvalue,doublescore);//批量添加或更新Longadd(Kkey,Set>tuples);//使用加法运算scoreDoubleincrementScore(K键,V值,双delta);delete删除提供三种方式:按key/value删除,按排名范围删除,按分数范围删除。//删除Longremove(Kkey,Object...values)bykey/value;//删除LongremoveRange(Kkey,longstart,longend)byrankingrange;//删除LongremoveRangeByScore(Kkeybyscorerange),双最小值,双最大值);检查1.列表查询:分为正序和逆序两类。下面的列表只是正序排列,倒序排列只需要在方法前加上reverse//通过排序范围获取列表值设置Setrange(Kkey,longstart,longend);//通过rankingrangeListvalueandscoresetSet>rangeWithScores(Kkey,longstart,longend);//获取listvaluesetSetrangeByScore(Kkey,doublemin,doublemax);//通过分数区间获取列表值和分数集合Set>rangeByScoreWithScores(Kkey,doublemin,doublemax);//删除Range对象,然后获取集合排名SetrangeByLex(Kkey,Rangerange);//通过Range对象删除和选择,然后获取限制数的集合排名SetrangeByLex(Kkey,Rangerange,Limitlimit);2.单人查询可以获得单人排名,通过key/valueFraction获取。下面只列出正序,反序只需要在方法前加上reverse//获取国家排名Longrank(Kkey,Objecto);//获取国家得分Doublescore(Kkey,Objecto);统计统计得分区间内的人数,统计集合的基数。//统计得分范围内的人数Longcount(Kkey,doublemin,doublemax);//统计集合baseLongzCard(Kkey);结束语以上是redis中排行榜功能的一些使用实例,以及redis的操作方法。Redis不仅可以作为缓存使用,还可以作为数据库使用,提供了很多我们都可以很好利用的功能。这里我使用redis来实现2020年东京奥运会金牌排名的显示。无论是方便批量更新还是获取个人排名,都非常高效,减轻了数据库操作的压力,取得了不错的效果。.看到这里了,点个赞吧,大家,源码在这里,点击金牌排行榜源码就可以玩了![]()