Mana yang lebih efisien: Beberapa tabel MySQL atau satu tabel besar?


103

Saya menyimpan berbagai detail pengguna di database MySQL saya. Awalnya itu diatur dalam berbagai tabel yang berarti data dihubungkan dengan UserIds dan dikeluarkan melalui panggilan yang terkadang rumit untuk menampilkan dan memanipulasi data sesuai kebutuhan. Menyiapkan sistem baru, hampir masuk akal untuk menggabungkan semua tabel ini menjadi satu tabel besar konten terkait.

  • Apakah ini akan menjadi bantuan atau penghalang?
  • Pertimbangan kecepatan dalam menelepon, memperbarui, atau mencari / memanipulasi?

Berikut adalah contoh dari beberapa struktur tabel saya:

  • pengguna - UserId, nama pengguna, email, kata sandi terenkripsi, tanggal pendaftaran, ip
  • user_details - data cookie, nama, alamat, detail kontak, afiliasi, data demografis
  • user_activity - kontribusi, terakhir online, terakhir dilihat
  • user_settings - pengaturan tampilan profil
  • user_interests - mengiklankan variabel yang dapat ditargetkan
  • user_levels - hak akses
  • user_stats - klik, penghitungan

Sunting: Sejauh ini saya telah memilih semua jawaban, semuanya memiliki elemen yang pada dasarnya menjawab pertanyaan saya.

Sebagian besar tabel memiliki hubungan 1: 1 yang merupakan alasan utama untuk melakukan denormalisasi.

Apakah akan ada masalah jika tabel mencakup lebih dari 100 kolom ketika sebagian besar sel ini cenderung tetap kosong?


Pertanyaan lain ini mungkin bisa membantu juga
Mosty Mostacho

Jawaban:


65

Beberapa tabel membantu dalam cara / kasus berikut:

(a) jika orang yang berbeda akan mengembangkan aplikasi yang melibatkan tabel yang berbeda, masuk akal untuk membaginya.

(b) Jika Anda ingin memberikan jenis otoritas yang berbeda kepada orang yang berbeda untuk bagian pengumpulan data yang berbeda, mungkin lebih mudah untuk memisahkannya. (Tentu saja, Anda dapat melihat cara menentukan pandangan dan memberikan otorisasi pada pandangan tersebut dengan tepat).

(c) Untuk memindahkan data ke tempat yang berbeda, terutama selama pengembangan, mungkin masuk akal untuk menggunakan tabel yang menghasilkan ukuran file yang lebih kecil.

(d) Jejak kaki yang lebih kecil dapat memberikan kenyamanan saat Anda mengembangkan aplikasi pada pengumpulan data spesifik dari satu entitas.

(e) Ini adalah kemungkinan: apa yang Anda pikir sebagai data nilai tunggal dapat berubah menjadi nilai ganda di masa depan. misalnya batas kredit adalah bidang nilai tunggal seperti sekarang. Tetapi besok, Anda dapat memutuskan untuk mengubah nilai sebagai (tanggal dari, tanggal ke, nilai kredit). Tabel terpisah mungkin berguna sekarang.

Pilihan saya adalah untuk beberapa tabel - dengan data yang dibagi dengan tepat.

Semoga berhasil.


3
@RohitKhatri: Sepengetahuan saya, memiliki banyak tabel akan meningkatkan kinerja dalam banyak kasus.
Hari Harker

1
@HariHarker Terima kasih atas jawaban Anda, tetapi saya menemukan bahwa Itu tergantung pada pola akses Anda.
Rohit Khatri

Sampai saat ini saya selalu menyimpan semua data dalam satu tabel, tapi kalau dipikir-pikir, itu memiliki banyak keuntungan untuk membagi data dalam hal kinerja (tergantung pada use case ofcourse), semantik (beberapa data lebih baik dikelompokkan dalam a tabel yang berbeda) dan pengembangan. Misalnya saya sedang mengembangkan sistem ERP kustom sekarang di atas sistem lama. Saya harus memperluas tabel database lama dengan kolom tambahan. Saya memutuskan untuk membuat tabel baru untuk data baru. Beberapa fitur baru berguna untuk sistem lama dan sekarang saya dapat dengan mudah mengintegrasikannya tanpa harus menulis ulang terlalu banyak pertanyaan lama
Ogier Schelvis

35

Menggabungkan tabel disebut denormalisasi.

Ini mungkin (atau mungkin tidak) membantu untuk membuat beberapa pertanyaan (yang membuat banyak JOIN) untuk berjalan lebih cepat dengan mengorbankan menciptakan neraka pemeliharaan.

MySQLhanya mampu menggunakan JOINmetode, yaitu NESTED LOOPS.

Ini berarti bahwa untuk setiap catatan dalam tabel penggerak, MySQLmenempatkan catatan yang cocok di tabel didorong dalam satu lingkaran.

Menemukan catatan adalah operasi yang cukup mahal yang mungkin memakan waktu puluhan kali selama pemindaian catatan murni.

Memindahkan semua catatan Anda ke dalam satu tabel akan membantu Anda menyingkirkan operasi ini, tetapi tabel itu sendiri bertambah besar, dan pemindaian tabel membutuhkan waktu lebih lama.

Jika Anda memiliki banyak catatan di tabel lain, maka peningkatan dalam pemindaian tabel dapat membebani keuntungan dari catatan yang dipindai secara berurutan.

Neraka pemeliharaan, di sisi lain, dijamin.


1
Jika Anda memiliki 10.000 pengguna dan Anda melakukan penggabungan dengan database yang diatur dengan kunci asing dengan benar, maka Anda hanya perlu melakukan pencarian intensif dengan melakukan sesuatu seperti pilih * dari pengguna di mana name = "bob". Setelah Anda memiliki bob maka Anda menggunakan indeks untuk menemukan tabel yang digabungkan ke bob yang secara signifikan lebih cepat karena Anda menggunakan id bob. Ini terjadi terlepas dari apakah Anda melakukan penggabungan dalam kueri atau membuat kueri kemudian membuat kueri tabel secara terpisah. Tentu saja semoga kueri kedua Anda didasarkan pada id bob dan bukan yang lain.
Rudy Garcia

17

Apakah semuanya berhubungan 1: 1? Maksud saya, jika pengguna dapat menjadi bagian, katakanlah, tingkat pengguna yang berbeda, atau jika minat pengguna direpresentasikan sebagai beberapa rekaman dalam tabel minat pengguna, maka menggabungkan tabel tersebut akan segera keluar dari pertanyaan.

Mengenai jawaban sebelumnya tentang normalisasi, harus dikatakan bahwa aturan normalisasi database telah sepenuhnya mengabaikan kinerja, dan hanya melihat apa itu desain database yang rapi. Seringkali itulah yang ingin Anda capai, tetapi ada kalanya masuk akal untuk melakukan denormalisasi secara aktif untuk mengejar kinerja.

Secara keseluruhan, saya akan mengatakan pertanyaannya adalah berapa banyak bidang yang ada di tabel, dan seberapa sering mereka diakses. Jika aktivitas pengguna sering kali tidak terlalu menarik, mungkin akan merepotkan untuk selalu menyimpannya pada catatan yang sama, karena alasan kinerja dan pemeliharaan. Jika beberapa data, seperti pengaturan, misalnya, sangat sering diakses, tetapi berisi terlalu banyak bidang, mungkin juga tidak nyaman untuk menggabungkan tabel. Jika Anda hanya tertarik pada perolehan kinerja, Anda dapat mempertimbangkan pendekatan lain, seperti menjaga pengaturan tetap terpisah, tetapi menyimpannya dalam variabel sesi sendiri sehingga Anda tidak perlu sering menanyakan database untuk mereka.


