Почему использование строковых ключей обычно считается плохой идеей?

24

Это беспокоило меня некоторое время. В большинстве случаев, когда речь идет о хранении данных в таких структурах, как хеш-таблицы, программисты, книги и статьи, настаивают на том, что индексация элементов в указанных структурах по значениям String считается плохой практикой. Тем не менее, до сих пор я не нашел ни одного такого источника, чтобы также объяснить, ПОЧЕМУ это считается плохой практикой. Зависит ли это от языка программирования? На базовых рамках? На реализацию?

Возьмите два простых примера, если это поможет:

SQL-подобная таблица, где строки индексируются первичным ключом String.

Словарь .NET, где ключами являются строки.


источник
9
Наличие строковых ключей не является плохой идеей. Я подозреваю, что эти заявления были сделаны в контексте, где доступен лучший тип ключа. У меня все время есть словари .net со строковыми ключами. Можете ли вы привести несколько примеров этого утверждения?
CodesInChaos
3
Обычно вам нужны первичные ключи, которые не меняются в течение срока службы объекта / строки. Так, например, usernameпервичный ключ usersтаблицы, вероятно, не лучшая идея, и вы бы предпочли идентификатор с автоматическим приращением. Но эта usernameстрока является лишь случайной, так как изменяемое свойство является главной проблемой
CodesInChaos
В базе данных подумайте, как бы индексировать строки, а не целые числа.
@CodesInChaos Хотелось бы, чтобы я мог вспомнить, где я нашел большинство случаев, но сейчас я могу вставить фрагмент, который напомнил мне о проблеме. Это было из слайд-шоу GDC от Valve, где обсуждались игровые диалоги и хранение фактов о мире в парах <key = string, value = object>.
2
Строки в порядке. Только не «волшебные» струны. Поэтому при использовании хеш-таблицы убедитесь, что в вашем коде нет пустых строк. Вам следует избегать больших текстовых значений в качестве ключей, потому что они не работают должным образом, но в большинстве реальных ситуаций короткая текстовая строка так же быстро, как целое число (они не являются массивными базами данных). Вы также можете использовать альтернативные ключи, например, первичный ключ - это число, но есть также «slug» или уникальная строка, которая также уникальна.
ipaul

Ответы:

17

Все это в основном связано с двумя вещами:

1) Скорость поиска (где целые числа, например, намного лучше)

2) Размер индексов (где взрываются строковые индексы)

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

PS Может не относиться к вашему вопросу, но Guids считаются плохими и для ключей базы данных (16-байтовое Guid против 4-байтового целого). На больших объемах данных Guids замедляют поиск.

кролик
источник
Не всегда - возможны добавочные GUID. Индексы все равно будут больше, но штраф за поиск не будет таким же плохим.
Сэм
7
На самом деле они в порядке. Вы должны посмотреть на взаимосвязь между временем ввода-вывода диска времени и сравнением значений в памяти. Поскольку время доступа к диску значительно превышает сравнение с памятью, единственное, что действительно имеет значение при анализе производительности базы данных, - это IO. Является ли ключ идентификатором GUID, строкой или целым числом, на самом деле не имеет значения. Размер индекса влияет на то, сколько значений индекса помещается на одной странице, но не имеет значения, является ли ключ 4-байтовым int (который может быть недостаточно большим и не может быть сгенерирован клиентом) или 16-байтовым значением. В некоторых базах данных rowId может иметь размер 16 байт.
ipaul
9

Есть еще одна проблема с использованием строк в качестве ключей, или, точнее, с использованием строковых литералов в качестве ключей, оставляя без внимания чисто соображения производительности / эффективности. Опечатки. Если вы используете строковые литералы в качестве ключей в словаре, вы настраиваете себя на неприятный сюрприз, когда "ReceiverId"становитесь "RecieverId". Установите константы для хранения значений ключей и используйте их каждый раз, когда вы обращаетесь к словарю.

