У меня есть столбец БД (тип varchar(255)
), в котором хранится URL-безопасная строка base 64, созданная SecureRandom.urlsafe_base64
. Вызов метода использует значения по умолчанию, поэтому результат должен быть 16 байт или 22 символа в длину.
Значение base64 используется для поиска записей, когда пользователь заходит на сайт, для маскировки идентификаторов БД. Мне нужен индекс для этого столбца из-за этого поиска, однако я не хочу индексировать весь столбец, потому что мог неэффективен для хранения.
Каков наилучший подход к определению оптимального префикса индекса для использования в этом случае? Я сейчас думаю примерно так:
EXPLAIN
для поиска в столбце с основанием 64, чтобы увидеть, сколько
записи должны быть провереныПроблема здесь в том, что я знаю, что SecureRandom
создает уникальные строки с основанием 64, но я не уверен, что это насколько уникален. Например, из 100 тыс. записей, если я использую префикс из 8 символов, будет ли этот префикс общим для 10 записей или 100?
Некоторые более конкретные вопросы о моем подходе:
Примечания:
SecureRandom
происходит от РубиSecureRandom
не меняет характеристики уникальности вывода base 64.Кроме того, таблица со 100 тыс. строк по сегодняшним меркам довольно мала. Я бы не стал беспокоиться о производительности индекса на этом уровне. Если бы у вас было 100 миллионов строк, я бы определенно сосредоточился на настройке производительности. Но всего со 100 тыс. строк... даже если вы использовали столбец полной длины (255 байт), все будет в порядке.
Почему бы не использовать SecureRandom.uuid
тогда они будут уникальными.
Это просто случайное число, верно? Не шифрование.
Использовать ли нет префикс; хотя это немного уменьшит размер индекса, во многих случаях это сделает использование индекса недействительным. Правда, 22 байта длиннее, чем 8-символьная строка или 4-байтовая INT
. Но обратная сторона отказа от использования индекса еще хуже.
Значение по умолчанию 16 (22) достаточно, чтобы сделать случайную строку достаточно уникальной, чтобы избежать случайных столкновений.
Не говорите VARCHAR(255)
, если максимальное значение равно 22. Говорите CHAR(22)
, если длина фиксированная, или VARCHAR(22)
, если вы разрешаете пользователю выбирать длину до 16.
Скажите CHARACTER SET ascii COLLATE ascii_bin
для столбца. Это позволит избежать (1) накладных расходов utf8 и (2) ошибки свертывания регистра.
Если вы собираетесь иметь индекс для миллиарда таких элементов, то возникнут серьезные проблемы с производительностью, как обсуждалось в здесь (хотя и в другом контексте). Миллион строк, вероятно, не проблема - это зависит от того, когда индекс станет больше, чем может быть кэшировано в ОЗУ в пуле буферов.
(Если я правильно помню формулу, для 8 символов, как вы описываете, будет один шанс на 300 КБ, что индекс с 300 000 записей будет включать дубликат. Но это не проблема.)
Я решил следовать шагам, которые я изложил в вопросе. В результате я получил информацию, необходимую для выбора префикса индекса.
В результате этого эксперимента произошли два изменения:
255
до 22
(также предложено @Rick James). Подробнее см. в вопросе, почему данные никогда не превышают 22 символов.Я протестировал префиксы длиной 1, 2, 4, 8 и 16 символов и использовал EXPLAIN
, чтобы увидеть, что сделает поиск в столбце. Я также тестировал без индекса в качестве базовой линии. Мой эксперимент был основан на наборе из 100 тыс. записей. Я мог бы протестировать большее количество записей, но для моих целей не было необходимости в большей точности.
Вот некоторые моменты эксперимента:
SecureRandom
выполняет сканирование таблицы.Помимо 4-значного префикса, с набором данных 100 000 и исходя из характеристик SecureRandom.urlsafe_base64
, я не могу быть более точным. Основываясь на текущем размере таблицы в производственной среде и приблизительном представлении о темпах ее роста, мы решили, что префикс из 8 символов сослужит нам хорошую службу в будущем, экономя при этом дисковое пространство и память.
Благодаря этому исследованию я также узнал о функции избирательности индексов в MySQL. Это позволило бы, используя один и тот же выборочный набор из 100 000 записей, протестировать количество записей, извлеченных для каждой длины префикса, без необходимости выполнять миграции для добавления/удаления различных индексов перед каждым тестом. Например, чтобы проверить селективность 4-символьного префикса:
SELECT count(*) AS count, LEFT(uuid_column, 4) AS prefix
FROM string_prefix_test GROUP BY count DESC LIMIT 10;
Более подробную информацию о селективности индекса можно найти в выпуске Высокая производительность MySQL 2012 года на стр. 160.
Далее я собираюсь изучить префиксы для индексов столбцов varchar
с данными, которые не обладают характеристиками уникальности SecureRandom.urlsafe_base64
. Это будет сложнее только потому, что сложнее получить репрезентативный набор данных.
Зачем вам уникальность? Пока они равномерно распределены, индекс будет создавать сбалансированное количество сегментов и будет эффективным при извлечении данных. И я предполагаю, что SecureRandom производит довольно сбалансированные значения.