Saya harus sepenuhnya tidak setuju dengan komentar Anda bahwa normalisasi hanya berfokus pada kerapian dan sepenuhnya mengabaikan kinerja. Ada trade off di kedua skenario dan denormalisasi benar-benar membahayakan integritas data. Saya akan mengatakan normalisasi database Anda benar-benar meningkatkan kinerja database secara keseluruhan daripada memiliki peningkatan kinerja cepat yang dapat diabaikan dari tabel yang didenormalisasi.
Rudy Garcia

Mengingat bahwa pembahasan secara khusus tentang hubungan 1: 1, memisahkan tabel bukanlah tugas normalisasi , bukan? Jika tidak ada informasi yang digandakan, itu normal bahkan ketika itu adalah satu tabel. (Yah, itu mungkin tidak memenuhi 3NFnormalisasi, jadi manfaatkan tabel kedua untuk menyelesaikannya, tetapi tampaknya bukan itu yang dimaksud OP untuk tabel lainnya.)
ToolmakerSteve

14

Apakah semua tabel tersebut memiliki 1-to-1hubungan? Misalnya, akankah setiap baris pengguna hanya memiliki satu baris yang sesuai di user_statsatau user_levels? Jika demikian, mungkin masuk akal untuk menggabungkannya menjadi satu tabel. Jika hubungannya tidak demikian 1 to 1 , mungkin tidak masuk akal untuk menggabungkan (mendenormalisasi) keduanya.

Memiliki mereka dalam tabel terpisah vs. satu tabel mungkin akan berdampak kecil pada kinerja kecuali jika Anda memiliki ratusan ribu atau jutaan catatan pengguna. Satu-satunya keuntungan nyata yang akan Anda dapatkan adalah dari menyederhanakan kueri Anda dengan menggabungkannya.

ETA:

Jika kekhawatiran Anda tentang memiliki terlalu banyak kolom , pikirkan tentang hal-hal apa yang biasanya Anda gunakan bersama dan gabungkan semuanya , tinggalkan sisanya dalam tabel terpisah (atau beberapa tabel terpisah jika diperlukan).

Jika Anda melihat cara Anda menggunakan data, perkiraan saya adalah Anda akan menemukan bahwa sekitar 80% kueri Anda menggunakan 20% dari data itu dengan 80% sisanya hanya digunakan sesekali. Gabungkan 20% yang sering digunakan ke dalam satu tabel, dan biarkan 80% yang tidak sering Anda gunakan di tabel terpisah dan Anda mungkin akan mendapatkan kompromi yang baik.


Ya, setiap tabel hanya memiliki 1 baris untuk setiap pengguna, cukup untuk menghemat sakit kepala karena mengelola banyak data duplikat. Inilah mengapa saya berpikir satu meja cocok. Jika data pengguna membentang beberapa baris, saya berharap tabel tersebut dipisahkan dari tabel pengguna utama.
Peter Craig

1
Jika setiap tabel memiliki relasi 1 banding 1 maka satu tabel akan lebih mudah digunakan. Tidak perlu membagi tabel dalam kasus itu. Memisahkan tabel menunjukkan bahwa ada lebih dari 1 baris, yang dapat mengarah ke kasus di mana pengembang lain akan memperlakukannya seperti itu.
Richard L

Pemikiran yang sangat menarik menerapkan 80/20 pada desain tabel database. Membuat saya berpikir juga tentang desain kelas OOP (saya terutama adalah pengembang Java) dan bertanya-tanya apakah hal yang sama mungkin efektif di sana (letakkan fungsionalitas aplikasi 80% utama di satu kelas dan sisanya di kelas lain).
Zack Macomber

1
@ZackMacomber - Tidak, pemisahan kelas harus didasarkan pada lokalitas referensi . Manfaat memecah menjadi beberapa kelas, adalah menggambar batas di sekitar unit fungsionalitas yang lebih kecil, sehingga lebih mudah untuk memahami / menguji / mengubah, dan jelas di mana unit itu berinteraksi dengan unit fungsionalitas lainnya. Tujuannya adalah untuk menjaga sebagian besar koneksi (referensi, panggilan) di dalam satu unit, dengan sedikit koneksi antar unit . Mendefinisikan beberapa antarmuka yang diimplementasikan oleh kelas, dengan antarmuka berbeda per kasus penggunaan, dapat menjadi langkah pertama yang berguna menuju pemisahan tersebut.
ToolmakerSteve

@ToolmakerSteve Pikiran yang baik +1
Zack Macomber

9

Membuat satu tabel besar bertentangan dengan prinsip basis data relasional. Saya tidak akan menggabungkan semuanya menjadi satu tabel. Anda akan mendapatkan banyak contoh data berulang. Jika pengguna Anda memiliki tiga minat misalnya, Anda akan memiliki 3 baris, dengan data pengguna yang sama hanya untuk menyimpan tiga kepentingan yang berbeda. Pasti pergi untuk beberapa pendekatan tabel 'dinormalisasi'. Lihat halaman Wiki ini untuk normalisasi database.

Sunting: Saya telah memperbarui jawaban saya, karena Anda telah memperbarui pertanyaan Anda ... Saya setuju dengan jawaban awal saya bahkan lebih sekarang sejak ...

sebagian besar dari sel-sel ini cenderung tetap kosong

Jika misalnya, pengguna tidak memiliki minat apa pun, jika Anda menormalkannya maka Anda tidak akan memiliki baris dalam tabel minat untuk pengguna itu. Jika Anda memiliki semuanya dalam satu tabel besar, maka Anda akan memiliki kolom (dan tampaknya banyak dari mereka) yang hanya berisi NULL.

Saya telah bekerja untuk perusahaan telepon yang memiliki banyak tabel, mendapatkan data dapat memerlukan banyak penggabungan. Ketika kinerja membaca dari tabel-tabel ini sangat penting, maka prosedur yang dibuat dapat menghasilkan tabel datar (yaitu tabel yang didenormalisasi) yang tidak memerlukan penggabungan, penghitungan, dll. Yang dapat ditunjukkan oleh laporan. Ini di mana kemudian digunakan bersama dengan agen server SQL untuk menjalankan pekerjaan pada interval tertentu (yaitu tampilan mingguan beberapa statistik akan dijalankan seminggu sekali dan seterusnya).


Saya menyukai pendekatan ini, karena data yang didenormalisasi hanya ada sementara, sebagai cuplikan dari suatu saat. Tidak ada masalah sisipkan / ubah / hapus - buang saja setelah selesai.
ToolmakerSteve

7

Mengapa tidak menggunakan pendekatan yang sama yang dilakukan Wordpress dengan memiliki tabel pengguna dengan informasi pengguna dasar yang dimiliki setiap orang dan kemudian menambahkan tabel "user_meta" yang pada dasarnya dapat berupa kunci, pasangan nilai yang terkait dengan id pengguna. Jadi jika Anda perlu menemukan semua informasi meta untuk pengguna, Anda bisa menambahkannya ke kueri Anda. Anda juga tidak selalu harus menambahkan kueri tambahan jika tidak diperlukan untuk hal-hal seperti masuk. Manfaat pendekatan ini juga membuat tabel Anda terbuka untuk menambahkan fitur baru kepada pengguna Anda seperti menyimpan pegangan twitter mereka atau minat masing-masing individu. Anda juga tidak perlu berurusan dengan labirin ID terkait karena Anda memiliki satu tabel yang mengatur semua metadata dan Anda akan membatasinya hanya ke satu asosiasi, bukan 50.

Wordpress secara khusus melakukan ini untuk memungkinkan fitur ditambahkan melalui plugin, oleh karena itu memungkinkan proyek Anda menjadi lebih skalabel dan tidak memerlukan perbaikan database lengkap jika Anda perlu menambahkan fitur baru.


wp_usermetaTabel Wordpress tumbuh secara geometris. Setiap pengguna menambahkan baris X ke wp_usermetatabel, satu baris untuk setiap bagian informasi meta yang ingin kami simpan untuk pengguna tersebut. Jika Anda menyimpan 8 bidang khusus untuk setiap pengguna, itu berarti wp_usermeta akan menjadi users * 8baris yang panjang. Tampaknya ini menyebabkan masalah kinerja, tapi saya tidak yakin apakah itu masalahnya atau bukan…
thirdender

1
Saya dapat melihat bagaimana hal ini dapat menyebabkan masalah kinerja jika Anda memiliki puluhan ribu pengguna. Pada dasarnya database harus mencari melalui 10.000 * 8 entri dalam tabel meta pengguna untuk menemukan yang Anda cari. Namun jika Anda hanya menanyakan data Meta saat diperlukan, menurut saya kinerja Anda akan lebih baik. Jika Anda selalu meminta meta data bahkan saat Anda tidak membutuhkannya, Anda mungkin mengalami masalah. Jika Anda selalu membutuhkan meta data, mungkin memisahkan tabel bukanlah pendekatan terbaik.
Rudy Garcia

1
Baru kemarin kami berurusan dengan tema WP yang memuat semua pengguna (menggunakan get_users()) hanya untuk menghitung pagination. Setelah kami mengoreksi kode agar menggunakan SELECT COUNT(…)kueri untuk penomoran halaman, waktu buka halaman berubah dari 28 detik menjadi sekitar 400ms. Saya masih bertanya-tanya bagaimana kinerja dibandingkan dengan tabel yang digabungkan atau satu tabel datar… Saya kesulitan menemukan metrik kinerja apa pun di web.
tiga

Berpikir tentang komentar saya sebelumnya, tampaknya memisahkan tabel masih efisien kecuali karena alasan tertentu, seperti contoh pagination di atas, Anda perlu memilih semua pengguna. Meskipun jika Anda mengambil semua informasi meta, Anda masih memiliki 80k entri di tabel usermeta. Itu sangat banyak untuk dicari. Mungkin seseorang dapat menguji pendekatan apa yang lebih baik dengan menjalankan skrip pada kedua implementasi dan menjalankannya 100 kali untuk mendapatkan rata-rata, saya mungkin akan melakukannya.
Rudy Garcia

1
Saya membaca ini lagi hari ini dan menyadari bahwa komentar saya tentang 10.000 * 8 entri adalah benar, namun cara kerja database seharusnya menjadikannya sebagian besar bukan masalah. Jika karena alasan tertentu Anda meraih semua 10.000 pengguna DAN juga informasi meta mereka, ini akan konyol. Saya tidak dapat memikirkan skenario apa pun di mana Anda menginginkan ini. Sebuah database akan dengan mudah mengambil meta untuk satu pengguna dengan kecepatan kilat meskipun karena kunci asing dan pengindeksan. Dengan asumsi model db Anda disiapkan dengan benar.
Rudy Garcia

5

Saya pikir ini adalah salah satu situasi "itu tergantung". Memiliki banyak tabel lebih bersih dan mungkin secara teoritis lebih baik. Tetapi ketika Anda harus menggabungkan 6-7 tabel untuk mendapatkan informasi tentang satu pengguna, Anda mungkin mulai memikirkan kembali pendekatan itu.


1

Saya akan mengatakan itu tergantung pada apa arti sebenarnya dari tabel lain. Apakah sebuah user_details berisi lebih dari 1 lebih / users dan seterusnya. Level normalisasi apa yang paling sesuai untuk kebutuhan Anda bergantung pada permintaan Anda.

Jika Anda memiliki satu tabel dengan indeks yang bagus, itu mungkin akan lebih cepat. Namun di sisi lain mungkin perawatannya lebih sulit.

Bagi saya, sepertinya Anda bisa melewati User_Details karena mungkin ini adalah hubungan 1 banding 1 dengan Users. Tapi sisanya mungkin banyak baris per pengguna?

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.