2010-09-17 4 views
11

Quelle est la meilleure clé primaire pour stocker l'adresse du site Web et les URL de la page? Pour éviter l'utilisation de l'ID auto-incrémental (qui n'est pas vraiment lié aux données), j'ai conçu le schéma en utilisant une signature SHA1 de l'URL comme clé primaire. Cette approche est utile de plusieurs façons: par exemple, je n'ai pas besoin de lire last_id de la base de données pour pouvoir préparer toutes les mises à jour de la table en calculant la clé et faire la vraie mise à jour en une seule transaction. Aucune violation de contrainte.Meilleure clé primaire pour stocker les URL

En tout cas, j'ai lu deux livres qui me disent que je me trompe. Dans "Haute performance MySQL", il est dit que la clé aléatoire n'est pas bonne pour l'optimiseur de DB. En outre, dans chaque livre de Joe Celko, il dit que la clé primaire devrait être une partie des données.

La question est: les clés naturelles pour les URL sont ... les URL elles-mêmes. Le fait est que si pour un site il est court (www.something.com), il n'y a pas de limite imposée pour une URL (voir http://www.boutell.com/newfaq/misc/urllength.html). Considérer que je dois stocker (et travailler avec) quelques millions d'entre eux.

Quelle est la meilleure clé, alors? Identifiants auto-incrémentés, URL, hachages d'URL?

+1

Je pense que cela dépendra beaucoup de ce que vous faites d'autre avec ces URL, patrons d'accès, etc. L'utilisation de SHA1 devrait être sûre des collisions, où une fonction de hachage plus courte (par exemple CRC32) serait évidemment inappropriée, mais les collisions peuvent toujours être possibles, vous seriez juste malchanceux. –

Répondre

15

Vous aurez besoin d'une clé primaire numérique auto-incrémentée. Pour les moments où vous devez passer des identifiants ou les joindre à d'autres tables (par exemple, des attributs facultatifs pour une URL), vous devez choisir un format petit et numérique. Comme pour les autres colonnes et index que vous voulez, cela dépend, comme toujours, de la façon dont vous allez les utiliser.

Une colonne stockant un hachage de chaque URL est une excellente idée pour presque toutes les applications qui utilisent un nombre significatif d'URL. Il fait en sorte que l'URL d'une URL soit aussi rapide que possible. Un deuxième avantage est que si vous rendez cette colonne UNIQUE, vous n'avez pas besoin de faire en sorte que la colonne stocke l'URL réelle, et vous pouvez utiliser REPLACE INTO et INSERT IGNORE comme des opérations simples et rapides d'écriture atomique.

J'ajouterais que l'utilisation de la fonction intégrée MD5() de MySQL est très bien pour cela. Son seul inconvénient est qu'un attaquant dévoué peut provoquer des collisions, ce dont je suis sûr que vous ne vous souciez pas. L'utilisation de la fonction intégrée facilite, par exemple, certains types de jointures. Il peut être un peu plus lent de passer une URL complète à travers le fil ("CHOISIR URL url WHERE hash = MD5 ('verylongurl')" au lieu de "WHERE hash = '32charhexstring'"), mais vous aurez l'option pour faire ça si tu veux. À moins que vous ne puissiez trouver un scénario concret où MD5() vous laissera tomber, n'hésitez pas à l'utiliser. La question difficile est de savoir si et comment vous allez avoir besoin de rechercher des URL d'une autre manière que leur texte intégral: par exemple, voulez-vous trouver toutes les URL commençant par "/ foo" sur n'importe quelle barre. com "hôte? Alors que "LIKE '% bar.com%/foo%'" fonctionnera dans les tests, il échouera lamentablement à l'échelle. Si vos besoins incluent des choses comme cela, vous pouvez trouver des moyens créatifs pour générer des index non-UNIQUE ciblés sur le type de données dont vous avez besoin ... peut-être une colonne domain_name, pour commencer. Vous devrez presque remplir ces colonnes à partir de votre application (les triggers et les procédures stockées posent beaucoup plus de problèmes qu'ils n'en valent, surtout si vous vous souciez des performances - ne vous embêtez pas). La bonne nouvelle est que les bases de données relationnelles sont très flexibles pour ce genre de choses. Vous pouvez toujours ajouter de nouvelles colonnes et les remplir plus tard. Je suggère pour les débutants: int uns_increment clé primaire auto_increment, caractère de hachage unique (32), et (en supposant que 64K caractères suffise) url de texte.

+0

+1 - Il existe de sérieuses implications en termes de performances sur des clés primitives plus larges, bien documentées par l'équipe SQL et ignorées par la plupart des développeurs. – TomTom

+0

Pourquoi stocker des hachages en tant qu'hex plutôt que sous forme décimale? –

1

Dépend de la façon dont vous utilisez la table. Si vous choisissez la plupart du temps avec WHERE url='<url>', alors c'est bien d'avoir une table à une colonne. Si vous pouvez utiliser un ID auto-incrémenté pour identifier une URL dans tous les endroits de votre application, utilisez l'auto-incrémentation

2

Vous supposez que vous parlez d'une URL complète, et pas seulement d'un nom d'hôte, y compris des paramètres CGI et autres.

SHA-1 hachage les URL rend toutes les clés longues, et rend le problème de tri relativement obscur. J'ai dû utiliser les index sur les hachages une fois pour masquer certaines données confidentielles tout en conservant la possibilité de joindre deux tables, et les performances étaient médiocres.

Il existe deux approches possibles. L'un est le naïf et le plus évident; cela fonctionnera bien dans mySQL. Il présente des avantages tels que la simplicité et la possibilité d'utiliser URL LIKE 'quel que soit%' pour effectuer une recherche efficace.

Mais si vous avez beaucoup d'URL concentrées dans quelques domaines ... par exemple ....

http://stackoverflow.com/questions/3735390/best-primary-key-for-storing-urls 
http://stackoverflow.com/questions/3735391/how-to-add-a-c-compiler-flag-to-extconf-rb 

etc, vous êtes à la recherche à des indices qui ne varient que dans les derniers caractères. Dans ce cas, vous pouvez envisager de stocker et d'indexer les URL avec leur ordre de caractères inversé. Cela peut conduire à un index plus accessible.

(Le produit serveur de table Oracle s'est construit dans le moyen de le faire avec un indice que l'on appelle inversée.)

Si je vous j'éviter une clé autoincrement à moins que vous devez joindre à plus de deux tables ON TABLE_A.URL = TABLE_B.URL ou une autre condition de jointure avec ce type de message.

+1

Une façon d'améliorer les performances des jointures sur les hachages consiste à ajouter une seconde colonne indexée avec une version plus «concentrée» des données de hachage. Un BIGINT avec les 64 premiers bits d'un MD5 peut être indexé plus efficacement qu'un CHAR (32). Les collisions seront un million de fois plus fréquentes, c'est-à-dire extrêmement rares. Votre WHERE peut se joindre aux deux colonnes ("O WH t1.inthash = t2.inthash ET t1.charhash = t2.charhash") et dans le cas extrêmement rare d'une collision BIGINT, le hash complet vous assurera que vous obtenez toujours la bonne réponse. –