[gelöst] Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
Helios
Lazarusforum e. V.
Beiträge: 93
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.0 Windows 10 64Bit / Lazarus 2.0.12 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

[gelöst] Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von Helios »

Hallo Forengemeinde,
meine Firebird Datenbank hat eine stattliche Größe erreicht. Meine dynamisch erzeugten SQL Abfragen sind i.d.R recht schnell. Trotzdem schaffe ich es bei besonderen Filtereinstellungen einen "Langläufer" zu erzeugen, die ich gerne noch über die gleiche GUI benutzerdefiniert/-freundlich abbrechen möchte (nur lesender Zugriff über Standard Select, die IBConnection würde ich dann gerne Schließen und neu wieder Öffnen. Abbruch der Applikation über Ctrl+C hat der Datenbank bisher keine Probleme bereitet aber das ist natürlich nicht im Sinne des Erfinders). Der Start Button der Abfrage sollte sich in einen Abbrechen Button umwandeln (das ist aber das kleinste Problem;-). Ich schätze das geht nur über einen Thread (Interaktion Thread in die GUI, da ist Vorsicht geboten habe ich gelesen). Das ganze sollte unter Windows und Linux (Einbinden von cthreads) laufen. Die Beispiele unter "Lazarus/examples" habe ich gesehen und ausprobiert, aber irgendwie passen die nicht auf meinen Anwendungsfall, da ich ja einen laufenden Thread abwürgen möchte und nicht bis zu seinem Ende warten. Weiterhin die Frage ist die SQL-Abfrage im Mainthread besser untergebracht oder muss die im separaten Thread liegen. Ich hoffe hier hat schon jemand entsprechende Erfahrungen gemacht und kann mir auf die Sprünge helfen.
Vielen Dank und Gruß
Helios
Zuletzt geändert von Helios am Di 1. Jun 2021, 00:28, insgesamt 1-mal geändert.

charlytango
Beiträge: 490
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von charlytango »

zu Threads kann ich dir leider nicht weiterhelfen.
Aber vielleicht zu SQL Abfragen.

Hast du schon identifiziert ob und warum das eigentliche SELECT "langsam" ist?
Und... ist es das SELECT, also die Suche in den Daten oder könnte auch die übermittelte Datenmenge das Problem sein?

Oft hilft das Schrauben an diesen beiden Themen schon enorm.
Möglicherweise helfen gezielt gesetzte Indizes. Oder DB-Einstellungen die der DB mehr Speicher spendieren.

Für besondere Fälle habe ich auch schon Zwischentabellen erzeugt (ich meine damit dass bei einem INSERT oder UPDATE in die Datenbank mittels Triggern automatisiert datenbankseitig redundante Zwischentabellen erzeugt werden, die dann schnell abgefragt werden können.

Auch die Syntax eines SELECTS kann entscheidend sein zu welchem Zeitpunkt der Optimizer einer DB einen bestimmten Index verwendet oder eben nicht.
Besonders wenn diverse Joins mit beteiligt sind.

LG

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5196
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von af0815 »

Faustregel 1: Jeder Thread braucht seine eigenes Connection-Objekt. Ist kein Problem, da die Coonetions sowieso intern in Pools gehalten werden. Zumindest bei den richtigen SQL-Server Treibern.

Faustregel 2: Das Zeitverhalten wird durch Thread nicht besser :-) Wie schon Charlytango geschrieben hat, Zwischentabellen mit den nötigsten Daten, das ganze mit Indizes unterstützt und dann erst die Joins mit den STammdaten darauf loslassen :-) Das hilft. Bei echten Datenmengen und vielen Joins greife ich bei MS-Servern immer zu Parametrisierten Abfragen in TSQL. Damit kann man dann die Antwortzeiten hinbekommen ohne Threads. Hehe 10-20 Sekunden sind die Leute meist sowieso von SAP uä. gewohnt hehe.

BTW: Schon mal mit Explain https://firebirdsql.org/file/documentat ... 11s05.html das ganze angesehen ? Ich verwende was ähnliches für MS-Server (auch die Versionen für Arme können das bei MS). Damit kann man sich ansehen, ob exzessive Tablescans gemacht werden oder ob sowieso alles über Indizes läuft.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Helios
Lazarusforum e. V.
Beiträge: 93
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.0 Windows 10 64Bit / Lazarus 2.0.12 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von Helios »

Hallo charlytango
hallo af0815,
die Abfragen verwenden soweit möglich entsprechende Indizes und mit entsprechenden Explain Plans habe ich die auch soweit es ging optimiert.
Das Problem ist eher, dass der Anwender die Möglichkeit hat über Filtereinstellungen die Abfragemenge versehentlich so groß auszuwählen, dass die Abfrage trotz optimierter Indizes bis zum Speicherüberlauf durchläuft. Wenn man die Filter richtig setzt, dauern die Abfragen wenige Sekunden. Wenn man es falsch macht hängt die Applikation eben bis zum Speicherüberlauf (unter Umständen) sehr lange. Nun habe ich die Möglichkeit eine Art KI zu entwickeln, die das SQL-Statement darauf hin abklopft, ob ein Langläufer erzeugt wird (das wird hoch komplex), oder ich gebe dem Anwender die Möglichkeit das abgesendete SQL-Statement abzubrechen und die Eingabe zu korrigieren und erneut abzuschicken. Die 2. Möglichkeit erscheint mir einfacher umsetzbar zu sein.
Mit Java+JDBC und einer Oracle DB ist das möglich siehe z.B.
http://theblasfrompas.blogspot.com/2008 ... -with.html.
Ich habe die Hoffnung, das das auch unter Lazarus/Firebird in ähnlicher Weise funktioniert, wie unter
https://www.getlazarus.org/forums/viewtopic.php?t=52
schon angedeutet, aber ich habe den Ansatz nicht läuffähig hinbekommen, vielleicht lag es ja am "FStopped" den ich nicht in der richtigen Stelle eingebaut habe und wie eingangs gesagt, mein Thread soll die Datenbankverbindung unterbrechen und sich selbst beenden und beim nächsten Select sich wieder neu mit dem DB-Server mit in einem frischen Thread verbinden.
Hat da jemand vielleicht doch noch eine zündende Idee?
Danke in voraus und Gruß
Helios

charlytango
Beiträge: 490
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von charlytango »

Grübel.....

Will mich da nicht zu weit rauslehnen, aber wenn ein Anwender die Möglichkeit hat mit umfassenden Filterdefinitionen den Server und die Performance zu killen dann läuft im Design etwas grob falsch.
Ich weiß natürlich dass manche Abteilungen in größeren Firmen gern "alles" haben wollen.(vielleicht weil sie das von Office-Produkten so gewohnt sind - nur dort dürfen sie den Mist selber vergeigen) Bekommen sie von mir auch, aber dann eben nur geschulte Anwender mit entsprechenden Rechten und dem Hinweis dass sie das System killen können. Oft sinkt dann das Interesse dramatisch.

Keine Ahnung welche Position du dem Auftraggeber gegenüber hast, aber Tagesgeschäft kann das nicht sein. Solche individuellen Abfragen löse ich meistens mit einer Handvoll vordefinierter SQL Statements die der User dann noch etwas parametrisieren kann.

Das zwingt die Benutzer sich über das Ziel der Abfragen Gedanken zu machen und man kann dann auch mit Zwischentabellen den Vorgang beschleunigen.

Ach ja... bei meinen Applikationen hat die Führungsebene die wenigsten Rechte ;)

Direkt kann ich dir mit Threads nicht helfen -- aber vielleicht hilft auch das: http://docwiki.embarcadero.com/RADStudi ... _(FireDAC) Du kannst den "Langläufer" mit einem Timeout verschicken und die nachfolgende Exception abfangen

Keine Ahnung wie das in Lazarus geht, aber wenns Delphi kann wird das auch möglich sein.

Helios
Lazarusforum e. V.
Beiträge: 93
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.0 Windows 10 64Bit / Lazarus 2.0.12 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von Helios »

Hallo charlytango,
Danke für Deine Rückmeldung!
Ich glaube mit den Systemen sind wir in einer Liga und bzgl. des Designs hat man manchmal eben nicht die Wahl bei einer entsprechenden Datenmenge alles schnell zu machen (na klar, alles prarallelisieren und viel Geld für eine entsprechenden Serverpark auszugeben geht immer, aber schauen wir mal...)
Die handvoll parametrisierter SQL-Statements bei Dir sind in etwa 150 auf "meinem" Produktiv-System.
Das System ist ausglegt für ca. 200 parallel arbeitende Mitarbeiter. Hier gibt es Kollegen, die können einen Tag auf ihre Daten warten oder Kollegen (wie mich;-) die brauchen die Daten frisch ermittelt innerhalb von einer Minute oder innerhalb weniger Sekunden. Die "Langläufer" kann man als Job auslagern und sich die Daten als eMail (Link) zusenden lassen, wenn sie dann mal ermittelt sind. Wenn ich mich bzgl. meiner Eingabefilter vertan habe und länger als eine Minute auf eine Abfrage warte, breche ich die Abfrage ab und erstelle sie mit einem besser eingrenzenden Filter neu und frage sie dann erneut ab. Der Abbruch kommt bei mir nicht so oft vor (Erfahrung!), aber bei manchen Kollegen, die das System nicht jeden Tag nutzen dann doch etwas häufiger. Die Akzeptanz des Systems hat deutlich mit Einführung des "Abbrechen" Knopfes zugenommen. Oracle hat diese Möglichkeit bei seinem SQL-Developer (nicht ohne Grund) auch implementiert. In meinem Projekt, das ich quasi als kleinen Bruder des o.g. Produktiv-System für den Offline Betrieb entwickle, ist der "Abbrechen" Knopf ganz oben auf der Wunschliste. Oracle wollte ich dafür nicht verwenden (wg. Lizenzen und dem Datenoverhead allein bei der Installation einer Oracle Datenbank). Die Kombination Lazarus+Firebird DB halte ich für viel interessanter.
Ich bin an dem Thema dran, vielleicht finde ich was passendes und dann stelle ich es zur Beurteilung hier rein.
Danke nochmal für Deine Antwort und Gruß
Helios

