首页 > 其他分享 >游戏设计之-排行榜处理

游戏设计之-排行榜处理

时间:2022-12-21 08:33:29浏览次数:43  
标签:o2 return rankPack RankScore 排行榜 score 设计 public 游戏

前言

我相信大部分人,乃至公司和团队在设计排行榜都考虑的是redis,zadd操作,不需要排序,维护获取,操作都极其简单;

无一例外我也是;

 

 在项目中运营了大量的模板,来处理各个木块的排行榜信息;

统一的会在晚上又一些结算处理;就牵涉一次性拉取,过滤,发放奖励,甚至还有删除操作;

同时为了节约运营陈本,定期还需要对redis里面无效数据进行删除,收缩操作;

 

 

目前redis已经达到6.55g,这只是其中一个平台运营部署;

运营过项目的都知道云redis的费用是非常高昂的;所以就需要定期做一些无用数据的删除操作; 

可是问题来了,随着运营时间的推移,每天早上总能看到如此的异常推送;

就问你揪心不?仔细一查才发现还是数据库相关的操作导致的卡主了;

于是下定决心对排行榜等相关功能进行调整;

削峰

1. 首先就是对数据库的一些删除操作,集中早上5-6点去处理他;这个时候在线玩家最少;处理一些垃圾数据最为合理;基本卡了也不会对玩家的感觉特别强烈反馈;

于是把原来处理的数据移除的代码调整为定时器创建异步任务在凌晨5-6点去处理;

2. 第二步就是为了避免玩家集体拉取数据,并对数据进行操作;考虑重构排行榜代码需求,

排行榜自定义实现

为了模拟排行榜实现的,

首先应该考虑,排行榜数据,既方便查找修改,又方便排序;

那么肯定需要考虑的是

一个map,用于存取和玩家相关积分数据;

一个list,用于排序积分;

首先我们来实现一个积分类

 1 package org.wxd.lang.rank;
 2 
 3 import lombok.Getter;
 4 import lombok.Setter;
 5 import lombok.experimental.Accessors;
 6 import org.wxd.io.ObjectFactory;
 7 import org.wxd.timer.TimeUtil;
 8 
 9 import java.util.Comparator;
10 import java.util.Objects;
11 
12 /**
13  * 排行
14  *
15  * @author: Troy.Chen(無心道, 15388152619)
16  * @version: 2022-12-08 21:27
17  **/
18 @Getter
19 @Setter
20 @Accessors(chain = true)
21 public class RankScore implements Comparable<RankScore> {
22 
23     /** 正序 */
24     public static final Comparator<RankScore> Sort = (o1, o2) -> {
25         if (o1.score != o2.score) {
26             return Double.compare(o1.score, o2.score);
27         }
28 
29         if (o1.scoreTime != o2.scoreTime) {
30             /**时间取值要倒叙*/
31             return Long.compare(o2.scoreTime, o1.scoreTime);
32         }
33 
34         return Long.compare(o1.uid, o2.uid);
35     };
36 
37     /** 倒叙 */
38     public static final Comparator<RankScore> BreSort = (o1, o2) -> {
39         if (o1.score != o2.score) {
40             return Double.compare(o1.score, o2.score);
41         }
42 
43         if (o1.scoreTime != o2.scoreTime) {
44             /**时间取值要倒叙*/
45             return Long.compare(o2.scoreTime, o1.scoreTime);
46         }
47 
48         return Long.compare(o1.uid, o2.uid);
49     };
50 
51     private long uid;
52     private double score;
53     private long scoreTime;
54 
55     public RankScore setScore(double score) {
56         this.score = score;
57         this.scoreTime = TimeUtil.currentTimeMillis();
58         return this;
59     }
60 
61     @Override public int compareTo(RankScore o2) {
62         return Sort.compare(this, o2);
63     }
64 
65     public int scoreIntValue() {
66         return (int) score;
67     }
68 
69     public long scoreLongValue() {
70         return (long) score;
71     }
72 
73     @Override public boolean equals(Object o) {
74         if (this == o) return true;
75         if (o == null || getClass() != o.getClass()) return false;
76         RankScore rankScore = (RankScore) o;
77         return uid == rankScore.uid;
78     }
79 
80     @Override public int hashCode() {
81         return Objects.hash(uid);
82     }
83 
84     @Override public String toString() {
85         return ObjectFactory.stringBuilder(sb -> {
86             sb.append(this.getClass().getSimpleName()).append("{");
87             sb.append("uid=").append(uid);
88             sb.append(", score=").append(score);
89             sb.append('}');
90         });
91     }
92 }
View Code

