Domanda

Ho una query ragionevolmente semplice (questa volta) da cui ho bisogno di TUTTI i risultati (li sto memorizzando in un foglio di calcolo Excel).La query stessa fa scadere il tempo del server, quindi come posso eseguirla senza che ciò accada?

È stato utile?

Soluzione

Il modo più semplice sarebbe quello di suddividere il dominio della query in più parti. Ad esempio, aggiungi alla clausola WHERE un'espressione che seleziona solo la prima metà dell'intervallo di chiavi, quindi esegui una seconda query per selezionare la metà inferiore. Quindi unisci l'output.

Altri suggerimenti

Puoi aumentare il timeout della richiesta per la pagina:

<cfsetting requestTimeout="3600" />

In questo modo avrai il tempo di elaborare tutte le voci.

Potresti anche voler dividere l'elenco in " blocchi " ;. Richiederà un po 'di ottimizzazione per scoprire qual è la dimensione ottimale del pezzo, ma potresti prendere i risultati 100 o 1000 linee alla volta e usare & Lt; cfflush & Gt; per spingere i risultati sullo schermo non appena diventano disponibili. Questo approccio ha anche il vantaggio di utilizzare meno memoria sul server coldFusion, poiché ogni riga estratta dal server SQL viene caricata nella memoria CFML e rimane lì fino a quando la variabile dell'oggetto query non viene sovrascritta o esce dall'ambito (alla fine di la pagina). Questo significa che puoi riempire facilmente la memoria di coldFusion leggendo diverse centinaia di migliaia di righe, specialmente se le righe sono & Quot; wide & Quot; (ovvero contenenti grandi varchar o testi).

prima di tutto vorrei controllare perché questa query impiega così tanto tempo.

cosa è possibile fare a livello di database per migliorare le prestazioni della query. sembra che forse non hai il database indicizzato correttamente. prendi la query e lanciala in qualche programma che puoi analizzare il piano di esecuzione. cerca i ritardi e affrontali.

per ottenere maggiori prestazioni, cerca di creare viste indicizzate se il tuo database supporta questo tipo di cose.

prossima occhiata alla memorizzazione nella cache di alcune parti della query. non c'è motivo di fare calcoli sui dati storici per ogni richiesta quando potrebbe essere fatto una volta e poi memorizzato nella cache da qualche parte in una tabella.

come per la fine della fusione fredda. assicurati di utilizzare java.io.BufferedWriter per creare il foglio di calcolo. l'utilizzo di un normale metodo di concatenazione di stringhe in CF è un cane lento e BufferedWriter è infinitamente più veloce. In allegato è un CFC che ho creato per creare fogli di calcolo delimitati da tabulazione, puoi modificarlo in base alle tue esigenze.

<!--- 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>

Come altri hanno sottolineato, potresti provare ad aumentare il timeout della richiesta della pagina, sebbene ciò non sia consigliabile se l'esecuzione della tua query viene misurata in minuti , piuttosto che secondi o millisecondi. CF soddisferà solo un determinato numero di richieste alla volta, quindi devi stare attento a bloccare una di quelle richieste in attesa di completamento di una query di 5 minuti.

Se si utilizza SQL Server o Oracle, penso che CFQUERY esponga il proprio attributo di timeout per query che è possibile impostare. Ancora una volta, questo non è consigliabile per query di lunga durata.

Nella mia esperienza, se la tua query è così complessa o restituisce così tanti dati che l'esecuzione di minuti richiede, allora è il momento di disaccoppiare l'esecuzione della query dalla richiesta che avvia esso. Esistono diversi modi per farlo, come ad esempio:

  1. Crea una sorta di sistema di accodamento per registrare richieste di servizio in sospeso. Potrebbe trattarsi di una tabella DB, di un file XML su disco, ecc. Quando l'utente richiede i propri dati, registra tale richiesta con questa coda.

  2. Scrivi un'attività pianificata (ad esempio Java, DTS o una pagina CF pianificata) che controlla periodicamente questa coda per lavoro. A seconda delle esigenze, è possibile escludere un thread in background per gestire ogni richiesta, oppure l'attività pianificata può gestirla direttamente. Se stai utilizzando pagine CF pianificate, ti consigliamo di suddividere il carico di lavoro totale in blocchi più piccoli che possono essere gestiti in modo iterativo, altrimenti avrai lo stesso problema che hai ora.

  3. Una volta che l'attività pianificata determina che una richiesta è stata compilata, inizia una sorta di notifica che l'elaborazione è pronta. Ad esempio, potresti inviare un'email all'utente per comunicare che i dati sono pronti, con un link per scaricare un file .csv creato su disco.

Ovviamente, la scelta giusta dipende molto dal problema specifico da risolvere. In generale, proverei queste cose, in questo ordine:

  1. Attacca in modo aggressivo il tempo di esecuzione della query. Puoi usare gli indici o scrivere meglio T-SQL?
  2. Se la query impiega un minuto o due e viene eseguita molto raramente, potrebbe essere accettabile aumentare il timeout della pagina o della query.
  3. Se la query viene eseguita spesso o richiede più di 2-3 minuti, mordere il proiettile e creare un sistema di batch o di accodamento per gestire la query in background.

Puoi impostare il timeout in base alla richiesta, anche se unire più query potrebbe essere un approccio migliore.

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

Utilizza l'indicizzazione correttamente. Crea chiavi esterne ovunque tu sia. Le query non scadranno mai per un db normalizzato.

Prestare molta attenzione con i join e le clausole, come se ci fosse una group by clausola nella query lì invece di usare la where clausola, la having clausola funzionerà più velocemente. Riducendo così i tempi di esecuzione delle query.

Utilizza la stima dei costi per verificare quale tabella impiega più tempo o necessita di normalizzazione nel tuo db.

Vorrei inserire la query in un thread separato, caricandola in un ambito persistente (ad esempio sessione).Inoltra a una pagina che verifica l'esistenza della query.Ripeti il ​​controllo finché la query non esiste, quindi inoltra a una pagina che la visualizza/elabora/qualunque cosa.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top