Ошибки в разработке баз данных, сделанные разработчиками приложений [закрыто]

566

Какие распространенные ошибки при разработке баз данных делают разработчики приложений?

Charles Faiga
источник
Почти дубликат stackoverflow.com/questions/346659/…
dkretz

Ответы:

1002

1. Не использовать соответствующие индексы

Это относительно легко, но все же это происходит постоянно. Внешние ключи должны иметь индексы на них. Если вы используете поле в a, WHEREвы должны (вероятно) иметь индекс на нем. Такие индексы часто должны охватывать несколько столбцов на основе запросов, которые необходимо выполнить.

2. Не обеспечивает ссылочную целостность

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

Довольно часто можно увидеть этот сбой в базах данных MySQL. Я не верю, что MyISAM это поддерживает. InnoDB делает. Вы найдете людей, которые используют MyISAM или тех, кто использует InnoDB, но не использует его в любом случае.

Больше здесь:

3. Использование естественных, а не суррогатных (технических) первичных ключей

Естественные ключи - это ключи, основанные на внешне значимых данных, которые (якобы) уникальны. Типичными примерами являются коды продуктов, двухбуквенные коды штатов (США), номера социального страхования и так далее. Суррогатные или технические первичные ключи - это те, которые не имеют абсолютно никакого значения вне системы. Они придуманы исключительно для идентификации объекта и, как правило, представляют собой автоматически увеличивающиеся поля (SQL Server, MySQL и т. Д.) Или последовательности (в первую очередь Oracle).

На мой взгляд, вы всегда должны использовать суррогатные ключи. Эта проблема возникла в следующих вопросах:

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

Помните, что даже страны могут прекратить свое существование (например, Югославия).

4. Написание запросов, требующих DISTINCTработы

Вы часто видите это в ORM-сгенерированных запросах. Посмотрите на вывод журнала из Hibernate, и вы увидите, что все запросы начинаются с:

SELECT DISTINCT ...

Это своего рода быстрый способ убедиться, что вы не возвращаете дублирующиеся строки и, следовательно, не получаете дублирующиеся объекты. Иногда вы увидите, как люди делают это. Если вы видите это слишком много, это настоящий красный флаг. Не так 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. Не достаточно нормализуется

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

Только на этой неделе я наткнулся на какой-то код, где кто-то взорвал массив и вставил его в одно поле в базе данных. Нормализуя это, можно было бы рассматривать элемент этого массива как отдельную строку в дочерней таблице (т. Е. Отношение «один ко многим»).

Это также появилось в методе Best для хранения списка идентификаторов пользователей :

Я видел в других системах, что список хранится в сериализованном массиве PHP.

Но отсутствие нормализации проявляется во многих формах.

Больше:

10. Нормализация слишком много

Это может показаться противоречием предыдущему пункту, но нормализация, как и многие другие, является инструментом. Это средство для достижения цели, а не самоцель. Я думаю, что многие разработчики забывают об этом и начинают воспринимать «средства» как «цели». Модульное тестирование является ярким примером этого.

Однажды я работал над системой, которая имела огромную иерархию для клиентов, которая имела вид:

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

так что вам нужно было объединить около 11 таблиц, прежде чем вы смогли получить какие-либо значимые данные. Это был хороший пример нормализации, зашедшей слишком далеко.

Точнее говоря, осторожная и продуманная денормализация может иметь огромные преимущества в производительности, но вы должны быть очень осторожны при этом.

Больше:

11. Использование эксклюзивных дуг

Исключительная дуга является распространенной ошибкой, когда таблица создается с двумя или более внешними ключами, где один и только один из них может быть ненулевым. Большая ошибка. С одной стороны, становится намного сложнее поддерживать целостность данных. В конце концов, даже при ссылочной целостности ничто не препятствует установке двух или более этих внешних ключей (несмотря на сложные ограничения проверки).

Из практического руководства по проектированию реляционных баз данных :

Мы настоятельно советуем по возможности воздерживаться от создания эксклюзивных дуг, и на то есть веская причина, по которой им может быть неудобно писать код и создавать дополнительные трудности при обслуживании.

12. Не анализировать производительность по запросам вообще

Прагматизм царит, особенно в мире баз данных. Если вы придерживаетесь принципов до такой степени, что они стали догмой, вы, вероятно, допустили ошибки. Возьмите пример совокупных запросов сверху. Совокупная версия может выглядеть «красиво», но ее производительность ужасна. Сравнение производительности должно было закончить дискуссию (но не сделало этого), но более конкретно: высказывание таких плохо информированных взглядов, во-первых, невежественно, даже опасно.

13. Чрезмерная зависимость от конструкций UNION ALL и особенно UNION

Термины UNION в SQL просто объединяют конгруэнтные наборы данных, что означает, что они имеют одинаковый тип и количество столбцов. Разница между ними заключается в том, что UNION ALL - это простая конкатенация, и ее следует использовать везде, где это возможно, тогда как UNION будет неявно выполнять DISTINCT для удаления дублирующихся кортежей.

Союзы, как DISTINCT, имеют свое место. Есть действительные заявки. Но если вы обнаружите, что делаете много из них, особенно в подзапросах, то вы, вероятно, делаете что-то не так. Это может быть причиной плохой конструкции запроса или плохо разработанной модели данных, которая заставляет вас делать такие вещи.

UNION, особенно когда они используются в соединениях или зависимых подзапросах, могут нанести вред базе данных. Старайтесь избегать их, когда это возможно.

14. Использование условий OR в запросах

Это может показаться безвредным. В конце концов, И все в порядке. ИЛИ должно быть тоже хорошо? Неправильно. По существу, условие AND ограничивает набор данных, тогда как условие OR увеличивает его, но не таким образом, чтобы его можно было оптимизировать. В частности, когда различные условия ИЛИ могут пересекаться, вынуждая оптимизатор эффективно выполнять операцию 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 не поддерживает полнотекстовый поиск, и я не думаю, что это неразумно.
Дерек Х
1
Я должен спросить о # 6. Использование таких представлений - одно из моих любимых занятий, но недавно я с ужасом узнал, что с индексами MySQL для базовых таблиц подчиняются, только если структура представления позволяет использовать алгоритм слияния. В противном случае используется временная таблица, и все ваши индексы бесполезны. Это еще более тревожно, когда вы понимаете, что такое поведение вызывает множество операций. Это отличный способ превратить 0,01-секундный запрос в 100-секундный. У кого-нибудь здесь есть опыт с этим? Проверьте ссылки в моем следующем комментарии.
Питер Бейли
5
Полностью не согласен с # 3. Да, страны могут прекратить свое существование, но код страны будет продолжать представлять то же самое. То же самое с кодами валют или штатов США. В этих случаях глупо использовать суррогатный ключ, что создает дополнительные накладные расходы в ваших запросах, поскольку вы должны включить дополнительное объединение. Я бы сказал, что безопаснее сказать, что вы, вероятно, должны использовать суррогат для пользовательских данных (таким образом, не страны, валюты и штаты США).
Томас
1
RE: # 11 Ограничение проверки, необходимое для обеспечения целостности данных, тривиально. Существуют и другие причины, чтобы избежать такого дизайна, но необходимость в «сложных» проверочных ограничениях не является одной из них.
Томас
2
С # 3 ты не честен. У искусственного ключа больше недостатков, чем «он может вам не понадобиться». В частности, использование естественного ключа даст вам возможность контролировать порядок, в котором данные из вашей таблицы записываются на диск. Если вы знаете, как будет запрашиваться ваша таблица, вы можете проиндексировать ее, чтобы строки с одновременным доступом оказались на одной странице. Кроме того, вы можете обеспечить целостность данных, используя уникальный составной индекс. Если вам это нужно, вам придется добавить его в дополнение к индексу искусственного ключа. Если указанным составным индексом является ваша собака, значит, 2 камня убиты одним камнем.
Шейн Х
110

