MongoDB 倾向于将数据都放在一个 Collection 下吗?
ringa_lee
ringa_lee 2017-04-24 09:09:33
0
3
794

举个例子,有一个用户信息和用户间关系的数据库,如果按照 SQL 的思路,会建立用户信息和用户关系两张表。那么,在 MongoDB 中,是倾向于将用户关系嵌入到用户信号,组成一个单独的文档吗?

ringa_lee
ringa_lee

ringa_lee

membalas semua(3)
Peter_Zhu

Bukan begitu.

Terdapat had atas pada saiz satu dokumen dalam Koleksi, iaitu 16MB pada masa ini, yang menjadikannya mustahil untuk anda menjejalkan segala-galanya ke dalam koleksi. Selain itu, jika struktur pengumpulan terlalu kompleks, ia bukan sahaja akan menjejaskan kecekapan pertanyaan dan kemas kini, tetapi juga menyebabkan kesukaran penyelenggaraan dan risiko operasi. Pernahkah anda cuba menyelamatkan dokumen secara tidak sengaja dengan berjabat tangan. Bagaimanapun, saya telah melakukannya.

Prinsip umum ialah:

  • Kluster mengikut kaedah pertanyaan

    • Letakkan data yang perlu dibaca bersama dengan kerap bersama-sama.
    • Letakkan maklumat yang berkait rapat secara logik.
    • Data dengan keperluan pengurangan peta/pengagregatan disatukan dan operasi ini hanya boleh beroperasi pada satu koleksi.
  • Pisah mengikut jumlah data

    • Jika anda mendapati anda perlu menggunakan tatasusunan dalam koleksi dan panjang tatasusunan akan terus meningkat, maka anda harus meletakkan kandungan data ke dalam koleksi khas, dan setiap keping data merujuk kepada kunci utama dokumen semasa (sama seperti mysql's 1..N Sama seperti kebergantungan kunci asing).
    • Jika anda mendapati bahawa dokumen tertentu terlalu dalam (lebih daripada 2 tahap), anda mungkin akan mempertimbangkan untuk membelahnya, jika tidak, akan terdapat masalah dengan prestasi dan kebolehselenggaraan.
  • Reka bentuk mengikut struktur jadual

    • MongoDB tidak mempunyai konsep struktur jadual, tetapi dalam penggunaan sebenar, jarang sekali untuk mengatakan bahawa koleksi mengandungi dokumen dengan pelbagai struktur Jika anda mendapati perbezaan dalam struktur dokumen semakin besar, anda harus mempertimbangkan bagaimana untuk mengabstrakkannya ke dalam struktur yang sama, membuang perkara yang diubah ke dalam koleksi lain, dan merujuk satu sama lain menggunakan kebergantungan kunci asing.

Sebagai contoh, semasa mereka bentuk sistem pengguna, koleksi pengguna harus mengandungi maklumat yang biasa digunakan seperti nama, serta lastLoginAt, yang hanya berkaitan dengan pengguna Mungkin juga maklumat tentang hak akses pengguna , tetapi jangan sertakan maklumat log Log masuk pengguna akan terus meningkat.

Mengenai hubungan antara pengguna, sama ada terdapat koleksi pengguna perlu dibincangkan. Jika anda hanya perlu menyimpan perhubungan antara pengguna dan merekodkan uid rakan, dan bilangan rakan tidak terlalu besar, beberapa ratus paling banyak, maka saya cenderung untuk meletakkannya dalam koleksi. Jika data perhubungan itu sendiri adalah kompleks, atau bilangan rakan adalah beribu-ribu, maka saya cenderung untuk memisahkannya.

Selain itu, paradigma reka bentuk model data rasmi Mongodb patut dibaca dan saya mengesyorkan anda melihat dengan teliti.

刘奇

Alamat asal: http://pwhack.me/post/2014-06-25-1 Sila nyatakan sumber untuk mencetak semula

Artikel ini dipetik daripada Bab 8 "Panduan Definitif untuk MongoDB", yang boleh menjawab dua soalan berikut dengan teliti:

  • /q/1010000000364944
  • /q/1010000000364944

Terdapat banyak cara untuk mewakili data, dan salah satu isu yang paling penting ialah sejauh mana data harus dinormalisasi. Normalisasi ialah proses penyebaran data ke dalam pelbagai koleksi yang berbeza, dan koleksi yang berbeza boleh merujuk data antara satu sama lain. Walaupun banyak dokumen boleh merujuk sekeping data tertentu, sekeping data ini hanya disimpan dalam satu koleksi. Oleh itu, jika anda ingin mengubah suai sekeping data ini, anda hanya perlu mengubah suai dokumen yang menyimpan sekeping data ini. Walau bagaimanapun, MongoDB tidak menyediakan alat gabungan, jadi berbilang pertanyaan diperlukan untuk melaksanakan pertanyaan gabungan antara koleksi yang berbeza.

Penyahnormalan adalah bertentangan dengan normalisasi: membenamkan data yang diperlukan untuk setiap dokumen dalam dokumen. Setiap dokumen mempunyai salinan datanya sendiri, bukannya semua dokumen secara kolektif merujuk salinan data yang sama. Ini bermakna jika maklumat berubah, semua dokumen berkaitan perlu dikemas kini, tetapi apabila pertanyaan dilaksanakan, hanya satu pertanyaan diperlukan untuk mendapatkan semua data.

Memutuskan bila untuk menormalkan dan bila untuk menyahnormalkan adalah sukar. Normalisasi boleh meningkatkan kelajuan penulisan data, dan penyahnormalan boleh meningkatkan kelajuan membaca data. Ini perlu ditimbang dengan teliti terhadap berpuluh-puluh keperluan aplikasi anda sendiri.

Contoh perwakilan data

Andaikan anda ingin menyimpan maklumat pelajar dan kursus. Satu cara untuk mewakili ini ialah menggunakan koleksi pelajar (setiap pelajar ialah dokumen) dan koleksi kelas (setiap kursus ialah dokumen). Kemudian gunakan studentsClasses koleksi ketiga untuk menyimpan hubungan antara pelajar dan kursus.

> db.studentsClasses.findOne({"studentsId": id});
{
  "_id": ObjectId("..."),
  "studentId": ObjectId("...");
  "classes": [
    ObjectId("..."),
    ObjectId("..."),
    ObjectId("..."),
    ObjectId("...")
  ]
}

Jika anda biasa dengan pangkalan data hubungan, anda mungkin telah mencipta jenis gabungan jadual ini sebelum ini, walaupun anda mungkin hanya mempunyai seorang pelajar dan satu kursus dalam setiap dokumen demerit (bukannya senarai "_id" kursus). Meletakkan kursus dalam tatasusunan adalah sedikit gaya MongoDB, tetapi dalam amalan anda biasanya tidak menyimpan data seperti ini kerana memerlukan banyak pertanyaan untuk mendapatkan maklumat sebenar.

Andaikan anda ingin mencari kursus yang dipilih oleh pelajar. Anda perlu terlebih dahulu mencari koleksi pelajar untuk mencari maklumat pelajar, kemudian bertanya kepada pelajarClasses untuk mencari kursus "_id", dan akhirnya menanyakan koleksi kelas untuk mendapatkan maklumat yang dikehendaki. Untuk mengetahui maklumat kursus, tiga pertanyaan perlu diminta daripada pelayan. Berkemungkinan anda tidak mahu menggunakan organisasi data jenis ini dalam MongoDB, melainkan maklumat pelajar dan maklumat kursus sering berubah dan tiada keperluan untuk kelajuan membaca data.

Anda boleh menyimpan pertanyaan jika anda membenamkan rujukan kursus dalam dokumen pelajar:

{
  "_id": ObjectId("..."),
  "name": "John Doe",
  "classes": [
    ObjectId("..."),
    ObjectId("..."),
    ObjectId("..."),
    ObjectId("...")
  ]
}

Medan "kelas" ialah tatasusunan yang menyimpan "_id" kursus yang perlu diambil oleh John Doe. Apabila anda perlu mengetahui maklumat tentang kursus ini, anda boleh menggunakan "_id" ini untuk menanyakan koleksi kelas. Proses ini hanya memerlukan dua pertanyaan. Cara penyusunan data ini bagus jika data tidak perlu diakses pada bila-bila masa dan tidak berubah pada bila-bila masa ("bila-bila masa" adalah lebih menuntut daripada "kerap").

Jika anda perlu mengoptimumkan lagi kelajuan membaca, anda boleh menyahnormalkan data sepenuhnya dan menyimpan maklumat kursus sebagai dokumen terbenam dalam medan "kelas" dokumen pelajar. Dengan cara ini, anda boleh mendapatkan maklumat kursus pelajar dengan hanya satu pertanyaan :

{
  "_id": ObjectId("..."),
  "name": "John Doe"
  "classes": [
    {
      "class": "Trigonometry",
      "credites": 3,
      "room": "204"
    },
    {
      "class": "Physics",
      "credites": 3,
      "room": "159"
    },
    {
      "class": "Women in Literature",
      "credites": 3,
      "room": "14b"
    },
    {
      "class": "AP European History",
      "credites": 4,
      "room": "321"
    }
  ]
}

Kelebihan kaedah di atas ialah ia hanya memerlukan satu pertanyaan untuk mendapatkan maklumat kursus pelajar. Kelemahannya ialah ia akan mengambil lebih banyak ruang storan dan penyegerakan data lebih sukar. Contohnya, jika Fizik menjadi kredit 4 mata (bukannya gred 3 mata), maka setiap pelajar yang mengambil kursus Fizik perlu mengemas kini dokumentasi mereka, bukan hanya dokumen "Fizik".

Akhir sekali, anda juga boleh mencampurkan data terbenam dan data rujukan: buat tatasusunan sub-dokumen untuk menyimpan maklumat biasa dan cari dokumen sebenar dengan rujukan apabila anda perlu menanyakan maklumat yang lebih terperinci:

{
  "_id": ObjectId("..."),
  "name": "John Doe",
  "classes": [
    {
      "_id": ObjectId("..."),
      "class": "Trigonometry"    
    },
    {
      "_id": ObjectId("..."),
      "class": "Physics"
    }, {
      "_id": ObjectId("..."),
      "class": "Women in Literature"
    }, {
      "_id": ObjectId("..."),
      "class": "AP European History"
    }
  ]
}

Kaedah ini juga merupakan pilihan yang baik, kerana maklumat yang dibenamkan boleh diubah suai mengikut keperluan perubahan Jika anda ingin memasukkan lebih (atau kurang) maklumat pada halaman, anda boleh menambah lebih (atau lebih) (kurang) maklumat adalah diletakkan dalam dokumen sebaris.

Satu lagi soalan penting untuk dipertimbangkan ialah, adakah maklumat dikemas kini dengan lebih kerap atau maklumat dibaca lebih kerap? Jika data akan dikemas kini dengan kerap, normalisasi adalah pilihan yang lebih baik. Jika data jarang berubah, ia tidak berbaloi untuk mengorbankan kelajuan membaca dan menulis untuk mengoptimumkan kecekapan kemas kini.

Sebagai contoh, contoh pengenalan buku teks kepada penormalan mungkin adalah untuk menyimpan pengguna dan alamat pengguna dalam koleksi berasingan. Walau bagaimanapun, orang jarang menukar alamat mereka, jadi kecekapan setiap pertanyaan tidak boleh dikorbankan untuk kejadian yang sangat tidak mungkin di mana seseorang menukar alamat mereka. Dalam kes ini, alamat harus dibenamkan dalam dokumen pengguna.

Jika anda memutuskan untuk menggunakan dokumen sebaris, anda perlu menyediakan tugas cron semasa mengemas kini dokumen untuk memastikan semua dokumen berjaya dikemas kini untuk setiap kemas kini. Sebagai contoh, kami cuba menyebarkan kemas kini kepada berbilang dokumen, dan pelayan ranap sebelum kemas kini melengkapkan semua dokumen. Ia adalah perlu untuk dapat mengesan masalah ini dan membuat semula kemas kini yang belum selesai.

Secara umumnya, semakin kerap data dijana, semakin kecil kemungkinan data itu perlu dibenamkan dalam dokumen lain. Jika bilangan medan terbenam atau medan terbenam bertambah selama-lamanya, maka kandungan ini hendaklah disimpan dalam koleksi berasingan dan diakses menggunakan rujukan dan bukannya dibenamkan dalam dokumen lain Maklumat seperti senarai ulasan atau senarai aktiviti hendaklah disimpan dalam koleksi berasingan dan harus tidak dibenamkan dalam dokumen lain.

Akhir sekali, jika medan tertentu adalah sebahagian daripada data dokumen, maka medan ini perlu dibenamkan ke dalam dokumen. Jika anda sering perlu mengecualikan medan semasa menanyakan dokumen, maka medan ini harus diletakkan dalam koleksi lain dan bukannya dibenamkan dalam dokumen semasa.

更适合内嵌 更适合引用
子文档较小 子文档较大
数据不会定期改变 数据经常改变
最终数据一致即可 中间阶段的数据必须一致
文档数据小幅增加 文档数据大幅增加
数据通常需要执行二次查询才能获得 数据通常不包含在结果中
快速读取 快速写入

Andaikan kami mempunyai koleksi pengguna. Di bawah ialah beberapa medan yang mungkin diperlukan dan sama ada ia perlu dibenamkan dalam dokumen pengguna.

Keutamaan pengguna (keutamaan akaun)

Keutamaan pengguna hanya berkaitan dengan pengguna tertentu dan mungkin perlu disoal bersama dengan maklumat pengguna lain dalam dokumen pengguna. Jadi keutamaan pengguna harus dibenamkan ke dalam dokumen pengguna.

Aktiviti terkini

Bidang ini bergantung pada kekerapan aktiviti telah berkembang dan berubah baru-baru ini. Jika ini ialah medan panjang tetap (seperti 10 acara terakhir), maka medan ini harus dibenamkan dalam dokumen pengguna.

Kawan

Biasanya anda tidak seharusnya membenamkan maklumat rakan ke dalam dokumen pengguna, sekurang-kurangnya tidak sepenuhnya. Bahagian seterusnya akan memperkenalkan kandungan aplikasi rangkaian sosial yang berkaitan.

Semua kandungan janaan pengguna

Tidak boleh dibenamkan dalam dokumentasi pengguna.

Asas

Bilangan rujukan kepada koleksi lain yang terkandung dalam koleksi dipanggil kardinaliti. Perhubungan biasa termasuk satu-dengan-satu, satu-ke-banyak, dan banyak-ke-banyak. Kiranya ada aplikasi blogging. Setiap catatan blog mempunyai tajuk, iaitu hubungan satu dengan satu. Setiap pengarang boleh mempunyai berbilang artikel, yang merupakan hubungan satu-dengan-banyak. Setiap artikel boleh mempunyai berbilang teg (teg) dan setiap teg boleh digunakan dalam berbilang artikel, jadi ini ialah perhubungan banyak-ke-banyak.

Dalam MongoDB, banyak boleh dibahagikan kepada dua subkategori: banyak dan sedikit. Sebagai contoh, hubungan antara pengarang dan artikel mungkin merupakan hubungan satu dengan satu: setiap pengarang hanya menerbitkan beberapa artikel. Mungkin terdapat hubungan banyak-ke-sedikit antara catatan blog dan teg: bilangan siaran sebenarnya mungkin lebih besar daripada bilangan teg. Terdapat hubungan satu-dengan-banyak antara catatan blog dan ulasan: setiap siaran boleh mempunyai banyak ulasan.

Selagi hubungan antara kurang dan lebih ditentukan, lebih mudah untuk membuat pertukaran antara data terbenam dan data rujukan. Secara umumnya, lebih baik menggunakan kaedah sebaris untuk perhubungan "kurang", dan lebih baik menggunakan kaedah rujukan untuk perhubungan "banyak".

Rakan, peminat dan perkara lain yang menyusahkan

Jalinkan kawan rapat dan jauhi musuh

Banyak aplikasi sosial perlu memautkan orang, kandungan, peminat, rakan dan perkara lain. Pertukaran antara menggunakan borang sebaris dan dirujuk untuk data yang sangat berkaitan ini bukanlah mudah. Bahagian ini akan memperkenalkan pertimbangan yang berkaitan dengan data graf sosial. Selalunya, mengikuti, rakan atau kegemaran boleh dipermudahkan ke dalam sistem publish-subscribe: seorang pengguna boleh melanggan pemberitahuan yang berkaitan dengan pengguna lain. Dengan cara ini, terdapat dua operasi asas yang perlu cekap: cara menyelamatkan pelanggan dan cara memberitahu semua pelanggan tentang sesuatu acara.

Terdapat tiga kaedah pelaksanaan langganan biasa. Cara pertama ialah membenamkan pengeluar kandungan dalam dokumen pelanggan:

{
    "_id": ObjectId("..."),
    "username": "batman",
    "email": "batman@waynetech.com",
    "following": [
        ObjectId("..."),
        ObjectId("...")
    ]
}

Kini, untuk dokumen pengguna tertentu, anda boleh menggunakan borang db.activities.find({"user": {"$in": user["following"]}}) untuk menanyakan semua maklumat aktiviti yang pengguna minati. Walau bagaimanapun, untuk sekeping maklumat aktiviti yang baru dikeluarkan, jika anda ingin mengetahui semua pengguna yang berminat dengan maklumat ini, anda perlu menanyakan medan "mengikut" semua pengguna.

Cara lain ialah membenamkan pelanggan ke dalam dokumen pengeluar:

{
    "_id": ObjectId("..."),
    "username": "joker",
    "email": "joker@mailinator.com",
    "followers": [
        ObjectId("..."),
        ObjectId("..."),
        ObjectId("...")
    ]
}

Apabila penerbit ini menerbitkan mesej baharu, kami boleh mengetahui pengguna mana yang perlu dimaklumkan dengan segera. Kelemahan ini ialah jika anda perlu mencari senarai pengguna yang diikuti pengguna, anda mesti menanyakan keseluruhan koleksi pengguna. Kebaikan dan keburukan kaedah ini betul-betul bertentangan dengan kaedah pertama.

Pada masa yang sama, kedua-dua kaedah mempunyai masalah lain: ia akan menjadikan dokumen pengguna menjadi lebih besar dan lebih besar, dan perubahan akan menjadi semakin kerap. Selalunya, medan "mengikuti" dan "pengikut" tidak perlu dikembalikan: berapa kerapkah senarai pengikut ditanya? Jika pengguna mengikuti orang tertentu dengan lebih kerap atau berhenti mengikuti sesetengah orang, ia juga akan membawa kepada banyak pemecahan. Oleh itu, penyelesaian terakhir menormalkan lagi data dan menyimpan maklumat langganan dalam koleksi berasingan untuk mengelakkan kekurangan ini. Normalisasi jenis ini mungkin agak banyak, tetapi ia berguna untuk medan yang kerap berubah dan tidak perlu dikembalikan bersama dokumen yang lain. Adalah masuk akal untuk melakukan normalisasi medan "pengikut" ini.

Gunakan koleksi untuk menyimpan perhubungan antara penerbit dan pelanggan Struktur dokumen mungkin seperti berikut:

{
    "_id": ObjectId("..."),   //被关注者的"_id"
    "followers": [
        ObjectId("..."),
        ObjectId("..."),
        ObjectId("...")
    ]
}

Ini boleh menjadikan dokumen pengguna lebih diperkemas, tetapi memerlukan pertanyaan tambahan untuk mendapatkan senarai peminat. Memandangkan saiz tatasusunan "pengikut" sering berubah, "usePowerOf2Sizes" boleh didayakan pada koleksi ini untuk memastikan koleksi pengguna adalah sekecil mungkin. Jika koleksi pengikut disimpan dalam pangkalan data lain, ia boleh dimampatkan tanpa menjejaskan koleksi pengguna terlalu banyak.

Mengatasi Kesan Wil Wheaton

Tidak kira apa strategi yang digunakan, medan sebaris hanya boleh berfungsi dengan berkesan apabila bilangan subdokumen atau rujukan tidak begitu besar. Bagi pengguna yang lebih terkenal, ia boleh menyebabkan dokumen yang digunakan untuk menyimpan senarai peminat melimpah. Satu penyelesaian untuk situasi ini ialah menggunakan dokumen "berterusan" apabila perlu. Contohnya:

> db.users.find({"username": "wil"})
{
    "_id": ObjectId("..."),
    "username": "wil",
    "email": "wil@example.com",
    "tbc": [
        ObjectId("123"),    // just for example
        ObjectId("456")     // same as above
    ],
    "followers": [
        ObjectId("..."),
        ObjectId("..."),
        ObjectId("..."),
        ...
    ]
}
{
    "_id": ObjectId("123"),
    "followers": [
        ObjectId("..."),
        ObjectId("..."),
        ObjectId("..."),
        ...
    ]
}
{
    "_id": ObjectId("456"),
    "followers": [
        ObjectId("..."),
        ObjectId("..."),
        ObjectId("..."),
        ...
    ]
}

Untuk situasi ini, anda perlu menambah logik yang berkaitan untuk mendapatkan data daripada tatasusunan "tbc" (untuk diteruskan) dalam aplikasi.

Katakan sesuatu

Tiada peluru perak.

伊谢尔伦

Jika perniagaan sentiasa perlu menanyakan perhubungan antara pengguna, adalah lebih baik untuk memisahkan perhubungan itu kepada Koleksi yang berasingan

Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan