Індекси: продуктивність від цілого до рядка, якщо кількість вузлів однакове


26

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

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

Ці стовпці є перерахунками . Вони мають фіксований розмір для кількості можливих значень, які вони можуть мати. Більшість довжин перерахунків не перевищують 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.
Ервін Брандстеттер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.