Быстрее ли нормализовать эту таблицу? - PullRequest
0 голосов
/ 08 июня 2009

У меня есть две таблицы, подобные этим:

Стол Люди:
VARCHAR Имя
INTEGER Возраст

Таблица сообщений
VARCHAR Сообщение
VARCHAR Имя

Существуют сотни операций вставки и удаления с таблицей сообщений с такими запросами:

insert into Message VALUES ('Hello there', 'John');
delete from Message where name = 'John';

Мой вопрос: стоит ли добавлять поле идентификатора в People и ссылаться на пользователя как идентификатор в сообщении? Будут ли следующие запросы выполняться намного быстрее?

FIRST - select id from User where name = 'John'
THEN - delete from Message where id = $id

Я думал, что сначала вставка и удаление при поиске идентификатора будет быстрее, чем при поиске по символам. Но насколько быстрее? Будет ли увеличение скорости компенсировать снижение производительности от необходимости дополнительного запроса к таблице User, чтобы найти идентификатор?

Ответы [ 11 ]

5 голосов
/ 08 июня 2009

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

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

4 голосов
/ 09 июня 2009

Ваш дизайн уже нормализован, при условии, что у вас есть уникальное ограничение для People.Name и существует ограничение ссылочной целостности между Message.Name и People.Name.

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

Если люди никогда не меняют своих имен в вашей системе, тогда это не проблема. В этом случае имя почти так же хорошо, как идентификатор - хотя некоторые СУБД могут работать лучше с индексированным числом, а не с индексированной строкой (?).

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

4 голосов
/ 08 июня 2009

Имя человека никогда не является хорошим Первичным ключом, поскольку имена НЕ являются уникальными. И они меняются со временем. Чуть лучше использовать суррогатный ключ (и да, объединение в Int часто происходит быстрее, и вы можете присоединиться к удаляемым элементам во многих базах данных, не используя более медленный подзапрос), особенно потому, что имена, как правило, длиннее нескольких символов.

3 голосов
/ 08 июня 2009

Это быстрее? Только профилирование покажет, однако. , .

Это IS рекомендуется размещать столбец id в Person и устанавливать ограничение внешнего ключа из Message to Person в Id (при условии, что все сообщения могут отправляться только людям в таблице Person). 1005 *

Вы все еще можете удалить сообщение одним оператором

delete from Message where id IN (select id from Person where Name = 'John')

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

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

Подробнее о иностранных ключах см. Здесь

1 голос
/ 09 июня 2009

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

В общем, намного, намного сложнее.

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

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

Но производительность может быть фактором. Учитывая данные в двух таблицах, база данных имеет несколько вариантов, как присоединиться. Он может использовать либо People , либо Messages в качестве базовой таблицы, и то, как будет выполнено соединение, повлияет на вещи (вложенные циклы, объединения хешей, сортировку / объединение и т. Д.).

Но, кроме того, нормализация может на быстрее . Что если ваша схема сложнее, чем вы описываете? Допустим, ваша таблица People содержит 50 полей, связанных с персоналом, а ваша таблица Messages имеет только одно поле сообщения из 20 символов. Если у вас есть случай с двумя людьми, но 100 000 сообщений, денормализованный будет на самом деле быстрее. Это потому, что ввод-вывод является самым большим ограничивающим фактором для баз данных Если вы должны выгрузить все данные в одном запросе, нормализованные данные будут извлекать эти 50 полей только один раз, а ваша таблица Messages будет плотно упакована данными. В денормализованной версии каждая строка Messages будет содержать 51 поле, и вы значительно увеличите количество операций ввода-вывода, чтобы получить тот же результат.

1 голос
/ 08 июня 2009

По моему опыту, пользовательская таблица бэкэнда веб-сайта является одной из тех таблиц, которые почти всегда остаются в памяти 100% времени. Он довольно важен для любой деятельности, поэтому он никогда не выпадает из буфера страниц. Так что я бы (и должен) определенно пойти по пути использования userId для всех ссылок, подобных этой.

1 голос
/ 08 июня 2009

Вам не нужно делать дополнительный запрос. Вы можете сделать что-то вроде этого:

DELETE FROM Message 
INNER JOIN User 
  ON Message.id = User.id 
WHERE User.name = 'John'
1 голос
/ 08 июня 2009

Если большинство имен короткие (не длиной от 15 до 20 символов), и таблица правильно проиндексирована, то быстродействие, которое вы получите от поля id, будет незначительным.

0 голосов
/ 15 июля 2009

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

0 голосов
/ 15 июля 2009

Все дело в IO и ремонтопригодности. Если ваш varchar содержит менее 4 байтов, ваш запрос будет выполняться быстрее, если вы используете varchar, а не целое число. Но это не большое улучшение, и вы теряете много производительности, если вам когда-либо понадобится сменить имя! Каждую строку в таблице сообщений необходимо обновить (например, удалить и вставить).

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

...