Що ефективніше: кілька таблиць MySQL або одна велика таблиця?


103

Я зберігаю різні дані користувачів у своїй базі даних MySQL. Спочатку він був створений у різних таблицях, тобто дані пов'язані з UserIds і виводяться через інколи складні дзвінки для відображення та маніпулювання даними, якщо потрібно. Встановлюючи нову систему, практично має сенс поєднувати всі ці таблиці в одну велику таблицю відповідного вмісту.

  • Це буде допомога чи перешкода?
  • Міркування щодо швидкості виклику, оновлення чи пошуку / маніпулювання?

Ось приклад деяких структур моїх таблиць:

  • користувачі - UserId, ім'я користувача, електронна пошта, зашифрований пароль, дата реєстрації, ip
  • user_details - дані cookie, ім’я, адреса, контактні дані, приналежність, демографічні дані
  • user_activity - внески, останній онлайн, останній перегляд
  • user_settings - налаштування відображення профілю
  • user_interests - рекламні орієнтовані змінні
  • user_levels - права доступу
  • user_stats - хіти, талі

Редагувати: Я відповів на всі відповіді досі, всі вони мають елементи, які по суті відповідають моєму питанню.

Більшість таблиць мають відношення 1: 1, що було основною причиною денормалізації.

Чи виникнуть проблеми, якщо таблиця охоплює понад 100 стовпців, коли велика частина цих комірок, ймовірно, залишиться порожньою?


Це інше питання , може бути корисно теж
Мостовский Mostacho

Відповіді:


65

Кілька таблиць допомагають у таких випадках / випадках:

(a) якщо різні люди будуть розробляти програми, що містять різні таблиці, є сенс розділити їх.

(b) Якщо ви хочете надати різні види повноважень різним людям для різної частини збору даних, можливо, буде зручніше розділити їх. (Звичайно, ви можете подивитися на визначення поглядів та надання відповідного дозволу на них).

(c) Для переміщення даних у різні місця, особливо під час розробки, може бути доцільним використання таблиць, що призводять до менших розмірів файлів.

(d) Менший друк на ногах може забезпечити комфорт під час розробки програм для збору конкретних даних для одного об'єкта.

(e) Це можливість: те, що ви думали як дані про єдине значення, у майбутньому може виявитись дійсно декількома значеннями. наприклад, ліміт кредиту - це єдине поле значення на даний момент. Але завтра ви можете вирішити змінити значення на (дата з дати, дату, вартість кредиту). Сплит-таблиці зараз можуть стати в нагоді

Мій голос був би за декілька таблиць - з відповідними розділеннями даних.

Удачі.


3
@RohitKhatri: Наскільки мені відомо, наявність більшості таблиць підвищить ефективність у більшості випадків.
Харі Харкер

1
@HariHarker Дякую за вашу відповідь, але я зрозумів, що це залежить від вашої схеми доступу.
Рохіт Хатрі

До недавнього часу я завжди зберігав усі дані в одній таблиці, але подумайте про це, у неї є багато переваг, щоб розділити дані з точки зору продуктивності (залежно від випадку використання курсу), семантики (деякі дані краще згрупувати в інша таблиця) та розвиток. Наприклад, я зараз розробляю користувальницьку ERP-систему, що перебуває поверх застарілої системи. Мені довелося розширювати старі таблиці бази додатковими колонками. Я вирішив скласти нові таблиці для нових даних. Деякі нові функції стають у нагоді для застарілої системи, і тепер я можу їх легко інтегрувати без необхідності переписувати занадто багато старих запитів
Ogier Schelvis

35

Поєднання таблиць називається денормалізацією.

Це може (або не може) допомогти зробити деякі запити (які роблять багато JOINs) для швидшого запуску за рахунок створення пекла технічного обслуговування.

MySQLздатний використовувати лише JOINметод, а саме NESTED LOOPS.

Це означає, що для кожного запису в таблиці керування MySQLрозташований відповідний запис у керованій таблиці в циклі.

Пошук запису - це досить дорога операція, яка може зайняти десятки разів довше, ніж чисте сканування записів.

Переміщення всіх записів в одну таблицю допоможе вам позбутися від цієї операції, але сама таблиця зростає, а сканування таблиці займає більше часу.

Якщо у вас є багато записів в інших таблицях, то збільшення сканування таблиць може призвести до надмірної переваги записів, які скануються послідовно.

З іншого боку, технічне обслуговування пекла.


1
Якщо у вас є 10000 користувачів, і ви правильно з'єднуєтеся з базою даних, створеною із зовнішніми ключами, тоді вам знадобиться лише інтенсивний пошук, виконуючи щось на зразок select * від користувачів, де name = "bob". Після того, як у вас є bob, ви використовуєте індекс, щоб знайти об'єднані таблиці для bob, що значно швидше, оскільки ви використовуєте id bob. Це трапляється незалежно від того, чи займаєтесь ви запитом чи запитувальним bob, а потім запитуєте таблицю окремо. Звичайно, сподіваємось, що ваш другий запит базується на id bob, а не на чомусь іншому.
Руді Гарсія

17

Чи всі вони стосунки 1: 1? Я маю на увазі, якщо користувач міг би належати, скажімо, до різних рівнів користувача, або якщо інтереси користувачів представлені у вигляді декількох записів у таблиці інтересів користувачів, про об'єднання цих таблиць негайно не виникне сумніву.

Що стосується попередніх відповідей щодо нормалізації, то слід сказати, що правила нормалізації бази даних повністю не враховували продуктивність, і лише дивимось на те, що є акуратною конструкцією бази даних. Це часто ви хочете досягти, але бувають випадки, коли є сенс активно денормалізуватись у досягненні результатів.

Загалом, я б сказав, що питання зводиться до того, скільки полів є в таблицях та як часто до них звертаються. Якщо діяльність користувачів часто не дуже цікава, то, можливо, просто неприємно завжди мати її на одній і тій же записи з міркувань продуктивності та обслуговування. Якщо до деяких даних, наприклад, до налаштувань, скажімо, до них звертаються дуже часто, але вони просто містять занадто багато полів, можливо, також не буде зручно об'єднувати таблиці. Якщо вас цікавить лише підвищення продуктивності, ви можете розглянути інші підходи, такі як збереження налаштувань окремо, але збереження їх у власній змінній сеансу, так що вам не доведеться запитувати в них базу даних дуже часто.


Я повинен повністю не погодитися з вашим коментарем, що нормалізація зосереджена лише на акуратності та повністю ігнорує продуктивність. Існує торгівля обома сценаріями, і денормалізація фактично ставить під загрозу цілісність даних. Я б сказав, що нормалізація вашої бази даних фактично покращує загальну продуктивність бази даних, а не швидке незначне підвищення продуктивності від денормалізованої таблиці.
Руді Гарсія

Зважаючи на те, що дискусія стосується саме відносин 1: 1, розділення таблиць не є завданням нормалізації , правда? Якщо дубльованої інформації немає, її нормально навіть тоді, коли її єдина таблиця. (Ну, це не може задовольнити 3NFнормалізації, тому користь від другої таблиці , щоб вирішити , що, але це , здається, не те , що OP має в увазі повторно інші таблиці.)
ToolmakerSteve

14

Чи всі ці таблиці мають 1-to-1стосунки? Наприклад, чи буде в кожному рядку користувача лише один відповідний рядок у user_statsабо user_levels? Якщо так, то може бути сенс об'єднати їх в одну таблицю. Якщо відносини не є 1 to 1 , можливо, не було б сенсу поєднувати (денормалізувати) їх.

Наявність їх в окремих таблицях проти однієї таблиці, ймовірно, мало вплине на продуктивність, окрім випадків, коли у вас є сотні тисяч або мільйони записів користувачів. Єдиний реальний прибуток, який ви отримаєте, - це спрощення запитів шляхом їх поєднання.

ETA:

Якщо ви турбуєтеся про те, що у вас є занадто багато стовпців , тоді подумайте, які речі ви зазвичай використовуєте разом і комбінуйте , залишивши решту в окремій таблиці (або декількох окремих таблицях, якщо потрібно).

Якщо ви подивитесь на те, як ви використовуєте дані, я гадаю, що ви побачите, що щось на зразок 80% ваших запитів використовує 20% цих даних, а решта 80% даних використовуються лише зрідка. Поєднайте часто використовувані 20% в одну таблицю і залиште 80%, які ви часто не використовуєте в окремих таблицях, і ви, мабуть, матимете хороший компроміс.


Так, у кожній таблиці є лише 1 рядок для кожного користувача, просто для збереження головного болю від керування великою кількістю дублюваних даних. Ось чому я думаю, що один стіл підходить. Якщо дані користувачів охоплювали декілька рядків, я б очікував, що ці таблиці будуть відокремлені від основної таблиці користувачів.
Пітер Крейг

1
Якщо кожна таблиця має відношення від 1 до 1, то одну таблицю було б простіше у використанні. У цьому випадку не потрібно розділяти таблицю. Розбиття таблиці підтверджує, що існує більше 1 ряду, що може призвести до випадку, коли інший розробник буде так ставитися до них.
Річард Л

Дуже цікава думка про застосування 80/20 до дизайну таблиць баз даних. Подумав також про дизайн класу OOP (я в першу чергу розробник Java) і задаюся питанням, чи може це бути ефективним там (покладіть первинну 80% функціональність додатків в одному класі, а решту в інших класах).
Зак Макомбер

1
@ZackMacomber - Ні, розподіл класів повинен базуватися на місцевості відліку . Перевага розбиття на кілька класів полягає в тому, щоб намалювати межу навколо меншої одиниці функціональності, щоб було легше зрозуміти / перевірити / змінити і зрозуміти, де цей блок взаємодіє з іншими одиницями функціональності. Мета - зберегти більшість з'єднань (посилань, дзвінків) всередині одного блоку, маючи декілька з'єднань між підрозділами . Визначення декількох інтерфейсів, які клас реалізує, з різним інтерфейсом на кожний випадок використання, може бути корисним першим кроком до розбиття.
ToolmakerSteve

@ToolmakerSteve Добрі думки +1
Зак Макомбер

9

Створення однієї масивної таблиці суперечить принципам реляційних баз даних. Я б не поєднав їх усіх в одну таблицю. Ви збираєтеся отримати кілька примірників повторних даних. Якщо у вашого користувача, наприклад, є три інтереси, у вас буде 3 рядки з однаковими даними користувача лише для зберігання трьох різних інтересів. Однозначно підходити до декількох «нормалізованих» підходів до таблиці. Дивіться цю сторінку Wiki для нормалізації роботи бази даних.

Редагувати: Я оновив свою відповідь, як і ви оновили своє запитання ... Я погоджуюся з моєю початковою відповіддю ще більше з моменту ...

значна частина цих комірок, ймовірно, залишаться порожніми

Якщо, наприклад, у користувача не було жодних інтересів, якщо ви нормалізуєте, тоді ви просто не будете мати рядок у таблиці інтересів для цього користувача. Якщо у вас є все в одній масивній таблиці, то у вас будуть стовпці (і, мабуть, їх багато), які містять просто NULL.

Я працював у телефонній компанії, де було багато таблиць, отримання даних може зажадати багатьох приєднань. Коли ефективність читання з цих таблиць була критичною, тоді створювалися процедури, які могли б генерувати плоску таблицю (тобто денормалізовану таблицю), яка не потребувала б з'єднань, обчислень тощо, на які могли вказувати звіти. Вони використовуються разом із агентом сервера SQL для виконання завдання через певні проміжки часу (тобто щотижневий перегляд деяких статистичних даних буде виконуватись раз на тиждень тощо).


Мені подобається такий підхід, тому що денормалізовані дані існують лише тимчасово, як знімок моменту часу. Немає проблем із вставкою / зміною / видаленням - просто викиньте її після завершення.
ToolmakerSteve

7

Чому б не використовувати той самий підхід, який Wordpress робить, створивши таблицю користувачів з базовою інформацією про користувача, яку має кожен, а потім додавши таблицю "user_meta", яка в основному може бути будь-яким ключем, паролем значень, пов'язаним з ідентифікатором користувача. Отже, якщо вам потрібно знайти всю мета-інформацію для користувача, ви можете просто додати її до свого запиту. Також вам не завжди доведеться додавати додатковий запит, якщо це не потрібно для таких речей, як вхід у систему. Перевага від такого підходу також залишає вашу таблицю відкритою для додавання нових функцій для ваших користувачів, таких як зберігання їх ручки щебетання або кожного окремого інтересу. Вам також не доведеться мати справу з лабіринтами асоційованих ідентифікаторів, оскільки у вас є одна таблиця, яка правила всіх метаданих, і ви обмежите її лише однією асоціацією замість 50.

Wordpress спеціально робить це, щоб додати функції за допомогою плагінів, тому дозволяє ваш проект бути більш масштабованим і не потребуватиме повного капітального ремонту бази даних, якщо вам потрібно додати нову функцію.


Таблиця Wordpress wp_usermetaросте геометрично. Кожен користувач додає X wp_usermetaтаблиці до таблиці, по одному рядку для кожного фрагмента метаінформації, який ми хочемо зберегти для цього користувача. Якщо ви зберігаєте 8 користувацьких полів для кожного користувача, це означає, що wp_usermeta буде users * 8довгими рядками. Здається, це спричиняє проблеми з продуктивністю, але я не впевнений, це проблема чи ні ...
третій

1
Я міг бачити, як це може спричинити проблеми з продуктивністю, якщо у вас є десятки тисяч користувачів. В основному, в базі даних доведеться шукати 10000 * 8 записів у мета-таблиці користувача, щоб знайти ті, які шукаєте. Однак якщо ви запитаєте лише дані Meta, коли це потрібно, я думаю, що ваша ефективність буде кращою. Якщо ви завжди запитуєте метадані, навіть коли вони вам не потрібні, у вас можуть виникнути проблеми. Якщо вам завжди потрібні метадані, то, можливо, розділення таблиць - не найкращий підхід.
Руді Гарсія

1
Лише вчора ми розглядали тему WP, яка завантажувала всіх користувачів (використовуючи get_users()) лише для обчислення сторінки. Після того, як ми виправили код, щоб SELECT COUNT(…)замість цього використати запит для сторінки, час завантаження сторінки піднявся від 28 секунд до приблизно 400 мс. Мені все ще цікаво, як продуктивність порівнюється з об'єднаними таблицями або єдиною плоскою таблицею ... У мене виникли проблеми з пошуком будь-яких показників ефективності в Інтернеті.
третій день

Думаючи про мій попередній коментар, здається, що розділення таблиці все ще ефективно, якщо з якихось причин, наприклад, наведеного вище прикладу розбиття сторінки, вам не потрібно буде вибрати всіх користувачів. Хоча якщо ви отримуєте всю метаінформацію, у вас все одно буде 80k записів у таблиці usermeta. Це дуже багато для пошуку. Можливо, хтось міг би перевірити, що є кращим підходом, запустивши скрипт на обох реалізаціях і запустивши його 100 разів, щоб отримати середнє значення, я можу просто зробити це.
Руді Гарсія

1
Я прочитав це ще раз сьогодні і зрозумів, що мій коментар щодо 10000 * 8 записів правдивий, проте спосіб роботи бази даних повинен зробити це, головним чином, не проблемою. Якби ви чомусь захопили всіх 10000 користувачів І, а також їх мета-інформацію, це було б смішно. Я не можу придумати жодного сценарію, де ви цього хотіли б. База даних легко знайде мета для одного користувача зі швидкістю блискавки, хоча через сторонні ключі та індексацію. Якщо припустимо, що модель db встановлена ​​правильно.
Руді Гарсія

5

Я думаю, що це одна з тих ситуацій, що "залежать". Маючи кілька таблиць - це чистіше і, мабуть, теоретично краще. Але коли вам доведеться приєднатися до 6-7 таблиць, щоб отримати інформацію про одного користувача, ви можете почати переосмислювати цей підхід.


1

Я б сказав, що це залежить від того, що насправді означають інші таблиці. Чи містять у user_details більше 1 / користувачів тощо. Який рівень нормалізації найкраще відповідає вашим потребам, залежить від ваших потреб.

Якщо у вас є одна таблиця з хорошим індексом, це, мабуть, буде швидше. Але з іншого боку, напевно, складніше підтримувати.

Для мене це виглядає так, що ви можете пропустити User_Details, оскільки це, ймовірно, стосується 1 до 1 з користувачами. Але решта, мабуть, багато рядків на користувача?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.