Можно сказать, что это тривиально и очевидно, но в огромном количестве примеров кода .NET в Интернете используются строковые литералы, пропагандирующие эту сомнительную практику. ASP.NET со всеми Sessions, ViewStates и QueryParams, разбросанными по базе кода, особенно виноват в этом.

scrwtp
источник
Не тривиально ИМХО. Я также видел случаи, когда есть ключи "1"и "1 "в той же таблице.
PSWG
Становится еще интереснее, когда вы добавляете чувствительность к регистру в микс. Видел множество людей, в том числе и меня, наткнуться прямо на это.
Тони Хопкинсон
Даже лучше, чем использование констант, по крайней мере в C #, вместо этого использовать выражения. Таким образом, вы можете генерировать свои строки из имен методов / свойств и т. Д., Чтобы ваши поиски строк стали безопасными по типу и удобными для рефакторинга.
GoatInTheMachine
4

Здесь есть много компромиссов. На самом деле я часто использую строковые ключи, но часто я включаю суррогатные вторичные ключи для объединений (очевидно, было бы наоборот, если бы я использовал MySQL). Однако есть случаи, когда я этого не делаю.

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

Есть две причины, по которым я обычно добавляю суррогатные ключи:

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

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

Однако в тех случаях, когда естественным ключом является определение, один столбец и текст, я обычно присоединяюсь к строковому ключу. Моя причина для этого состоит в том, что это часто избегает соединений при поиске. Наиболее распространенное использование - это обеспечение правильного дизайна БД вокруг варианта использования типов enum. В большинстве случаев они не требуют дополнительного объединения для обычных запросов. Так что в этом случае строковые ключи в качестве ключей соединения имеют смысл.

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

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

Крис Траверс
источник
Это не ненулевое значение, если вам удастся обернуть целочисленный тип обратно в ноль. Для 32-разрядного типа без знака это всего 4G, что тревожно близко к «миллиардам записей»…
Donal Fellows
Если у вас есть БД, который вы можете сказать «ошибка, а не обтекание», это ноль. Во всяком случае, легче управлять возможностью столкновения с увеличивающимися целыми числами, чем с псевдослучайными значениями.
Крис Трэверс
1

Существует множество потенциальных проблем с использованием строк в качестве ключей, особенно когда речь идет о таблицах, подобных SQL. Как упомянул @bunny, индексы для ваших таблиц будут больше, но я думаю, что более важно, любые отношения внешнего ключа к таблице будут включать ОБА таблицы, содержащие строку, а не более легкий (целочисленный) идентификатор , Если вы обнаружите, что существует еще больше таблиц со ссылками на первую, строковые ключи будут распространяться по всей вашей базе данных.

Мэтью Флинн
источник
1

Это не плохая идея сама по себе, обычно 20/20 задним числом - плохой компромисс в дизайне. Гибкость и ассортимент струн в зависимости от дополнительных затрат и сложности.

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

Тони Хопкинсон
источник
0

Вы каким-то образом получили неверные данные из Hashtable.

Вы имели в виду "Дневной телефон" или "Вечерний телефон"?

или

Вы имели в виду 1234567 или 1234576?

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

Фил В.
источник
1
И, таким образом, вы получаете список констант, используя имя константы в вашем коде для представления магического числа ... Java перечисляет для спасения, чтобы абстрагировать его еще дальше, оставляя вам только имя и порядковый номер отображение невидимое.
jwenting
-1

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

moss23
источник
2
Этот ответ не добавляет ничего, что еще не было сказано в других ответах, которые говорят это лучше.
Мартин Питерс
-2

строковый ключ будет иметь смысл, когда дело доходит до таблицы поиска с 10-100 короткими строковыми записями; связанные данные более читабельны + например, отслеживание изменений (числовой идентификатор / идентификатор guid по сравнению со строкой, например, «Администратор»); Кстати, база данных членства ASP.NET использует строковые ключи для AspNetRoles.

Альфред Хичкок
источник