Ключевые ошибки в дизайне и программировании базы данных, сделанные разработчиками

  • Эгоистичный дизайн и использование базы данных. Разработчики часто рассматривают базу данных как свое личное постоянное хранилище объектов, не учитывая потребности других заинтересованных сторон в данных. Это также относится к разработчикам приложений. Плохая структура базы данных и целостность данных затрудняют работу с данными третьих сторон и могут существенно увеличить затраты на жизненный цикл системы. Отчеты и MIS имеют тенденцию быть плохим кузеном в разработке приложений и делаются только в качестве запоздалой мысли.

  • Злоупотребление денормализованными данными. Чрезмерное превышение денормализованных данных и попытка сохранить их в приложении - это рецепт для проблем целостности данных. Используйте денормализацию экономно. Отсутствие необходимости добавлять объединение в запрос не является оправданием для денормализации.

  • Боится писать SQL. SQL не является ракетостроением и на самом деле довольно хорошо справляется со своей работой. Уровни O / R-отображения достаточно хороши для выполнения 95% простых запросов, которые хорошо вписываются в эту модель. Иногда SQL - лучший способ сделать эту работу.

  • Догматические политики «Нет хранимых процедур». Независимо от того, считаете ли вы хранимые процедуры злыми, такого рода догматическому отношению нет места в программном проекте.

  • Не понимая дизайн базы данных. Нормализация - твой друг, и это не ракетостроение. Объединение и кардинальность - довольно простые понятия - если вы вовлечены в разработку приложений для баз данных, то на самом деле нет оправдания тому, что они не понимают.

ConcernedOfTunbridgeWells
источник
2
Можно утверждать, что транзакции должны выполняться в транзакционной базе данных и отчетности, а MIS - в отдельной аналитической базе данных. Поэтому вы получаете лучшее из обоих миров, и все счастливы (за исключением бедной кружки, которая должна написать сценарий преобразования данных, чтобы построить последний из первого).
Крис Симпсон
Не только плохая кружка, пишущая ETL - любой, кто использует данные из системы, данные низкого качества в приложении MIS, которое упаковано, потому что несколько ключевых отношений на самом деле не записаны в источнике, любой, кто вовлечен в бесконечные махинации выверки, которые следуют из-за низкого качества данных.
ConcernedOfTunbridgeWells
Я не мог больше не согласиться с первым пунктом. Базы данных предназначены для постоянства, они не для межпроцессного взаимодействия. Почти всегда есть лучшие решения этой проблемы. Если для этого нет явного требования, вы абсолютно ДОЛЖНЫ обращаться с базой данных, как если бы никто, кроме вашего приложения, никогда не использовал ее. Даже если существует явное требование, проведите анализ пользовательской истории и основных причин, и вы нередко найдете гораздо лучший способ выполнить намерение запрашивающего. Опять же, я работаю в компании, где фраза CQRS встречается довольно часто
Джордж Мауэр,
3
Тривиальный пример: у меня есть система администрирования страховых полисов, и мне нужно загрузить состояние 5 миллионов требований в систему перестраховочного обеспечения, чтобы рассчитать возможные возмещения. Системы представляют собой более ранние COTS-пакеты клиент-сервер, предназначенные для взаимодействия даже с более старыми системами мэйнфреймов. Оба должны быть согласованы для целей финансового контроля. Эта работа выполняется один раз в месяц. По вашей логике я написал бы серию пользовательских историй, определяющих требования, и попросил бы поставщиков процитировать добавление оболочки веб-службы в их существующие продукты.
ConcernedOfTunbridgeWells
2
Тогда ваш администратор базы данных будет ленивым или некомпетентным.
ConcernedOfTunbridgeWells
80
  1. Не используется контроль версий в схеме базы данных
  2. Работа напрямую с живой базой данных
  3. Непонимание и понимание более сложных концепций базы данных (индексы, кластерные индексы, ограничения, материализованные представления и т. Д.)
  4. Неспособность проверить на масштабируемость ... тестовые данные только из 3 или 4 строк никогда не дадут вам реальной картины реального живого исполнения
Rad
источник
1
Я второй, в значительной степени, № 1 и № 2. Каждый раз, когда я изменяю базу данных, я сбрасываю ее схему и делаю ее версию; У меня есть три установки базы данных, одна dev, промежуточная и живая - НИЧЕГО не проверяют на живых БД !!
Ixmatus
Здесь, в Red Gate, мы предприняли шаги, чтобы улучшить вашу первую точку с помощью SQL Source Control! Из разговоров, которые у меня были во время моего исследования, я думаю, что люди больше не разрабатывают против производственных баз данных, но часто делаются «срочные» исправления, которые обычно возвращаются к средам разработки, что является другой проблемой.
Дэвид Аткинсон
46

Чрезмерное использование и / или зависимость от хранимых процедур.

Некоторые разработчики приложений рассматривают хранимые процедуры как прямое расширение кода среднего уровня / внешнего интерфейса. Похоже, что это общая черта разработчиков стека Microsoft (я один из них, но я вырос из этого) и создает много хранимых процедур, которые выполняют сложную бизнес-логику и обработку рабочих процессов. Это гораздо лучше сделано в другом месте.

Хранимые процедуры полезны, когда фактически доказано, что некоторые реальные технические факторы требуют их использования (например, производительность и безопасность). Например, хранение агрегации / фильтрации больших наборов данных «близко к данным».

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

Работая с командой Java в прошлом, я быстро обнаружил, что в этой среде часто встречается полная противоположность. Архитектор Java однажды сказал мне: «База данных предназначена для данных, а не для кода».

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

Эшли Хендерсон
источник
4
Хранимые процедуры, как правило, становятся островком вреда в любом проекте, где они используются, поэтому некоторые разработчики устанавливают правило «Нет хранимых процедур». Похоже, между ними существует открытый конфликт. Ваш ответ дает хороший пример того, когда на самом деле выбрать тот или иной путь.
Уоррен П
Преимущества: безопасность - вам не нужно давать приложениям возможность «удалять * из ...»; настройки - администраторы баз данных могут настраивать запросы без необходимости перекомпиляции / развертывания всего приложения; анализ - после изменения модели данных легко перекомпилировать несколько процедур, чтобы убедиться, что они все еще действительны; и, наконец, учитывая, что SQL выполняется ядром базы данных (а не вашим приложением), тогда понятие «база данных для данных, а не код» просто запаздывает.
NotMe
Итак, вы бы включили свою бизнес-логику в пользовательский интерфейс, где она была отделена от данных, которыми манипулируют? Это не кажется хорошей идеей, особенно потому, что манипулирование данными наиболее эффективно, когда выполняется сервером базы данных, а не в обходах интерфейса пользователя. Это также означает, что управлять приложением труднее, потому что вы не можете полагаться на то, что база данных контролирует свои данные и потенциально может иметь разные версии пользовательского интерфейса с различными манипуляциями с данными. Фигово. Я не позволяю ничему касаться моих данных, кроме как через хранимую процедуру.
Дэвид Т. Макнет
Если необходимо отделить бизнес-логику от пользовательского интерфейса, можно использовать многоуровневые архитектуры. Или библиотека с бизнес-объектами и логикой, используемая различными приложениями / пользовательскими интерфейсами. Хранимые процедуры блокируют ваши данные / бизнес-логику для конкретной базы данных, поэтому изменение базы данных в этом случае обходится очень дорого. И огромная стоимость это плохо.
тоже
@too: Смена базы данных в большинстве случаев стоит очень дорого. Не берите в голову идею потери производительности и безопасности, которую обеспечивает конкретная СУБД. Кроме того, дополнительные уровни увеличивают сложность и снижают производительность, а дополнительные уровни связаны с вашим конкретным языком. Наконец, более вероятно, что используемый язык изменится, чем сервер базы данных.
NotMe
41

Проблема номер один? Они тестируют только на игрушечных базах. Поэтому они понятия не имеют, что их SQL будет сканироваться, когда база данных станет больше, и кто-то должен прийти и починить ее позже (этот звук, который вы можете услышать, - мои зубы скрипят).

