Помилки в розробці бази даних, допущені розробниками додатків [закрито]


566

Які поширені помилки в розробці бази даних, які роблять розробники додатків?


Відповіді:


1002

1. Не використовуючи відповідних індексів

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

2. Не забезпечення референтної цілісності

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

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

Більше тут:

3. Використання природних, а не сурогатних (технічних) первинних ключів

Натуральні ключі - це ключі, засновані на зовнішньо значущих даних, які (нібито) унікальні. Найпоширенішими прикладами є коди товарів, двобуквені коди штатів (США), номери соціального страхування тощо. Сурогатні або технічні первинні ключі - це ті, які абсолютно не мають значення поза системою. Вони винайдені виключно для ідентифікації сутності та є типовими полями автоматичного збільшення (SQL Server, MySQL, інші) або послідовностями (особливо, Oracle).

На мою думку, ви завжди повинні використовувати сурогатні ключі. Це питання виникло в таких питаннях:

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

Пам'ятайте, навіть країни можуть припинити своє існування (наприклад, Югославія).

4. Написання запитів, які потребують DISTINCTроботи

Ви часто бачите це в запитах, створених ORM. Подивіться на вихід журналу зі сплячого режиму, і ви побачите, що всі запити починаються з:

SELECT DISTINCT ...

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

З чого я ненавиджу ДИСТИНКТ :

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

5. Вигідне агрегування над приєднаннями

Ще одна поширена помилка розробників додатків баз даних - це не усвідомлювати, наскільки дорожче агрегацію (тобто GROUP BYпункт) можна порівняти з приєднанням.

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

З оператора SQL - "приєднатися" до "згрупувати і мати" :

Перший запит:

SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3

Час запиту: 0,312 с

Другий запит:

SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
AND t1.roleid = 1

Час запиту: 0.016 с

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

6. Не спрощуючи складні запити через перегляди

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

  • Партія : люди та організації;
  • Роль партії : те, що робили ці сторони, наприклад, працівник та роботодавець;
  • Рольові стосунки між сторонами : як ті ролі пов’язані між собою.

Приклад:

  • Тед - Людина, будучи підтипом Партії;
  • Тед має багато ролей, одна з яких - «Співробітник»;
  • Intel - це організація, будучи підтипом Сторони;
  • Intel має багато ролей, одна з яких - «Роботодавець»;
  • Intel використовує Теда, тобто існує взаємозв'язок між їхніми ролями.

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

CREATE VIEW vw_employee AS
SELECT p.title, p.given_names, p.surname, p.date_of_birth, p2.party_name employer_name
FROM person p
JOIN party py ON py.id = p.id
JOIN party_role child ON p.id = child.party_id
JOIN party_role_relationship prr ON child.id = prr.child_id AND prr.type = 'EMPLOYMENT'
JOIN party_role parent ON parent.id = prr.parent_id = parent.id
JOIN party p2 ON parent.party_id = p2.id

І раптом у вас є дуже простий перегляд потрібних даних, але на дуже гнучкої моделі даних.

7. Не саніруючі введення

Це величезна. Зараз мені подобається PHP, але якщо ви не знаєте, чим займаєтесь, створити сайти, вразливі для атаки, дуже просто. Ніщо не підводить це краще, ніж історія маленького Боббі Столи .

Дані, надані користувачем за допомогою URL-адрес, даних форми та файлів cookie, завжди повинні розглядатися як ворожі та захищені. Переконайтеся, що ви отримуєте те, що очікуєте.

8. Не використовуючи підготовлені заяви

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

SELECT * FROM users WHERE username = 'bob'

проти

SELECT * FROM users WHERE username = ?

або

SELECT * FROM users WHERE username = :username

залежно від вашої платформи.

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

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

Підготовлені заяви також краще захистять вас від атак на ін'єкції SQL.

9. Недостатньо нормалізується

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

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

Це також з'явилося в кращому методі зберігання списку ідентифікаторів користувачів :

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

Але відсутність нормалізації буває у багатьох формах.

Більше:

10. Занадто нормалізуючи

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

Я колись працював над системою, яка мала величезну ієрархію для клієнтів, яка працювала на кшталт:

Licensee ->  Dealer Group -> Company -> Practice -> ...

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

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

Більше:

11. Використання ексклюзивних дуг

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

Від практичного посібника до реляційного дизайну баз даних :

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

12. Не роблячи аналізу ефективності запитів

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

13. Надмірна залежність від UNION ALL і особливо конструкцій UNION

UNION в термінах SQL просто об'єднує впорядковані набори даних, тобто вони мають один і той же тип і кількість стовпців. Різниця між ними полягає в тому, що UNION ALL - це проста конкатенація, і її слід віддавати перевагу, де це можливо, тоді як UNION неявно зробить DISTINCT для видалення повторюваних кортежів.

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

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

14. Використання умов АБО у запитах

Це може здатися нешкідливим. Зрештою, ANDs нормально. АБО має бути гаразд? Неправильно. В основному умова AND обмежує набір даних, тоді як умова АБО зростає, але не таким чином, що піддається оптимізації. Особливо, коли різні умови АБО можуть перетинатися, змушуючи оптимізатор ефективно виконувати операцію DISTINCT на результаті.

Погано:

... WHERE a = 2 OR a = 5 OR a = 11

Краще:

... WHERE a IN (2, 5, 11)

Тепер ваш оптимізатор SQL може ефективно перетворити перший запит у другий. Але це не може. Просто не робіть цього.

15. Не розробляючи їх модель даних, щоб піддаватися високоефективним рішенням

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

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

Передчасна оптимізація - корінь усього зла

16. Неправильне використання транзакцій баз даних

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

В ідеалі найпростіший спосіб досягти цього полягає в тому, що вся конструкція системи повинна прагнути підтримувати всі зміни даних за допомогою окремих операторів INSERT / UPDATE / DELETE. У цьому випадку ніяких спеціальних операцій з обробкою транзакцій не потрібно, оскільки система двигуна бази даних повинна робити це автоматично.

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

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

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

17. Не розуміючи парадигми на основі набору

Мова SQL дотримується певної парадигми, яка підходить для конкретних проблем. Незважаючи на різноманітні розширення для конкретних постачальників, мова намагається вирішувати проблеми, які є тривіальними в язиках, таких як Java, C #, Delphi тощо.

Ця нерозуміння проявляється кількома способами.

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

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


9
Щодо тверджень MySQL про зовнішні ключі, ви праві, що MyISAM їх не підтримує, але ви маєте на увазі, що просто використання MyISAM - це поганий дизайн. Причиною, що я використовував MyISAM, є те, що InnoDB не підтримує пошук FullText, і я не вважаю це необґрунтованим.
Дерек H

1
Я повинен запитати про №6. Використання подібних представлень - це одна з моїх улюблених речей, але я, на жаль, дізнався, що з MySQL індекси на нижчих таблицях виконуються лише у тому випадку, якщо структура представлення дозволяє використовувати алгоритм злиття. В іншому випадку використовується таблиця темп, і всі ваші індекси марні. Це ще тривожніше, коли ти розумієш, що купа операцій викликає таку поведінку. Це прекрасний спосіб перетворити запит .01 сек на запит на 100 секунд. Хтось ще має досвід цього? Перевірте посилання в моєму наступному коментарі.
Пітер Бейлі

5
Повністю не згоден з №3. Так, країни можуть припинити своє існування, але код країни і надалі представлятиме те саме. Те саме з кодами валют або штатами США. Німецько використовувати сурогатний ключ у цих випадках і створює більше накладних витрат у ваших запитах, оскільки ви повинні включати додаткове приєднання. Я б сказав, що безпечніше сказати, що ви, мабуть, повинні використовувати сурогат для даних, що стосуються користувачів (таким чином, не для країн, валют та штатів США).
Томас

1
RE: # 11 Обмеження для перевірки, необхідне для забезпечення цілісності даних, є тривіальним. Є й інші причини уникнення цього дизайну, але необхідність "складного" обмеження перевірки не одна з них.
Томас

2
З №3 ви не чесні. У штучного ключа є більше недоліків, ніж "він вам може не знадобиться". Зокрема, використання природного ключа дасть вам можливість контролювати порядок запису даних на вашу таблицю. Якщо ви знаєте, як буде проведена запит на вашу таблицю, ви можете проіндексувати її, щоб рядки, що звертаються одночасно, опиняться на одній сторінці. Крім того, ви можете забезпечити цілісність даних, використовуючи унікальний складений індекс. Якщо вам це потрібно, вам доведеться додати його до вашого індексу штучного ключа. Якщо зазначений складений індекс - ваш pkey, це 2 птахи, вбиті одним каменем.
Шейн Н

110

Основні помилки проектування бази даних та програмування, допущені розробниками

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

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

  • Боїться написання SQL. SQL не є ракетною наукою і насправді дуже добре виконує свою роботу. Шари відображення O / R досить добре справляються з 95% запитів, які є простими і добре вписуються в цю модель. Іноді SQL - найкращий спосіб виконати цю роботу.

  • Догматична політика "Без збережених процедур". Незалежно від того, чи вважаєте ви, що зберігаються процедури є злими, подібне догматичне ставлення не має місця в програмному проекті.

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


2
Можна стверджувати, що трансакції повинні здійснюватися в транзакційній базі даних та звітності, а MIS слід робити в окремій базі даних аналізу. Тому ви отримуєте найкраще з обох світів, і всі радіють (за винятком бідного кухоль, який повинен написати сценарій перетворення даних, щоб побудувати останнє з першого).
Кріс Сімпсон

Не тільки поганий кухоль, що пише ETL - кожен, хто використовує дані з системи, дані низької якості в додатку MIS, який є в комплекті, тому що кілька ключових зв’язків насправді не записуються в джерелі, хтось, хто бере участь у нескінченних помилках примирення. від поганої якості даних.
СтурбованоOfTunbridgeWells

Я не міг би більше погодитися з першим. Бази даних призначені для постійності, вони не для міжпроцесорної комунікації. Майже завжди є кращі рішення цієї проблеми. Якщо немає явної вимоги до цього, ви абсолютно ДОЛЖНЕ ставитись до бази даних так, ніби ніхто, крім вашої програми, ніколи не використовуватиме її. Навіть якщо є явна вимога, зробіть на ній деяку історію користувача та аналіз першопричини, і ви часто знайдете набагато кращий спосіб заповнити наміри запитувача. Потім я знову працюю в компанії, де фраза CQRS дещо поширена
Джордж Мауер

3
Тривіальний приклад: у мене є система адміністрування страхового полісу і мені потрібно завантажити 5 мільйонів вимог у відступну систему перестрахування для обчислення потенційного стягнення. Системи - це більш старі пакети COTS клієнт-сервер, призначені для взаємодії з навіть старими системами мейнфреймів. Обидва мають бути узгоджені для цілей фінансового контролю. Ця робота виконується раз на місяць. За вашою логікою я б написав серію історій користувачів з визначенням вимог і попросив би продавців процитувати додавання обгортки веб-служб до своїх існуючих продуктів.
СтурбованоOfTunbridgeWells

2
Тоді ваша DBA або ледача, або некомпетентна.
Занепокоєний

80
  1. Не використовується контроль версій на схемі бази даних
  2. Робота безпосередньо проти живої бази даних
  3. Не читати та не розуміти більш просунуті концепції баз даних (індекси, кластерні індекси, обмеження, матеріалізовані представлення тощо)
  4. Якщо не вдалося перевірити масштабованість ... дані тесту лише 3 або 4 рядки ніколи не дадуть вам реальну картину реального виконання в реальному часі

1
Я другий, важко, №1 та №2. Щоразу, коли я вношу зміни до БД, я скидаю її схему та версію; У мене є три налаштування баз даних: розробка, постановка і жива - НІКОЛИ ніколи не "тестується" на прямій БД !!
Іксмат

Тут у Red Gate ми вжили заходів, щоб покращити вашу першу точку за допомогою керування джерелами SQL! З розмов, які я проводив під час свого дослідження, я думаю, що люди вже не розвиваються проти виробничих баз даних, але часто робляться «аварійні» виправлення, які, як правило, знаходять свій шлях до середовищ розробки, а це вже інша проблема.
Девід Аткінсон

46

Надмірне використання та / або залежність від збережених процедур.

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

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

Нещодавно мені довелося допомогти підтримувати та вдосконалювати великий настільний додаток Delphi, 70% бізнес-логіки та правил якого були реалізовані в 1400 збережених процедурах SQL Server (решта - в обробниках подій інтерфейсу). Це було кошмаром, насамперед через те, що чітко було запроваджено ефективне тестування модулів на TSQL, відсутність інкапсуляції та бідні інструменти (налагоджувачі, редактори).

Раніше працюючи з командою Java, я швидко виявив, що часто повне протилежне стосується цього середовища. Якось архітектор Java сказав мені: "База даних - це дані, а не код."

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


4
Збережені процедури, як правило, стають островом болю в будь-якому проекті, де вони використовуються, тому деякі розробники формують правило "Без збережених процедур". Так виглядає, що між ними відкритий конфлікт. Ваша відповідь дає хороший випадок, коли насправді вибрати той чи інший шлях.
Warren P

Переваги: ​​безпека - не потрібно надавати програмам можливість "видаляти * з ..."; налаштування - DBA може налаштувати запити без необхідності перекомпілювати / розгортати всю програму; аналіз - легко перекомпілювати купу документів після зміни моделі даних, щоб переконатися, що вони все ще дійсні; і, нарешті, враховуючи, що SQL виконується двигуном бази даних (а не вашим додатком), тоді поняття "база даних для даних, а не код" просто відстала.
NotMe

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

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

@too: Зміна бази даних у більшості випадків дуже дорога. Не зважайте на ідею втрати функціональних можливостей та безпеки, які надає конкретна СУБД. Крім того, додаткові яруси додають складності та знижують продуктивність, а додаткові шари прив’язуються до вашої конкретної мови. Нарешті, швидше за все, мова, що використовується, зміниться, ніж сервер бази даних.
NotMe

41

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


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

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


28

Погана продуктивність, викликана співвіднесеними підзапити

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

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

select e.first_name, e.last_name
from employee e
where e.start_date > 
        (select max(ds.transaction_date)
         from daily_sales ds
         where ds.store_id = e.store_id and
               ds.total < 10000)

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

select e.first_name, e.last_name
from employee e,
     (select ds.store_id,
             max(s.transaction_date) transaction_date
      from daily_sales ds
      where ds.total < 10000
      group by s.store_id) dsx
where e.store_id = dsx.store_id and
      e.start_date > dsx.transaction_date

У цьому прикладі запит у пункті від тепер є вбудованим переглядом (знову ж таки, певний синтаксис Oracle) та виконується лише один раз. Залежно від вашої моделі даних, цей запит, ймовірно, буде виконуватися набагато швидше. Коли кількість працівників зростала, це було б краще, ніж перший запит. Перший запит фактично міг би працювати краще, якби було мало працівників та багато магазинів (а можливо, у багатьох магазинах не було працівників), а таблиця daily_sales була індексована на store_id. Це не вірогідний сценарій, але показує, як корельований запит міг би бути краще, ніж альтернативний.

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


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

21

На моєму досвіді:
Не спілкування з досвідченими спеціалістами DBA.


17

Використання Access замість "реальної" бази даних. Є безліч чудових маленьких і навіть безкоштовних баз даних, таких як SQL Express , MySQL і SQLite, які працюватимуть і масштабуватимуться набагато краще. Програми часто потребують масштабування несподіваними способами.


16

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


14

Використання Excel для зберігання (величезної кількості) даних.

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


Excel добре підходить для звітів, представлення даних та інших завдань, але не слід розглядати як базу даних.


14

Я хотів би додати: Улюблений "Елегантний" код над високоефективним кодом. Код, який найкраще працює проти баз даних, часто неприємний для розробника додатків.

Вважаю, що дурниці щодо передчасної оптимізації. Бази даних повинні враховувати ефективність в оригінальному дизайні та в будь-якій подальшій розробці. Продуктивність - це 50% дизайну бази даних (40% - це цілісність даних, а останні 10% - безпека), на мою думку. Бази даних, які не будуються знизу вгору, будуть працювати погано, коли реальні користувачі та реальний трафік будуть розміщені проти бази даних. Передчасна оптимізація не означає ніякої оптимізації! Це не означає, що ви повинні писати код, який майже завжди буде погано працювати, оскільки вам це простіше (наприклад, курсори, які ніколи не повинні бути дозволені у виробничій базі даних, якщо все інше не вийшло). Це означає, що вам не потрібно дивитись на те, щоб вичавити цю останню ефективність, поки не потрібно. Багато відомо про те, що буде краще працювати в базах даних,


2
+1 - Програмування бази даних передбачає оптимізацію поведінки механічних компонентів. Однак зауважте, що Кнут каже, що передчасна оптимізація є коренем усього зла приблизно 97% часу (або слів для цього). Дизайн бази даних - це одна сфера, де вам справді доводиться замислюватися над цим.
ЗанепокоєнняOfTunbridgeWells

2
Ах ... те, про що ви говорите, - це оптимізація, яка не є передчасною. Деяке врахування реального використання потрібно з самого початку в дизайні баз даних (і в дійсному дизайні додатків). Правило Кнут насправді не тривіально дотримуватися, тому що ви повинні вирішити, що є передчасним, а що ні - воно насправді зводиться до того, щоб "не проводити оптимізацію без даних". Ранні рішення, пов'язані з ефективністю, про які ви говорите, мають дані - певні конструкції встановлять неприйнятні обмеження щодо майбутньої продуктивності, і ви можете їх обчислити.
Роб Уілан

13

Не використовуються параметризовані запити. Вони дуже зручні в зупинці ін'єкції SQL .

Це конкретний приклад не санітарії вхідних даних, згаданий в іншій відповіді.


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

12

Я ненавиджу це, коли розробники використовують вкладені оператори select або навіть функції, які повертають результат оператора select всередині запиту "SELECT".

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

Приклад:

SELECT
    (SELECT TOP 1 SomeValue FROM SomeTable WHERE SomeDate = c.Date ORDER BY SomeValue desc) As FirstVal
    ,(SELECT OtherValue FROM SomeOtherTable WHERE SomeOtherCriteria = c.Criteria) As SecondVal
FROM
    MyTable c

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

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

Кращим (не обов’язково ідеальним) прикладом може бути щось на зразок:

SELECT
     s.SomeValue As FirstVal
    ,o.OtherValue As SecondVal
FROM
    MyTable c
    LEFT JOIN (
        SELECT SomeDate, MAX(SomeValue) as SomeValue
        FROM SomeTable 
        GROUP BY SomeDate
     ) s ON c.Date = s.SomeDate
    LEFT JOIN SomeOtherTable o ON c.Criteria = o.SomeOtherCriteria

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


12

Для баз даних на основі SQL:

  1. Не користуючись CLUSTERED INDEXES або вибираючи неправильні стовпці (и) для CLUSTER.
  2. Не використовувати тип даних SERIAL (автономери) як ПЕРШИЙ КЛЮЧ, щоб приєднатися до ІНОЗЕМНОГО КЛЮЧА (INT) у відносинах таблиці батько / дитина.
  3. Не ОНОВЛЕННЯ СТАТИСТИКИ на столі, коли багато записів були ВСТАНОВЛЕНІ або ВІДКРИТІ.
  4. Не реорганізувати (тобто вивантажувати, скидати, відновлювати, завантажувати та повторно індексувати) таблиці, коли багато рядків було вставлено або видалено (деякі двигуни фізично зберігають видалені рядки в таблиці із прапором видалення.)
  5. Не скориставшись ФРАГМЕНТУ НА ВИПУСКУ (якщо вона підтримується) на великих таблицях з високими ставками транзакцій.
  6. Вибір неправильного типу даних для стовпця!
  7. Вибір правильної назви стовпця.
  8. Не додавання нових стовпців у кінці таблиці.
  9. Не створювати належних індексів для підтримки часто використовуваних запитів.
  10. створення індексів на стовпцях з мало можливими значеннями та створення непотрібних індексів.
    ... ще додати.

1
Суперечка: 2) насправді є поганою практикою. Я бачу, на що ви потрапляєте - ви хочете отримати унікальний індекс цього автономера, і використовувати його як сурогатний ключ. Але первинний ключ не повинен бути авточислом, оскільки це не те, що первинний ключ IS: первинний ключ - це "те, про що йдеться в записі", який (крім речей, таких як операції з продажу) НЕ є автономним номером, а деяким унікальним бітом інформації про модельовану організацію.
Девід Т. Макнет

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

@David: Я виправданий! .. Не потрібно використовувати автономери в якості основного ключа, все ще можна мати індексований серійний стовпець у батьків, приєднавшись до сурогату у дитини, щоб гарантувати, що відношення не буде розірвано, маючи інше стовпчик як значущий основний, щоб знайти рядок!
Френк Р.

Це питання семантики, наприкінці дня ... і Microsoft вважає за краще, щоб первинні ключі були безглуздими, а не значущими. Дебати навколо неї тривають, але я потрапляю до "змістовного" табору. :)
Девід Т. Макнет

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

  • Використання команд DDL на збережених об'єктах (таких як таблиці, представлення даних) у збережених процедурах.

  • Боязнь використання збережених процедур або страх використовувати ORM-запити, де б це було більш ефективним / доцільним для використання.

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


8

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


Як далеко занадто далеко? Якщо жодна інформація не дублюється, як ви можете скористатись ними далі?
finnw

Нормалізація - це баланс видалення зайвих даних та збільшення гнучкості порівняно зі зниженням продуктивності та збільшенням складності. Пошук правильного балансу вимагає досвіду, і він змінюється з часом. Див. En.wikipedia.org/wiki/Database_normalization для інформації про те, коли денормалізувати
Натан Воксленд

8

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


Наслідок цього - це вивантаження надто великої кількості запитів на додаток замість того, щоб зберігати його в db, де він належить. LINQ в цьому особливо поганий.
3Dave

8
  • Відхилення ORM як сплячого з-поміж причин, таких як "це занадто магічно" або "не в моїй базі даних".
  • Занадто сильно покладаючись на ОРМ, як у сплячому режимі, і намагатися підключити його там, де це не підходить.

8

1 - Необов'язково використовувати функцію на значення в пункті, де результат, що цей індекс не використовується.

Приклад:

where to_char(someDate,'YYYYMMDD') between :fromDate and :toDate

замість

where someDate >= to_date(:fromDate,'YYYYMMDD') and someDate < to_date(:toDate,'YYYYMMDD')+1

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

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

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

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

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


7

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

Уникайте дублювання даних, як чума. Деякі люди відзначають, що невелике дублювання не зашкодить і покращить продуктивність. Гей, я не кажу, що вам доведеться мучити вашу схему в Третій звичайній формі, поки вона не буде настільки абстрактною, що навіть DBA не знає, що відбувається. Просто розумійте, що кожного разу, коли ви дублюєте набір імен, поштових індексів чи кодів доставки, копії з часом синхронізуються один з одним. Це станеться. І тоді ви будете бити себе, коли будете виконувати сценарій технічного обслуговування щотижня.

І останнє: використовуйте чітку, послідовну, інтуїтивну конвенцію про іменування. Таким же чином, як добре написаний фрагмент коду повинен бути читабеним, хороша схема SQL або запит повинні бути читабельними і практично розповідати , що вона робить, навіть без коментарів. Ви подякуєте собі через півроку, коли вам доведеться обслуговувати на столах. "SELECT account_number, billing_date FROM national_accounts"з нескінченно простішою роботою, ніж "ВИБРАТИ ACCNTNBR, BILLDAT ОТ NTNLACCTS".


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

6

Не виконувати відповідний запит SELECT перед запуском DELETE-запиту (особливо на виробничих базах даних)!


5

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


1
Я можу собі уявити жахи, які виникають у цих ситуаціях ... Бази даних без схем набагато краще підходять для швидкого прототипування та ітеративного розвитку, але, як і все інше, така гнучкість має різні компроміси.
Zsolt Török

4

a) Значення запиту жорсткого кодування в рядку
b) Введення коду запиту бази даних у дії "OnButtonPress" у додатку Windows Forms

Я бачив і те, і інше.


4
"Введення коду запиту БД у дії" OnButtonPress "у додатку Форма Windows" У чому помилка бази даних?
рекурсивна

@recursive: це величезна вразливість SQL для ін'єкцій. Будь-хто може надіслати на ваш сервер довільний SQL, і він буде запущений дослівно.
Білл Карвін

Погоджено з @recursive. Вони справді не мають нічого спільного з проблемами БД.
p.campbell

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

4

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


4
  1. Думаючи, що вони є DBA та модельєрами / дизайнерами даних, коли вони не мають офіційної індоктринації будь-якого типу в цих областях.

  2. Думаючи, що для їхнього проекту не потрібна DBA, тому що це все просто / тривіально.

  3. Недостатньо правильно розрізнити роботу, яку слід виконати в базі даних, та роботу, яку слід виконати в додатку.

  4. Не підтверджує резервне копіювання або не створює резервне копіювання.

  5. Вбудований сирий SQL у свій код.



3

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


3

Не розуміючи, як працює СУБД під кришкою.

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

Конкретно:

  1. Чи знаєте ви, що таке кластерний індекс? Ви думали про це, коли розробляли свою схему?

  2. Чи знаєте ви, як правильно використовувати індекси? Як повторно використовувати індекс? Чи знаєте ви, що таке індекс покриття?

  3. Так здорово, у вас є індекси. Наскільки великий у вашому індексі 1 рядок? Наскільки великим буде індекс, коли у вас буде багато даних? Це легко впишеться в пам’ять? Якщо це не буде, це марно як індекс.

  4. Ви коли-небудь використовували EXPLAIN в MySQL? Чудово. Тепер будьте чесні до себе: Ви зрозуміли навіть половину побаченого? Ні, ви, мабуть, цього не зробили. Виправте це.

  5. Ви розумієте кеш запитів? Чи знаєте ви, що робить запит неможливим?

  6. Ви використовуєте MyISAM? Якщо ви потребуєте повнотекстового пошуку, MyISAM все одно лайно. Використовуйте Сфінкс. Потім перейдіть до Inno.


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

3
  1. Використання ORM для масового оновлення
  2. Вибір більше даних, ніж потрібно. Знову, як правило, це робиться при використанні ORM
  3. Стріляння sqls у циклі.
  4. Не маючи хороших тестових даних і не помічаючи погіршення продуктивності лише на даних, що живуть.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.