Question

J'ai une requête relativement simple (cette fois) sur laquelle j'ai besoin de TOUS les résultats (je les stocke dans un tableur Excel). La requête elle-même expire le serveur, alors comment puis-je l'exécuter sans que cela se produise?

Était-ce utile?

La solution

Le moyen le plus simple serait de diviser le domaine de la requête en plusieurs parties. Par exemple, ajoutez à la clause WHERE une expression ne sélectionnant que la première moitié de la plage de clés, puis exécutez une seconde requête pour sélectionner la moitié inférieure. Puis fusionnez la sortie.

Autres conseils

Vous pouvez augmenter le délai de requête pour la page:

<cfsetting requestTimeout="3600" />

Cela garantira que vous aurez le temps de traiter toutes les entrées.

Vous pouvez également vouloir diviser la liste en & "; morceaux &" ;. Il faudra quelques ajustements pour déterminer la taille optimale du bloc, mais vous pouvez saisir les résultats 100 ou 1000 lignes à la fois et utiliser & Lt; cfflush & Gt; pour afficher les résultats à l'écran dès qu'ils sont disponibles. Cette approche présente également l’avantage d’utiliser moins de mémoire sur le serveur coldFusion, car chaque ligne extraite du serveur SQL est chargée dans la mémoire CFML et y reste jusqu’à ce que la variable d’objet de requête soit écrasée ou disparaisse (à la fin de la page). Cela signifie que vous pouvez facilement remplir la mémoire coldFusion en lisant plusieurs centaines de milliers de lignes, en particulier si les lignes sont & "Wide &"; (c'est-à-dire contenant de grandes varchars ou des textes).

Tout d'abord, je voudrais voir pourquoi cette requête prend si longtemps.

Que pouvez-vous faire au niveau de la base de données pour améliorer les performances de la requête? Cela ressemble peut-être à ne pas indexer correctement la base de données. Prenez la requête et jetez-la dans un programme permettant d'analyser le plan d'exécution. cherchez les décalages et adressez-vous à eux.

pour plus de performances, envisagez de créer des vues indexées si votre base de données prend en charge ce type de choses.

regardons maintenant la mise en cache de certaines parties de la requête. il n'y a aucune raison de faire des calculs sur les données historiques pour chaque demande alors que cela pourrait être fait une fois puis mis en cache quelque part dans une table.

comme pour la fin de coldfusion. Assurez-vous d'utiliser java.io.BufferedWriter pour créer la feuille de calcul. L'utilisation d'une méthode de concaténation de chaînes normale dans CF est lente et BufferedWriter est infiniment plus rapide. Vous trouverez ci-joint un CFC que j'ai créé pour créer des feuilles de calcul délimitées par des tabulations. Vous pouvez le modifier selon vos besoins.

<!--- init --->
<cffunction name="init" access="public" returntype="Any" output="false">
    <cfargument name="name" type="string" required="true">
    <cfset var local = {}>

    <!--- name of file when downloading --->
    <cfset variables.name = arguments.name & ".xls">
    <!--- name of temp file --->
    <cfset variables.filename = CreateUUID() & ".csv">
    <!--- full path to temp file for downloading --->
    <cfset variables.fullfilename = expandpath("/_temp") & "\" & variables.filename>
    <!--- file write java object --->
    <cfset variables.filewriter = CreateObject("java","java.io.FileWriter").init(
            variables.fullfilename
            ,JavaCast("boolean","true")
        )>
    <!--- buffered writer java object --->
    <cfset variables.bufferedwriter = CreateObject("java","java.io.BufferedWriter").init(
                variables.filewriter
            )>
    <!--- row delimeter --->
    <cfset variables.row = chr(10)>
    <!--- col delimeter --->
    <cfset variables.col = chr(9)>
    <!--- header container --->
    <cfset variables.headers = []>
    <!--- data container --->
    <cfset variables.data = []>
    <cfset newrow()>
    <cfreturn this>
</cffunction>


<!--- addheader --->
<cffunction name="addheader" access="public" returntype="void" output="false">
    <cfargument name="str" type="string" required="true">
    <cfset arrayappend(variables.headers, arguments.str)>
</cffunction>

<!--- newrow --->
<cffunction name="newrow" access="public" returntype="void" output="false">
    <cfset arrayappend(variables.data, arraynew(1))>
    <cfset variables.data_counter = arraylen(variables.data)>
</cffunction>

<!--- adddata --->
<cffunction name="adddata" access="public" returntype="void" output="false">
    <cfargument name="str" type="string" required="true">
    <cfset arrayappend(variables.data[variables.data_counter], arguments.str)>
</cffunction>

<!--- flush --->
<cffunction name="flush" access="public" returntype="void" output="false">
    <cfset var local = {}>

    <!--- write headers --->
    <cfset local.counter = 0>
    <cfset local.headers_count = arraylen(variables.headers)>
    <cfloop array="#variables.headers#" index="local.header">
        <cfset local.counter++>
        <cfset variables.bufferedwriter.write(local.header & variables.col)>
    </cfloop>

    <cfif not arrayisempty(variables.headers)>
        <cfset variables.bufferedwriter.write(variables.row)>
    </cfif>

    <!--- write data --->
    <cfloop array="#variables.data#" index="local.data">
        <cfloop array="#local.data#" index="local.cell">
            <cfset variables.bufferedwriter.write(local.cell & variables.col)>
        </cfloop>
        <cfset variables.bufferedwriter.write(variables.row)>
    </cfloop>

    <cfset variables.bufferedwriter.close()>
    <cfsetting showdebugoutput="No">
    <cfheader name="Content-Description" value="File Transfer">
    <cfheader name="Content-Disposition" value="attachment;filename=#variables.name#">
    <cfcontent type="application/vnd.ms-excel" file="#variables.fullfilename#" deletefile="true" reset="true">
</cffunction>

Comme d'autres l'ont fait remarquer, vous pouvez essayer d'augmenter le délai de requête de la page, bien que cela ne soit pas conseillé si l'exécution de votre requête est mesurée en minutes , plutôt qu'en secondes ou en millisecondes. CF ne traite qu'un nombre défini de demandes à la fois. Vous devez donc veiller à ne pas bloquer l'une de ces demandes en attente d'une requête de 5 minutes.

Si vous utilisez SQL Server ou Oracle, je pense que CFQUERY expose son propre attribut de délai d'expiration par requête que vous pouvez définir. Encore une fois, cela n’est pas conseillé pour les requêtes très longues.

D'après mon expérience, si votre requête est si complexe ou si elle renvoie tellement de données qu'il faut minutes , il est temps de dissocier l'exécution de la requête de la requête qui l'a initiée. il. Vous pouvez le faire de différentes manières, par exemple:

  1. Créez une sorte de système de file d'attente pour enregistrer les demandes de service en attente. Cela peut être une table de base de données, un fichier XML sur disque, etc. Lorsque votre utilisateur demande leurs données, vous enregistrez cette demande avec cette file d'attente.

  2. Ecrivez une tâche planifiée (par exemple, Java, DTS ou une page CF planifiée) qui vérifie périodiquement le bon fonctionnement de cette file d'attente. Selon vos besoins, vous pouvez créer un fil d’arrière-plan pour traiter chaque demande, ou peut-être la tâche planifiée le traite-t-elle directement. Si vous utilisez des pages CF planifiées, vous souhaiterez diviser la charge de travail totale en fragments plus petits pouvant être traités de manière itérative, sinon vous aurez le même problème que vous avez maintenant.

  3. Une fois que la tâche planifiée a déterminé qu'une demande a été remplie, elle déclenche une sorte de notification indiquant que le traitement est prêt. Par exemple, vous pouvez envoyer un courrier électronique à l'utilisateur pour lui dire que les données sont prêtes, avec un lien pour télécharger un fichier .csv créé sur le disque.

Évidemment, le bon choix dépend beaucoup du problème spécifique à résoudre. En général, j'essaierais ces choses, dans cet ordre:

  1. Attaquez agressivement le temps d'exécution de la requête. Pouvez-vous utiliser des index ou écrire mieux en T-SQL?
  2. Si la requête prend une minute ou deux et qu'elle est exécutée très rarement, il peut être acceptable d'augmenter les délais d'attente des pages ou des requêtes.
  3. Si la requête est souvent exécutée ou prend plus de 2 à 3 minutes, mordez dans le mille et créez un système de traitement par lots ou en file d'attente pour gérer la requête en arrière-plan.

Vous pouvez définir le délai d'expiration par requête, même si la fusion de plusieurs requêtes peut s'avérer une meilleure approche.

<cfsetting 
enableCFoutputOnly = "yes|no" 
requestTimeOut = "value in seconds"
showDebugOutput = "yes|no" >

Utilisez l'indexation correctement. Créez des clés étrangères partout où vous le pouvez. Les requêtes n'expireront jamais pour une base normalisée.

Soyez très prudent avec les jointures et les clauses, comme si vous avez une clause group by dans votre requête au lieu d'utiliser la clause where, la clause having fonctionnera plus rapidement. Réduisant ainsi le temps d'exécution des requêtes.

Utilisez l’estimation du coût pour déterminer quelle table prend le plus de temps ou a besoin de normalisation dans votre base de données.

Je jetterais la requête dans un thread séparé, en la chargeant dans une portée persistante (par exemple, une session). Transférer vers une page qui vérifie l'existence de la requête. Répétez la vérification jusqu'à ce que la requête existe, puis transférez-la vers une page qui l’affiche / traite / whatevers.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top