Messenger数据库(第1部分):我们设计基础框架

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



我们的数据库不会像VKontakteBadoo那样大,并且分布不大,而是“可以”,但它很好-功能强大,快速且可安装在单个 PostgreSQL 服务器上 -因此您可以在侧面的某个地方部署服务的单独实例。

因此,我们将不涉及分片,复制和地理分布系统的问题,而将重点放在数据库内部的电路解决方案上。

步骤1:一点业务专长


我们不会以抽象的方式设计消息传递,而是将其嵌入到公司的社交网络环境中。 就是说,在我们这里的人不是在“发短信”,而是在解决某些业务问题的背景下相互交流。

业务挑战是什么?..让我们看一下开发部门主管Vasily的例子。
  • “尼古拉,这个补丁今天需要这个补丁!”
    因此,可以在文档的上下文中进行通信。
  • “ Kolya,晚上去DotA吗?”
    也就是说,即使有一对对话者,也可以同时针对不同主题进行交流
  • “彼得·尼古拉(Peter Nikolai),查看新服务器的价格附件。”
    因此,一封邮件可以有多个收件人 。 在这种情况下,邮件可能包含附件
  • “ Semyon,你看起来也一样。”
    并且应该可以邀请新成员加入现有信件。

让我们关注一下“明显的”需求清单。
如果不了解任务的适用细节及其所设置的约束,那么实际上不可能设计出有效的数据库方案来解决该问题。

步骤2:最小逻辑


到目前为止,一切都变得非常类似于电子邮件通信-一种传统的业务工具。 是的,从算法上讲,许多业务任务彼此相似,因此解决它们的工具在结构上将相似。

让我们修复由此产生的实体关系逻辑图。 为了简化对模型的理解,我们将使用最原始的选项来显示ER模型,而不会带来UML或IDEF表示法的复杂性:



在我们的示例中,文件的人员,文档和二进制“正文”是“外部”实体,它们独立存在而无需我们提供服务。 因此,我们将来会简单地将它们视为UUID在“某处”的某些链接。
尽可能简单地绘制图表 -您将向他们展示的大多数都不是阅读UML / IDEF的专家。 但是-一定要画画。

步骤3:绘制表格结构


关于表和字段名称
字段和表的“俄罗斯”名称可以区别对待,但这只是一个问题。 由于我们在“ Tensor”中没有外国开发人员,并且PostgreSQL允许我们甚至使用象形文字来给出名称,如果它们用引号引起来 ,则我们希望明确,明确地命名对象,以免产生误解。

由于许多人一次写消息,所以其中一些人可以脱机执行此操作,最简单的选择是不仅将UUID用作外部实体的标识符 ,而且还使用我们服务内部的所有对象的标识符 。 此外,它们甚至可以在客户端生成-这将帮助我们支持在数据库短期内无法访问的情况下发送消息,并且发生冲突的可能性非常小。

数据库中表的粗略结构如下所示:
表:RU
CREATE 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) ); 

在这里,我们应用了两种用于创建辅助表的典型方法:

  • 乘法记录
    我们将消息的一个源记录一次形成多个源记录,并为不同的所有者(发送者和接收者)将其放入不同类型的寄存器中。 但是现在每个注册表都位于索引上-因为在典型情况下,我们只希望看到第一页。
  • 记录唯一性
    每次在特定主题内发送消息时,只需检查是否已存在这样的条目就足够了。 如果没有,请将其添加到我们的“词典”中。

在本文的下一部分,我们将讨论数据库结构中各节的实现

Source: https://habr.com/ru/post/zh-CN483176/


All Articles