Zer0e's Blog

游戏邮件系统设计

字数统计: 2.2k阅读时长: 7 min
2024/10/31 Share

前言

昨天睡前突发奇想,想着要不设计什么系统来思考思考,正好前段时间看到一篇关于站内信的设计文章,里面也有不少可以琢磨的。但是又没想法要设计啥系统,便在各个游戏里琢磨,想着o神这个邮件系统应该也有不少思考,因此决定设计一个游戏邮件系统。注意这个游戏的邮件系统只针对管理员向用户发送的邮件系统,用户间不存在邮件交互。

在这里也先叠个甲,本人非游戏从业中,只是从一个后端开发,架构师的角度去思考这个问题,结合业务的话我也只能进行一定猜测,本文的所编写的任何东西不构成生产建议或实践!欢迎友好交流的同学一起讨论和学习。

正文

需求明确

首先我们需要明确设计的目标,我列了以下几个点,当然还有一些没有罗列,我们就按照简单的需求来设计。

  • 管理员(运营)可以向用户发送邮件。
  • 收到的邮件主体包括标题,接收时间,发送人,正文,附件。
  • 用户间不可以相互发送邮件。
  • 运营可以根据用户条件并且指定时间发送邮件。
  • 用户接收群发邮件时接收时间可以为上线时间或者运营确定的时间。
  • 邮件可以有过期时间,到达过期时间后,邮件将被删除。

需求还是比较简单并且清晰的。

其中值得一提的是,最后一个点我认为是出于性能的考虑,可以防止大量的数据同时写入。也可以排除掉不活跃的用户。

其他的一些需求,比如收藏邮件,其实和主体并没有什么冲突,只需要加字段即可实现。

接下来就讲讲具体的设计思路。

设计思路

说是设计,其实这个系统只要把字段逻辑理清楚,尤其是数据如何存储,比如分表如何分,字段设计完成后,其他如上层开发就简单一些。

字段设计

我一直觉得实体类设计是比较简单的一环,尤其是当业务清楚的时候。

先给出第一版设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
@Data
public class Attachment {

private Long itemId;

private Integer num;
}



@Data
public class SystemEmail {

private Long id;

private String sender;

/**
* 标题
*/
private String subject;

/**
* 是否为确定的接收时间
* true 用户邮件的接收时间为系统邮件指定的时间
* false 用户邮件的接收时间为拉取系统邮件的时间
*/
private Boolean timeExacted;

/**
* 接收时间
*/
private LocalDateTime receivedTime;
/**
* 主体
*/
private String content;

/**
* 附件列表
*/
private List<Attachment> attachments;

/**
* 邮件过期时间
*/
private LocalDateTime expiredTime;

/**
* 用户id列表
* 当用户id在列表中时,可以拉取
* 否则需要检测条件是否满足
*/
private List<Long> uidList;

/**
* 邮件发送条件
*/
private List<String> conditions;


private LocalDateTime createTime;

private Long createUserId;

/**
* 是否删除
*/
private Integer deleted;
}


@Data
public class UserEmail {
/**
* The Id.
*/
private Long id;

/**
* 用户id
*/
private Long uid;
/**
* 发送人
*/
private String sender;
/**
* 标题
*/
private String subject;
/**
* 接收时间
*/
private LocalDateTime receivedTime;
/**
* 主体
*/
private String content;

/**
* 附件列表
*/
private List<Attachment> attachments;

/**
* 邮件过期时间
*/
private LocalDateTime expiredTime;

/**
* 系统邮件来源
*/
private Long systemEmailId;

/**
* 记录创建时间
*/
private LocalDateTime createTime;

/**
* 是否删除
*/
private Integer deleted;
}

这里先谈谈我的设计思路。

首先所有的邮件是由系统去创建的,并且在数据库存储上,不可能是系统发送邮件后就对所有用户写入邮件数据,所以必然应该有一张或者多张表存储系统邮件信息,然后再根据一个触发条件(一般是登录)去将系统邮件的内容写入到个人邮件数据中。因此用户邮件UserEmail和系统邮件SystemEmail应该有一个关联id。

