Tại sao MongoDB sử dụng tài liệu nhúng?

Vì vậy, các bộ sưu tập riêng biệt sẽ tốt nếu bạn cần chọn các tài liệu riêng lẻ, cần kiểm soát nhiều hơn đối với truy vấn hoặc có nhiều tài liệu

Tài liệu được nhúng rất tốt khi bạn muốn có toàn bộ tài liệu, tài liệu có $slice nhận xét hoặc không có nhận xét nào cả

Khi xây dựng một ứng dụng mới, thường thì một trong những điều đầu tiên bạn muốn làm là thiết kế mô hình dữ liệu của nó. Trong các cơ sở dữ liệu quan hệ như MySQL, bước này được chính thức hóa trong quá trình chuẩn hóa, tập trung vào việc loại bỏ dư thừa khỏi một tập hợp các bảng. MongoDB, không giống như cơ sở dữ liệu quan hệ, lưu trữ dữ liệu của nó trong các tài liệu có cấu trúc thay vì các bảng cố định được yêu cầu trong cơ sở dữ liệu quan hệ. Chẳng hạn, các bảng quan hệ thường yêu cầu mỗi giao điểm hàng-cột chứa một giá trị vô hướng duy nhất. Các tài liệu MongoDB BSON cho phép cấu trúc phức tạp hơn bằng cách hỗ trợ các mảng giá trị (trong đó mỗi mảng có thể bao gồm nhiều tài liệu con)

Chương này khám phá một trong các tùy chọn mà mô hình tài liệu phong phú của MongoDB để ngỏ cho bạn. câu hỏi liệu bạn có nên nhúng các đối tượng có liên quan vào nhau hay tham chiếu chúng theo ID. Tại đây, bạn sẽ tìm hiểu cách cân nhắc hiệu suất, tính linh hoạt và độ phức tạp với nhau khi đưa ra quyết định này

Mô hình hóa và chuẩn hóa dữ liệu quan hệ

Trước khi chuyển sang cách tiếp cận của MongoDB cho câu hỏi nhúng tài liệu hoặc liên kết tài liệu, chúng ta sẽ tìm hiểu một chút về cách bạn lập mô hình các loại quan hệ nhất định trong cơ sở dữ liệu quan hệ (SQL). Trong cơ sở dữ liệu quan hệ, mô hình hóa dữ liệu thường tiến triển bằng cách mô hình hóa dữ liệu của bạn dưới dạng một loạt bảng, bao gồm các hàng và cột, xác định chung lược đồ dữ liệu của bạn. Lý thuyết cơ sở dữ liệu quan hệ đã xác định một số cách đưa dữ liệu ứng dụng vào các bảng, được gọi là các biểu mẫu thông thường. Mặc dù thảo luận chi tiết về mô hình quan hệ nằm ngoài phạm vi của văn bản này, nhưng có hai hình thức được chúng tôi đặc biệt quan tâm ở đây. dạng bình thường đầu tiên và dạng bình thường thứ ba

Dù sao thì một hình thức bình thường là gì?

Chuẩn hóa lược đồ thường bắt đầu bằng cách đưa dữ liệu ứng dụng của bạn vào dạng bình thường đầu tiên (1NF). Mặc dù có những quy tắc cụ thể xác định chính xác ý nghĩa của 1NF, nhưng điều đó hơi vượt quá những gì chúng tôi muốn đề cập ở đây. Đối với mục đích của chúng tôi, chúng tôi có thể coi dữ liệu 1NF là bất kỳ dữ liệu nào ở dạng bảng (bao gồm các hàng và cột), với mỗi giao điểm hàng-cột (“ô”) chứa chính xác một giá trị. Yêu cầu mà mỗi ô chứa chính xác một giá trị này, như chúng ta sẽ thấy sau, là một yêu cầu mà MongoDB không áp đặt, với khả năng đạt được một số hiệu suất tốt. Quay lại trường hợp quan hệ của chúng ta, hãy xem xét một ứng dụng danh bạ điện thoại. Dữ liệu ban đầu của bạn có thể ở dạng sau, được hiển thị trong Bảng 1-1

idnamephone_numberzip_code

1

Đụn rơm

555-111-1234

30062

2

Mike

555-222-2345

30062

3

cú chọc

555-333-3456

01209

Dữ liệu này thực sự đã ở dạng bình thường đầu tiên. Tuy nhiên, giả sử rằng chúng tôi muốn cho phép nhiều số điện thoại cho mỗi liên hệ, như trong Bảng 1-2

idnamephone_numberszip_code

1

Đụn rơm

555-111-1234

30062

2

Mike

555-222-2345;555-212-2322

30062

3

cú chọc

555-333-3456;555-334-3411

01209

Bây giờ chúng ta có một bảng không còn ở dạng bình thường đầu tiên. Nếu chúng tôi thực sự lưu trữ dữ liệu ở dạng này trong cơ sở dữ liệu quan hệ, chúng tôi sẽ phải quyết định lưu trữ

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
3 dưới dạng văn bản
SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
4 không có cấu trúc hay dưới dạng các cột riêng biệt (i. e. ,
SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
5,
SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
6). Giả sử chúng ta quyết định lưu trữ
SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
3 dưới dạng cột văn bản, như trong Bảng 1-2. Nếu chúng ta cần triển khai thứ gì đó như ID người gọi, tìm tên cho một số điện thoại nhất định, truy vấn SQL của chúng ta sẽ giống như sau

SELECT name FROM contacts WHERE phone_numbers LIKE '%555-222-2345%';

Thật không may, việc sử dụng mệnh đề

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
8 không phải là tiền tố có nghĩa là truy vấn này yêu cầu quét toàn bộ bảng để thỏa mãn

Ngoài ra, chúng ta có thể sử dụng nhiều cột, mỗi cột cho một số điện thoại, như trong Bảng 1-3

Bảng 1-3. Danh bạ điện thoại v2. 1 (nhiều cột)

idnamephone_number0phone_number1zip_code

1

Đụn rơm

555-111-1234

VÔ GIÁ TRỊ

30062

2

Mike

555-222-2345

555-212-2322

30062

3

cú chọc

555-333-3456

555-334-3411

01209

Trong trường hợp này, truy vấn ID người gọi của chúng tôi trở nên khá dài dòng

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';

Các bản cập nhật cũng phức tạp hơn, đặc biệt là xóa một số điện thoại, vì chúng tôi cần phân tích cú pháp trường

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
3 và viết lại hoặc tìm và vô hiệu hóa trường số điện thoại phù hợp. Biểu mẫu thông thường đầu tiên giải quyết những vấn đề này bằng cách chia nhỏ nhiều số điện thoại thành nhiều hàng, như trong Bảng 1-4

idnamephone_numberzip_code

1

Đụn rơm

555-111-1234

30062

2

Mike

555-222-2345

30062

2

Mike

555-212-2322

30062

2

cú chọc

555-333-3456

01209

2

cú chọc

555-334-3411

01209

Bây giờ chúng tôi đã trở lại dạng bình thường đầu tiên, nhưng chúng tôi phải đưa một số dự phòng vào mô hình dữ liệu của mình. Tất nhiên, vấn đề với sự dư thừa là nó tạo ra khả năng không nhất quán, trong đó các bản sao khác nhau của cùng một dữ liệu có các giá trị khác nhau. Để loại bỏ sự dư thừa này, chúng ta cần tiếp tục chuẩn hóa dữ liệu bằng cách chia dữ liệu thành hai bảng. Bảng 1-5 và Bảng 1-6. (Và đừng lo lắng, chúng tôi sẽ sớm quay lại MongoDB và cách nó có thể giải quyết các vấn đề dư thừa của bạn mà không cần chuẩn hóa ngay bây giờ. )

Bảng 1-5. Danh bạ điện thoại v4 (danh bạ)

contact_idnamezip_code

1

Đụn rơm

30062

2

Mike

30062

3

cú chọc

01209

Bảng 1-6. Danh bạ điện thoại v4 (số)

contact_idphone_number

1

555-111-1234

2

555-222-2345

2

555-212-2322

3

555-333-3456

3

555-334-3411

Là một phần của bước này, chúng tôi phải xác định một cột chính xác định duy nhất từng hàng trong bảng để chúng tôi có thể tạo liên kết giữa các bảng. Trong mô hình dữ liệu được trình bày trong Bảng 1-5 và Bảng 1-6, cặp

for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
0 tạo thành khóa của bảng danh bạ và cặp
for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
1 tạo thành khóa của bảng số. Trong trường hợp này, chúng tôi có một mô hình dữ liệu không dư thừa, cho phép chúng tôi cập nhật tên liên hệ, mã zip hoặc nhiều số điện thoại khác nhau mà không phải lo lắng về việc cập nhật nhiều hàng. Đặc biệt, chúng ta không còn phải lo lắng về sự không nhất quán trong mô hình dữ liệu

Như đã đề cập, điều thú vị về chuẩn hóa là nó cho phép cập nhật dễ dàng mà không có bất kỳ sự dư thừa nào. Mỗi sự kiện về miền ứng dụng có thể được cập nhật bằng cách thay đổi chỉ một giá trị, tại giao điểm của một hàng-cột. Vấn đề phát sinh khi bạn cố gắng lấy lại dữ liệu. Chẳng hạn, trong ứng dụng danh bạ điện thoại của chúng ta, chúng ta có thể muốn có một biểu mẫu hiển thị một liên hệ cùng với tất cả các số điện thoại của người đó. Trong những trường hợp như thế này, lập trình viên cơ sở dữ liệu quan hệ đạt được một

for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
2

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;

Kết quả của truy vấn này?

Bảng 1-7. Kết quả của truy vấn THAM GIA

tên_số_điện_thoại

cú chọc

555-333-3456

cú chọc

555-334-3411

Thật vậy, cơ sở dữ liệu đã cung cấp cho chúng tôi tất cả dữ liệu chúng tôi cần để đáp ứng thiết kế màn hình của chúng tôi. Vấn đề thực sự là cơ sở dữ liệu phải làm gì để tạo tập kết quả này, đặc biệt nếu cơ sở dữ liệu được hỗ trợ bởi một đĩa từ đang quay. Để biết lý do tại sao, chúng ta cần xem xét ngắn gọn một số đặc điểm vật lý của các thiết bị đó

Đĩa quay có đặc tính là mất nhiều thời gian hơn để tìm đến một vị trí cụ thể trên đĩa so với khi ở đó, đọc dữ liệu liên tục từ đĩa (xem Hình 1-1). Chẳng hạn, một đĩa hiện đại có thể mất 5 mili giây để tìm đến nơi mà nó có thể bắt đầu đọc. Tuy nhiên, khi nó ở đó, nó có thể đọc dữ liệu với tốc độ 40–80 MB mỗi giây. Đối với một ứng dụng như danh bạ điện thoại của chúng tôi, giả sử mỗi hàng có dung lượng lớn 1.024 byte, việc đọc một hàng khỏi đĩa sẽ mất từ ​​12 đến 25 micro giây

Tại sao MongoDB sử dụng tài liệu nhúng?

Hình 1-1. Tìm kiếm đĩa so với truy cập tuần tự

Kết quả cuối cùng của tất cả toán học này? . Khi nói đến truy cập đĩa, tìm kiếm ngẫu nhiên là kẻ thù. Lý do tại sao điều này rất quan trọng trong bối cảnh này là vì

for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
2 thường yêu cầu tìm kiếm ngẫu nhiên. Với mô hình dữ liệu được chuẩn hóa của chúng tôi, một kế hoạch có khả năng cho truy vấn của chúng tôi sẽ giống với mã Python sau

for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)

Vì vậy, cuối cùng sẽ có ít nhất một đĩa tìm kiếm cho mọi liên hệ trong cơ sở dữ liệu của chúng tôi. Tất nhiên, chúng tôi đã giới thiệu về cách thức hoạt động của

for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
4, giả sử rằng tất cả những gì nó cần làm là một lần tìm kiếm đĩa đơn. Thông thường, điều này thực sự được thực hiện bằng cách đọc một chỉ mục trên
for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
5 được khóa bởi
for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
0, có khả năng dẫn đến nhiều lần tìm kiếm đĩa hơn

Tất nhiên, các hệ thống cơ sở dữ liệu hiện đại đã phát triển các cấu trúc để giảm thiểu một số vấn đề này, chủ yếu bằng cách lưu vào bộ nhớ đệm các đối tượng được sử dụng thường xuyên (đặc biệt là các chỉ mục) trong RAM. Tuy nhiên, ngay cả với những tối ưu hóa như vậy, việc nối các bảng là một trong những thao tác tốn kém nhất mà cơ sở dữ liệu quan hệ thực hiện. Ngoài ra, nếu cuối cùng bạn cần mở rộng cơ sở dữ liệu của mình thành nhiều máy chủ, bạn sẽ gặp sự cố khi tạo liên kết phân tán, một hoạt động phức tạp và thường chậm

Không chuẩn hóa cho hiệu suất

Một bí mật bẩn thỉu (không thực sự quá bí mật) về cơ sở dữ liệu quan hệ là một khi chúng ta đã trải qua quá trình mô hình hóa dữ liệu để tạo ra mô hình dữ liệu dạng chuẩn đẹp thứ n, chúng ta thường cần phải chuẩn hóa mô hình để giảm số lượng

for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
2

Trong trường hợp này, chúng tôi chỉ có thể quay lại lưu trữ dự phòng

for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
8 và
for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
0 trong hàng. Tất nhiên, làm điều này dẫn đến sự dư thừa mà chúng tôi đang cố gắng loại bỏ và dẫn đến độ phức tạp của ứng dụng lớn hơn, vì chúng tôi phải đảm bảo cập nhật dữ liệu ở tất cả các vị trí dư thừa của nó

MongoDB. Dù sao thì ai cũng cần bình thường hóa?

Trong bước kết hợp này, MongoDB với khái niệm rằng dữ liệu của bạn không phải lúc nào cũng phải ở dạng bảng, về cơ bản loại bỏ hầu hết quá trình chuẩn hóa cơ sở dữ liệu truyền thống, bắt đầu với biểu mẫu bình thường đầu tiên. Trong MongoDB, dữ liệu được lưu trữ trong tài liệu. Điều này có nghĩa là khi biểu mẫu chuẩn đầu tiên trong cơ sở dữ liệu quan hệ yêu cầu mỗi giao điểm hàng-cột chứa chính xác một giá trị, thì MongoDB cho phép bạn lưu trữ một mảng giá trị nếu bạn muốn.

May mắn thay cho chúng tôi với tư cách là nhà thiết kế ứng dụng, điều đó mở ra một số khả năng mới trong thiết kế lược đồ. Vì MongoDB có thể mã hóa các thuộc tính đa giá trị như vậy nên chúng ta có thể nhận được nhiều lợi ích về hiệu suất của biểu mẫu không chuẩn hóa mà không gặp khó khăn trong việc cập nhật dữ liệu dư thừa. Thật không may cho chúng tôi, nó cũng làm phức tạp quá trình thiết kế lược đồ của chúng tôi. Không còn một “con đường trong vườn” của thiết kế cơ sở dữ liệu chuẩn hóa để đi xuống nữa, và câu trả lời khi gặp phải các vấn đề về thiết kế lược đồ chung trong MongoDB là “nó phụ thuộc vào. ”

Trước khi tìm hiểu chi tiết về thời điểm và lý do sử dụng các loại mảng của MongoDB, hãy xem lại tài liệu MongoDB là gì. Các tài liệu trong MongoDB được mô hình hóa theo định dạng JSON (Ký hiệu đối tượng JavaScript), nhưng thực tế được lưu trữ trong BSON (JSON nhị phân). Tóm lại, điều này có nghĩa là tài liệu MongoDB là một từ điển gồm các cặp khóa-giá trị, trong đó giá trị có thể là một trong số các loại

  • Các loại JSON nguyên thủy (e. g. , số, chuỗi, Boolean)
  • Các loại BSON nguyên thủy (e. g. , ngày giờ, ObjectId, UUID, biểu thức chính quy)
  • Mảng giá trị
  • Các đối tượng bao gồm các cặp khóa-giá trị
  • Vô giá trị

Trong ứng dụng danh bạ điện thoại mẫu của chúng tôi, chúng tôi có thể lưu trữ thông tin liên hệ của Jenny trong một tài liệu như sau

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
1

Như bạn có thể thấy, giờ đây chúng tôi có thể lưu trữ thông tin liên hệ ở định dạng Bảng 1-2 ban đầu mà không cần trải qua quá trình chuẩn hóa. Ngoài ra, chúng ta có thể “bình thường hóa” mô hình của mình để loại bỏ mảng, tham chiếu tài liệu liên hệ theo trường

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
10 của nó

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
3

Phần còn lại của chương này được dành để giúp bạn quyết định liệu tham chiếu hay nhúng có phải là giải pháp chính xác trong các ngữ cảnh khác nhau hay không

Một lý do khiến bạn có thể muốn nhúng các mối quan hệ một-nhiều của mình là vị trí dữ liệu. Như đã thảo luận trước đó, đĩa quay rất tốt trong việc truyền dữ liệu tuần tự và rất kém trong việc tìm kiếm ngẫu nhiên. Và vì MongoDB lưu trữ các tài liệu liên tục trên đĩa, nên việc đưa tất cả dữ liệu bạn cần vào một tài liệu có nghĩa là bạn không bao giờ phải tìm kiếm nhiều hơn mọi thứ bạn cần

MongoDB cũng có một hạn chế (do mong muốn phân vùng cơ sở dữ liệu dễ dàng) là không có sẵn các hoạt động

for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
2. Chẳng hạn, nếu bạn đã sử dụng tham chiếu trong ứng dụng danh bạ điện thoại, ứng dụng của bạn có thể thực hiện như sau

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
5

Tuy nhiên, nếu chúng ta thực hiện phương pháp này, chúng ta sẽ gặp phải một vấn đề thực sự tồi tệ hơn một hoạt động 'THAM GIA' trong quan hệ. Cơ sở dữ liệu không chỉ phải thực hiện nhiều lần tìm kiếm để tìm dữ liệu của chúng tôi mà chúng tôi còn đưa thêm độ trễ vào quá trình tra cứu vì hiện tại phải mất hai lượt truy cập cơ sở dữ liệu để truy xuất dữ liệu của chúng tôi. Do đó, nếu ứng dụng của bạn thường xuyên truy cập thông tin của các liên hệ cùng với tất cả các số điện thoại của họ, thì gần như chắc chắn bạn sẽ muốn nhúng các số đó vào bản ghi liên hệ.

Nhúng cho tính nguyên tử và cách ly

Một mối quan tâm khác có lợi cho việc nhúng là mong muốn về tính nguyên tử và sự cô lập trong việc ghi dữ liệu. Khi chúng tôi cập nhật dữ liệu trong cơ sở dữ liệu của mình, chúng tôi muốn đảm bảo rằng quá trình cập nhật của chúng tôi thành công hoặc thất bại hoàn toàn, không bao giờ có "thành công một phần" và bất kỳ trình đọc cơ sở dữ liệu nào khác không bao giờ thấy thao tác ghi không đầy đủ. Cơ sở dữ liệu quan hệ đạt được điều này bằng cách sử dụng các giao dịch đa câu lệnh. Chẳng hạn, nếu chúng ta muốn

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
12 Jenny từ cơ sở dữ liệu đã chuẩn hóa của mình, chúng ta có thể thực thi mã tương tự như sau

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
7

Vấn đề khi sử dụng phương pháp này trong MongoDB là MongoDB được thiết kế không có giao dịch đa tài liệu. Nếu chúng tôi cố gắng xóa Jenny khỏi lược đồ MongoDB đã “chuẩn hóa” của mình, chúng tôi sẽ cần thực thi đoạn mã sau

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
8

Tại sao không có giao dịch?

MongoDB được thiết kế từ đầu để dễ dàng mở rộng quy mô cho nhiều máy chủ phân tán. Hai trong số những vấn đề lớn nhất trong thiết kế cơ sở dữ liệu phân tán là hoạt động tham gia phân tán và giao dịch phân tán. Cả hai hoạt động này đều phức tạp để triển khai và có thể mang lại hiệu suất kém hoặc thậm chí là thời gian ngừng hoạt động trong trường hợp máy chủ không thể truy cập được. Bằng cách “xử lý” những vấn đề này và hoàn toàn không hỗ trợ liên kết hoặc giao dịch đa tài liệu, MongoDB đã có thể triển khai giải pháp phân đoạn tự động với các đặc tính hiệu suất và quy mô tốt hơn nhiều so với những gì bạn thường mắc phải nếu bạn phải thực hiện liên kết quan hệ và

Sử dụng phương pháp này, chúng tôi đưa ra khả năng Jenny có thể bị xóa khỏi bộ sưu tập

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
13 nhưng số của cô ấy vẫn còn trong bộ sưu tập
for number_row in find_by_contact_id(numbers, 3):
    yield (contact_row.name, number_row.number)
5. Cũng có khả năng một tiến trình khác đọc cơ sở dữ liệu sau khi Jenny bị xóa khỏi bộ sưu tập
SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
13, nhưng trước khi số của cô ấy bị xóa. Mặt khác, nếu chúng tôi sử dụng lược đồ nhúng, chúng tôi có thể xóa Jenny khỏi cơ sở dữ liệu của mình chỉ bằng một thao tác

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
2

Ghi chú

Một điểm đáng chú ý là nhiều hệ thống cơ sở dữ liệu quan hệ nới lỏng yêu cầu các giao dịch phải được cách ly hoàn toàn với nhau, đưa ra nhiều mức độ cách ly khác nhau. Do đó, nếu bạn có thể cấu trúc các bản cập nhật của mình thành các bản cập nhật một tài liệu duy nhất, thì bạn có thể nhận được tác động của mức cô lập tuần tự hóa (thận trọng nhất) mà không có bất kỳ lần truy cập hiệu suất nào trong hệ thống cơ sở dữ liệu quan hệ

Tham chiếu cho tính linh hoạt

Trong nhiều trường hợp, nhúng là phương pháp mang lại hiệu suất tốt nhất và đảm bảo tính nhất quán của dữ liệu. Tuy nhiên, trong một số trường hợp, một mô hình chuẩn hóa hơn hoạt động tốt hơn trong MongoDB. Một lý do bạn có thể xem xét việc chuẩn hóa mô hình dữ liệu của mình thành nhiều tập hợp là tính linh hoạt tăng lên mà điều này mang lại cho bạn khi thực hiện các truy vấn

Chẳng hạn, giả sử chúng ta có một ứng dụng viết blog chứa các bài đăng và bình luận. Một cách tiếp cận là sử dụng lược đồ nhúng

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';
0

Mặc dù lược đồ này hoạt động tốt để tạo và hiển thị nhận xét cũng như bài đăng, nhưng giả sử chúng tôi muốn thêm một tính năng cho phép bạn tìm kiếm tất cả nhận xét của một người dùng cụ thể. Truy vấn (sử dụng lược đồ nhúng này) sẽ như sau

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';
1

Sau đó, kết quả của truy vấn này sẽ là các tài liệu có dạng sau

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';
2

Hạn chế chính của phương pháp này là chúng tôi nhận được nhiều dữ liệu hơn chúng tôi thực sự cần. Đặc biệt, chúng tôi không thể chỉ yêu cầu những nhận xét của Stuart; . Sau đó, việc lọc thêm sẽ được yêu cầu trong mã Python của chúng tôi

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';
3

Mặt khác, giả sử chúng ta quyết định sử dụng lược đồ chuẩn hóa

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';
4

Truy vấn của chúng tôi để truy xuất tất cả các nhận xét của Stuart hiện khá đơn giản

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';
5

Nói chung, nếu mẫu truy vấn của ứng dụng của bạn nổi tiếng và dữ liệu có xu hướng chỉ được truy cập theo một cách, thì phương pháp nhúng sẽ hoạt động tốt. Ngoài ra, nếu ứng dụng của bạn có thể truy vấn dữ liệu theo nhiều cách khác nhau hoặc bạn không thể đoán trước các mẫu mà dữ liệu có thể được truy vấn, thì cách tiếp cận "chuẩn hóa" hơn có thể tốt hơn. Ví dụ: trong lược đồ “được liên kết” của chúng tôi, chúng tôi có thể sắp xếp các nhận xét mà chúng tôi quan tâm hoặc hạn chế số lượng nhận xét được trả về từ một truy vấn bằng cách sử dụng các toán tử

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
16 và
SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
17, trong khi trong trường hợp được nhúng, chúng tôi bị kẹt

