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

如何使用从头开始设计信使数据库的示例将业务需求转换为特定的数据结构。

Messenger 数据库(第 1 部分):设计基础框架
我们的基地不会那么大和分散, 喜欢 VKontakte или Badoo上,但“原来如此”,但它很好——功能齐全、速度快、 适合一台服务器 PostgreSQL - 例如,您可以在侧面的某个地方部署该服务的单独实例。

因此,我们不会涉及分片、复制和地理分布式系统的问题,而是关注数据库内部的电路解决方案。

第 1 步:一些业务细节

我们不会抽象地设计消息传递,而是将其集成到环境中 企业社交网络。 也就是说,我们的员工不只是“通信”,而是在解决某些业务问题的背景下相互沟通。

企业的任务是什么?...让我们看一下开发部主管瓦西里的例子。

  • “尼古拉,为了完成这项任务,我们今天需要一个补丁!”
    这意味着通信可以在某些背景下进行 文件.
  • “Kolya,你今晚要去Dota吗?”
    也就是说,即使是一对对话者也可以同时进行交流 关于各种主题.
  • “彼得,尼古拉,在附件中查找新服务器的价目表。”
    所以,一条消息可以有 几个收件人。 在这种情况下,消息可能包含 附加的文件.
  • “谢苗,你也看看。”
    并且应该有机会进行现有的通信 邀请新成员.

现在让我们详细讨论一下这个“明显”需求列表。

在不了解问题的应用细节及其限制的情况下,设计 有效 数据库模式来解决它几乎是不可能的。

第 2 步:最小逻辑电路

到目前为止,一切都与电子邮件通信(一种传统的商业工具)非常相似。 是的,“从算法上来说”,许多业务问题彼此相似,因此解决这些问题的工具在结构上也相似。

让我们修复已经获得的实体关系逻辑图。 为了让我们的模型更容易理解,我们将使用最原始的显示选项 急诊室模型 没有 UML 或 IDEF 符号的复杂性:

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

在我们的示例中,文件的人、文档和二进制“主体”是独立存在的“外部”实体,无需我们的服务。 因此,我们将来会简单地将它们视为 UUID“某处”的一些链接。

图表尽可能简单 - 您将向其展示的大多数人都不是阅读 UML/IDEF 的专家。 但一定要画好。

第 3 步:绘制表结构草图

关于表名和字段名字段和表的“俄语”名称可以不同地对待,但这是一个品味问题。 因为 在张量这里 没有外国开发者,并且 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
);

描述格式时最简单的事情就是开始“展开”连接图 来自未引用的表 自己对任何人都没有。

第四步:找出非显而易见的需求

就是这样,我们设计了一个数据库,您可以在其中完美地编写和 不知何故 阅读。

让我们站在服务用户的立场上思考——我们想用它做什么?

  • Последниесообщения
    按时间顺序排列 基于各种标准的“我的”消息的注册表。 我是收件人之一,我是作者,他们写信给我但我没有回复,他们没有回复我,......
  • 通信参加者
    谁参与了这场漫长的聊天?

我们的结构使我们能够“总体上”解决这两个问题,但速度不会很快。 问题是在第一个任务中进行排序 无法创建索引,适合每个参与者(并且您必须提取所有记录),并解决您需要的第二个 提取所有消息 关于这个话题。

非预期的用户任务可能会加粗 交叉生产力.

第 5 步:智能反规范化

我们的两个问题都将通过附加表格来解决,我们将在其中 重复部分数据,有必要在它们上形成适合我们任务的索引。
Messenger 数据库(第 1 部分):设计基础框架

表 : 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)
);

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

  • 乘以记录
    使用一个初始消息记录,我们在不同类型的寄存器中为不同的所有者(发送者和接收者)创建多个后续记录。 但现在每个寄存器都落在索引上 - 毕竟,在典型情况下,我们只想看到第一页。
  • 独特记录
    每次在特定主题内发送消息时,检查此类条目是否已存在就足够了。 如果没有,请将其添加到我们的“词典”中。

在文章的下一部分我们将讨论 分区的实现 进入我们数据库的结构。

来源: habr.com

添加评论