接下来我实现代码

  1 package test;
  2 
  3 import org.junit.Test;
  4 import org.wxd.lang.RandomUtils;
  5 import org.wxd.lang.rank.RankScore;
  6 import org.wxd.system.MarkTimer;
  7 
  8 import java.util.ArrayList;
  9 import java.util.HashMap;
 10 import java.util.List;
 11 
 12 /**
 13  * @author: Troy.Chen(無心道, 15388152619)
 14  * @version: 2022-12-08 20:48
 15  **/
 16 public class RankPackTest {
 17 
 18     public class RankPack {
 19         HashMap<Long, RankScore> rankMap = new HashMap<>();
 20         List<Long> rankList = new ArrayList<>();
 21 
 22         public void addScore(long uid, double score) {
 23             /*忽律并发问题 可以自行改为 ConcurrentHashMap*/
 24             rankMap.computeIfAbsent(uid, l -> {
 25                 RankScore rankScore = new RankScore().setUid(uid).setScore(score);
 26                 /*能到这里初始化,那么list里面必然也没有数据*/
 27                 rankList.add(uid);
 28                 return rankScore;
 29             });
 30         }
 31 
 32         public void sort() {
 33             /*忽律并发问题*/
 34             rankList.sort((o1, o2) -> {
 35                 RankScore r1 = rankMap.get(o1);
 36                 RankScore r2 = rankMap.get(o2);
 37                 return r1.compareTo(r2);
 38             });
 39         }
 40 
 41         public void breSort() {
 42             /*忽律并发问题*/
 43             rankList.sort((o1, o2) -> {
 44                 RankScore r1 = rankMap.get(o1);
 45                 RankScore r2 = rankMap.get(o2);
 46                 return r2.compareTo(r1);
 47             });
 48         }
 49 
 50     }
 51 
 52     RankPack rankPack = new RankPack();
 53 
 54     @Test
 55     public void test() {
 56         init();
 57         sort();
 58         sort();
 59         sort();
 60         sort2();
 61         sort2();
 62         sort2();
 63     }
 64 
 65     public int randomScore() {
 66         return RandomUtils.random(100, 10000);
 67     }
 68 
 69     public void init() {
 70         MarkTimer build = MarkTimer.build();
 71         int speed = 1;
 72         for (int i = 0; i < 300; i++) {
 73             rankPack.addScore(speed, randomScore());
 74             speed++;
 75         }
 76         rankPack.breSort();
 77         float v = build.execTime();
 78         System.out.println(rankPack.getClass().getSimpleName() + " - 数量:" + rankPack.rankMap.size() + ", 全部排序耗时:" + v);
 79         show();
 80     }
 81 
 82     public void sort() {
 83         MarkTimer build = MarkTimer.build();
 84         rankPack.rankMap.values().forEach(rankScore -> rankScore.setScore(randomScore()));
 85         rankPack.breSort();
 86         float v = build.execTime();
 87         System.out.println(rankPack.getClass().getSimpleName() + " - 数量:" + rankPack.rankMap.size() + ", 全部排序耗时:" + v);
 88         show();
 89     }
 90 
 91     public void sort2() {
 92         MarkTimer build = MarkTimer.build();
 93         RankScore randomItem = (RankScore) RandomUtils.randomItem(rankPack.rankMap.values());
 94         randomItem.setScore(randomScore());
 95         rankPack.breSort();
 96         float v = build.execTime();
 97         show();
 98         int index = rankPack.rankList.indexOf(randomItem.getUid());
 99         System.out.println(v + " - " + randomItem + " - 当前排名:" + (index + 1));
100     }
101 
102     public void show() {
103 //        AtomicInteger atomicInteger = new AtomicInteger();
104 //        for (int i = 0; i < 10; i++) {
105 //            Long uid = rankPack.rankList.get(i);
106 //            RankScore rankScore = rankPack.rankMap.get(uid);
107 //            System.out.println(rankScore.toString() + " - 排名:" + (i + 1));
108 //        }
109     }
110 
111 }