Боб Мур
источник
2
Размер базы данных важен, но большая проблема связана с нагрузкой - даже если вы тестируете на реальном наборе данных, вы не тестируете производительность своих запросов, когда база данных находится под производственной нагрузкой, что может стать настоящим откровением.
davidcl
Я бы сказал, что размер базы данных важнее, чем загрузка. Я много раз видел, что отсутствовали важные индексы - никогда не было проблем с производительностью во время тестов, потому что вся база данных помещалась в память
Danubian Sailor
31

Не используя индексы.

Кристоф Эрреман
источник
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

В этом примере запрос в предложении from теперь является встроенным представлением (опять же с определенным синтаксисом Oracle) и выполняется только один раз. В зависимости от вашей модели данных этот запрос, вероятно, будет выполняться намного быстрее. Это будет работать лучше, чем первый запрос, так как количество сотрудников растет. Первый запрос мог бы работать лучше, если бы было мало сотрудников и много магазинов (и, возможно, во многих магазинах не было сотрудников), а таблица daily_sales была проиндексирована на store_id. Это маловероятный сценарий, но показывает, как коррелированный запрос может работать лучше, чем альтернативный.

Я видел, как начинающие разработчики коррелировали подзапросы много раз, и это обычно оказывало серьезное влияние на производительность. Однако при удалении коррелированного подзапроса обязательно просмотрите план объяснения до и после, чтобы убедиться, что производительность не ухудшится.

адам
источник
1
Отличный момент, и чтобы подчеркнуть один из ваших связанных моментов - проверьте свои изменения. Научитесь использовать планы объяснения (и посмотрите, что на самом деле делает база данных для выполнения вашего запроса и сколько это стоит), выполняйте тесты в большом наборе данных и не делайте свой SQL слишком сложным и нечитаемым / не поддерживаемым для оптимизации это на самом деле не улучшает реальную производительность.
Роб Уилан
21

По моему опыту:
не общаюсь с опытными администраторами баз данных.

Kb.
источник
17

Использование Access вместо «настоящей» базы данных. Существует множество отличных небольших и даже бесплатных баз данных, таких как SQL Express , MySQL и SQLite, которые будут работать и масштабироваться намного лучше. Приложения часто нужно масштабировать неожиданными способами.

Nathan Voxland
источник
16

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

TheTXI
источник
14

Использование Excel для хранения (огромных объемов) данных.

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


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

ML--
источник
14

Я хотел бы добавить: Любимый "элегантный" код по сравнению с высокопроизводительным кодом. Код, который лучше всего работает с базами данных, часто уродлив для глаз разработчиков приложений.

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

HLGEM
источник
2
+1 - программирование базы данных предполагает оптимизацию поведения механических компонентов. Обратите внимание, однако, что Кнут говорит, что преждевременная оптимизация является корнем всего зла около 97% времени (или слов на этот счет). Проектирование баз данных - это та область, где вы действительно должны об этом думать заранее.
ConcernedOfTunbridgeWells
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% или больше, одновременно уменьшая использование процессора и памяти.

CStroliaDavis
источник
12

Для баз данных на основе SQL:

  1. Не пользуясь КЛАСТЕРНЫМИ ИНДЕКСАМИ или выбирая неправильные столбцы для КЛАСТЕРА.
  2. Не использовать тип данных SERIAL (autonumber) в качестве PRIMARY KEY для соединения с FOREIGN KEY (INT) в отношении родительских / дочерних таблиц.
  3. НЕ ОБНОВЛЯТЬ СТАТИСТИКУ в таблице, когда много записей ВСТАВЛЕНО или УДАЛЕНО.
  4. Не реорганизовывать (т.е. выгружать, отбрасывать, пересоздавать, загружать и переиндексировать) таблицы, когда много строк было вставлено или удалено (некоторые механизмы физически сохраняют удаленные строки в таблице с флагом удаления.)
  5. Не использовать преимущества FRAGMENT ON EXPRESSION (если поддерживается) для больших таблиц с высокой скоростью транзакций.
  6. Выбор неправильного типа данных для столбца!
  7. Не выбрал правильное имя столбца.
  8. Не добавление новых столбцов в конце таблицы.
  9. Не создаются правильные индексы для поддержки часто используемых запросов.
  10. создание индексов для столбцов с несколькими возможными значениями и создание ненужных индексов.
    ... еще предстоит добавить.
Фрэнк Компьютер
источник
1
Придирка: 2) на самом деле плохая практика. Я вижу, к чему вы стремитесь - вам нужен уникальный индекс для этого автонумера и использовать его в качестве суррогатного ключа. Но первичный ключ не должен быть автономным, так как это не то, чем является первичный ключ: первичный ключ - это «то, о чем идет речь в записи», который (за исключением таких вещей, как транзакции продаж) НЕ является автономером, а каким-то уникальным битом. информации о моделируемой сущности.
Дэвид Т. Макнет
Основная причина использования автономного номера для первичного и внешнего ключей заключается в том, чтобы гарантировать, что соединение родитель-потомок может поддерживаться независимо от изменений в любых других столбцах. использование другого первичного ключа, такого как имя клиента или другие данные, может быть рискованным!
Фрэнк Р.
@ Дэвид: Я исправлен! .. нет необходимости использовать автонумерацию в качестве первичного ключа, можно все же иметь индексированный последовательный столбец в родительском, присоединяя суррогат в дочернем, чтобы гарантировать, что отношение не будет разорвано, а другой столбец как значимый первичный, чтобы найти строку!
Фрэнк Р.
В конце концов, это проблема семантики ... и Microsoft предпочитает, чтобы первичные ключи были бессмысленными, а не осмысленными. Споры вокруг него бушуют, но я попадаю в «значимый» лагерь. :)
Дэвид Т. Макнет
9
  • Не делать резервных копий до исправления некоторых проблем в производственной базе данных.

  • Использование команд DDL для хранимых объектов (таких как таблицы, представления) в хранимых процедурах.

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

  • Игнорирование использования профилировщика базы данных, который может точно сказать, во что в конечном итоге преобразуется ваш запрос ORM, и, следовательно, проверить логику или даже отладку, если не используется ORM.

оборота WhoIsNinja
источник
8

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

Натан Воксланд
источник
Как далеко это слишком далеко? Если данные не дублируются, как вы можете продолжить?
Finnw
Нормализация - это баланс удаления избыточных данных и повышения гибкости по сравнению со снижением производительности и повышенной сложностью. Нахождение правильного баланса требует опыта и со временем меняется. См. En.wikipedia.org/wiki/Database_normalization для получения информации о том, когда денормализовать
Nathan Voxland
8

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

finnw
источник
Следствием этого является разгрузка слишком большого количества запросов к приложению, а не хранение его в БД, к которой оно относится. LINQ особенно плохо об этом.
3Dave
8
  • Увольнение ORM, такого как Hibernate, вышло из-под контроля по причинам, таким как «это слишком волшебно» или «нет в моей базе данных».
  • Слишком сильно полагаться на ORM, такой как Hibernate, и пытаться использовать его там, где это не подходит.
Адам Яскевич
источник
8

1 - Излишне использовать функцию для значения в предложении where, а результат этого индекса не используется.

Пример:

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, а отдача огромна.

Избегайте дублирования данных, таких как чума. Некоторые люди защищают, что небольшое дублирование не повредит, и улучшит работу. Эй, я не говорю, что вы должны пытать свою схему в третьей нормальной форме, пока она не станет настолько абстрактной, что даже администратор базы данных не будет знать, что происходит. Просто поймите, что всякий раз, когда вы дублируете набор имен, почтовых индексов или отгрузочных кодов, копии в конечном итоге не синхронизируются друг с другом. Это случится. И тогда вы будете пинать себя, когда будете запускать скрипт еженедельного обслуживания.

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

pbailey19
источник
Если вы установите их правильно, они не будут, но это включает использование триггеров, на которые у многих людей аллергия.
HLGEM
6

Не выполняется соответствующий запрос SELECT перед выполнением запроса DELETE (особенно в производственных базах данных)!

Jamol
источник
5

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

Skatterbrainz
источник
1
Я могу представить себе ужасы, которые возникают в этих ситуациях ... Базы данных без схем гораздо лучше подходят для быстрого создания прототипов и итеративной разработки, но, как и все остальное, такая гибкость сопровождается различными компромиссами.
Жолт Тёрёк
4

а) Жесткое кодирование значений запроса в строке.
б) Помещение кода запроса базы данных в действие «OnButtonPress» в приложении Windows Forms.

Я видел оба.

Benoit
источник
4
«Ввод кода запроса БД в действие« OnButtonPress »в приложении Windows Form» В чем здесь ошибка базы данных?
рекурсивный
@recursive: это огромная уязвимость в SQL-инъекциях. Любой может отправить произвольный SQL на ваш сервер, и он будет запущен дословно.
Билл Карвин
Согласился с @recursive. Это действительно не имеет ничего общего с проблемами БД.
p.campbell
б) это ошибка архитектуры. Конечно, в любом случае, кодирование запросов непосредственно в вашем приложении - плохая идея.
3Dave
4

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

chefsmart
источник
4
  1. Думая, что они администраторы баз данных и разработчики моделей данных, когда у них нет формальной идеологической обработки в этих областях.

  2. Думая, что их проект не требует DBA, потому что все это легко / тривиально.

  3. Неправильное разграничение между работой, которая должна выполняться в базе данных, и работой, которая должна выполняться в приложении.

  4. Не проверять резервные копии или не создавать резервные копии.

  5. Встраивание сырого SQL в их код.

Джоунси
источник
3

Отсутствие понимания модели параллелизма баз данных и того, как это влияет на развитие. После факта легко добавлять индексы и настраивать запросы. Однако приложения, разработанные без должного учета горячих точек, конфликта ресурсов и правильной работы (при условии, что то, что вы только что прочитали, все еще действительны!), Могут потребовать значительных изменений в базе данных и уровне приложения, чтобы исправить их позже.

Эйнштейн
источник
3

Не понимая, как работает СУБД под капотом.

Вы не можете правильно управлять палкой, не понимая, как работает сцепление. И вы не можете понять, как использовать базу данных, не понимая, что на самом деле вы просто пишете в файл на своем жестком диске.

В частности:

  1. Вы знаете, что такое кластерный индекс? Вы думали об этом, когда разрабатывали свою схему?

  2. Вы знаете, как правильно использовать индексы? Как повторно использовать индекс? Вы знаете, что такое индекс покрытия?

  3. Так здорово, у вас есть индексы. Насколько велика 1 строка в вашем индексе? Насколько большим будет индекс, когда у вас много данных? Это легко впишется в память? Если это не так, это бесполезно в качестве индекса.

  4. Вы когда-нибудь использовали EXPLAIN в MySQL? Отлично. Теперь будьте честны с собой: вы поняли хотя бы половину увиденного? Нет, ты, вероятно, не сделал. Исправь это.

  5. Вы понимаете Query Cache? Знаете ли вы, что делает запрос не кэшируемым?

  6. Вы используете MyISAM? Если вам нужен полнотекстовый поиск, MyISAM все равно дерьмо. Используйте Сфинкса. Затем переключитесь на Inno.

Шейн Х
источник
2
Лучшей аналогией может быть то, что нельзя правильно устранить неполадки с механической коробкой передач без понимания сцепления. Множество людей правильно водят ручку переключения передач, не зная, как работает сцепление.
Майкл Пасха
3
  1. Использование ORM для массовых обновлений
  2. Выбор больше данных, чем необходимо. Опять же, обычно это делается при использовании ORM
  3. Стрельба sqls в петле.
  4. Отсутствие хороших тестовых данных и снижение производительности только на реальных данных.
Sriram
источник