Tham chiếu cho các mối quan hệ có tiềm năng cao

Một yếu tố khác có thể ảnh hưởng đến mô hình được chuẩn hóa hơn bằng cách sử dụng tham chiếu tài liệu là khi bạn có mối quan hệ một-nhiều với tính đối xứng rất cao hoặc không thể đoán trước. Chẳng hạn, một blog nổi tiếng với lượng độc giả tham gia lớn có thể có hàng trăm hoặc thậm chí hàng nghìn bình luận cho một bài đăng nhất định. Trong trường hợp này, nhúng mang theo các hình phạt đáng kể với nó

  • Tài liệu càng lớn thì càng sử dụng nhiều RAM
  • Tài liệu đang phát triển cuối cùng phải được sao chép sang không gian lớn hơn
  • Tài liệu MongoDB có giới hạn kích thước cứng là 16 MB

Vấn đề chiếm quá nhiều RAM là RAM thường là tài nguyên quan trọng nhất trên máy chủ MongoDB. Cụ thể, cơ sở dữ liệu MongoDB lưu trữ các tài liệu được truy cập thường xuyên trong RAM và các tài liệu đó càng lớn thì càng ít tài liệu phù hợp. Càng ít tài liệu trong RAM, máy chủ càng có nhiều khả năng xảy ra lỗi trang để truy xuất tài liệu và cuối cùng lỗi trang dẫn đến I/O đĩa ngẫu nhiên

Trong trường hợp nền tảng blog của chúng tôi, chúng tôi có thể chỉ muốn hiển thị ba nhận xét đầu tiên theo mặc định khi hiển thị một mục blog. Sau đó, lưu trữ tất cả 500 nhận xét cùng với mục nhập chỉ đơn giản là lãng phí RAM đó trong hầu hết các trường hợp

Điểm thứ hai, các tài liệu đang phát triển cần được sao chép, liên quan đến hiệu suất cập nhật. Khi bạn thêm vào mảng nhận xét được nhúng, cuối cùng thì MongoDB sẽ cần di chuyển tài liệu đến một khu vực có nhiều không gian hơn. Chuyển động này, khi nó xảy ra, làm chậm đáng kể hiệu suất cập nhật

Điểm cuối cùng, về giới hạn kích thước của tài liệu MongoDB, có nghĩa là nếu bạn có khả năng không bị ràng buộc trong mối quan hệ của mình, thì có thể hết dung lượng hoàn toàn, ngăn không cho đăng nhận xét mới trên một mục nhập. Mặc dù đây là điều cần lưu ý nhưng bạn thường sẽ gặp sự cố do áp lực bộ nhớ và sao chép tài liệu trước khi đạt đến giới hạn kích thước 16 MB

Mối quan hệ nhiều-nhiều

Một yếu tố cuối cùng có lợi cho việc sử dụng tham chiếu tài liệu là trường hợp nhiều-nhiều hoặc M. N mối quan hệ. Chẳng hạn, giả sử chúng ta có một hệ thống thương mại điện tử lưu trữ các sản phẩm và danh mục. Mỗi sản phẩm có thể thuộc nhiều danh mục và mỗi danh mục có thể chứa nhiều sản phẩm. Một cách tiếp cận mà chúng ta có thể sử dụng là bắt chước một lược đồ quan hệ nhiều-nhiều và sử dụng một “bộ sưu tập tham gia”

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';
6

Mặc dù cách tiếp cận này cung cấp cho chúng tôi một mô hình chuẩn hóa độc đáo, nhưng các truy vấn của chúng tôi cuối cùng lại thực hiện rất nhiều “liên kết” ở cấp ứng dụng

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';
7