运行一下看看效果

 

 视乎性能还可以,等等,我们是不是忽律一个问题,现在排行榜只有300个对象哦,加到3万试试

 

 很明显看得出来,全排序情况下直接翻了30倍左右;单个项修改后排序直接翻了差不多40倍;

到这里或许有很多已经满足需求了,;

 可是我的需求远不止如此,这很明显时间消耗太久了,不知道有么有更为适合的方案来处理性能;

突然想到一个问题,或许了解树结构的同学知道,树装结构数据,在插入数据的时候就已经排序了,并且根据hash索引,性能非常高;

由此我们想到,

 

 那么我们排序很耗时;我能不能不做这个排序操作,通过索引数据结构来达到目的呢?

改造树装结构

我们把刚才hashmap改为treemap

吧list改为treeset试试效果

 

 测试一下,感觉确实是快了很多呢

 

 我们来打印一下数据看看情况

 

 

哦豁,为什么数据不对;

不是我们预期的效果,

然后研究了treeset数据结构就知道,它是排序的,但是他是add的时候排序的,一旦add就不再变更了;

那我们能不能尝试修改的时候先移除,再修改,然后在add呢?

 

我们来改造一下addScore方法块;

  

 

 排序正常了

 

 可以看出运行结构,整体差距不算特别大;

如果考虑并发性能问题;可以把

TreeMap 换成 ConcurrentSkipListMap
TreeSet 换成 ConcurrentSkipListSet

java自带的跳表结构,添加删除查询,都会非常高效;

最后粘贴一下最新的全部测试代码

  1 package test;
  2 
  3 import org.junit.Test;
  4 import org.wxd.lang.RandomUtils;
  5 import org.wxd.lang.rank.RankScore;
  6 import org.wxd.system.MarkTimer;
  7 
  8 import java.util.concurrent.ConcurrentSkipListMap;
  9 import java.util.concurrent.ConcurrentSkipListSet;
 10 
 11 /**
 12  * @author: Troy.Chen(無心道, 15388152619)
 13  * @version: 2022-12-08 20:48
 14  **/
 15 public class RankPackTest {
 16 
 17     public class RankPack {
 18         ConcurrentSkipListMap<Long, RankScore> rankMap = new ConcurrentSkipListMap<>();
 19         ConcurrentSkipListSet<RankScore> rankList = new ConcurrentSkipListSet<>();
 20 
 21         public void addScore(long uid, double score) {
 22             /*忽律并发问题 可以自行改为 ConcurrentHashMap*/
 23             RankScore score1 = rankMap.computeIfAbsent(uid, l -> {
 24                 RankScore rankScore = new RankScore().setUid(uid);
 25                 return rankScore;
 26             });
 27             rankList.remove(score1);
 28             score1.setScore(score);
 29             rankList.add(score1);
 30         }
 31 
 32 //        public void sort() {
 33 //            /*忽律并发问题*/
 34 //            rankList.sort((o1, o2) -> {
 35 //                RankScore r1 = rankMap.get(o1);
 36 //                RankScore r2 = rankMap.get(o2);
 37 //                return r1.compareTo(r2);
 38 //            });
 39 //        }
 40 //
 41 //        public void breSort() {
 42 //            /*忽律并发问题*/
 43 //            rankList.sort((o1, o2) -> {
 44 //                RankScore r1 = rankMap.get(o1);
 45 //                RankScore r2 = rankMap.get(o2);
 46 //                return r2.compareTo(r1);
 47 //            });
 48 //        }
 49 
 50     }
 51 
 52     RankPack rankPack = new RankPack();
 53 
 54     @Test
 55     public void test() {
 56         sort();
 57         sort();
 58         sort();
 59         sort();
 60         sort2();
 61         sort2();
 62         sort2();
 63         sort2();
 64     }
 65 
 66     public int randomScore() {
 67         return RandomUtils.random(100, 10000);
 68     }
 69 
 70     public void sort() {
 71         MarkTimer build = MarkTimer.build();
 72         int speed = 1;
 73         for (int i = 0; i < 30000; i++) {
 74             rankPack.addScore(speed, randomScore());
 75             speed++;
 76         }
 77 //        rankPack.breSort();
 78         float v = build.execTime();
 79         System.out.println(rankPack.getClass().getSimpleName() + " - 数量:" + rankPack.rankList.size() + ", 全部排序耗时:" + v);
 80         show();
 81     }
 82 
 83     public void sort2() {
 84         MarkTimer build = MarkTimer.build();
 85         RankScore randomItem = (RankScore) RandomUtils.randomItem(rankPack.rankMap.values());
 86         rankPack.addScore(randomItem.getUid(), randomScore());
 87 //        rankPack.breSort();
 88         float v = build.execTime();
 89         show();
 90         int index = 0;
 91         for (RankScore rankScore : rankPack.rankList) {
 92             if (rankScore.getUid() == randomItem.getUid()) break;
 93             index++;
 94         }
 95         System.out.println(v + " - " + randomItem + " - 当前排名:" + (index + 1));
 96     }
 97 
 98     public void show() {
 99         int i = 0;
100         for (RankScore rankScore : rankPack.rankList) {
101             System.out.println(rankScore.toString() + " - 排名:" + (i + 1));
102             i++;
103             if (i >= 10) break;
104         }
105     }
106 
107 }

