лучший способ сохранить URL-адрес в MySQL для приложений с интенсивным чтением и записью - PullRequest
3 голосов
/ 01 марта 2011

Каков наилучший способ эффективного хранения URL в mysql для приложений с интенсивным чтением и записью?

Я буду хранить более 500 000 веб-адресов (все начинаются с http: // или https: //. Без других протоколов) и сохраняю весь URL (http://example.com/path/?variable=a) в один столбец, по-видимому, в значительной степени излишним, поскольку доменное имя и путь будут сохранены в mysql несколько раз.

Итак, вначале я думал разбить их (то есть домен, путь, переменные и т. Д.), Чтобы избавиться от избыточности. Но я видел некоторые сообщения, говорящие, что это не рекомендуется. Есть идеи по этому поводу?

Кроме того, приложению часто приходится извлекать URL-адреса без первичных ключей, то есть для поиска URL-адреса необходимо искать текст. URL может быть проиндексирован, но мне интересно, какая разница в производительности будет между сохранением всего URL-адреса и разбитого-URL, если они все проиндексированы в innodb (без полнотекстовой индексации).

Broken-down-url придется пройти через дополнительные шаги по их объединению. Кроме того, это будет означать, что мне придется извлекать данные 4 раза из разных таблиц (протокол, домен, путь, переменная), но это также делает хранимые данные в каждой строке короче, и в каждой таблице будет меньше строк. Может ли это ускорить процесс?

Ответы [ 2 ]

8 голосов
/ 01 марта 2011

Я много занимался этим, и моя общая философия - использовать метод частоты использования. Это громоздко, но позволяет вам проводить отличную аналитику данных:

CREATE TABLE URL (
   ID            integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   DomainPath    integer unsigned NOT NULL,
   QueryString   text
) Engine=MyISAM;

CREATE TABLE DomainPath (   
   ID            integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   Domain        integer unsigned NOT NULL,
   Path          text,
   UNIQUE (Domain,Path)
) Engine=MyISAM;

CREATE TABLE Domain (   
   ID            integer unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   Protocol      tinyint NOT NULL,
   Domain        varchar(64)
   Port          smallint NULL,
   UNIQUE (Protocol,Domain,Port)
) Engine=MyISAM;

Как правило, у вас будут одинаковые пути в одном домене, но разные строки запросов для каждого пути.

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

text имеет тенденцию быть медленным, поэтому вы можете изменить «Путь» на varchar после некоторого использования. Большинство серверов умирают примерно за 1 КБ для URL, но я видел несколько крупных и ошибаюсь из-за того, что не теряю данные.

Ваш поисковый запрос громоздок, но если вы абстрагируете его в своем коде, нет проблем:

SELECT CONCAT(
    IF(D.Protocol=0,'http://','https://'),
    D.Domain,
    IF(D.Port IS NULL,'',CONCAT(':',D.Port)), 
    '/', DP.Path, 
    IF(U.QueryString IS NULL,'',CONCAT('?',U.QueryString))
)
FROM URL U
INNER JOIN DomainPath DP ON U.DomainPath=DP.ID
INNER JOIN Domain D on DP.Domain=D.ID
WHERE U.ID=$DesiredID;

Сохраните номер порта, если он нестандартный (не-80 для http, не-443 для https), в противном случае сохраните его как NULL, чтобы указать, что он не должен быть включен. (Вы можете добавить логику в MySQL, но она становится намного ужаснее.)

Я бы всегда (или никогда) убирал "/" с Пути, а также "?" из QueryString для экономии места. Только потеря могла бы отличить

http://www.example.com/
http://www.example.com/?

Что, если важно, то я бы изменил тэкс, чтобы он никогда не раздевался и просто включал его. Технически,

http://www.example.com 
http://www.example.com/

То же самое, поэтому удаление косой черты в пути всегда в порядке.

Итак, для разбора:

http://www.example.com/my/path/to/my/file.php?id=412&crsource=google+adwords

Мы будем использовать что-то вроде parse_url в PHP для получения:

array(
    [scheme] => 'http',
    [host] => 'www.example.com',
    [path] => '/my/path/to/my/file.php',
    [query] => 'id=412&crsource=google+adwords',
)

Затем вы должны проверить / вставить (с соответствующими блокировками, не показаны):

SELECT D.ID FROM Domain D 
WHERE 
    D.Protocol=0 
    AND D.Domain='www.example.com' 
    AND D.Port IS NULL

(если не существует)

INSERT INTO Domain ( 
    Protocol, Domain, Port 
) VALUES ( 
    0, 'www.example.com', NULL 
);

Затем у нас есть $DomainID, идущие вперед ...

Затем вставьте в DomainPath:

SELECT DP.ID FORM DomainPath DP WHERE 
DP.Domain=$DomainID AND Path='/my/path/to/my/file.php';

(если он не существует, вставьте его аналогично)

Затем у нас есть $DomainPathID, идущие вперед ...

SELECT U.ID FROM URL 
WHERE 
    DomainPath=$DomainPathID 
    AND QueryString='id=412&crsource=google+adwords'

и вставьте, если необходимо.

Теперь позвольте мне отметить , что важно , что приведенная выше схема будет медленной для высокопроизводительных сайтов. Вы должны изменить все, чтобы использовать какой-то хеш, чтобы ускорить SELECT с. Короче, техника такая:

CREATE TABLE Foo (
     ID integer unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT,
     Hash varbinary(16) NOT NULL,
     Content text
) Type=MyISAM;

SELECT ID FROM Foo WHERE Hash=UNHEX(MD5('id=412&crsource=google+adwords'));

Я намеренно исключил его из вышеперечисленного, чтобы упростить его, но сравнение ТЕКСТА с другим ТЕКСТОМ для выборок идет медленно и разрывается для действительно длинных строк запроса. Также не используйте индекс фиксированной длины, потому что он также сломается. Для строк произвольной длины, где важна точность, допустима частота сбоев хеша.

Наконец, если вы можете, сделайте MD5-хеш-клиентскую часть, чтобы сохранить отправку больших двоичных объектов на сервер для выполнения операции MD5. Большинство современных языков поддерживают встроенный MD5:

SELECT ID FROM Foo WHERE Hash=UNHEX('82fd4bcf8b686cffe81e937c43b5bfeb');

Но я отвлекся.

0 голосов
/ 01 марта 2011

Это действительно зависит от того, что вы хотите сделать с данными. если вы делаете статистику с URL, например, чтобы увидеть, какие домены являются самыми популярными, можно было бы разбить его на это. Но если вы просто храните их и получаете полный доступ к URL-адресу, нет смысла разделять их.

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

Что бы вы ни делали - не забывайте всегда использовать int как первичные ключи настолько, насколько это возможно, так как это самый быстрый поиск.

Если вы действительно хотите разделить свои URL-адреса, вы можете рассмотреть возможность хранения отдельных таблиц, чтобы не блокировать вашу таблицу (в innoDB это не имеет значения, поскольку таблица не блокируется), но с отдельными таблицами, Вы можете просто использовать foreign / primary_keys / ints для ссылки на нужные вам строки.

Хорошее чтение - запись в блоге Friendfeed , которая также может дать вам несколько идей:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...