在微服务架构中管理与 MongoDb 的关系

时间:2021-03-17 13:26:51

标签: java mongodb microservices entity-relationship

我使用微服务已经有一段时间了,总是使用关系数据库。我正在查看 MongoDb,但不确定如何处理涉及不同微服务的实体关系。举个例子:

public class Employee implements Serializable {
   private String id;
   ...
}

public class Department implements Serializable {
    private String id;
    private String desc;
    private List<Employee> employees = new ArrayList<>();
    ...
}

这两个实体由两个不同的微服务管理,一对多关系由 Department 实体管理。到目前为止,一切都很好。

使用关系数据库(作为一种可选关系,并且一名员工可能属于多个部门),我会将其映射到 Departments 微服务中,其中一张表包含两个字段:employee_iddepartment_id。当客户端调用 getDepartmentWithEmployees(depId) 时,此微服务将读取表并从 Employees 微服务中获取适当的员工。

但是,在 MongoDb 数据库中,据我所知,当我存储 Department 对象时,它会存储所有关联的 Employee。这不是复制信息吗?有没有办法,也许,MongoDb 不存储有关员工的所有信息,而只存储他们的 id?或者还有其他答案?

我很确定这是一个非常基本的问题,但我对所有这些东西都不熟悉。

提前致谢。

1 个答案:

答案 0 :(得分:3)

<块引用>

但是,在 MongoDB 数据库中,据我所知,当我存储 Department 对象时,它会存储所有关联的员工。这不是复制信息吗?

首先,上面的说法是不正确的。从 MongoDB 的角度来看,作为 BSON 提供的任何内容都按原样存储。如果您为员工提供部门,那么是的,它应该。您可以在创建部门后应用部分更新...(例如使用 $set 运算符)。但是,我认为您的问题的范围比这更广泛。

恕我直言,为数据库中的每个文档/表创建纳米服务并不是一个好方法。特别是当服务只负责基本的 CRUD 操作时。您应该首先定义您的有界上下文、聚合根等……简而言之,在将业务需求映射到域对象之前不要尝试设计表格。我想说的是使用 DDD 原则:)

这些是我目前发现的策略。在设计微服务时,您还应该考虑每种策略的利弊。 (见底部参考。)

关系型数据库映射到 NoSQL 的一般原则

  • 一对一关系
    • 嵌入
    • 使用外键链接
  • 1:M 关系
    • 嵌入
    • 使用外键链接
    • (混合)分桶策略
  • N:M 关系
    • 双向引用
    • 单向引用

1:1 关系

1:1 关系可以通过两种方式映射;

  • 将关系嵌入为文档
  • 链接到单独集合中的文档

表格:

// Employee document
{
   "id": 123,
   "Name":"John Doe"
}

// Address document
{
   "City":"Ankara",
   "Street":"Genclik Street",
   "Nr":10
}

示例:嵌入 (1:1)

  • 优点:可以通过一次读取操作检索地址。

{
  "id": 123,
  "Name":"John Doe",
  "Address": {
    "City":"Ankara",
    "Street":"Genclik Street",
    "Nr":10
  } 
}

示例:使用外键链接 (1:1)

{
   "id": 763541685,  // link this
   "Name":"John Doe"
}

带有文档密钥的地址;

{
   "employee_id": 763541685,
   "City":"Ankara",
   "Street":"Genclik street",
   "Nr":10
}

1:M 关系

初始:

// Department collection
{
  "id": 1,
  "deparment_name": "Software",
  "department_location": "Amsterdam"
}

/// Employee collection
[
    {
      "employee_id": 46515,
      "employee_name": "John Doe"
    },
    {
      "employee_id": 81584,
      "employee_name": "John Wick"
    }
]

示例:嵌入 (1:M)

警告:

  • 员工名单可能很大!
  • 在写入繁重的系统中使用这种方法时要小心。 IO 负载会因索引、复制等内务操作而增加。
  • 员工分页很难!!!
{
  "id": 1,
  "deparment_name": "Software",
  "department_location": "Amsterdam",
  "employess": [
                   {
                     "employee_id": 46515,
                     "employee_name": "John Doe"
                   },
                   {
                     "employee_id": 81584,
                     "employee_name": "John Wick"
                   }
               ]
}

示例:链接 (1:M)

我们可以从员工文档中链接department_id。

  • 优点:更容易分页
  • 缺点: Retrieve all employees that are belong to department X. 此查询将需要大量读取操作!
[
    {
      "employee_id": 46515,
      "employee_name": "John Doe",
      "department_id": 1
    },
    {
      "employee_id": 81584,
      "employee_name": "John Wick",
      "department_id": 1
    }
]

示例:分桶策略(混合 1:M)

我们会将员工分成多个桶,每个桶最多 100 名员工。

{
    "id":1,
    "Page":1,
    "Count":100,
    "Employees":[
        {
            "employee_id": 46515,
            "employee_name": "John Doe"
        },
        {
            "employee_id": 81584,
            "employee_name": "John Wick"
        }
    ]
}

N:M 关系

<块引用>

要选择双向嵌入单向嵌入,用户必须确定最大大小 N 和大小 M。
例如;如果 N 是一本书最多 3 个类别,而 M 是一个类别中最多 5,000,000 本书,您应该选择单向嵌入。
如果 N 最大为 3,M 最大为 5,那么双向嵌入可能会很好地工作。 schema basics

示例:双向引用 (N:M)

在双向嵌入中,我们将在作者文档的 book 字段下包含 Book 外键。

作者收藏

[
    {
       "id":1,
       "Name":"John Doe",
       "Books":[ 1, 2 ]
    },{
       "id":2,
       "Name": "John Wick",
       "Books": [ 2 ]
    }
]

图书收藏:

[
    {
       "id": 1,
       "title": "Brave New World",
       "authors": [ 1 ]
    },{
       "id":2,
       "title": "Dune",
       "authors": [ 1, 2 ]
    }
]

示例:单向引用 (N:M)

示例书籍和类别:这种情况是几本书属于几个类别,但几个类别可以包含许多书籍。

  • 优势:优化读取性能
  • 之所以选择在书籍中嵌入对类别的所有引用,是因为类别中的书籍比书籍中的类别多得多。

类别

[
  {
    "id": 1,
    "category_name": "Brave New World"
  },
  {
    "id": 2,
    "category_name": "Dune"
  }
]

带有 Book 外键的 Categories 文档示例

[
    {
      "id": 1,
      "title": "Brave New World",
      "categories": [ 1, 2 ],
      "authors": [ 1 ] 
    },
    {
      "id": 2,
      "title": "Dune",
      "categories": [ 1],
      "authors": [ 1, 2 ] 
    }
]

参考资料

  1. Case study: An algorithm for mapping the relational databases to mongodb
  2. The Little MongoDB Schema Design Book
  3. 6 Rules of Thumb for MongoDB Schema Design