Pangkalan data Messenger (bahagian 2): membahagikan "untuk keuntungan"

Kami telah berjaya mereka bentuk struktur pangkalan data PostgreSQL kami untuk menyimpan surat-menyurat, setahun telah berlalu, pengguna sedang mengisinya secara aktif, dan kini ia mengandungi berjuta-juta rekod, dan... sesuatu mula perlahan.

Pangkalan data Messenger (bahagian 2): membahagikan "untuk keuntungan"
Fakta adalah bahawa Apabila saiz jadual bertambah, begitu juga "kedalaman" indeks. - walaupun secara logaritma. Tetapi lama kelamaan ini memaksa pelayan untuk melaksanakan tugas baca/tulis yang sama memproses banyak kali lebih banyak halaman datadaripada pada mulanya.

Di sinilah ia datang untuk menyelamatkan pembahagian.

Biar saya ambil perhatian bahawa kita tidak bercakap tentang sharding, iaitu, mengedarkan data antara pangkalan data atau pelayan yang berbeza. Kerana walaupun membahagikan data ke beberapa pelayan, anda tidak akan menyingkirkan masalah indeks "bengkak" dari semasa ke semasa. Adalah jelas bahawa jika anda mampu untuk meletakkan pelayan baharu beroperasi setiap hari, maka masalah anda tidak akan lagi terletak pada satah pangkalan data tertentu.

Kami tidak akan mempertimbangkan skrip khusus untuk melaksanakan pembahagian "dalam perkakasan", tetapi pendekatan itu sendiri - apa dan bagaimana harus "dipotong menjadi kepingan", dan apa yang menyebabkan keinginan sedemikian.

Konsep

Mari tentukan matlamat kami sekali lagi: kami ingin memastikan bahawa hari ini, esok dan dalam setahun, jumlah data yang dibaca oleh PostgreSQL semasa sebarang operasi baca/tulis kekal lebih kurang sama.

Bagi apa apa data terkumpul secara kronologi (mesej, dokumen, log, arkib, ...) pilihan semula jadi sebagai kunci pembahagian ialah tarikh/masa acara. Dalam kes kami, acara sedemikian adalah saat menghantar mesej.

Ambil perhatian bahawa pengguna hampir selalu bekerja hanya dengan yang "terkini". data sedemikian - mereka membaca mesej terkini, menganalisis log terkini,... Tidak, sudah tentu, mereka boleh menatal lebih jauh ke belakang dalam masa, tetapi mereka melakukan ini sangat jarang.

Daripada kekangan ini adalah jelas bahawa penyelesaian mesej yang optimum adalah bahagian "harian". - Lagipun, pengguna kami hampir selalu membaca apa yang datang kepadanya "hari ini" atau "semalam".

Jika kita menulis dan membaca hampir hanya dalam satu bahagian pada siang hari, maka ini juga memberi kita penggunaan memori dan cakera yang lebih cekap - kerana semua indeks bahagian mudah dimuatkan ke dalam RAM, berbeza dengan indeks "besar dan gemuk" di seluruh jadual.

langkah demi langkah

Secara umum, semua yang dinyatakan di atas kelihatan seperti satu keuntungan berterusan. Dan ia boleh dicapai, tetapi untuk ini kita perlu berusaha keras - kerana keputusan untuk membahagikan salah satu entiti membawa kepada keperluan untuk "melihat" yang berkaitan.

Mesej, sifat dan unjurannya

Memandangkan kami memutuskan untuk memotong mesej mengikut tarikh, adalah wajar untuk membahagikan entiti-sifat yang bergantung pada mereka (fail yang dilampirkan, senarai penerima) dan juga mengikut tarikh mesej.

Memandangkan salah satu tugas biasa kami ialah melihat daftar mesej dengan tepat (belum dibaca, masuk, semua), adalah juga logik untuk "menarik mereka masuk" ke dalam pembahagian mengikut tarikh mesej.

Pangkalan data Messenger (bahagian 2): membahagikan "untuk keuntungan"

Kami menambah kunci pembahagian (tarikh mesej) pada semua jadual: penerima, fail, pendaftaran. Anda tidak perlu menambahkannya pada mesej itu sendiri, tetapi gunakan DateTime yang sedia ada.

Topik

Memandangkan hanya terdapat satu topik untuk beberapa mesej, tiada cara untuk "memotong" dalam model yang sama; anda perlu bergantung pada sesuatu yang lain. Dalam kes kami ia adalah ideal tarikh mesej pertama dalam surat-menyurat β€” iaitu, saat penciptaan, sebenarnya, topik.

Pangkalan data Messenger (bahagian 2): membahagikan "untuk keuntungan"

Tambahkan kunci pembahagian (tarikh topik) pada semua jadual: topik, peserta.

Tetapi sekarang kita mempunyai dua masalah sekaligus:

  • Di bahagian manakah saya harus mencari mesej mengenai topik tersebut?
  • Di bahagian manakah saya harus mencari topik daripada mesej?

