Чому приєднання погано при розгляді масштабованості?


92

Чому приєднання погані або "повільні". Я знаю, що чув це ще раз. Я знайшов цю цитату

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

джерело

Я завжди думав, що вони швидкі, особливо коли шукаю ПК. Чому вони 'повільні'?

sql  join 

Відповіді:


98

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

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

Але пам’ятайте, альтернатива - це відсутність взагалі двох окремих фрагментів даних; ви повинні помістити дві різні точки даних в один і той же запис. Ви не можете комбінувати дві різні дані без наслідків десь, тому переконайтеся, що ви розумієте компроміс.

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

  • Приєднуйтесь до сурогатного ключа (стовпець для автономера / особи), а не природний ключ. Це означає менші (і, отже, більш швидкі) порівняння під час операції з'єднання
  • Покажчики
  • Матеріалізовані / індексовані погляди (вважайте це за попередньо обчислене з'єднання або керовану денормалізацію)
  • Обчислені стовпці. Ви можете використовувати це для хешування або попереднього обчислення ключових стовпців об’єднання, так що те, що було б складним порівнянням для об’єднання, тепер набагато менше і потенційно попередньо індексується.
  • Розділи таблиці (допомагає у великих наборах даних, розподіляючи навантаження на кілька дисків або обмежуючи те, що могло бути скануванням таблиці, до сканування розділів)
  • OLAP (попередньо обчислює результати певних видів запитів / об'єднань. Це не зовсім вірно, але ви можете сприймати це як загальну денормалізацію)
  • Реплікація, групи доступності, доставка журналів чи інші механізми, що дозволяють декільком серверам відповідати на запити на читання для однієї бази даних, і таким чином масштабувати ваше навантаження між кількома серверами.
  • Використання шару кешування, як Redis, щоб уникнути повторного запуску запитів, які потребують складних об'єднань.

Я б хотів сказати, що головна причина, що взагалі існують реляційні бази даних, - це дозволяти вам ефективно приєднуватися * . Це, звичайно, не просто для зберігання структурованих даних (ви могли б зробити це за допомогою плоских файлових конструкцій, таких як CSV або XML). Деякі з перерахованих мною варіантів дозволять вам повністю заздалегідь побудувати своє приєднання, тому результати вже зроблені перед тим, як ви надсилаєте запит - так само, як якщо б ви денормалізували дані (правда, ціною повільніших операцій запису).

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

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

* Тобто існують як сутності, відмінні від простих колекцій таблиць. Додатковою причиною справжнього rdbms є безпечний одночасний доступ.


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

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

У своєму третьому пункті ви згадуєте "Матеріалізовані / індексовані види". Ви говорите про регулярні перегляди SQL чи щось інше?
slolife

@slolife регулярні перегляди sql - це подібні до запуску додаткового запиту у фоновому режимі під час використання запиту, який посилається на подання. Але ви також можете сказати sql-серверу "матеріалізувати" деякі перегляди. Коли ви це зробите, сервер sql зберігатиме додаткову копію даних перегляду, як і звичайну таблицю, щоб при посиланні на перегляд у запиті більше не потрібно запускати цей запит у фоновому режимі, оскільки дані вже є . Ви також можете розміщувати різні покажчики на перегляді, ніж вихідну таблицю, щоб надалі допомогти вам налаштувати продуктивність.
Джоель Куехорн

Дякую, Джоел. Мені доведеться розібратися в цьому.
slolife

29

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

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


2
... за винятком MySQL, який, схоже, має проблеми з продуктивністю при великій кількості приєднань незалежно від того, як виглядають ваші індекси. Або, принаймні, це було в минулому.
Powerlord

2
Слід зазначити, якщо є відомі проблеми з конкретною СУБД (і, можливо, навіть версією), то ця порада може мати сенс, але як загальна порада вона досить оманлива, якщо ви використовуєте реляційну базу даних. Тим не менш, нереляційні механізми зберігання стають все більш популярними Amazon SimpleDB і CouchDB ( couchdb.apache.org ) є прикладами. Якщо вам краще послужити, залишивши реляційну модель позаду, ви, ймовірно, повинні залишити продукти, оптимізовані для себе, і шукати інші інструменти.
Tendayi Mawushe

13

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


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

11

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

В академічному світі ми дізнаємося про такі речі, як різні нормальні форми (1-й, 2-й, 3-й, Бойс-Кодд тощо), а також дізнаємося про різні типи клавіш (первинні, іноземні, почергові, унікальні тощо) та як ці речі поєднуються разом для створення бази даних. І ми дізнаємося зачатки SQL, а також маніпулюючи як структурою, так і даними (DDL & DML).

У корпоративному світі багато академічних конструкцій виявляються суттєво менш життєздатними, ніж нас припускали. Прекрасним прикладом є поняття первинного ключа. В академічному плані саме той атрибут (або колекція атрибутів) однозначно ідентифікує один рядок у таблиці. Таким чином, у багатьох проблемних областях правильний академічний первинний ключ є складовою з 3 або 4 атрибутів. Однак майже кожен у сучасному корпоративному світі використовує автоматично сформоване послідовне ціле число як основний ключ таблиці. Чому? Дві причини. По-перше, це робить модель набагато чистішою, коли ви мігруєте FK-файли всюди. Другий, і найбільш германний до цього питання, полягає в тому, що отримання даних через з'єднання відбувається швидше і ефективніше в одному цілому цілому, ніж це на 4 колонках вархара (як уже згадувалося кількома людьми).

Давайте трохи глибше розберемося зараз у двох конкретних підтипах реальних баз даних. Перший тип - транзакційна база даних. Це основа для багатьох програм електронної комерції чи управління контентом, які керують сучасними сайтами. Завдяки БД транзакцій ви оптимізуєтесь до "пропускної здатності транзакцій". Більшість комерційних або контентних додатків мають збалансувати ефективність запитів (з певних таблиць) із ефективністю вставки (в інших таблицях), хоча для кожного додатка доведеться вирішувати свої унікальні проблеми, керовані бізнесом.

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

У кожному конкретному випадку розробнику або DBA необхідно ретельно збалансувати як функціональні, так і криві продуктивності, і існує багато хитрощів для підвищення продуктивності з обох сторін рівняння. В Oracle ви можете робити те, що називається "план пояснення", щоб ви могли конкретно бачити, як запит аналізується та виконується. Ви прагнете максимізувати належне використання БД індексів. Один насправді неприємний "ні-ні" - це ставити функцію в пункті запиту, де. Щоразу, коли ви це зробите, ви гарантуєте, що Oracle не буде використовувати жодних індексів у цьому конкретному стовпці, і ви, ймовірно, побачите повне або часткове сканування таблиці в плані пояснення. Це лише один конкретний приклад того, як може бути записаний запит, який закінчується повільним, і він не має нічого спільного з об'єднаннями.

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

Поговоримо про нормалізацію протягом хвилини. Це ще одна значною мірою позитивна академічна тема, яка може переоцінитись. Більшість випадків, коли ми говоримо про нормалізацію, ми дійсно маємо на увазі усунення дублікатів даних, помістивши їх у власну таблицю та перемістивши FK. Люди зазвичай пропускають всю залежність, описану 2NF та 3NF. І все ж у крайньому випадку, безумовно, можливо мати ідеальну базу даних BCNF, яка є величезною і повною звіром, щоб написати код, тому що це так нормалізується.

То де ми балансуємо? Не існує єдиної найкращої відповіді. Усі кращі відповіді, як правило, є компромісом між простотою обслуговування структури, простотою обслуговування даних та простотою створення / обслуговування коду. Загалом, чим менше дублюється даних, тим краще.

То чому приєднання іноді повільні? Іноді це поганий реляційний дизайн. Іноді це неефективне індексування. Іноді це проблема обсягу даних. Іноді це жахливо написаний запит.

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


10

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

Є багато причин не деномалізувати. По-перше, швидкість вибору запитів не є єдиним або навіть головним питанням баз даних. Цілісність даних - це перше питання. Якщо ви денормалізуєте, тоді вам доведеться застосувати методи, щоб зберегти дані денормалізованими в міру зміни батьківських даних. Отже, припустимо, ви берете на себе зберігання імені клієнта у всіх таблицях, а не приєднання до таблиці клієнта на client_Id. Тепер, коли ім’я клієнта змінюється (на 100% шанси деякі імена клієнтів змінюватимуться з часом), тепер вам потрібно оновити всі дочірні записи, щоб відобразити цю зміну. Якщо ви зробите це за допомогою каскадного оновлення, і у вас мільйон записів дітей, наскільки швидко ви гадаєте, що це буде, і скільки користувачів зазнають проблем із блокуванням та затримкою своєї роботи, поки це відбувається? Більшість людей, які денормалізують, тому що "

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

Приєднуйтесь досить швидко, якщо ви робите кілька речей. По-перше, використовуйте клавіш suggorgate, int приєднання майже забороняє найшвидше з'єднання. Другий завжди індексує зовнішній ключ. Використовуйте похідні таблиці або умови об’єднання, щоб створити менший набір даних для фільтрування. Якщо у вас є велика дуже складна база даних, найміть професійну базу даних, яка має досвід розбиття та управління величезними базами даних. Існує маса методик для підвищення продуктивності, не позбавляючись від приєднань.

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


8

Приєднання відбувається повільно, якщо

  • дані неправильно індексуються
  • результати погано фільтруються
  • приєднання запиту погано написано
  • набори даних дуже великі і складні

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

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


7

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

Подобається це:

SELECT  SUM(transaction)
FROM    customers
JOIN    accounts
ON      account_customer = customer_id

Навіть якщо індекс визначений на account_customer, всі записи з останнього все одно повинні бути відскановані.

Для списку запитів це пристойні оптимізатори, ймовірно, навіть не враховують шлях доступу до індексу, роблячи а HASH JOINчи MERGE JOINзамість цього.

Зверніть увагу, що для такого запиту:

SELECT  SUM(transaction)
FROM    customers
JOIN    accounts
ON      account_customer = customer_id
WHERE   customer_last_name = 'Stellphlug'

приєднання, швидше за все, буде швидким: по-перше, індекс на customer_last_nameбуде використовуватися для фільтрації всіх Stellphlug (яких, звичайно, не дуже багато), потім account_customerбуде видано сканування індексу для кожного Stellphlug для пошуку його транзакцій.

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


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

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

так, але це все одно не дешева експлуатація. ви можете зберігати суму в якомусь полі та оновлювати кожну транзакцію.
Андрій

@jemfinch: ні, вони не будуть. Для цього знадобиться сканувати весь індекс лише для відфільтрування клієнтів, а потім сканування індексу клієнта в вкладеному циклі. A HASH JOINбуло б набагато швидшим, тому саме це буде використовуватися за винятком усіх основних баз даних, крім тих MySQL, які просто зроблять customersпровід у вкладеному циклі (оскільки він менший за розміром)
Quassnoi,

4

Joins are fast.Приєднання слід вважати стандартною практикою з правильно нормалізованою схемою бази даних. Приєднання дозволяють змістовно поєднувати різні групи даних. Не бійтеся приєднання.

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

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

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

Правда також, що птах летить швидше без крил, а лише прямо вниз.


3

Приєднання вимагають додаткової обробки, оскільки їм потрібно шукати більше файлів і більше індексів, щоб "об'єднати" дані разом. Однак "дуже великі набори даних" все відносні. Що таке визначення великого? У випадку з JOIN, я думаю, що це посилання на великий набір результатів, а не на загальний набір даних.

Більшість баз даних можуть дуже швидко обробити запит, який вибирає 5 записів з первинної таблиці та приєднує 5 записів із пов’язаної таблиці для кожного запису (за умови правильних індексів). Ці таблиці можуть мати сотні мільйонів записів кожна, а то й мільярди.

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

Не уникайте СПІЛКУВАННЯ, просто знайте, що вам може знадобитися оптимізувати / денормалізувати, коли набори даних стають "дуже великими".


3

Також із цитованої вами статті:

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

і

І якщо ви не дуже великий веб-сайт, вам, мабуть, не потрібно турбуватися про цей рівень складності.

і

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

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


2

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


Я не впевнений, що це правда. Я знаю, що Терадата, безумовно, може розподілити приєднання серед Ампер. Очевидно, що певні типи об'єднань можуть бути складнішими / нерозв'язними, ніж інші.
Кейд Ру

індекси можуть бути розділені на RDBMS, починаючи від mysql і оракула. AFAIK, що масштабує (розподіляється і може бути паралельним).
Нерозумно

2

Правильно розроблені таблиці, що містять відповідні індекси та правильно написані запити, не завжди повільні. Де ви коли-небудь чули це:

Чому приєднання погані чи «повільні»

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


1

Кількість тимчасових даних, які генеруються, може бути величезною залежно від об'єднань.

Наприклад, одна база даних, яка тут працює, мала загальну функцію пошуку, де всі поля були необов’язковими. Процедура пошуку об’єднувалась за кожним столом перед початком пошуку. На початку це добре працювало. Але зараз, коли основна таблиця має понад 10 мільйонів рядків ... не так вже й багато. Зараз пошук займає 30 хвилин або більше.

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

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

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

Використання процесора SQL-сервера також пішло ДО ВИГОДИ.


@BoltBait: Чи є повідомлення про винос, що ви завжди повинні намагатися зменшити кількість рядків, перш ніж виконувати приєднання?
unutbu

Це, безумовно, творило чудеса в моєму випадку. Але я б не оптимізував систему, поки вона не стане необхідною.
BoltBait

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

1

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

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


1

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

Наведені в статті приклади - Flickr та eBay - є винятковими випадками IMO, тому вони мають (і заслуговують) виняткову відповідь. Автор спеціально зазначає відсутність RI та ступінь дублювання даних у статті.

Більшість програм - знову ж таки, IMO - отримують вигоди від перевірки та зменшення дублювання, що надаються СУБД.


0

Вони можуть бути повільними, якщо зробити це неохайно. Наприклад, якщо ви зробите 'select *' під час приєднання, вам, можливо, знадобиться певний час, щоб повернути речі. Однак якщо ви ретельно вибираєте, які стовпці повертати з кожної таблиці, та з відповідними індексами на місці, проблем не повинно бути.

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