Индексы: целое число против производительности строки, если число узлов одинаково

26

Я занимаюсь разработкой приложения на Ruby on Rails с базой данных PostgreSQL (9.4). Для моего варианта использования столбцы в таблицах будут просматриваться очень часто, поскольку весь смысл приложения заключается в поиске очень специфических атрибутов в модели.

В настоящее время я решаю, использовать ли integerтип или просто использовать типичный строковый тип (например character varying(255), по умолчанию в Rails ) для столбцов, так как я не уверен, какая разница в производительности будет в индексе.

Эти столбцы являются перечислениями . Они имеют фиксированный размер для количества возможных значений, которые они могут иметь. Большинство значений enum не превышают 5, что означает, что индекс будет более или менее фиксированным на протяжении всего жизненного цикла приложения ; таким образом, целочисленные и строковые индексы будут одинаковыми по количеству узлов.

Тем не менее, строка, которая будет проиндексирована, может иметь длину около 20 символов, что в памяти примерно в 5 раз больше целого числа (если целое число равно 4 байта, и строки являются чистым ASCII с 1 байтом на символ, то это верно). Я не знаю, как движки баз данных выполняют поиск по индексу, но если ему нужно «сканировать» строку, пока она не совпадет в точности , то по сути это означает, что поиск строки будет в 5 раз медленнее, чем поиск целых чисел; «сканирование» до совпадения для целочисленного поиска будет 4 байта вместо 20. Вот что я представляю:

Значение поиска (целое число) 4:

сканирование ............................ НАЙДЕНО | получение записей ... | BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |

Значение поиска (строка) "some_val" (8 байт):

Сканирование ................................................. .................................... НАЙДЕНО | получение записей ... | BYTE_1 | BYTE_2 | BYTE_3 | BYTE_4 | BYTE_5 | BYTE_6 | BYTE_7 | BYTE_8 | ... |

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

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


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

Крис Сирефице
источник

Ответы:

32

Краткий ответ: integerбыстрее varcharили textво всех аспектах. Не имеет большого значения для небольших столов и / или коротких клавиш. Разница увеличивается с длиной ключей и количеством строк.

строка ... длиной 20 символов, что в памяти примерно в 5 раз больше целого числа (если целое число составляет 4 байта, и строки являются чистым ASCII по 1 байту на символ, то это верно)

Чтобы быть точным, типы символов ( textили varchar) занимают ровно 21 байт для 20 символов ASCII на диске и 23 байта в оперативной памяти. Детальная оценка:

Также важно: COLLATIONправила могут сделать сортировку символьных данных более дорогой - в отличие от числовых типов данных:

Размер индекса, вероятно, ответственен за львиную долю разницы в производительности в большинстве случаев. Рассмотрим накладные расходы для каждого кортежа индекса (в основном те же, что и для таблицы): 4 байта для указателя элемента и 24 байта для заголовка кортежа. Таким образом, кортеж индекса integerбудет составлять 36 байтов (включая 4 байта отступа выравнивания ), а для varchar(20)20 символов ASCII это будет 52 байта (также включая заполнение). Детали:

Вся теория в стороне: лучше всего просто проверить

Postgres 9.5 представил оптимизацию для сортировки длинных строк символьных данных (ключевое слово «сокращенные ключи» ). Но ошибка в некоторых функциях библиотеки C в Linux вынудила проект отключить функцию сопоставления не-C в Postgres 9.5.2. Подробности в примечаниях к выпуску.

Однако, если вы на самом деле используете enumтипы Postgres , большинство из этих соображений не имеют значения, так как в integerлюбом случае они реализуются с использованием внутренних значений. Руководство:

enumЗначение занимает четыре байта на диске.

Помимо: varchar(255)используется для ранних версий SQL Server, которые могут использовать более эффективный тип данных внутри, до 255 символов. Но ограничение нечетной длины в 255 символов совершенно не влияет на производительность в Postgres.

Эрвин Брандштеттер
источник
1
В SQL Server нет скрытой оптимизации для varchar(255)сравнения, например varchar(260). В SQL Server 6.x могло быть что-то подобное, но долгое время это не было так.
a_horse_with_no_name
@a_horse_with_no_name: спасибо, я разъяснил соответственно.
Эрвин Брандштеттер
Извините, что
потратил
Этот ответ все еще действителен для Postgres 10, пожалуйста?
Мэтти
1
@Matty: Все еще в силе. И я пока не вижу ничего изменяющегося для 11 стр.
Эрвин Брандштеттер