Truy xuất một danh mục với các sản phẩm của nó cũng phức tạp tương tự. Ngoài ra, chúng ta có thể lưu trữ các đối tượng được nhúng hoàn toàn vào nhau

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';
8

Truy vấn của chúng tôi bây giờ đơn giản hơn nhiều

SELECT name FROM contacts
    WHERE phone_number0='555-222-2345'
        OR phone_number1='555-222-2345';
9

Tất nhiên, nếu chúng tôi muốn cập nhật sản phẩm hoặc danh mục, chúng tôi phải cập nhật nó trong bộ sưu tập của chính nó cũng như mọi nơi mà nó đã được nhúng vào tài liệu khác

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
0

Đối với liên kết nhiều-nhiều, cách tiếp cận thỏa hiệp thường là tốt nhất, nhúng danh sách các giá trị

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
10 thay vì toàn bộ tài liệu

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
1

Truy vấn của chúng tôi hiện phức tạp hơn một chút nhưng chúng tôi không còn phải lo lắng về việc cập nhật sản phẩm ở mọi nơi sản phẩm được đưa vào danh mục

SELECT name, phone_number
  FROM contacts LEFT JOIN numbers
    ON contacts.contact_id=numbers.contact_id
  WHERE contacts.contact_id=3;
2

Thiết kế lược đồ trong MongoDB có xu hướng mang tính nghệ thuật hơn là khoa học và một trong những quyết định trước đó bạn cần đưa ra là có nên nhúng mối quan hệ một-nhiều dưới dạng một mảng các tài liệu con hay không hoặc có nên theo một cách tiếp cận quan hệ hơn và

Hai lợi ích lớn nhất khi nhúng các tài liệu con là vị trí dữ liệu trong tài liệu và khả năng MongoDB thực hiện cập nhật nguyên tử cho tài liệu (nhưng không phải giữa hai tài liệu). Cân nhắc với những lợi ích này là giảm tính linh hoạt khi bạn nhúng, vì bạn đã “kết hợp trước” tài liệu của mình, cũng như khả năng xảy ra sự cố nếu bạn có mối quan hệ cấp cao

Cuối cùng, quyết định phụ thuộc vào các kiểu truy cập của ứng dụng của bạn và có ít quy tắc khó và nhanh hơn trong MongoDB so với trong cơ sở dữ liệu quan hệ. Sử dụng một cách khôn ngoan tính linh hoạt mà MongoDB mang lại cho bạn trong thiết kế lược đồ sẽ giúp bạn tận dụng tối đa cơ sở dữ liệu phi quan hệ mạnh mẽ này

Tài liệu nhúng trong MongoDB là gì?

MongoDB cung cấp cho bạn một tính năng thú vị được gọi là Tài liệu nhúng hoặc Tài liệu lồng nhau. Tài liệu nhúng hoặc tài liệu lồng nhau là những loại tài liệu chứa tài liệu bên trong tài liệu khác .

Khi nào chúng ta nên nhúng một tài liệu với một tài liệu khác trong MongoDB?

Tài liệu MongoDB được nhúng hoặc lồng nhau là một tài liệu bình thường được lồng bên trong một tài liệu khác trong bộ sưu tập MongoDB. Tài liệu được nhúng đặc biệt hữu ích khi tồn tại mối quan hệ một-nhiều giữa các tài liệu .

Ưu điểm chính của mô hình nhúng trong cơ sở dữ liệu tài liệu là gì?

Sử dụng phần cứng hiệu quả hơn . nhúng dữ liệu cung cấp cho chúng tôi các tài liệu lớn hơn với nhiều bản sao của cùng một dữ liệu; .

Tại sao MongoDB dựa trên tài liệu?

MongoDB được xây dựng trên kiến ​​trúc mở rộng quy mô đã trở nên phổ biến với các nhà phát triển thuộc mọi loại để phát triển các ứng dụng có thể mở rộng với các lược đồ dữ liệu đang phát triển. Là một cơ sở dữ liệu tài liệu, MongoDB giúp các nhà phát triển dễ dàng lưu trữ dữ liệu có cấu trúc hoặc phi cấu trúc . Nó sử dụng định dạng giống như JSON để lưu trữ tài liệu.