如何使用为Messenger设计“从头开始”基础的示例将业务需求转换为特定的数据结构?

我们的数据库不会像
VKontakte或
Badoo那样大,并且分布不大,而是“可以”,但它很好-功能强大,快速且可
安装在单个 PostgreSQL
服务器上 -因此您可以在侧面的某个地方部署服务的单独实例。
因此,我们将不涉及分片,复制和地理分布系统的问题,而将重点放在数据库内部的电路解决方案上。
步骤1:一点业务专长
我们不会以抽象的方式设计消息传递,而是将其嵌入到
公司的社交网络环境中。 就是说,在我们这里的人不是在“发短信”,而是在解决某些业务问题的背景下相互交流。
业务挑战是什么?..让我们看一下开发部门主管Vasily的例子。
- “尼古拉,这个补丁今天需要这个补丁!”
因此,可以在文档的上下文中进行通信。 - “ Kolya,晚上去DotA吗?”
也就是说,即使有一对对话者,也可以同时针对不同主题进行交流 。 - “彼得·尼古拉(Peter Nikolai),查看新服务器的价格附件。”
因此,一封邮件可以有多个收件人 。 在这种情况下,邮件可能包含附件 。 - “ Semyon,你看起来也一样。”
并且应该可以邀请新成员加入现有信件。
让我们关注一下“明显的”需求清单。
如果不了解任务的适用细节及其所设置的约束,那么实际上不可能设计出有效的数据库方案来解决该问题。
步骤2:最小逻辑
到目前为止,一切都变得非常类似于电子邮件通信-一种传统的业务工具。 是的,从算法上讲,许多业务任务彼此相似,因此解决它们的工具在结构上将相似。
让我们修复由此产生的实体关系逻辑图。 为了简化对模型的理解,我们将使用最原始的选项来显示
ER模型,而不会带来UML或IDEF表示法的复杂性:

在我们的示例中,文件的人员,文档和二进制“正文”是“外部”实体,它们独立存在而无需我们提供服务。 因此,我们将来会简单地将它们视为UUID在“某处”的某些链接。
尽可能简单地绘制图表 -您将向他们展示的大多数都不是阅读UML / IDEF的专家。 但是-一定要画画。
步骤3:绘制表格结构
关于表和字段名称字段和表的“俄罗斯”名称可以区别对待,但这只是一个问题。 由于
我们在“ Tensor”中没有外国开发人员,并且PostgreSQL允许我们甚至使用象形文字来给出名称,如果它们
用引号引起来 ,则我们希望明确,明确地命名对象,以免产生误解。
由于许多人一次写消息,所以其中一些人可以
脱机执行此操作,最简单的选择是不仅将
UUID用作外部实体的
标识符 ,而且还使用我们服务内部的所有对象的
标识符 。 此外,它们甚至可以在客户端生成-这将帮助我们支持在数据库短期内无法访问的情况下发送消息,并且发生冲突的可能性非常小。
数据库中表的粗略结构如下所示:
表:RUCREATE TABLE ""( "" uuid PRIMARY KEY , "" uuid , "" text ); CREATE TABLE ""( "" uuid PRIMARY KEY , "" uuid , "" uuid , "" timestamp , "" text ); CREATE TABLE ""( "" uuid , "" uuid , PRIMARY KEY("", "") ); CREATE TABLE ""( "" uuid PRIMARY KEY , "" uuid , "BLOB" uuid , "" text );
表格:EN CREATE TABLE theme( theme uuid PRIMARY KEY , document uuid , title text ); CREATE TABLE message( message uuid PRIMARY KEY , theme uuid , author uuid , dt timestamp , body text ); CREATE TABLE message_addressee( message uuid , person uuid , PRIMARY KEY(message, person) ); CREATE TABLE message_file( file uuid PRIMARY KEY , message uuid , content uuid , filename text );
描述格式的最简单方法是开始“松开” 未引用任何人本身的表中的链接图。
步骤4:找出明显需求
就是这样,我们设计了一个基础,您可以在其中编写和阅读内容。
让我们将自己置于服务用户的位置-我们想用它做什么?
- 最近的帖子
这是按各种特征按时间顺序排序的“我的”消息的记录。 我是收件人之一,我是作者,他们给我写信的地方,但我没有回答,他们没有回答我的地方... - 通讯员
谁参与了这场漫长的聊天?
我们的结构使我们能够“总体上”解决这两个问题,但是很快-不。 问题在于,作为第一个任务的一部分进行排序,
不可能创建适合每个参与者
的索引 (您将必须检索所有记录),而要解决第二个问题,则需要
检索有关该主题
的所有消息 。
意外的用户任务可能会终止生产力 。
步骤5:合理的非正规化
我们的两个问题都将有助于解决其他表,在这些表中,我们将
复制必要的
部分数据以形成适合于我们的表的索引。

表:RU CREATE TABLE ""( "" uuid , "" smallint , "" timestamp , "" uuid , PRIMARY KEY("", "", "") ); CREATE INDEX ON ""("", "", "" DESC); CREATE TABLE ""( "" uuid , "" uuid , PRIMARY KEY("", "") );
表格:EN CREATE TABLE message_registry( owner uuid , registry smallint , dt timestamp , message uuid , PRIMARY KEY(owner, registry, message) ); CREATE INDEX ON message_registry(owner, registry, dt DESC); CREATE TABLE theme_participant( theme uuid , person uuid , PRIMARY KEY(theme, person) );
在这里,我们应用了两种用于创建辅助表的典型方法:
- 乘法记录
我们将消息的一个源记录一次形成多个源记录,并为不同的所有者(发送者和接收者)将其放入不同类型的寄存器中。 但是现在每个注册表都位于索引上-因为在典型情况下,我们只希望看到第一页。 - 记录唯一性
每次在特定主题内发送消息时,只需检查是否已存在这样的条目就足够了。 如果没有,请将其添加到我们的“词典”中。
在本文的下一部分,我们将讨论数据库结构中各节的
实现 。