此外系统邮件应该有个字段自由控制接收时间,这里我命名的是timeExacted,意思是时间是否确定。

那其实数据结构字段就差不多了。

数据表设计

既然实体类已经设计完成,那其实表的设计就按照实体类即可,但是其实还需要考虑挺多点。

首先,老生常谈的问题,分库分表。当然,对于这个系统而言,我们可以只考虑分表的情况,底层的性能我们可以先不考虑,先从业务上进行分表的设计。

分库分表

为什么要分表?防止单表的数据量达到一定量级总而造成性能问题。首先用户邮件的设计上,我们采用uid字段去进行区分,对于单表来说只需要简单的sql查询即可完成个人邮件检索。

1
select * from t_user_email where uid = 1000 order by receviedTime desc;

但是考虑到用户的量级上,假设一个服务器上有1亿个活跃账号,每个账号有100封邮件,那么此时用户邮件表中就有100亿条数据,这个量级是十分恐怖的。尽管有邮件过期机制,但是长时间运营的游戏必然会有很多永久邮件存储在用户上。

因此必须要分表。那么怎么分呢?以阿里的规范来说,单表超过500w条记录或者2GB时,才进行分库分表,我们先以这个标准去进行分表,假设单表500w,100亿条数据我让AI回答了一下,它说需要2000个表才可以存的下。那我们就以两倍进行预估,那么就需要分4000个表。

假设mysql能抗住这么大的一个并发,那也得分4000个表去存储,一般情况下是根据uid进行hash取模,然后落到不同的表上。

但实际情况下,单库分4000个表是不现实的,因为单库根本扛不住这么多的流量,这就有点脱裤子放屁了。所以应该是先分库然后再分表,这里的4000最接近4096,因此可以先进行模64分库,然后用相同字段div 64后模64进行分表,这样会稍微合理一些。

但随着时代的变迁,分库分表会将成为历史。据我所知,马哈鱼的游戏基本上都是用的阿里的PolarDB分布式数据库,无需去关心分库分表。

系统设计

这个主要就是业务上的一些实现了,如果说不再考虑分库分表的话,其实业务系统上的就比较简单了。向用户发送邮件的流程应该如下

  1. 运营向系统邮件表写入邮件内容。写入完成后,邮件系统发送通知到主进程。
  2. 一些内部流转邮件,例如签到奖励,可以直接写入用户邮件表中。
  3. 用户此时正在游戏中,主系统收到用户邮件通知,或者登陆进游戏后触发邮件拉取检查。
  4. 此时邮件系统将系统邮件写入用户邮件表。用户收到邮件到达通知。
  5. 用户收到邮件。
  6. 阅读和领取操作。

其中有个比较关键的点就是邮件拉取。比如拉取的时间范围应该是什么?如何去判断是否已经拉取过?

首先拉取范围,应该是用户上次登录时间到此时的所有邮件。当然邮件有过期时间,可以加上条件过滤掉,其实不会有那么多。

是否拉取过那就很简单了,判断systemEmailId是否有就好了。

总结

写到这里其实逻辑差不多清楚了,但是我没有选择去实现这个。业务上的逻辑其实还是得深入研究。本文主要还是从表的设计入手去分析如何实现。顺带讲了讲分库分表的事,虽然我认为它一定会退出历史舞台。

当然也还有很多东西没讲到,比如过期删除的逻辑,条件判断逻辑等等。

其实有些东西只有当你确确实实要去用代码去实现时才能发现问题所在。

说实话写到后面其实自己差不多已经倦了,所以打算结束这篇思考。花了两三天陆陆续续完成这篇文章。总的来说还是思考了不少东西,但也让我意识到自己可能对于代码实现不是那么有激情了,也许是年纪大了。

后面应该就去新公司那边了,看看能不能学习到一些新的东西。

CATALOG
  1. 1. 前言
  2. 2. 正文
    1. 2.1. 需求明确
    2. 2.2. 设计思路
      1. 2.2.1. 字段设计
      2. 2.2.2. 数据表设计
        1. 2.2.2.1. 分库分表
      3. 2.2.3. 系统设计
  3. 3. 总结