sstvmaster
Beiträge: 521
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: W10, L 2.2.2
CPU-Target: 32+64bit
Wohnort: Dresden

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von sstvmaster »

Ist zwar alt, aber vielleicht hilft es.

https://stackoverflow.com/questions/134 ... n-firebird
LG Maik

Windows 10,
- Lazarus 2.2.2 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.3 (fixes) + fpc 3.3.1 (main/trunk)

Helios
Lazarusforum e. V.
Beiträge: 93
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.0 Windows 10 64Bit / Lazarus 2.0.12 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von Helios »

Hallo sstvmaster,
vielen Dank für Deine Rückmeldung. Das Statement
DELETE FROM MON$STATEMENTS WHERE MON$STATEMENT_ID = ...
hatte ich auch bereits in der engeren Auswahl, um das SQL Statement zu beenden.
Bein meinen Versuchen gerade auf einer Firebird Linux (Debian) Datenbank blieben die Statements aktiv und die Connection wird geblockt. Das ist schade, denn ich hatte gedacht, die Connection kann für ein neues SQL-Statement wiederverwendet werden.
Ich schaue mir das auch nochmal unter Windows an, kann aber dauern...
Danke für den Tipp und Gruß
Helios

sstvmaster
Beiträge: 521
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: W10, L 2.2.2
CPU-Target: 32+64bit
Wohnort: Dresden

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von sstvmaster »

Bitte gern, ich weis jetzt nicht welche SQL Komponente du nutzt, aber Zeos sollte das mit dem Abort können.

https://sourceforge.net/p/zeoslib/code- ... 6.pas#l369

https://sourceforge.net/p/zeoslib/code- ... .pas#l2717
LG Maik

Windows 10,
- Lazarus 2.2.2 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.3 (fixes) + fpc 3.3.1 (main/trunk)

Helios
Lazarusforum e. V.
Beiträge: 93
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.0 Windows 10 64Bit / Lazarus 2.0.12 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von Helios »

Hallo sstvmaster,
tatsächlich habe ich bis dato nur mit der SQLdb gearbeitet. Bisher hatte ich noch nicht den Bedarf auf Zeos umzusteigen. Mit der SQL-Query-Abbruch Funktion ist nun vielleicht der Zeitpunkt gekommen die ZeosLib einzusetzen (soeben per Packagemanager installiert). Kennst Du ein Beispiel mit der ich die "AbortOperation" Funktion korrekt einsetze? Tante Google hat da nix sofort ausgespuckt :D .
Danke und Gruß
Helios

Helios
Lazarusforum e. V.
Beiträge: 93
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.0 Windows 10 64Bit / Lazarus 2.0.12 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von Helios »

Hallo Zusammen,
mit etwas mehr Wissen und einem bisher unüberwindbaren Problem möchte ich das Thema nochmal aufrollen.
Neu habe ich die ZEOS ZConnection und ZQuery verwendet (lässt sich ja schnell von der Standard Lazarus Installation über den Packetmanager nachinstallieren)
und hat sich auch bei großen Datenmengen als schnell und weniger RAM hungrig gezeigt.
Das erste Beispielprojekt "FirebirdSQLCancel.zip" (ohne Thread)
FirebirdSQLCancel.zip
(65.05 KiB) 51-mal heruntergeladen
funktioniert soweit und benutzerdefiniert (über Rechtsklick) kann man dann ein Firebird SQL Statement welches den Status 2 (=Stalled) hat, beenden.
Grundlage dafür ist der von sstvmaster vorgeschlagene Weg über ein DELETE auf MON$STATEMENTS. Funktioniert recht gut auf Windows und Linux.
Voraussetzung ist aber, das man eine 2. DB Session aufbauen kann (Server nicht Embedded Firebird Nutzung).

Das zweite Projekt "SQLThread2.zip"
SQLThread2.zip
(65.7 KiB) 56-mal heruntergeladen
basiert auf einen Thread und nutzt das gleiche Konzept, kann aber aus der gleichen Session heraus aufgerufen werden (sollte embeddedfähig sein)
hat aber einen SCHWEREN FEHLER! Sobald man das Applikations Fenster vergrößert, minimiert über den Bildschirmrand verschiebt oder den Taskmanager aufruft schmiert es ab. Vorher ist es aber voll funktional und selbst das Beenden der SQL-Session über das "FirebirdSQLCancel.zip" Tool funktioniert von "aussen" korrekt.
Irgendwie scheine ich mit meinen Thread in die Windowsereignisroutine reinzupfuschen (vermutlich in und um den "FForm.ZQuery1.Open" Bereich, den ich nicht in den Main Thread verlagern kann, da dann das ganze Thread-Konzept nutzlos ist). Meine Hoffnung ist, das jemand mit mehr Multithread Erfahrung mir hier weiterhelfen kann.

Das Gute vielleicht noch als Hinweis: Die diversen Test hat die Firebird Datenbank (2GB) bisher schadlos überstanden (spricht aus meiner Sicht voll für das Produkt:D)
Danke für das Interesse und für jede Rückmeldung zum genannten Thread-Problem
Helios

Michl
Beiträge: 2500
Registriert: Di 19. Jun 2012, 12:54

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von Michl »

Habe zwar kein Firebird hier, sehe aber einen groben Fehler. Wie oben af0815 schon andeutete, darfst du nicht ungekapselt vom Thread auf die Komponenten vom MainThread zugreifen, am besten überhaupt nicht. Erstelle eine separate Connection, Query etc. dynamisch im Thread. Dann kannst du eine Query öffnen, Daten auslesen, schreiben, etc. Allerdings darf die Query auch keine Datasource vom Formular füttern. Daß es stellenweise z.Zt. bei dir funktioniert ist nicht garantiert.

Ich nutze bei Postgres einen WorkerThread nur, um große Datenmengen aus dem Speicher wegzuschreiben oder wieder zu holen. Das meiste andere muss der MainThread leisten.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 5196
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von af0815 »

Thread bedingt non GUI. Man kann allerdings zwischen dem Thread und der GUI (Main Thread) in aktbekannter Manier Daten austauschen.

Übrigends wären deine Abstürze unter Linux mit Gtk2 Erfahrungsgemäss viel heftiger. :D

Du hast richtig beobachtet, das du unter anderen dem Messageverwaltungssystem hineinpfuscht, besonders weil du im falschen Threadkontext daherkommst. Das ist bei Thread recht häufig, wenn du aus dem falschen Thread auf die GUI zugreifst. Passiert mir hin und wieder bei Callbacks, zB bei V4L oder gstreamer. Dawerden die callbacks gerne aus einem Thread heraus aufgerufen. Da hilft auch nur der gesicherte Datenaustausch.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Helios
Lazarusforum e. V.
Beiträge: 93
Registriert: Mi 29. Jun 2011, 22:36
OS, Lazarus, FPC: Lazarus 2.2.0 Windows 10 64Bit / Lazarus 2.0.12 Debian 11 „Bullseye" 64Bit
CPU-Target: 64Bit
Wohnort: Leonberg

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von Helios »

Hallo af0815 und Michl,
ich habe den Vorschlag von Dir Michl aufgenommen und das ZConnection, ZQuery und das DataSource in den Thread verlagert.
Das Problem mit dem Resize des Anwendungsfensters ist damit behoben auch wenn die SQL Abfrage via Thread läuft und
abgeschlossen wird (es scheint die GUI ist nun von dem Thread genügend entkoppelt).
Das letzte Problem welches ich noch habe ist, die erhaltenen Datensätze vom Thread in das Hauptprogramm zu übertragen.
Das sollte ja eigentlich im Main Thread über die synchronisierte Prozedur "TSQLThread.Completed" möglich sein, oder?
Ein "FForm.DataSource1 := FDatasource;" reicht da leider nicht (wäre auch zu schön gewesen :D ).
Wie kann ich das performant und funktional machen?

Danke für jeden Rat und schönen Sonntag noch.
Gruß
Helios
SQLThread3.zip
(65.94 KiB) 54-mal heruntergeladen

Michl
Beiträge: 2500
Registriert: Di 19. Jun 2012, 12:54

Re: Firebird SQL Abfragen in einen Thread auslagern/stoppen/neustarten

Beitrag von Michl »

Die Datasource vom Thread kannst du nicht im MainThread verwenden (eigentlich brauchst du dort auch keine, die Query genügt). Du könntest aber die Daten rüberschieben (Liste, Array) und z.B. in einem DrawGrid, Edit etc. anzeigen. Ob das gut für dich ist, weiß ich nicht.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Antworten