序言

作为一个游戏服务端研发人员,从业10余年,被问及最多的话题是什么?

1,你们怎么处理高并发,
2,你们的吞吐量是多少?
3,你们数据怎么落地,服务器有状态还是无状态。
4,xxxxxxxxxxx

 做如此类的问题,我相信这几个典型在被同行,领导,运营方,提出和问到最多的问题了。

 今天我们重点是讲解数据落地方案。比如吞吐量啊,高并发啊在前面的文章也提到过,有兴趣的小伙伴可以自行查看哦

如果有什么问题就提出来,

论《数据落地》的方案总结

言归正传

在此我先描述一下,游戏服务器的有状态和无状态区别,这是本人的描述好理解或许和你们不太一样别太介意就行;

我所说的无状态是指类似http服务器一样,没有数据缓存,所有的数据操作流程是

-> read db -> use -> save db;

有状态是指数据缓存在程序内部变量,第一次需要的时候发现缓冲池中没有加载到

-> memory cache -> read cache -> use -> save db(异步定时落地) -> 长时间未使用 memory delect;

 本人在这么多年的游戏服务端研发中,都是做的有状态服务,

其实不管是有状态还是无状态都会牵涉一个问题,那就是数据落地;

一般来讲我们的数据落地都分为,同步落地和异步落地两个大类,

同时还有两个分支方案,就是全量落地和增量落地;

 也就是说分为:

同步全量落地,同步增量落地,

异步全量落地,异步增量落地,

具体方案其实都是根据你的业务需求来,如果要保证万无一失,那么肯定是同步落地最为保险,比如TB,JD订单系统,但是带来的效果就是响应慢,

我们知道不管是秒杀还是双十一的血拼抢购,你是不是总感觉抢不到?或者提交订单慢的要死?《当然这不在本次讨论的范围》

我们今天讲解的是在游戏内如何做到数据落地;

我们先来建立一个实体模型类

 1 package com.ty.backdata;
 2 
 3 import java.io.Serializable;
 4 
 5 /**
 6  * @program: com.ty.minigame
 7  * @description: 数据测试项
 8  * @author: Troy.Chen(失足程序员 , 15388152619)
 9  * @create: 2020-08-27 09:04
10  **/
11 public class DataModel implements Serializable {
12 
13     private static final long serialVersionUID = 1L;
14 
15     private long id;
16     private String name;
17     private int level;
18     private long exp;
19 
20     public long getId() {
21         return id;
22     }
23 
24     public void setId(long id) {
25         this.id = id;
26     }
27 
28     public String getName() {
29         return name;
30     }
31 
32     public void setName(String name) {
33         this.name = name;
34     }
35 
36     public int getLevel() {
37         return level;
38     }
39 
40     public void setLevel(int level) {
41         this.level = level;
42     }
43 
44     public long getExp() {
45         return exp;
46     }
47 
48     public void setExp(long exp) {
49         this.exp = exp;
50     }
51 
52     @Override
53     public String toString() {
54         return "DataModel{" +
55                 "id=" + id +
56                 ", name='" + name + '\'' +
57                 ", level=" + level +
58                 ", exp=" + exp +
59                 '}';
60     }
61 }

通常情况下我们怎么做数据落地

 通常情况下的同步全量更新

论《数据落地》的方案总结

这就是说,每一次操作都需要把数据完全写入到数据库,不管属性是否有变化;

这样一来全量更新就有一个性能问题,如果我的模型有很多属性(这里排除设计问题就是有很多属性),而且某些属性内容特别多,

然后这时候我们只是修改了其中一个不重要的数据,比方说

论《数据落地》的方案总结

 玩家通过打怪获得一点经验值,修改了经验值属性之后,需要save data;

这里只能全量更新;这样实际上浪费了很多 io 性能,因为数据根本没变化但是依然 save to db;

那么我们在这个时候我们是否就应该考虑,如何抛弃掉没有变化的属性值呢?

这里我们就需要考虑如何做到增量更新方案;

首先我们在考虑一点,增量更新就得有数据标识状态,

可能我们首先考虑到的第一方案是这样的

我们修改一下datamodel类

首先我们新增一个Map 属性对象来存储有变化的值

 论《数据落地》的方案总结

 接下来是重点了,我们来修改属性的set方法

改造后的模型类就是这样的,

 1 package com.ty.backdata;
 2 
 3 import com.alibaba.fastjson.annotation.JSONField;
 4 
 5 import java.io.Serializable;
 6 import java.util.HashMap;
 7 import java.util.Map;
 8 
 9 /**
10  * @program: com.ty.minigame
11  * @description: 数据测试项
12  * @author: Troy.Chen(失足程序员 , 15388152619)
13  * @create: 2020-08-27 09:04
14  **/
15 public class DataModel implements Serializable {
16 
17     private static final long serialVersionUID = 1L;
18 
19     /*存储有变化的属性 由于这个字段属性是不用落地到数据库的 需要加入过滤标识*/
20     @JSONField(serialize = false, deserialize = false)
21     private transient Map<String, Object> updateFieldMap = new HashMap<>();
22 
23     /**
24      * 存储有变化的属性
25      *
26      * @return
27      */
28     public Map<String, Object> getUpdateFieldMap() {
29         return updateFieldMap;
30     }
31 
32     private long id;
33     private String name;
34     private int level;
35     private long exp;
36 
37     public long getId() {
38         return id;
39     }
40 
41     public void setId(long id) {
42         this.id = id;
43         /*我们考虑数据库的属性映射就用属性名字做为映射名*/
44         this.updateFieldMap.put("id", id);
45     }
46 
47     public String getName() {
48         return name;
49     }
50 
51     public void setName(String name) {
52         this.name = name;
53         /*我们考虑数据库的属性映射就用属性名字做为映射名*/
54         this.updateFieldMap.put("name", name);
55     }
56 
57     public int getLevel() {
58         return level;
59     }
60 
61     public void setLevel(int level) {
62         this.level = level;
63         /*我们考虑数据库的属性映射就用属性名字做为映射名*/
64         this.updateFieldMap.put("level", level);
65     }
66 
67     public long getExp() {
68         return exp;
69     }
70 
71     public void setExp(long exp) {
72         this.exp = exp;
73         /*我们考虑数据库的属性映射就用属性名字做为映射名*/
74         this.updateFieldMap.put("exp", exp);
75     }
76 
77     @Override
78     public String toString() {
79         return "DataModel{" +
80                 "id=" + id +
81                 ", name='" + name + '\'' +
82                 ", level=" + level +
83                 ", exp=" + exp +
84                 '}';
85     }
86 }
View Code

相关文章: