суррогатный против естественного ключа:точные цифры различий в производительности?
-
22-07-2019 - |
Вопрос
Существует здоровая дискуссия между суррогатными и естественными ключами:
Мое мнение, которое, кажется, совпадает с большинством (это незначительное большинство), заключается в том, что вам следует использовать суррогатные ключи, если только естественный ключ не является полностью очевидным и гарантированно не изменится.Затем вы должны обеспечить уникальность естественного ключа.Что означает суррогатные ключи почти все время.
Пример двух подходов, начиная с таблицы компаний:
1:Суррогатный ключ:В таблице есть поле ID, которое является PK (и идентификатором).Названия компаний должны быть уникальными в зависимости от штата, поэтому здесь существует ограничение уникальности.
2:Естественный ключ:Таблица использует CompanyName и State в качестве PK - удовлетворяет как PK, так и уникальности.
Допустим, что Company PK используется в 10 других таблицах.Моя гипотеза, не подкрепленная никакими цифрами, заключается в том, что подход с использованием суррогатного ключа был бы здесь намного быстрее.
Единственный убедительный аргумент, который я видел в пользу естественного ключа, - это для таблицы "многие ко многим", которая использует два внешних ключа в качестве естественного ключа.Я думаю, что в таком случае это имеет смысл.Но вы можете попасть в беду, если вам понадобится рефакторинг;я думаю, это выходит за рамки данного поста.
Кто-нибудь видел статью, в которой сравнивается различия в производительности на наборе таблиц, которые используют суррогатные ключи против. тот же набор таблиц , использующий естественные ключи?Поиск по SO и Google не дал ничего стоящего, просто много теоретических разработок.
Важное обновление:Я начал строить набор тестовых таблиц которые отвечают на этот вопрос.Это выглядит примерно так:
- PartNatural - таблица частей, которая использует уникальный номер партнера в качестве PK
- PartSurrogate - таблица частей, которая использует идентификатор (int, identity) в качестве PK и имеет уникальный индекс для PartNumber
- Завод - ИДЕНТИФИКАТОР (int, identity) как PK
- Инженер - идентификатор (int, identity) как PK
Каждая деталь соединена с заводом, и каждый экземпляр детали на заводе соединен с инженером.Если у кого-то есть проблемы с этим тестовым стендом, сейчас самое время.
Решение
Используйте и то, и другое!Естественные ключи предотвращают повреждение базы данных (возможно, более подходящим словом будет "несогласованность").Когда "правильный" естественный ключ (для устранения повторяющихся строк) будет работать плохо из-за длины или количества задействованных столбцов, в целях повышения производительности также может быть добавлен суррогатный ключ, который будет использоваться в качестве внешних ключей в других таблицах вместо естественного ключа...Но естественный ключ должен оставаться альтернативным ключом или уникальным индексом, чтобы предотвратить повреждение данных и обеспечить согласованность базы данных...
Большая часть шуток (в "дебатах" по этому вопросу) может быть вызвана тем, что является ложным предположением - что вы должны использовать Первичный Ключ для соединений и внешних ключей в других таблицах.ЭТО ЛОЖЬ.Вы можете использовать ЛЮБОЙ Клавиша в качестве целевого объекта для внешних ключей в других таблицах.Это может быть Первичный ключ, альтернативный ключ или любой уникальный индекс или уникальное ограничение.А что касается соединений, вы можете использовать вообще что угодно для условия соединения, это даже не обязательно должен быть ключ, или idex, или даже уникальный !!(хотя, если он не уникален, вы получите несколько строк в создаваемом им декартовом произведении).
Другие советы
Естественные ключи отличаются от суррогатных ключей значением, а не типом.
Любой тип может быть использован для суррогатного ключа, например VARCHAR
для сгенерированного системой slug
или что-то еще.
Однако наиболее часто используемыми типами суррогатных ключей являются INTEGER
и RAW(16)
(или любой другой тип вашего RDBMS
использует ли для GUID
's),
Сравнение суррогатных целых чисел и натуральных целых чисел (например SSN
) занимает точно такое же время.
Сравнивая VARCHAR
s make учитывают параметры сортировки, и они, как правило, длиннее целых чисел, что делает их менее эффективными.
Сравнение набора из двух INTEGER
вероятно, также менее эффективно, чем сравнение одного INTEGER
.
Для небольших по размеру типов данных эта разница, вероятно, проценты от процентов о времени, необходимом для извлечения страниц, обхода индексов, получения защелок базы данных и т.д.
И вот цифры (в MySQL
):
CREATE TABLE aint (id INT NOT NULL PRIMARY KEY, value VARCHAR(100));
CREATE TABLE adouble (id1 INT NOT NULL, id2 INT NOT NULL, value VARCHAR(100), PRIMARY KEY (id1, id2));
CREATE TABLE bint (id INT NOT NULL PRIMARY KEY, aid INT NOT NULL);
CREATE TABLE bdouble (id INT NOT NULL PRIMARY KEY, aid1 INT NOT NULL, aid2 INT NOT NULL);
INSERT
INTO aint
SELECT id, RPAD('', FLOOR(RAND(20090804) * 100), '*')
FROM t_source;
INSERT
INTO bint
SELECT id, id
FROM aint;
INSERT
INTO adouble
SELECT id, id, value
FROM aint;
INSERT
INTO bdouble
SELECT id, id, id
FROM aint;
SELECT SUM(LENGTH(value))
FROM bint b
JOIN aint a
ON a.id = b.aid;
SELECT SUM(LENGTH(value))
FROM bdouble b
JOIN adouble a
ON (a.id1, a.id2) = (b.aid1, b.aid2);
t_source
это просто фиктивная таблица с 1,000,000
ряды.
aint
и adouble
, bint
и bdouble
содержат точно такие же данные, за исключением того, что aint
имеет целое число в виде PRIMARY KEY
, в то время как adouble
имеет пару из двух одинаковых целых чисел.
На моем компьютере оба запроса выполняются в течение 14,5 секунд, + /- 0,1 секунды
Разница в производительности, если таковая имеется, находится в пределах диапазона колебаний.