2011-09-07 2 views
1

Tenir compte de cette requête:performances SQL sur la correspondance multiple identifiant et un JOIN

SELECT DISTINCT (linkindex_tags.link_id) 
    , links_sorted.link_title 
    , links_sorted.link_url 
FROM linkindex_tags 
INNER JOIN links_sorted ON links_sorted.link_id = linkindex_tags.link_id 
ORDER BY 
(
IF (word_id = 400, 1,0)+ 
IF (word_id = 177, 1,0)+ 
IF (word_id = 114, 1,0)+ 
IF (word_id = 9, 1,0)+ 
IF (word_id = 270, 1,0)+ 
IF (word_id = 715, 1,0)+ 
IF (word_id = 279, 1,0)+ 
IF (word_id = 1, 1,0)+ 
IF (word_id = 1748, 1,0) 
) DESC 
LIMIT 0,15; 

Ainsi, la recherche pour les matchs à une série de son et odering word_id par le score de ces matchs (par exemple, trouver un lien avec 5 word_ids puis il est un score de 5)

La table linkindex_tags est actuellement 552,196 lignes (33 Mo) mais expan à plusieurs millions Le tableau link_sorted est actuellement 823600 (558MB - OBV plus de données par ligne) lignes, mais également étendre plus . La table linkindex_tags est susceptible d'être environ 8-12 fois plus grande que links_sorted.

Temps d'exécution: 7,069 secondes sur une machine Windows 7 Core i3 locale. Mon serveur est CentOs 64bit RAM 8 Go Intel Xeon 3470 (Quad Core) - donc cela aidera dans la question un peu comme je peux attribuer une allocation de RAM décent.

Il court lentement et se demandait si mon approche était erronée. Voici les bits lents de la rupture du profil:

copie au TMP tableau - (heure) 3,88124 - (%) 55,08438
copie au TMP tableau sur le disque - (heure) 2,683123 - (%) 8,08010
convertir PGRPI à MyISAM - (temps) 0,37656 - (%) 5,34432

Voici le EXPLIQUER:

id - 1 
select_type - SIMPLE 
table - linkindex_tags 
type - index 
possible_keys - link_id,link_id_2 
key - link_id 
key_len - 8 
ref - \N 
rows - 552196 
Extra - Using index; Using temporary; Using filesort 

2nd row 

id - 1 
select_type - SIMPLE 
table - links_sorted 
type - eq_ref 
possible_keys - link_id 
key - link_id 
key_len - 4 
ref - flinksdb.linkindex_tags.link_id 
rows - 1 
Extra - 

Et enfin, le schéma de 2 table:

CREATE TABLE IF NOT EXISTS `linkindex_tags` (
    `linkindex_tag_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `link_id` int(10) unsigned NOT NULL, 
    `word_id` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`linkindex_tag_id`), 
    UNIQUE KEY `link_id` (`link_id`,`word_id`), 
    KEY `link_id_2` (`link_id`), 
    KEY `word_id` (`word_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=0 ; 

CREATE TABLE IF NOT EXISTS `links_sorted` (
    `link_sorted_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `site_id` int(10) unsigned NOT NULL, 
    `link_id` int(10) unsigned NOT NULL, 
    `link_title` char(255) NOT NULL, 
    `link_duration` char(20) NOT NULL, 
    `link_url` char(255) NOT NULL, 
    `active` tinyint(4) NOT NULL, 
    PRIMARY KEY (`link_sorted_id`), 
    UNIQUE KEY `link_id` (`link_id`), 
    KEY `link_title` (`link_title`,`link_url`,`active`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=0 ; 

Avoir à coller avec INT, car il peut entrer une plage plus grande que MEDIUMINT. Sans la jointure, juste obtenir les ids la requête est rapide maintenant j'ai augmenté certains paramètres de MySQL. Je ne connais pas trop les paramètres MySQL et leurs effets, donc si vous avez besoin de moi pour changer quelques paramètres et faire des tests par tous les moyens, c'est parti!

Oh et moi avons joué avec les paramètres de mysql.ini pour qu'ils soient comme ça - juste deviner et jouer vraiment!

key_buffer = 512M 
max_allowed_packet = 1M 
table_cache = 512M 
sort_buffer_size = 512M 
net_buffer_length = 8K 
read_buffer_size = 512M 
read_rnd_buffer_size = 512K 

Comment puis-je accélérer cette requête?

+0

Je ne sais pas si cela vous donnera beaucoup d'avantages de performance, mais vous pouvez réécrire tout ce tri complexe en ceci: 'commande par word_id en (400, 177, 114, 9, 270, 715, 279, 1, 1748) desc' – Karolis

+0

Merci pour cela, sauf si je me trompe, ne marquera pas les matchs .. – dolyth

+0

effectivement ignorer cela, Johan a expliqué cela – dolyth

Répondre

0

Quelques commentaires:

DISTINCTS
SELECT DISTINCT fonctionne sur tous les champs sélectionnés, peu importe combien de () vous utilisez, utilisez une clause GROUP BY à la place si vous ne souhaitez 1 champ soit distinct.
Notez que cela rendra les résultats de votre requête indéterminés!
Conserver le distinct, ou regrouper les autres champs dans un GROUP_CONCAT si vous voulez empêcher cela.

ORDER BY
Un champ ne peut avoir qu'une seule valeur à la fois, ajoutant un autre IF de ensemble, quand il ne peut y avoir qu'un seul qui correspond à une perte de temps, utilisez un IN à la place.
Un booléen = 1 pour vrai, 0 pour faux, vous n'avez pas besoin d'un IF supplémentaire pour l'affirmer.


Si vous avez un beaucoup de lignes, envisager d'ajouter un où cela peut réduire le nombre de lignes à l'étude, sans en altérer le résultat.

?
Est-ce que la série: 400,177,114,9,270,715,279,1,1748 est le même type de construction magique que le 4-8-15-16-23-42 dans Lost?

SELECT lt.link_id 
    , GROUP_CONCAT(ls.link_title) as link_titles 
    , GROUP_CONCAT(ls.link_url) as link_urls 
FROM linkindex_tags lt 
INNER JOIN links_sorted ls ON ls.link_id = lt.link_id 
WHERE lt.word_id <= 1748 
GROUP BY lt.link_id 
ORDER BY 
(
    lt.word_id IN (400,177,114,9,270,715,279,1,1748) 
) DESC 
LIMIT 15 OFFSET 0; 
+0

Impressionnant - se rapprocher. J'ai effectué quelques tests - je ne sais pas pourquoi vous avez besoin de GROUP_CONCAT car les résultats sont identiques sans eux et la deuxième exécution de la requête (je suppose que la mise en cache) exécute la requête beaucoup plus rapidement sans eux. Le GROUP BY (lt.link_id) ne supprime-t-il pas ce besoin? - oh, les nombres sont une série de word_id placés à partir de php quand il construit la requête. – dolyth

+0

Si vous avez plusieurs résultats pour ls.link_title et ls.link_url, vous avez besoin de 'group_concat', sinon vous ne le faites pas. Pour ce qui est des chiffres, j'espérais pouvoir les utiliser à la loterie. – Johan

+0

@Dolyth, l'exécution de la deuxième requête est toujours plus rapide et oui, cela est dû à la mise en cache. Tant la mise en cache de la préparation de la requête que les résultats de la requête, vous pouvez désactiver ce dernier en utilisant 'SELECT NO_SQL_CACHE .. reste de la requête ...' Ceci donnera des résultats beaucoup plus réalistes sur les répétitions. – Johan