![]() |
Datenbank: MySQL • Version: 5.6.16 • Zugriff über: UniDAC
[MySQL, UniDAC] Query vorzeitig abbrechen?
Ahoi DP,
ich lasse mir in einem Thread Daten zur Anzeige in einem Graphen aus einer MySQL DB holen. Dies mache ich immer, wenn jemand im Graphen die Achsen verschiebt oder den X-Zoom ändert. Das klappt bisher auch recht gut. Leider passiert es aber insbesondere beim Zoom mit dem Scrollrad, dass eine neue Abfrage gestartet wird bevor die davor fertig ist. Durch das entsprechend nötige Syncen führt das am Ende dazu, dass man nur maximal so schnell scrollen kann, wie die DB die Daten holt. Oder genauer: Man sieht das Endergebnis erst dann, wenn auch der letzte Thread fertig geworden ist - welcher leider noch nichtmals immer der ist, der zuletzt gestartet wurde, wodurch man manchmal unvollständige Graphen bekommt, da sie noch zu einem anderen Zeitabschnitt oder Zoom gehören. Aktuell behelfe ich mir, in dem ich ggf. noch laufende Threads bei Eingang einer neuen Zoom- oder Verschiebeoperation versuche sich "gracefully" beenden zu lassen, und falls das nicht innerhalb von 100ms passiert ist, den Thread mit KillThread() brutal wegzuschießen. Das geht! Aber leider befürchte ich dadurch massive Speicherlöcher und irgendwann auch Handle-Knappheit, da sich natürlich jeder Thread eine eigene UniConnection und UniQuery erstellt, die beim Abschießen wahrscheinlich nicht freigegeben werden, und ich keine Ahnung habe, was auf Seiten des SQL Servers noch offen bleibt. Gibt es irgend eine Möglichkeit eine gerade laufende Query vor Rückkehr abzubrechen? Das wäre die mir liebste und einfachste Lösung.
Delphi-Quellcode:
Die ganzen "if Aborted then ..." sind mein Versuch den Thread vernünftig zu beenden. Die Aufrufende Methode sieht so aus:
procedure TKATChartUpdateThread.Execute;
var con: TUniConnection; qry: TUniQuery; startDate, endDate: TDateTime; tmpSeries: TObjectList; seriesIDs: TList; aborted: Boolean; begin aborted := false; seriesIDs := nil; Screen.Cursor := crAppStart; con := TUniConnection.Create(nil); con.Server := FChart.FConnection.Server; con.Database := FChart.FConnection.Database; con.Username := FChart.FConnection.Username; con.Password := FChart.FConnection.Password; con.ProviderName := FChart.FConnection.ProviderName; con.Connect; qry := TUniQuery.Create(nil); qry.Connection := con; if Abort then begin aborted := true; Exit; end; try startDate := UnixToDateTime(FChart.FLeftXValue); endDate := UnixToDateTime(FChart.FRightXValue); if Abort then begin aborted := true; Exit; end; tmpSeries := TObjectList.Create(true); FChart.FSeriesIDLock.Enter; try seriesIDs := TList.Create; seriesIDs.Assign(FChart.FSeriesIDs); finally FChart.FSeriesIDLock.Leave; end; if Abort then begin aborted := true; tmpSeries.Free; Exit; end; for i := 0 to seriesIDs.Count-1 do begin k := tmpSeries.Add(TKATChartSeries.Create(Integer(seriesIDs[i]))); qry.SQL.Text := BuildSumSQL(Integer(seriesIDs[i]), con); qry.ParamByName('t1').AsDateTime := startDate; qry.ParamByName('t2').AsDateTime := endDate; qry.Open; while not qry.Eof do begin TKATChartSeries(tmpSeries[k]).AddValue(qry.FieldByName('qMIN').AsFloat, qry.FieldByName('qMAX').AsFloat, qry.FieldByName('qAVG').AsFloat, DateTimeToUnix(qry.FieldByName('qDate').AsDateTime)); qry.Next; if Abort then begin aborted := true; tmpSeries.Free; Exit; end; end; qry.Close; end; if Abort then begin FChart.IsBusy := false; aborted := true; tmpSeries.Free; Exit; end; FChart.FSeriesLock.Enter; try if Assigned(FChart.FSeries) then FreeAndNil(FChart.FSeries); FChart.FSeries := tmpSeries; finally FChart.FSeriesLock.Leave; end; finally Screen.Cursor := crDefault; qry.Free; con.Free; if Assinged(seriesIDs) then seriesIDs.Free; if not aborted then Synchronize(FChart.UpdateReady); end; end;
Delphi-Quellcode:
Die einzige Operation die in dem Thread potenziell lange dauert ist die Zeile
procedure TKATChart.MakeSeries;
begin if Assigned(FUpdateThread) then begin FUpdateThread.Abort := true; if WaitForSingleObject(FUpdateThread.Handle, 100) = WAIT_TIMEOUT then TerminateThread(FUpdateThread.Handle, 0); end; FUpdateThread := TKATChartUpdateThread.Create(self); end;
Delphi-Quellcode:
. Aber da komme ich auf keine mir bekannte Art mehr zwischen. Was kann man hier tun? Insbesondere fürchte ich, dass nach dem TerminateThread die DB noch immer denkt das Ergebnis wird gebraucht, und die Operation unnötig zu Ende führt, was mich Performance bei allen nachfolgenden Queries kosten dürfte. Und Ressourcen die dauerhaft offen bleiben. Sehr unschön.
qry.Open;
|
AW: [MySQL, UniDAC] Query vorzeitig abbrechen?
Ich habe jetzt die SQL-Funktion "KILL QUERY CONNECTION_ID();" gefunden, die ich aus meinem Hauptthread heraus über die selbe Connection sende, die ich im Thread erstelle. (Eigentlich für sich genommen schon unschön, aber es ist ein Versuch.)
Leider braucht das Ausführen des "KILL QUERY" fast so lange, als würde ich auf das normale Beenden der eigentlichen Abfrage warten. Das ist natürlich nicht ganz die erhoffte Wirkung :( |
AW: [MySQL, UniDAC] Query vorzeitig abbrechen?
Moin...:P
Frage am Rande. Du arbeitest im Thread direkt mit dem Chart auf der Form? Wenn mal mehrere gestartet wurden dann alle auf den gleichen Chart? |
AW: [MySQL, UniDAC] Query vorzeitig abbrechen?
Das Chart ist eine Eigenentwicklung die darauf ausgelegt ist damit so arbeiten zu können, keines der einschlägigen bekannten Charts. Alle nicht atomaren "Quergriffe" sind in Critical-Sections fein säuberlich abgesichert, und gezeichnet wird nur im Hauptthread. Und ja, alle UpdateThreads werden für das selbe Chart gestartet - ich habe in diesem Programm nur eines.
Ich hatte heute noch ein wenig mit dem SQL "KILL" gespielt, und diversen anderen Möglichkeiten, aber irgendwie war alles entweder langsam oder hat (teils auch nachvollziehbar) AVs gehagelt. Letztlich bin ich jetzt dabei gelandet bei nötigem Daten-Update erst einen Timer auf 100ms zu starten, und erst wenn in der Zeit kein neues Update-auslösendes Ereignis auftritt starte ich einen neuen Thread. Ansonsten wird der Timer wieder neu aufgezogen. Als Fallback bleibt das gezeigte, also letztlich KillThread(). Ich bekomme nach 1-2 Minuten wildesten Herumzoomens in meinem Chart nur so rund 10 verwaiste ProzessIDs im MySQL Server, die mit Beenden meines Programms auch brav verschwinden. Damit kann ich leben. Auch der Speicherverbrauch ist nicht angestiegen, zwischendrin sogar wieder gesunken, und nach Prozessende räumt Windows ja eh wieder alles weg. (Zumindest schien mir das im Taskmanager so.) Da ich nach langer Recherche bisher keinen zufriedenstellnden ordentlichen und schnellen (in der Ausführung) Weg gefunden habe, und das Programm ohnehin nur so 5-10 Mal im Monat zur Auswertung zum Druck von ein paar Energiemessdaten benutzt wird - selten länger als eine Stunde am Stück und von weniger als 5 PCs aus - werde ich mich vorerst mit dieser leicht krüppeligen Lösung zufrieden geben müssen. Bedienbarkeit und Zeitmangel sind hier wichtiger als der Stolz es ordentlich gemacht zu haben. Zähneknirschend. Andere Projekte klopfen schon wild an der Tür. |
AW: [MySQL, UniDAC] Query vorzeitig abbrechen?
Das du auf das Ende aller Threads warten musst, liegt natürlich auch daran, dass du einen Löck von deinem Chart-Objekt in jedem Thread verwendest.
Den "Throttle" würde ich auf jeden Fall auch verwenden, die Threads würde ich aber unabhängige Listen erstellen lassen und erst zum Abschluss diese Listen an das eigentliche Objekt zurückliefern. Das sollte dein Problem im Prinzip lösen. |
AW: [MySQL, UniDAC] Query vorzeitig abbrechen?
Bei den UniDAC-Komponenten gibt es die Möglichkeit FetchAll = False zu setzen.
Vielleicht hilft dir das weiter. cu |
AW: [MySQL, UniDAC] Query vorzeitig abbrechen?
Zitat:
Delphi-Quellcode:
Es wird also im Thread erst eine temporäre Liste erstellt, die dann mit einer einfachen Referenzzuweisung an das Chart gegeben wird. (Und vorher die alte freigegeben natürlich.)
FChart.FSeriesLock.Enter;
try if Assigned(FChart.FSeries) then FreeAndNil(FChart.FSeries); FChart.FSeries := tmpSeries; finally FChart.FSeriesLock.Leave; end; Theoretisch könnte ich das einfach laufen lassen und nachher nur per gesetztem Abort-Flag das Umhängen der Liste unterlassen bei abgebrochenen Threads. Ich hatte aber gehofft die gerade beim Scrollen mit der Maus dann schnell erzeugten 10-20 Queries die dann ja noch laufen der DB nicht zumuten zu müssen, die vor allem ja auch noch weiterhin sekündlich neue Daten die von einer Server-Applikation kommen wegschreiben muss. Fand's einfach unschön. Die Verlangsamung kam dadurch, dass ich versucht habe den Thread aus dem Hauptthread heraus sauber inmitten einer SQL-Query abzubrechen - was scheinbar ein Ding der Unmöglichkeit ist. Zitat:
@zagota: Wenn ich das richtig verstehe begrenzt das lediglich die Anzahl der Datensätze die übertragen werden. Die Query muss aber trotzdem vorher vollständig ausgeführt werden. Bei mir sind nicht große Datenmengen das Problem (maximal ~1000 Sätze pro Result, meistens eher um 500), sondern die Queries selbst. Ich habe hier schon, für das was sie tut, wirklich gute Geschwindigkeiten. Dinge wie "Ermittle aus sekündlichen Messdaten den Durchschnitt, Minimum und Maximum für Zeitabschnitte von je X Sekunden und Y Minuten, gruppiert nach Zeitabschnitten, und das Summiert aus 3 (von ca. 500) Tabellen mit je knapp 8mio Datensätzen (bisher), im Zeitraum von 01.01.2016 bis 01.02.2016. In, je nach dem wie günstig man den Server gerade erwischt, 1-2 Sekunden, manchmal sogar drunter.) Gemessen an der Aufgabe ist das echt gut, aber zum immer vollständig neu Updaten beim herumscrollen und zoomen ist es zu langsam. Die konkreten Zeitabschnitte ermittle ich aus der Breite des Charts, so dass niemals mehr als 1 Datensatz im Result pro Pixel Breite anfällt. Zu den sekündlichen Daten führe ich in den gleichen Tabellen auch die minütlichen und stündlichen bei jedem Update mit, die ich selektiere wenn 1px >= 1min bzw. Stunde ist. Bis auf die o.g. Kleinigkeit klappt das erfreulich gut. (Ja, die nötig werdende jährliche Festplattenarchivierung ist allen Beteiligten klar. Das ist i.O. :) (Das Datenbank-File hat jetzt schon, nach etwa 3,5 Monaten, ca. 150GB :stupid: )) |
AW: [MySQL, UniDAC] Query vorzeitig abbrechen?
Zitat:
cu |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:11 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz