Lược đồ linh hoạt của MongoDB cho phép nhiều mẫu khi mô hình hóa mối quan hệ giữa các thực thể. Ngoài ra, đối với nhiều trường hợp sử dụng, mô hình dữ liệu không chuẩn hóa [lưu trữ dữ liệu liên quan ngay trong một tài liệu] có thể là lựa chọn tốt nhất vì tất cả thông tin được lưu giữ ở một nơi, do đó ứng dụng yêu cầu ít truy vấn hơn để tìm nạp tất cả dữ liệu. Tuy nhiên, cách tiếp cận này cũng có những nhược điểm của nó, chẳng hạn như khả năng trùng lặp dữ liệu, tài liệu lớn hơn và kích thước tài liệu tối đa
Nói chung, MongoDB khuyên bạn nên sử dụng các mô hình dữ liệu được chuẩn hóa khi các ưu điểm của việc nhúng bị bỏ qua do tác động của sự trùng lặp. Trong bài đăng trên blog này, chúng tôi xem xét các khả năng khác nhau của việc liên kết các tài liệu với các tham chiếu thủ công và DBRef khi có nhu cầu làm việc với các mối quan hệ
DBRef là phần tử gốc của MongoDB để thể hiện các tham chiếu đến các tài liệu khác có định dạng rõ ràng
class Book {
// ...
private Publisher publisher;
}
5 chứa thông tin về cơ sở dữ liệu đích, bộ sưu tập và giá trị id của phần tử tham chiếu, phù hợp nhất để liên kết đến các tài liệu được phân phối trên các bộ sưu tập khác nhauMặt khác, các tham chiếu thủ công có cấu trúc đơn giản hơn [bằng cách chỉ lưu trữ id của tài liệu được tham chiếu], nhưng do đó, không linh hoạt khi nói đến các tham chiếu bộ sưu tập hỗn hợp
Sau khi thiết lập thuật ngữ, hãy giới thiệu các loại miền nổi tiếng, chẳng hạn như
class Book {
// ...
private Publisher publisher;
}
6 và class Book {
// ...
private Publisher publisher;
}
7, và mối quan hệ rõ ràng của chúng với nhauclass Book {
private String isbn13;
private String title;
private int pages;
}
class Publisher {
private String name;
private String arconym;
private int foundationYear;
}
Nhúng
class Book {
// ...
private Publisher publisher;
}
7 vào trong mỗi và mọi class Book {
// ...
private Publisher publisher;
}
6 không phải là một lựa chọn hấp dẫn, vì nó sẽ dẫn đến sao chép dữ liệu và tạo gánh nặng không cần thiết cho việc lưu trữ và khả năng bảo trìclass Book {
// ...
private Publisher publisher;
}
Mặc dù định dạng lưu trữ này cho phép cập nhật nguyên tử và mang lại sự linh hoạt nhất khi truy vấn các thuộc tính cụ thể, nhưng việc lặp lại thông tin
class Book {
// ...
private Publisher publisher;
}
7, như được hiển thị trong đoạn mã bên dưới, có thể không xứng đáng với chi phí này________số 8
Điều tương tự cũng xảy ra với việc nhúng một bộ sưu tập
{
"_id" : "617cfb",
"isbn13" : "978-0345503800",
"title" : "The Warded Man",
"pages" : 432,
"publisher" : {
"name" : "Del Rey Books",
"arconym" : "DRB",
"foundationYear" : 1977
}
}
1 trong class Book {
// ...
private Publisher publisher;
}
7, điều này gây ra các tài liệu lớn không cần thiết. Bình thường hóa mô hình và làm việc với các tài liệu được liên kết có thể giảm thiểu sự cốBước đầu tiên là xác định hướng của mối quan hệ, để tìm ra phần nào của mối quan hệ cần giữ tham chiếu, nếu không phải cả hai. Quyết định này sẽ ảnh hưởng đến các tùy chọn tra cứu, lưu trữ và truy vấn của chúng tôi sau này
Liên kết với DBrefs
Trong trường hợp này,
class Book {
// ...
private Publisher publisher;
}
7 giữ các tham chiếu đến {
"_id" : "617cfb",
"isbn13" : "978-0345503800",
"title" : "The Warded Man",
"pages" : 432,
"publisher" : {
"name" : "Del Rey Books",
"arconym" : "DRB",
"foundationYear" : 1977
}
}
1 được liên kết. Ý tưởng là lưu trữ các tham chiếu đó dưới dạng một mảng trong tài liệu class Book {
// ...
private Publisher publisher;
}
7class Book {
// ...
private Publisher publisher;
}
4Trong đoạn mã trên, thuộc tính sách được chú thích bằng
{
"_id" : "617cfb",
"isbn13" : "978-0345503800",
"title" : "The Warded Man",
"pages" : 432,
"publisher" : {
"name" : "Del Rey Books",
"arconym" : "DRB",
"foundationYear" : 1977
}
}
6. Điều này khuyên lớp ánh xạ Dữ liệu mùa xuân lưu trữ các phần tử của thuộc tính dưới dạng các phần tử {
"_id" : "617cfb",
"isbn13" : "978-0345503800",
"title" : "The Warded Man",
"pages" : 432,
"publisher" : {
"name" : "Del Rey Books",
"arconym" : "DRB",
"foundationYear" : 1977
}
}
7 gốc của MongoDB, giống như sauclass Book {
// ...
private Publisher publisher;
}
7Sử dụng chú thích
{
"_id" : "617cfb",
"isbn13" : "978-0345503800",
"title" : "The Warded Man",
"pages" : 432,
"publisher" : {
"name" : "Del Rey Books",
"arconym" : "DRB",
"foundationYear" : 1977
}
}
6 cho phép chúng tôi giảm dung lượng lưu trữ bằng cách không lặp lại tất cả thông tin class Book {
// ...
private Publisher publisher;
}
7 trong Sách, điều này tốt. Tuy nhiên, phương pháp này có nhược điểm của nó. class Book {
// ...
private Publisher publisher;
}
6 không còn chứa thông tin về nhà xuất bản, điều này có thể ảnh hưởng đến các truy vấn tra cứu {
"_id" : "617cfb",
"isbn13" : "978-0345503800",
"title" : "The Warded Man",
"pages" : 432,
"publisher" : {
"name" : "Del Rey Books",
"arconym" : "DRB",
"foundationYear" : 1977
}
}
1 theo thuộc tính của class Book {
// ...
private Publisher publisher;
}
7. Thay vào đó, việc thiếu tham chiếu ngược từ class Book {
// ...
private Publisher publisher;
}
6 đến nhà xuất bản cũng sẽ ảnh hưởng đến hiệu suất khi tra cứu class Book {
// ...
private Publisher publisher;
}
7 cho một class Book {
// ...
private Publisher publisher;
}
6 nhất định, vì bây giờ chúng ta phải đưa ra một truy vấn đối với bộ sưu tập class Book {
// ...
private Publisher publisher;
}
7 khớp với một class Book {
// ...
private Publisher publisher;
}
47 so với trường class Book {
// ...
private Publisher publisher;
}
48 của một nhà xuất bản, thay vào đó . Ngoài ra, mảng class Book {
// ...
private Publisher publisher;
}
48 trong class Book {
// ...
private Publisher publisher;
}
7 sử dụng một đối tượng phức tạp lưu trữ nhiều thông tin hơn mức cần thiết, trong khi một tham chiếu thủ công chỉ sử dụng id là đủ, vì tất cả các đối tượng tham chiếu được giữ trong cùng một bộ sưu tập đíchMay mắn thay, có nhiều cách để cải thiện, bắt đầu bằng việc thêm tham chiếu ngược vào
class Book {
// ...
private Publisher publisher;
}
7, [ví dụ: bằng class Book {
// ...
private Publisher publisher;
}
72 của nó]class Book {
private String isbn13;
private String title;
private int pages;
}
class Publisher {
private String name;
private String arconym;
private int foundationYear;
}
3Liên kết với Tài liệu tham khảo thủ công
Tiếp theo, hãy chuyển từ DBRef sang các tham chiếu thủ công để lưu trữ bộ sưu tập các tham chiếu
class Book {
// ...
private Publisher publisher;
}
6. Bước rõ ràng là xóa chú thích {
"_id" : "617cfb",
"isbn13" : "978-0345503800",
"title" : "The Warded Man",
"pages" : 432,
"publisher" : {
"name" : "Del Rey Books",
"arconym" : "DRB",
"foundationYear" : 1977
}
}
6 và thay thế class Book {
// ...
private Publisher publisher;
}
75 bằng class Book {
// ...
private Publisher publisher;
}
76, như trong đoạn trích bên dướiclass Book {
private String isbn13;
private String title;
private int pages;
}
class Publisher {
private String name;
private String arconym;
private int foundationYear;
}
8class Book {
private String isbn13;
private String title;
private int pages;
}
class Publisher {
private String name;
private String arconym;
private int foundationYear;
}
9Để thêm một
class Book {
// ...
private Publisher publisher;
}
6 mới vào trường class Book {
// ...
private Publisher publisher;
}
78 của class Book {
// ...
private Publisher publisher;
}
7, chúng ta có thể sử dụng câu lệnh sauclass Book {
// ...
private Publisher publisher;
}
3Theo cách tiếp cận này sẽ tối ưu hóa định dạng lưu trữ và đưa ra tuyên bố rất rõ ràng về kiểu dữ liệu được sử dụng trong cả mô hình miền cũng như cơ sở dữ liệu. Tuy nhiên, chỉ
class Book {
// ...
private Publisher publisher;
}
78 không cung cấp cho bạn ngữ cảnh của bộ sưu tập để tra cứu các giá trị có trong trường class Book {
// ...
private Publisher publisher;
}
78Liên kết bằng cách sử dụng Tài liệu tham khảo thủ công khai báo
Bắt đầu với Spring Data MongoDB 3. 3. 0, các tham chiếu thủ công có thể được thể hiện theo cách khai báo bằng cách sử dụng chú thích
class Book {
private String isbn13;
private String title;
private int pages;
}
class Publisher {
private String name;
private String arconym;
private int foundationYear;
}
32class Book {
// ...
private Publisher publisher;
}
7Theo mặc định, điều này yêu cầu lớp ánh xạ trích xuất giá trị
class Book {
// ...
private Publisher publisher;
}
72 của thực thể được tham chiếu để lưu trữ, tự tải tài liệu được tham chiếu khi đọcclass Book {
// ...
private Publisher publisher;
}
0Bởi vì lớp ánh xạ nhận thức được liên kết giữa các tài liệu, các câu lệnh cập nhật, chẳng hạn như câu lệnh được hiển thị trước đó, phát hiện liên kết và trích xuất id để lưu trữ
class Book {
// ...
private Publisher publisher;
}
1Ngoài ra, tham chiếu ngược từ
class Book {
// ...
private Publisher publisher;
}
6 đến class Book {
// ...
private Publisher publisher;
}
7 có thể được lập mô hình theo cách này. Trong trường hợp này, có thể trì hoãn việc truy xuất nhà xuất bản cho đến khi truy cập thuộc tính lần đầu tiên để tránh sự chậm trễ khi tải háo hứcclass Book {
// ...
private Publisher publisher;
}
2Bằng cách sử dụng các liên kết khai báo, giờ đây chúng tôi có thể duy trì chức năng lập bản đồ trong khi tối ưu hóa cho việc lưu trữ. Tuy nhiên, chúng ta cần cẩn thận khi thêm các phiên bản
class Book {
// ...
private Publisher publisher;
}
6 mới, vì chúng cũng cần được thêm vào trường class Book {
// ...
private Publisher publisher;
}
48 của class Book {
// ...
private Publisher publisher;
}
7, để thiết lập liên kếtclass Book {
// ...
private Publisher publisher;
}
3Đoạn mã trên phác thảo rất rõ bản chất phi nguyên tử khi làm việc với các liên kết giữa các tài liệu, có thể yêu cầu chạy các hoạt động trong Giao dịch
Tham khảo kiểu một-nhiều
Tùy thuộc vào nhu cầu của ứng dụng của bạn, có thể đảo ngược mối quan hệ giữa
class Book {
// ...
private Publisher publisher;
}
6 và class Book {
// ...
private Publisher publisher;
}
7 để phần tử liên kết chỉ được lưu trữ trong các tài liệu class Book {
// ...
private Publisher publisher;
}
6. Điều này cho phép bạn lưu trữ {
"_id" : "617cfb",
"isbn13" : "978-0345503800",
"title" : "The Warded Man",
"pages" : 432,
"publisher" : {
"name" : "Del Rey Books",
"arconym" : "DRB",
"foundationYear" : 1977
}
}
1 mà không phải suy nghĩ về việc cập nhật tài liệu class Book {
// ...
private Publisher publisher;
}
7, như chúng ta đã thấy trong đoạn trích trước. Để làm được như vậy, chúng ta cần làm hai việc. Đầu tiên, chúng ta cần yêu cầu lớp ánh xạ bỏ qua các liên kết lưu trữ từ class Book {
// ...
private Publisher publisher;
}
7 đến class Book {
// ...
private Publisher publisher;
}
6 và thứ hai, cập nhật truy vấn tra cứu khi truy xuất liên kết {
"_id" : "617cfb",
"isbn13" : "978-0345503800",
"title" : "The Warded Man",
"pages" : 432,
"publisher" : {
"name" : "Del Rey Books",
"arconym" : "DRB",
"foundationYear" : 1977
}
}
1Phần ban đầu khá dễ dàng, áp dụng thêm chú thích
class Book {
private String isbn13;
private String title;
private int pages;
}
class Publisher {
private String name;
private String arconym;
private int foundationYear;
}
87 cho thuộc tính class Book {
// ...
private Publisher publisher;
}
48. Phần khác yêu cầu chúng tôi cập nhật thuộc tính class Book {
private String isbn13;
private String title;
private int pages;
}
class Publisher {
private String name;
private String arconym;
private int foundationYear;
}
89 của chú thích class Book {
private String isbn13;
private String title;
private int pages;
}
class Publisher {
private String name;
private String arconym;
private int foundationYear;
}
32 bằng truy vấn tùy chỉnhclass Book {
// ...
private Publisher publisher;
}
4Trong đoạn mã trên, chúng tôi sử dụng hỗ trợ biểu thức trong trình phân tích cú pháp truy vấn của Dữ liệu mùa xuân. Khi làm như vậy, chúng tôi có thể truy cập tài liệu thô
class Book {
// ...
private Publisher publisher;
}
7 bằng cách sử dụng thuộc tính class Book {
private String isbn13;
private String title;
private int pages;
}
class Publisher {
private String name;
private String arconym;
private int foundationYear;
}
92 và có thể trích xuất mã định danh của nó để sau đó sử dụng nó khi truy vấn bộ sưu tập class Book {
// ...
private Publisher publisher;
}
6 để tìm các phần tử phù hợpChú thích cuối
Có một gốc tổng hợp duy nhất với dữ liệu được nhúng có nhiều lợi thế. Tuy nhiên, điều quan trọng là phải hiểu cách mô hình hóa các mối quan hệ một khi những lợi thế đó bị thay thế bởi các mối quan tâm khác, chẳng hạn như kích thước lưu trữ hoặc khả năng hoạt động. Chúng tôi đã thấy rằng, bằng cách chuyển từ phương pháp nhúng sang DBRefs và chuyển sang tham chiếu thủ công, chúng tôi có thể giảm kích thước bộ nhớ. Tuy nhiên, chúng tôi phải giải quyết các vấn đề khác, chẳng hạn như các thay đổi ảnh hưởng đến nhiều tài liệu và các tùy chọn truy vấn hạn chế.
class Book {
private String isbn13;
private String title;
private int pages;
}
class Publisher {
private String name;
private String arconym;
private int foundationYear;
}
32 có thể là một công cụ mạnh mẽ cho phép bạn thể hiện và tùy chỉnh các liên kết giữa các tài liệu. Bạn có thể tìm hiểu thêm về nó trong Tài liệu tham khảo của chúng tôiTuy nhiên, trước khi bạn rời đi, vui lòng luôn ghi nhớ rằng các liên kết giữa các tài liệu yêu cầu các máy chủ khứ hồi bổ sung. Do đó, hãy đảm bảo có sẵn các chỉ mục hỗ trợ tra cứu của bạn. Một bộ sưu tập các tài liệu được liên kết được tải hàng loạt, khôi phục thứ tự trên cơ sở nỗ lực tốt nhất trong bộ nhớ của ứng dụng của bạn
Ngoài ra, hãy luôn tự hỏi cái nào là tốt nhất cho ứng dụng của bạn?