如何设计一个灵活的 MySQL 引用多个表的通知架构?

How do I design a flexible MySQL notification schema that references multiple tables?

我决定在另一个 Whosebug post 的启发下开发一个通知系统,它建议我将通知与通知正文分开。因此,我有以下架构:

notifications
id | user_id | notification_object_id | is_read

notification_objects
id | message | type

我以这种方式设置它,这样我就可以创建一个通知对象并将其发送给多个用户,以防通知对象适用于多个人。类型可以按类型将消息压缩到一个通知中,就像 Facebook 所说的那样,"John Doe, John Smith, and 27 other users commented..." 而不是给你 29 个单独的通知。

(table 和列的命名模式适用于 CakePHP 3 的 'contains')

我需要帮助的是如何绑定将在请求中发挥作用的不同用户或对象。用户可能已经发表评论,所以我可能需要发消息的用户的 user_id 并需要将其存储在某处。

除用户外还有其他情况;有人可能想知道评论的 ID,这样他就可以点击通知并直接转到 comment/81。我有很多用例,我想存储不同类型的 id,这些 id 是其他 tables(用户、评论、文章等)的外键,其中一些可能需要两个外键(比如你想要提及“Chris has commented on your article 'How to beg Whosebug for help'”)并在通知中链接 Chris 的 user_id 和文章的文章 ID。

每种不同类型的通知可以有不同的值,并且 table 它需要从中提取信息。从哪里开始创建此模式比较好?

我将尝试从面向对象的角度回答这个问题。当使用 classes 而非 tables 时,您可能会以 AbstractNotificationObject class 具有基本属性(日期、时间,可能是一条消息)和一些专业化 class,例如 NewCommentNotificationObject 具有附加属性(评论、评论用户)或 NewFriendNotificationObject.深入到数据库模式,然后您可以应用一种典型的 ORM 模式。

具体table继承(只是为了完整性)

使用 concrete table inhericance 可能不是一个好主意,因为它会归结为每个通知 单独的 table输入 (即 comment_notification_objects table 或更多)。这不会很好地扩展,并且您将无法使用外键从 notification table 引用它们。此外,您将无法 select 使用单个查询(没有像 UNION 之类的东西)的一组不同类型的通知。

单一table继承

Single table inheritance 将仅使用 带有类型鉴别器列 的单个 table。这个单一的 table(我们称它为 notification_objects)需要包含一个 type 列来描述记录实际上是什么类型的通知(您实际上已经在您的架构中拥有它)。您还需要用于特殊通知类型可能具有的所有属性的列(例如,user_idcomment_id 列)。

   +--------------+
   | notification |
   +--------------+
   | id           |
+--| object_id    |
|  +--------------+
|
|  +-----------------------+
|  | notification_objects  |
|  +-----------------------+
+->| id PK                 |
   | date                  |
   | type                  |
   | user_id FK            |--...
   | comment_id FK         |--...
   | friend_id FK          |--...
   | [more fields to come] |
   +-----------------------+

优点:

  1. 使用单个查询加载所有通知
  2. 使用外键保持参照完整性

缺点

  1. 缩放不佳(您需要为每种通知类型添加新列)
  2. Table 将被稀疏填充(许多列将为 NULL)

当您有一组不定期更改的中等大小的通知类型时,我会推荐此架构

Classtable继承

Class table inheritance介于两者之间;在这里,您将为每种通知类型创建一个中央 notification_objects table 和单独的 table(例如,带有 id 的附加 comment_notification_object table列(这又是 notification_object table 和 user_idcolumn_id:

的外键
   +--------------+
   | notification |
   +--------------+
   | id           |
+--| object_id    |
|  +--------------+
|
|  +----------------------+      +------------------------------+
|  | notification_objects |      | comment_notification_objects |
|  +----------------------+      +------------------------------+
+->| id PK                |<--+--| id PK/FK                     |
   | date                 |   |  | comment_id FK                |--...
   | type                 |   |  | user_id FK                   |--...
   +----------------------+   |  +------------------------------+
                              |
                              |  +--------------------------------+
                              |  | newfriend_notification_objects |
                              |  +--------------------------------+
                              +--| id PK/FK                       |
                                 | friend_id FK                   |--...
                                 +--------------------------------+

                                 [more TABLES to come...]

Class table 继承还允许您使用单个 select 查询来查询所有通知。根据您需要的数据量,您需要将 JOIN 添加到相应的专用 tables.

优点:

  1. 在不修改现有结构的情况下扩展 well/Add 新类型
  2. 使用外键保持参照完整性

缺点

  1. 当您需要的字段超过最少的字段集时,您需要加入专门的 tables(可能会影响性能)

当您有很多不同的通知类型或经常扩展您的对象模型并且需要定期 change/add 新通知类型时,我会推荐此模式。

在关系框之外

你的问题明确询问了关系模式,所以我专注于此。跳出这个框框思考时,替代解决方案可能包括:

  • 无模式 NoSQL 数据库
  • 遵循publish/subscribe架构的消息系统