Kami boleh, sudah tentu, terus mencari di semua bahagian, tetapi ini akan menjadi sangat menyedihkan dan akan menafikan semua kemenangan kami. Oleh itu, untuk mengetahui di mana tepat untuk melihat, kami akan membuat pautan logik/penunjuk ke bahagian:

  • kami akan menambah dalam mesej medan tarikh topik
  • mari tambah topik tarikh mesej ditetapkan surat-menyurat ini (boleh menjadi jadual berasingan, atau pelbagai tarikh)

Pangkalan data Messenger (bahagian 2): membahagikan "untuk keuntungan"

Memandangkan terdapat sedikit pengubahsuaian pada senarai tarikh mesej untuk setiap surat-menyurat individu (lagipun, hampir semua mesej jatuh pada 1-2 hari bersebelahan), saya akan menumpukan pada pilihan ini.

Secara keseluruhan, struktur pangkalan data kami mengambil bentuk berikut, dengan mengambil kira pembahagian:

Jadual: RU, jika anda tidak suka abjad Cyrillic dalam nama jadual/medan, lebih baik jangan melihat

-- сСкции ΠΏΠΎ Π΄Π°Ρ‚Π΅ сообщСния
CREATE TABLE "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅_YYYYMMDD"(
  "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅"
    uuid
      PRIMARY KEY
, "Π’Π΅ΠΌΠ°"
    uuid
, "Π”Π°Ρ‚Π°Π’Π΅ΠΌΡ‹"
    date
, "Автор"
    uuid
, "ДатаВрСмя" -- ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ ΠΊΠ°ΠΊ Π΄Π°Ρ‚Ρƒ
    timestamp
, "ВСкст"
    text
);

CREATE TABLE "АдрСсат_YYYYMMDD"(
  "ДатаБообщСния"
    date
, "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅"
    uuid
, "ΠŸΠ΅Ρ€ΡΠΎΠ½Π°"
    uuid
, PRIMARY KEY("Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅", "ΠŸΠ΅Ρ€ΡΠΎΠ½Π°")
);

CREATE TABLE "Π€Π°ΠΉΠ»_YYYYMMDD"(
  "ДатаБообщСния"
    date
, "Π€Π°ΠΉΠ»"
    uuid
      PRIMARY KEY
, "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅"
    uuid
, "BLOB"
    uuid
, "Имя"
    text
);

CREATE TABLE "РССстрБообщСний_YYYYMMDD"(
  "ДатаБообщСния"
    date
, "Π’Π»Π°Π΄Π΅Π»Π΅Ρ†"
    uuid
, "ВипРССстра"
    smallint
, "ДатаВрСмя"
    timestamp
, "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅"
    uuid
, PRIMARY KEY("Π’Π»Π°Π΄Π΅Π»Π΅Ρ†", "ВипРССстра", "Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅")
);
CREATE INDEX ON "РССстрБообщСний_YYYYMMDD"("Π’Π»Π°Π΄Π΅Π»Π΅Ρ†", "ВипРССстра", "ДатаВрСмя" DESC);

-- сСкции ΠΏΠΎ Π΄Π°Ρ‚Π΅ Ρ‚Π΅ΠΌΡ‹
CREATE TABLE "Π’Π΅ΠΌΠ°_YYYYMMDD"(
  "Π”Π°Ρ‚Π°Π’Π΅ΠΌΡ‹"
    date
, "Π’Π΅ΠΌΠ°"
    uuid
      PRIMARY KEY
, "Π”ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚"
    uuid
, "НазваниС"
    text
);

CREATE TABLE "УчастникВСмы_YYYYMMDD"(
  "Π”Π°Ρ‚Π°Π’Π΅ΠΌΡ‹"
    date
, "Π’Π΅ΠΌΠ°"
    uuid
, "ΠŸΠ΅Ρ€ΡΠΎΠ½Π°"
    uuid
, PRIMARY KEY("Π’Π΅ΠΌΠ°", "ΠŸΠ΅Ρ€ΡΠΎΠ½Π°")
);

CREATE TABLE "Π”Π°Ρ‚Ρ‹Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠΉΠ’Π΅ΠΌΡ‹_YYYYMMDD"(
  "Π”Π°Ρ‚Π°Π’Π΅ΠΌΡ‹"
    date
, "Π’Π΅ΠΌΠ°"
    uuid
      PRIMARY KEY
, "Π”Π°Ρ‚Π°"
    date
);

Jimat sesen pun

Nah, bagaimana jika kita tidak menggunakan pilihan keratan klasik berdasarkan pengedaran nilai medan (melalui pencetus dan warisan atau PARTITION BY), dan "secara manual" pada peringkat aplikasi, anda akan melihat bahawa nilai kunci pembahagian sudah disimpan dalam nama jadual itu sendiri.

Jadi jika anda begitu Adakah anda sangat bimbang tentang jumlah data yang disimpan?, maka anda boleh menyingkirkan medan "tambahan" ini dan menangani jadual tertentu. Benar, semua pilihan dari beberapa bahagian dalam kes ini perlu dipindahkan ke bahagian aplikasi.

Sumber: www.habr.com

Tambah komen