2010-11-12 39 views
9

Un collègue m'a dit que l'exécution d'une instruction SQL place toujours les données dans la RAM/swap par le serveur de base de données. Il n'est donc pas pratique de sélectionner de grands ensembles de résultats.L'exécution d'une instruction prend-elle toujours en mémoire le jeu de résultats?

Je pensais que ce code

my $sth = $dbh->prepare('SELECT million_rows FROM table'); 
while (my @data = $sth->fetchrow) { 
    # process the row 
} 

récupère le jeu de résultats ligne par ligne, sans qu'il soit chargé de la RAM. Mais je ne trouve aucune référence à cela dans les documents DBI ou MySQL. Comment le jeu de résultats est-il réellement créé et récupéré? Est-ce que cela fonctionne de la même manière pour les sélections simples et les jointures?

+0

question sur le but, pourquoi avez-vous besoin d'aller chercher des millions de lignes d'enregistrement et itérer chercher tout? 'mysqldump' devrait être plus approprié – ajreal

+0

@ajreal: J'ai besoin de traiter toutes les lignes dans l'ordre d'insertion et de générer des rapports. – planetp

+0

ok, est-ce rationnel de le faire? en utilisant la fonction mysql pour générer la vue, la table temporaire n'est pas suffisante pour le rapport? ou même envisager de vider les résultats BIG dans le fichier, et ouvrir le fichier pour le traitement – ajreal

Répondre

6

Votre collègue a raison. Par défaut, le module perl DBD :: mysql utilise mysql_store_result qui lit en effet dans toutes les données SELECT et le met en cache dans la mémoire vive (RAM). Si vous ne modifiez pas cette valeur par défaut, lorsque vous récupérez ligne par ligne dans DBI, il suffit de les lire dans ce tampon mémoire.

Ceci est généralement ce que vous voulez, sauf si vous avez de très grands ensembles de résultats. Sinon, jusqu'à ce que vous récupériez les dernières données de mysqld, il doit tenir ces données prêtes et je crois comprendre qu'elles provoquent des blocs sur les mêmes lignes (blocs? Tables?). Gardez à l'esprit que les machines modernes ont beaucoup de mémoire vive (RAM). Un ensemble de résultats de millions de lignes n'est généralement pas un gros problème. Même si chaque rangée est assez grande à 1 Ko, cela ne représente que 1 Go de RAM plus les frais généraux.

Si vous souhaitez traiter des millions de lignes de BLOB, vous voulez peut-être utiliser mysql_use_result - ou vous voulez SÉLECTIONNER ces lignes en segments avec des utilisations progressives de LIMIT x,y.

Voir mysql_use_result et mysql_store_result dans perldoc DBD::mysql pour plus de détails.

+0

+ 1, ne savait pas que DBD :: mysql fait cela. Cependant, votre commentaire selon lequel vous ne devriez pas vous soucier de l'épuisement de la RAM est un conseil BAD - en règle générale, vous ne devriez obtenir que les données dont vous avez besoin et si vous n'avez pas besoin de millions de lignes, vous ne devriez pas tous les avoir. Une telle approche ruinera l'extensibilité irréparable (la situation est un peu meilleure si la bibliothèque effectue une mise en cache au niveau de l'application et non au niveau de la session, mais pas toujours). Si ce cache est souvent invalidé, vous récupérez 1 Go de données. beaucoup moins) – Unreason

1

Je ne suis pas très familier avec cela, mais il me semble que DBD :: mysql peut tout extraire d'avance ou seulement au besoin, basé sur l'attribut mysql_use_result. Consultez la documentation DBD :: mysql et MySQL.

5

Ceci n'est pas vrai (si nous parlons du serveur de base de données lui-même, pas des couches client).

MySQLMySQL peut tamponner l'ensemble du résultat, mais ce n'est pas nécessairement fait, et si cela est fait, pas nécessairement RAM.

Le ResultSet est tamponnées si vous utilisez des vues ligne (SELECT FROM (SELECT …)), la requête doit trier (qui est présenté comme using filesort), ou le plan exige la création d'une table temporaire (qui est présenté comme using temporary dans le plan de requête) .

Même si using temporary, MySQL conserve uniquement la table en mémoire lorsque sa taille ne dépasse pas la limite définie dans tmp_table. Lorsque la table dépasse cette limite, elle est convertie de memory en MyISAM et stockée sur le disque.

Vous, cependant, pouvez explicitement instruire MySQL pour mettre en tampon le jeu de résultats en ajoutant l'instruction SQL_BUFFER_RESULT au SELECT le plus à l'extérieur. Pour plus de détails, voir le docs pour plus de détails.

3

Non, ce n'est pas comme cela que ça fonctionne.

La base de données ne contient pas de lignes dans la RAM/l'échange.

Cependant, il tentera, et mysql s'efforce ici de mettre en cache autant que possible (index, résultats, etc ...). Votre configuration mysql donne des valeurs pour les tampons de mémoire disponibles pour différents types de caches (pour différents types de moteurs de stockage) - vous ne devez pas permettre l'échange de ce cache.

Testez
Bottom line - il devrait être très facile à tester ce client en utilisant uniquement (je ne sais pas le DBI de Perl, il pourrait, mais je en doute, faire quelque chose qui force mysql pour charger tout sur préparer). Quoi qu'il en soit ... le tester:

Si vous émettez réellement un préparer sur SELECT SQL_NO_CACHE million_rows FROM table, puis récupérer seulement quelques lignes sur des millions. Vous devez ensuite comparer les performances avec SELECT SQL_NO_CACHE only_fetched_rows FROM table et voir comment ces tarifs. Si la performance est comparable (et rapide) alors je crois que vous pouvez appeler le bluff de votre collègue.

De même si vous activez le journal des instructions réellement émises à mysql et que vous nous en fournissez une transcription, nous (non perl folks) pouvons donner une réponse plus définitive sur ce que ferait mysql.