大家如果有兴趣可以自己测试哦;

 好了这里提供集中思路来处理排行榜相关的数据;以及排名功能;

不知道园子里的朋友们,还有没有更好的思路;

听说大家喜欢看,机会是留给有耐心的人

 

标签:o2,return,rankPack,RankScore,排行榜,score,设计,public,游戏
From: https://www.cnblogs.com/shizuchengxuyuan/p/16994992.html

相关文章

  • 领域驱动设计系列(2)浅析VO、DTO、DO、PO的概念、区别和用处
    PO:persistantobject持久对象最形象的理解就是一个PO就是数据库中的一条记录。好处是可以把一条记录作为一个对象处理,可以方便的转为其它对象。BO:businessobject业务对象主......
  • MOOD|所以真的是“游戏”的问题吗?
       刚刚我妈抢走了我的手机和平板,因为我在平板上玩游戏。   我没想到这些会发生在一个21岁的人身上。不过好像在我和我妈之间,每天都在发生着一些荒诞且幼稚的事......
  • Delphi 经典游戏程序设计40例 的学习 例40 动画检查器
     unitR40;interfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,Buttons,StdCtrls,Clipbrd,ExtCtrls;......
  • 架构设计(八):数据库的水平扩展和垂直扩展
    架构设计(八):数据库的水平扩展和垂直扩展作者:Grey原文地址:博客园:架构设计(八):数据库的水平扩展和垂直扩展CSDN:架构设计(八):数据库的水平扩展和垂直扩展数据库的扩展有两种方......
  • 如何设计一个分布式 ID 发号器?
    大家好,我是树哥。在复杂的分布式系统中,往往需要对大量的数据和消息进行唯一标识,例如:分库分表的ID主键、分布式追踪的请求ID等等。于是,设计「分布式ID发号器」就成为了......
  • 设计模式,mvc,mvp,mvvm
    mvcmodel模型-视图view-控制器controller视图可以直接访问模型,所以视图里面包括模型信息,mvc关注的是模型不变,所以在mvc中,模型不依赖视图,但是view是依赖model的原理:模......
  • UE4学习笔记26——【UI】UI动画和暂停游戏
    P68.UI动画和暂停游戏P68(需要包含第一人称射击模板) 新建一个文件夹(WJJ2119P68),在此文件夹中,右键“用户界面——控件蓝图”,重命名为“Pause”,然后打开此控件蓝图;左......
  • 订单自动确认或取消设计方案
    订单自动确认或取消设计方案 前不见古人,后不见来者。念天地之悠悠,独怆然而涕下。简介系统订单自动确认或取消的设计方案,最常见的一个业务比如N天后自动确认订......
  • 重构 改善既有代码的设计 第2版 电子书 pdf
    作者:[美]MartinFowler出版社:人民邮电出版社出品方:异步图书副标题:改善既有代码的设计原作名:Refactoring:ImprovingtheDesignofExistingCode,SecondEditi......
  • 利用 TCP 完成文件传输的设计
    利用TCP完成文件传输的设计实验目的输入文件路径,利用TCP实现客户文件向服务器的传输,并实现对TCP的基本封装实验原理1.服务器(1)建立socket(2)确定服务器scokaddr_i......