AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken [MySQL, UniDAC] Query vorzeitig abbrechen?
Thema durchsuchen
Ansicht
Themen-Optionen

[MySQL, UniDAC] Query vorzeitig abbrechen?

Ein Thema von Medium · begonnen am 28. Feb 2016 · letzter Beitrag vom 1. Mär 2016
Antwort Antwort
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#1

[MySQL, UniDAC] Query vorzeitig abbrechen?

  Alt 28. Feb 2016, 13:03
Datenbank: MySQL • Version: 5.6.16 • Zugriff über: UniDAC
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:
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;
Die ganzen "if Aborted then ..." sind mein Versuch den Thread vernünftig zu beenden. Die Aufrufende Methode sieht so aus:

Delphi-Quellcode:
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;
Die einzige Operation die in dem Thread potenziell lange dauert ist die Zeile qry.Open; . 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.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#2

AW: [MySQL, UniDAC] Query vorzeitig abbrechen?

  Alt 28. Feb 2016, 13:32
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
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Benutzerbild von haentschman
haentschman
Online

Registriert seit: 24. Okt 2006
Ort: Seifhennersdorf / Sachsen
5.293 Beiträge
 
Delphi 12 Athens
 
#3

AW: [MySQL, UniDAC] Query vorzeitig abbrechen?

  Alt 28. Feb 2016, 16:28
Moin...
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?
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#4

AW: [MySQL, UniDAC] Query vorzeitig abbrechen?

  Alt 28. Feb 2016, 21:21
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.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#5

AW: [MySQL, UniDAC] Query vorzeitig abbrechen?

  Alt 28. Feb 2016, 23:12
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.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
zagota

Registriert seit: 3. Sep 2014
38 Beiträge
 
#6

AW: [MySQL, UniDAC] Query vorzeitig abbrechen?

  Alt 29. Feb 2016, 09:44
Bei den UniDAC-Komponenten gibt es die Möglichkeit FetchAll = False zu setzen.
Vielleicht hilft dir das weiter.

cu
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.679 Beiträge
 
Delphi 2007 Enterprise
 
#7

AW: [MySQL, UniDAC] Query vorzeitig abbrechen?

  Alt 29. Feb 2016, 23:45
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.
Der Lock betrifft nicht die ganze Abfrage, sondern nur diesen kleinen Block hier:
Delphi-Quellcode:
    FChart.FSeriesLock.Enter;
    try
      if Assigned(FChart.FSeries) then
        FreeAndNil(FChart.FSeries);
      FChart.FSeries := tmpSeries;
    finally
      FChart.FSeriesLock.Leave;
    end;
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.)
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:
Das sollte dein Problem im Prinzip lösen.
Im Prinzip tut es das auch, aber eben mit dem kleinen Nachteilchen, dass die DB unnötig weiterrödeln muss. Dank des "Throttles" dürfte das jetzt allerdings deutlich weniger und seltener sein, so dass ich mal probieren könnte auf KillThread() ganz zu verzichten!

@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 ))
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)

Geändert von Medium (29. Feb 2016 um 23:48 Uhr)
  Mit Zitat antworten Zitat
zagota

Registriert seit: 3. Sep 2014
38 Beiträge
 
#8

AW: [MySQL, UniDAC] Query vorzeitig abbrechen?

  Alt 1. Mär 2016, 06:39
@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.
Habe ich nicht versucht, bei einer Sortierung wird die Query sicherlich vollständig ausgeführt.
cu

Geändert von zagota ( 1. Mär 2016 um 07:12 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:28 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz