web-dev-qa-db-fra.com

Mysql rejoindre deux grandes tables est très lente

J'ai deux tables, l'une d'elles contient une histoire d'URL téléchargées, tandis que l'autre table contient des détails sur chaque URL.

La requête suivante regroupe les URL par le nombre de répétitions au cours de la dernière heure.

SELECT COUNT(history.url) as total, history.url
FROM history
WHERE history.time > UNIX_TIMESTAMP()-3600
GROUP BY history.url
ORDER BY COUNT(history.url) DESC 
LIMIT 30

La requête ci-dessus prend environ 800 ms à exécuter, pas assez vite, mais acceptable,

cependant, lorsqu'il y jointe avec la table de cache, la nouvelle requête prend environ 25 ans à exécuter, ce qui est très lent.

SELECT th.total, th.url, tc.url, tc.json 
FROM (SELECT COUNT(history.url) as total, history.url
      FROM history 
      WHERE history.time > UNIX_TIMESTAMP()-3600
      GROUP BY history.url
      ORDER BY COUNT(history.url) DESC 
      LIMIT 30
) th
INNER JOIN (SELECT cache.url, cache.json FROM cache) tc
    ON th.url = tc.url
GROUP BY th.url
ORDER BY th.total DESC
LIMIT 30

Je pense que cela pourrait se produire car dans "TC", la table en cache entière est chargée et contient 1 Million + entrées.

Lorsque j'utilise la première requête, puis d'itérer par programme les résultats, puis exécutez une requête SELECT dans le cache pour chaque résultat, il est beaucoup plus rapide. Y a-t-il de toute façon pour accélérer ma deuxième requête?

P.s. J'utilise innodb

Mettre à jour la sortie de la deuxième requête avec expliquer enter image description here

La structure de la table 'Histoire' enter image description here

La structure de la table 'cache' enter image description here

1
Ali AlNoaimi

Il est généralement judicieux d'indexer des colonnes qui participent à des prédicats de jointure ou des clauses. Une erreur commune consiste à créer plusieurs index de colonne au lieu de moins d'index multi-colonnes. Ici, nous pouvons bénéficier de l'URL et du temps de l'histoire (examinez toutes les requêtes sur ces tables et vous pouvez constater que vous pouvez ajouter des colonnes supplémentaires à ces index):

CREATE INDEX x01_history_url ON HISTORY (URL, TIME);
CREATE INDEX x01_cache_url ON CACHE (URL);

Deuxièmement, essayez d'inhérent les requêtes. MySQL a des limitations sur le type de requennie de requêtes qu'il est capable de le faire, de sorte que la nidification peut provoquer des frais généraux inutiles.

SELECT COUNT(th.url) as total, tc.url, tc.json 
FROM history th
JOIN cache tc
    ON th.url = tc.url
WHERE th.time > UNIX_TIMESTAMP()-3600
GROUP BY tc.url, tc.json
ORDER BY COUNT(th.url) DESC 
LIMIT 30

Notez que cette requête est sémantiquement différente de votre requête afin d'obtenir un résultat différent. Si tel est un problème, vous voudrez peut-être garder la limite de 30 construction dans une sous-requête comme avant. Vous pouvez également envisager si vous pouvez ajouter une limite similaire au cache, existe-t-il une limite supérieure sur le nombre de rangées de cache que vous devez enquêter pour obtenir 30 rangées au total?

INNER JOIN (SELECT cache.url, cache.json 
            FROM cache
            ORDER BY ? LIMIT ?) tc
4
Lennart

Pour la première requête:

SELECT COUNT(*) as total,  -- * is the common pattern
       url
    FROM history
    WHERE time > NOW() - INTERVAL 1 HOUR
    GROUP BY url
    ORDER BY COUNT(*) DESC 
    LIMIT 30

INDEX(time, url)  -- in this order
INDEX(url, time)  -- maybe this order

Utiliser les deux index; Différentes versions MySQL et une plage de temps différente peuvent utiliser un index par rapport à l'autre.

Pour la deuxième requête, n'utilisez pas inutilement une table dérivée:

INNER JOIN (SELECT cache.url, cache.json FROM cache) tc
   ON th.url = tc.url

->

INNER JOIN cache tc
    ON th.url = tc.url

Et cache nécessite INDEX(url).

Les deux tables 1: 1 ou 1: plusieurs ou plusieurs: 1 ou plusieurs: beaucoup? Cela peut faire la différence pour le GROUP/ORDER/LIMIT.

Pendant que vous y êtes, veuillez fournir SHOW CREATE TABLE Pour les tables.

1
Rick James