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

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

30 ответов


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

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

2. Не обеспечение ссылочной целостности

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

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

подробнее здесь:

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

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

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

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

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

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

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

SELECT DISTINCT ...

это немного ярлык для обеспечения того, чтобы вы не возвращали повторяющиеся строки и, таким образом, получали повторяющиеся объекты. Иногда вы увидите, как люди делают то же самое. Если вы видите это слишком часто, это настоящий красный флаг. Только не это!--11--> плохо или не имеет действительных приложений. Это так (по обоим пунктам), но это не суррогат или временная остановка для правильного написания запросы.

С почему я ненавижу DISTINCT:

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

5. Предпочтение агрегации над соединениями

еще одна распространенная ошибка разработчиков приложений баз данных-не понимать, насколько дороже агрегация (т. е. GROUP BY предложения) можно сравнить с соединениями.

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

С инструкция SQL - "join" vs " group а":

первый запрос:

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

время запроса: 0.312 s

второй запрос:

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 s

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

6. Не упрощая сложные запросы через views

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

  • партии: люди и организации;
  • Роль Партии: все эти партии, например работник и Работодатель;
  • Партийные Ролевые Отношения: как эти роли связаны друг с другом.

пример:

  • Тед-Это человек, являющийся подтипом партии;
  • у Ted много ролей, одна из которых-сотрудник;
  • Intel-это организация, являющаяся подтипом партии;
  • Intel имеет много ролей, одна из которых является работодателем;
  • Intel использует Ted, что означает, что существует связь между их соответствующая роль.

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

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-адреса, формируют данные и печенье всегда следует рассматривать как враждебные и продезинфицировать. Убедитесь, что вы получаете то, что ожидаете.

8. Не используя подготовленные заявления

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

SELECT * FROM users WHERE username = 'bob'

vs

SELECT * FROM users WHERE username = ?

или

SELECT * FROM users WHERE username = :username

в зависимости от вашей платформы.

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

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

подготовленные операторы также лучше защитят вас от атак SQL-инъекций.

9. Недостаточно нормализовать


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

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

  • злоупотребление денормализованными данными. Overdoing денормализованных данных и пытается поддерживать его в приложении является рецептом для проблем целостности данных. Использовать denormalisation экономно. Не желая, чтобы добавить соединение на запрос-это не повод для denormalising.

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

  • догматические политики "нет хранимых процедур".

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


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

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

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

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

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

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

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


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


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


низкая производительность, вызванная коррелированными подзапросами

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

простить придуманный пример и синтаксис 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. Это не вероятный сценарий, но показывает как коррелированный запрос может работать лучше, чем альтернатива.

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


мой опыт:
Не общаться с опытными DBAs.


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


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


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

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


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


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

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


Не использовать параметризованные запросы. Они довольно удобны в остановке SQL Injection.

Это конкретный пример не дезинфицирующих входных данных, упомянутых в другом ответе.


Я ненавижу, когда разработчики используют вложенные операторы 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% или более, одновременно уменьшая использование процессора и памяти.


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

  1. не использовать кластеризованные индексы или выбирать неправильные столбцы для кластера.
  2. не использовать тип данных SERIAL (autonumber) в качестве первичного ключа для присоединения к внешнему ключу (INT) в отношении родительской/дочерней таблицы.
  3. не обновлять статистику в таблице, когда многие записи были вставлены или удалены.
  4. не реорганизовывать (т. е. разгружать, сбрасывать, воссоздавать, нагружать и переиндексировать) таблицы когда многие строки были вставлены или удалены (некоторые движки физически сохраняют удаленные строки в таблице с флагом delete.)
  5. не используя фрагмент на выражение (если поддерживается) на больших таблицах, которые имеют высокие ставки транзакций.
  6. выбор неправильного типа данных для столбца!
  7. не выбирая правильное имя столбца.
  8. не добавлять новые столбцы в конец таблицы.
  9. не создавать правильные индексы для поддержки часто используемых запросы.
  10. создание индексов на столбцах с несколькими возможными значениями и создание ненужных индексов.
    ...еще нужно добавить.

  • не принимая резервную копию перед исправлением некоторых проблем внутри производственной базы данных.

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

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

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


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


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


  • отклонение ORM, как Hibernate из рук, по таким причинам, как" это слишком волшебно "или" не на мой

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 в тексте таким образом, который на самом деле читается вашим коллегой программистом.


Это было сказано раньше, но:индексы, индексы, индексы. Я видел так много случаев неэффективных корпоративных веб-приложений, которые были исправлены, просто сделав небольшое профилирование (чтобы увидеть, какие таблицы сильно пострадали), а затем добавив индекс в эти таблицы. Это даже не требует больших знаний в области написания SQL, и выигрыш огромен.

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

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


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


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


a) Hardcoding значения запроса в строке
b) ввод кода запроса базы данных в действие "OnButtonPress" в приложении Windows Forms

Я видел оба.


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


  1. думая, что они DBAs и Data modelers/designers, когда у них нет формальной индоктринации любого рода в этих областях.

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

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

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

  5. внедрение raw SQL в их код.



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


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

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

в частности:

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

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

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

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

  5. вы понимаете кэш запросов? Вы знаете, что делает запрос ООН-cachable?

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


  1. использование ORM для массовых обновлений
  2. выбор большего количества данных, чем необходимо. Опять же, обычно делается при использовании ORM
  3. запуск sqls в цикле.
  4. Не имея хороших тестовых данных и заметив снижение производительности только на живых данных.