Как хранить URL-адреса в MySQL
Мне нужно хранить потенциально 100 миллионов URL-адресов в базе данных. Каждый URL-адрес должен быть уникальным, поэтому я буду использовать ON DUPLICATE KEY UPDATE и подсчитать повторяющиеся URL-адреса.
Однако я не могу создать индекс в поле URL, так как мое поле varchar составляет 400 символов. MySQL жалуется и говорит; "# 1071 - Указанный ключ слишком длинный, максимальная длина ключа - 767 байт". (Varchar 400 займет 1200 байт)
Каков наилучший способ сделать это, если вам нужно обработать минимальные 500 000 URL-адресов в день на одном сервере?
Мы уже думаем, используя MongoDB для того же приложения, поэтому мы можем просто запросить MongoDB и найти дубликат URL-адреса и обновить строку. Тем не менее, я не сторонник решения этой проблемы с использованием MongoDB, и я хотел бы использовать только MySQL на этом этапе, поскольку я хотел бы быть как можно более легким в начале и быстрее завершить этот раздел проекта. (Мы еще не играли с MongoDB и не хотим тратить время на этом этапе)
Есть ли другая возможность сделать это, используя меньше ресурсов и времени. Я думал, чтобы получить хеш MD5 URL-адреса и сохранить его. И я могу сделать это поле УНИКАЛЬНЫМ. Я знаю, будет столкновение, но вполне нормально иметь 5-10-20 дубликатов в 100 миллионах URL-адресов, если это единственная проблема.
Есть ли у вас предложения? Я также не хочу тратить 10 секунд, чтобы вставить только один URL-адрес, так как он будет обрабатывать URL-адреса 500 тыс. В день.
Что бы вы предложили?
Изменить: согласно запросу это определение таблицы. (Я не использую MD5 на данный момент, это для тестирования)
mysql> DESC url;
+-------------+-----------------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-----------------------+------+-----+-------------------+-----------------------------+
| url_id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| url_text | varchar(400) | NO | | | |
| md5 | varchar(32) | NO | UNI | | |
| insert_date | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| count | mediumint(9) unsigned | NO | | 0 | |
+-------------+-----------------------+------+-----+-------------------+-----------------------------+
5 rows in set (0.00 sec)
Ответы
Ответ 1
В соответствии с спецификацией DNS максимальная длина имени домена:
Сам DNS помещает только одно ограничение на конкретные метки
которые могут использоваться для идентификации записей ресурсов. Это одно ограничение относится к длине ярлыка и имени. Длина любая одна метка ограничена от 1 до 63 октетов. Полный домен
имя ограничено 255 октетами (включая разделители).
255 * 3 = 765 < 767 (Только едва:-))
Однако обратите внимание, что каждый компонент может содержать только 63 символа.
Итак, я бы предложил нарезать url в биты компонента.
Использование http://foo.example.com/a/really/long/path?with=lots&of=query¶meters=that&goes=on&forever&and=ever
Возможно, это было бы адекватно:
- флаг протокола [ "http" → 0] (сохранить "http" как 0, "https" как 1 и т.д.)
- subdomain [ "foo" ] (255 - 63 = 192 символа: я мог бы вычесть еще 2, потому что min tld - 2 символа)
- домен [ "example" ], (63 символа)
- tld [ "com" ] (4 символа для обработки "info" tld)
- path [ "a/really/long/path" ] (пока вы хотите - сохранить в отдельной таблице)
- queryparameters [ "with = lots & of = query & parameters = that & go = on & forever & and = ever" ] (хранить в отдельной таблице ключей/значений)
- номер порта/аутентификация, который редко используется, может быть в отдельной таблице с ключами, если это действительно необходимо.
Это дает вам приятные преимущества:
- Индекс находится только на частях URL-адреса, которые вам нужно искать (меньший индекс!)
- запросы могут быть ограничены различными частями URL (например, каждый URL-адрес в домене facebook).
- любой URL-адрес, который слишком длинный, поддомен/домен является поддельным
- легко отбрасывать параметры запроса.
- легко сделать нечувствительное к регистру имя домена /tld поиск
- отказаться от синтаксического сахара ( "://" после протокола "." между субдоменом/доменом, доменом /tld, "/" между tld и path, "?" перед запросом "&" "=" в запрос)
- Избегает проблемы с большой разреженной таблицей. Большинство URL-адресов не будут иметь параметров запроса или длинных путей. Если эти поля находятся в отдельной таблице, ваша основная таблица не будет принимать размер. При выполнении запросов больше записей будет вписываться в память, что приведет к более быстрой работе с запросами.
- (здесь больше преимуществ).
Ответ 2
Чтобы индексировать поле шириной до 767 символов, оно должно быть ascii или аналогичным, оно не может быть utf8, поскольку оно использует 3 байта на char, поэтому максимальный для индексированных полей utf-8 равен 255
Конечно, поле 767 ascii url, превзошло ваши первоначальные спецификации 400 символов. Конечно, некоторые URL-адреса превышают лимит 767. Возможно, вы можете хранить и индексировать первые 735 символов плюс хеш md5. Вы также можете иметь текстовое поле full_url для сохранения исходного значения.
Обратите внимание, что ascii charset достаточно хорош для URL-адресов
Ответ 3
Хорошо сформированный URL-адрес может содержать только символы в диапазоне ASCII - другие символы должны быть закодированы. Таким образом, предполагая, что URL-адреса, которые вы собираетесь хранить, хорошо сформированы (а если нет, вы можете исправить их до вставки их в базу данных), вы можете определить свой символ столбца url_text, установленный в ASCII (latin1 в MySQL). С ASCII один char представляет собой один байт, и вы сможете индексировать все 400 символов, как вы хотите.
Ответ 4
Вероятности ложного столкновения с MD5 (128 бит) могут быть сформулированы следующим образом:
"Если у вас есть 9 триллионов разных предметов, в 9 триллионах есть только один шанс, что два из них имеют тот же MD5".
Чтобы выразить это другим способом, он, скорее всего, попадет в метеор, выиграв мега-лотерею.
Ответ 5
Вы можете изменить url_text из VarChar (400) в текст, затем вы можете добавить полный текстовый индекс к нему, чтобы вы могли искать наличие URL-адреса перед его вставкой.