Ankündigung

Einklappen
Keine Ankündigung bisher.

erledigt Neuester Datensatz pro "Gruppe"

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • erledigt Neuester Datensatz pro "Gruppe"

    Hallo,

    ich lese die Speicherauslastung mehrerer Rechner aus und möchte in einem übergeordneten Script die Daten auswerten

    aktuell habe ich es so:

    Code:
    select max(created),hostname,mountpoint,free,`usage` from diskfree group by hostname,mountpoint
    liefert mir das richtige Ergebnis, jedoch ist es lt. MySQL-Sezifikation falsch, da ich free und usage im select aber nicht im group by habe und auch keine Aggregationsfunktion darauf anwende. wenn ich dies aber tue, passt mein ergebnis nicht mehr.


    meine Tabelle sieht so aus:

    Code:
    CREATE TABLE `diskfree` (
        `id` INT(11) NOT NULL AUTO_INCREMENT,
        `created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
        `hostname` VARCHAR(6) NOT NULL COLLATE 'utf8_unicode_ci',
        `mountpoint` VARCHAR(15) NOT NULL COLLATE 'utf8_unicode_ci',
        `free` INT(11) NOT NULL COMMENT 'freier Speicher (Avail) in MB',
        `usage` INT(11) NOT NULL COMMENT 'belegt (Use) in %',
        PRIMARY KEY (`id`),
        INDEX `created` (`created`),
        INDEX `hostname` (`hostname`),
        INDEX `mountpoint` (`mountpoint`)
    )
    COLLATE='utf8_unicode_ci'
    ENGINE=MyISAM
    AUTO_INCREMENT=16753
    ;
    beispieldaten:

    Code:
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:15:02', 'QDE02L', '/var', 16340, 15);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:15:02', 'QDE02L', '/', 1685, 52);
     INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:15:01', 'QDE02M', '/home', 3620, 33);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:15:01', 'QDE02M', '/', 44988, 6);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:15:01', 'QDE02N', '/var', 8709, 9);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:15:01', 'QDE02N', '/', 1132, 68);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:00:01', 'QDE02L', '/var', 16340, 15);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:00:01', 'QDE02L', '/', 1685, 52);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:00:01', 'QDE02M', '/home', 3620, 33);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:00:01', 'QDE02M', '/', 44988, 6);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:00:01', 'QDE02N', '/var', 8709, 9);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 14:00:01', 'QDE02N', '/', 1132, 68);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 13:45:02', 'QDE02N', '/var', 8709, 9);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 13:45:02', 'QDE02N', '/', 1132, 68);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 13:45:01', 'QDE02M', '/home', 3620, 33);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 13:45:01', 'QDE02M', '/', 44988, 6);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 13:45:01', 'QDE02L', '/var', 16340, 15);
    INSERT INTO `diskfree` (`created`, `hostname`, `mountpoint`, `free`, `usage`) VALUES ('2018-12-05 13:45:01', 'QDE02L', '/', 1685, 52);
    Ergebnis sollte so sein (alle Datensätze mit dem aktuellsten Zeitstempel über alle hostnames und mountpoints):

    Code:
    "created"    "hostname"    "mountpoint"    "free"    "usage"
    "2018-12-05 14:15:02"    "QDE02L"    "/var"    "16340"    "15"
    "2018-12-05 14:15:02"    "QDE02L"    "/"    "1685"    "52"
    "2018-12-05 14:15:01"    "QDE02M"    "/home"    "3620"    "33"
    "2018-12-05 14:15:01"    "QDE02M"    "/"    "44988"    "6"
    "2018-12-05 14:15:01"    "QDE02N"    "/var"    "8709"    "9"
    "2018-12-05 14:15:01"    "QDE02N"    "/"    "1132"    "68"
    schön wäre es, wenn ich den vorletzten Datensatz (jeweils "2018-12-05 14:00:01") mit ranjoinen könnte (dafür würde noch ein tinyint-flag dazukommen).

    ich habe schon bei google gesucht, aber die Lösungen sehen recht komplex aus (mit variablen und subqueries) und ich habe die noch nicht hinbekommen auf meine doch recht einfache Tabelle

    Gruß Frank

  • #2
    Nimm doch einfach ein Order By created anstatt max().
    Code:
    COLLATE='utf8_unicode_ci'
    ENGINE=MyISAM
    Warum nicht engine InnoDB und die collation auf Standard belassen, also ut8_general_ci?

    Kommentar


    • #3
      ich weis nicht wieviele Gruppen es sind/bleiben (für ein nachfolgendes limit x)...evtl. kann ich vorher die anzahl ermitteln und danach dann orderby+limit anwenden

      welchen Hintergrund hat die engine/Collate bei dem Problem? habe die Tabelle einfach mit den defaultwerten der DB angelegt...auch der Unterschied zwischen unicode_ci und general_ci ist mir nicht ganz klar...lässt sich aber sicher via google ermitteln

      Kommentar


      • #4
        MySQL liefert dir irgendeinen Datensatz (den ersten, den es findet? den letzten? man weiss es eben nicht) in der Situation... der Wert, den du bekommst ist weder falsch noch richtig - er passt einfach nicht in den Kontext.

        Warum aggregierst du die Daten nicht? Das wäre in meinen Augen "richtiger"
        Über 90% aller Gewaltverbrechen passieren innerhalb von 24 Stunden nach dem Konsum von Brot.

        Kommentar


        • #5
          Zitat von lstegelitz Beitrag anzeigen
          Warum aggregierst du die Daten nicht? Das wäre in meinen Augen "richtiger"
          Weil er nicht irgendwelche Aggregate der Spalte haben möchte, sondern die zum Maximum von created gehörenden Werte der Zeile in der entsprechenden Gruppierung. Die Lösungen mit Subselect werden mitunter langsam.
          Hab irgendwo dafür Muster abgelegt, muss mal schauen, bringe es auch nicht aus dem FF.

          Kommentar


          • #6
            Window-Funktionen wie rank() können helfen, frische MySQL-Inkarnationen können dies nun wohl.
            PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

            Kommentar


            • #7
              Dies hier mal mit einer größeren Anzahl Daten gründlich prüfen:
              Code:
              SELECT t1.created as max_created,t1.hostname,t1.mountpoint,t1.free,t1.`usage`
              FROM diskfree t1
              LEFT JOIN diskfree t2
              ON
                t1.hostname = t2.hostname
                AND t1.mountpoint = t2.mountpoint
                AND t1.created < t2.created
              WHERE t2.created IS NULL
              Ist sehr schnell, da keine Subselects benötigt werden.

              Kommentar


              • #8
                Zitat von jspit Beitrag anzeigen
                Dies hier mal mit einer größeren Anzahl Daten gründlich prüfen:
                ..
                Ist sehr schnell, da keine Subselects benötigt werden.
                Das Group by ist natürlich überflüssig, da im Select nicht aggregiert wird. Ich vermute mal es ist einfach nur da, weil es vom ursprünglichen Statement des TE stammt.
                Klingt vielleicht pingelig, aber Anfänger schauen sich dann sowas ab...
                Und wichtig ist natürlich, ob das Join Kriterium hinreichend ist.

                Ansonsten siehe akretschmer für sowas gibt es (nun auch in der neuesten mysql version) window functions.
                Und das sollte nach meinem Verständnis dann ca doppelt so schnell sein, wie die Lösung oben.
                Ach warum so bescheiden, es könnte 100% schneller sein.


                Ist natürlich uninteressant, wenn es nur um ein paar 1000 Datensätze geht.

                Kommentar


                • #9
                  Das überflüssige Group By hatte ich so in einer alten Vorlage und so übernommen. Wird von der DB praktisch ignoriert. Werde es korrigieren, das es für das Verständnis nicht förderlich ist.

                  ich lese die Speicherauslastung mehrerer Rechner aus
                  Die Anzahl der Datensätze dürfte sich nach dieser Aussage in Grenzen halten.
                  Ein Verglich der SQL aus #7 mit einer Lösung welche rank() benutzt wäre sicher mal interessant.

                  Kommentar


                  • #10
                    Hallo

                    die Anzahl der Datensätze wächst schon rasant (aktuell wird zum testen auch nichts gelöscht)...aktuell habe ich 17000 Datensätze in der Tabelle

                    Zitat von jspit Beitrag anzeigen
                    Dies hier mal mit einer größeren Anzahl Daten gründlich prüfen:
                    Code:
                    SELECT t1.created as max_created,t1.hostname,t1.mountpoint,t1.free,t1.`usage`
                    FROM diskfree t1
                    LEFT JOIN diskfree t2
                    ON
                    t1.hostname = t2.hostname
                    AND t1.mountpoint = t2.mountpoint
                    AND t1.created < t2.created
                    WHERE t2.created IS NULL
                    Ist sehr schnell, da keine Subselects benötigt werden.
                    ist leider zu langsam

                    Code:
                    "id"    "select_type"    "table"    "type"    "possible_keys"    "key"    "key_len"    "ref"    "rows"    "Extra"
                    "1"    "SIMPLE"    "t1"    "ALL"    \N    \N    \N    \N    "17196"    "" <<<<<<<<<<<
                    "1"    "SIMPLE"    "t2"    "ref"    "created,hostname,mountpoint"    "hostname"    "20"    "t1.hostname"    "2564"    "Using where; Not exists"
                    glaube eine ähnliche Lösung habe ich auch schonmal gefunden und probiert...deswegen bin ich bisher bei meinem falschen group by hängen geblieben...

                    eine schnelle Variante könnte ich mir nur vorstellen, wenn ich sortiere (nach created) und mir dann die ersten Datensätze anzeigen lasse (limit)

                    also
                    Code:
                    select * from diskfree order by created desc LIMIT 0, @groupcount
                    dazu muss ich aber irgendwie erst groupcount ermitteln

                    und left join den 2. datensatz

                    Kommentar


                    • #11
                      Ist zu langsam?? Nenne mal eine konkrete Zeit. Was hast du als Hardwarebasis? Ein RasPi 2?
                      eine schnelle Variante könnte ich mir nur vorstellen, wenn ich sortiere (nach created) und mir dann die ersten Datensätze anzeigen lasse (limit)
                      Garantiert nicht. Du kannst für created noch ein Zeitfenster vorgeben, läufst aber Gefahr dabei, das bestimmte Gruppierungen rausfallen.

                      Kommentar


                      • #12
                        ist eine Virtuelle Maschine wo ich an die HW nicht rankomme (Firmenserver), von daher kann ich schlecht HW-Details nennen,, aber ein Raspi ists sicher nicht

                        habe das Query nach 1 Minute abgebrochen...

                        Edit...habs mal laufen lassen

                        Code:
                        /* Betroffenen Zeilen: 0  Gefundene Zeilen: 6  Warnungen: 0  Dauer von 1 Abfrage: 00:03:15 */
                        ein einfaches

                        Code:
                        SELECT t1.created as max_created,t1.hostname,t1.mountpoint,t1.free,t1.`usage`
                        FROM diskfree t1
                        order by created
                        limit 50
                        ist sofort da...

                        wie meinst das mit dem Zeitfenster? jeder der hosts erzeugt alle 15 minuten einen Eintrag pro mountpoint, also sind die Datensätze (letzter - vorletzter) 15 minuten auseinander und der letzte muss < NOW()-15 min sein, vielleicht kann man so rangehen, um die Datensätze zu minimieren

                        Kommentar


                        • #13
                          Zitat von frank-w Beitrag anzeigen
                          wie meinst das mit dem Zeitfenster? jeder der hosts erzeugt alle 15 minuten einen Eintrag pro mountpoint, also sind die Datensätze (letzter - vorletzter) 15 minuten auseinander und der letzte muss < NOW()-15 min sein, vielleicht kann man so rangehen, um die Datensätze zu minimieren
                          Genau. vom Grundsatz
                          Code:
                           WHERE t1.created > :createdFrom AND t2.created IS NULL
                          Edit: Die Zeit schockt mich schon für einen einfachen Join und einer für DB kleinen Datenmenge.

                          Kommentar


                          • #14
                            Ich fall vom Glauben ab. Beim Test mit ähnlichen Daten (kleinerer Umfang) ist das hier
                            Code:
                            SELECT t1.created as max_created,t1.hostname,t1.mountpoint,t1.free,t1.`usage`
                            FROM diskfree t1
                            WHERE t1.created = (
                              SELECT MAX(t2.created)
                              FROM diskfree t2
                              WHERE t1.hostname = t2.hostname
                                AND t1.mountpoint = t2.mountpoint
                              )
                            schneller. Hatte immer im Hinterkopf subselects sind langsam.
                            frank-w Mich würde mal interessieren wie lange dies bei dir läuft.

                            Mit Zeitfenstern kann dies dann auch noch beschleunigt werden.

                            Kommentar


                            • #15
                              Zitat von jspit Beitrag anzeigen
                              Ich fall vom Glauben ab.
                              Edit: Die Zeit schockt mich schon für einen einfachen Join und einer für DB kleinen Datenmenge.
                              ich denke das Problem ist, dass erstmal alles gejoint werden muss bevor das WHERE gemacht werden muss, deshalb sind es ALLE Datensätze und nicht nur der Teil des WHERE

                              es kann durchaus sein (bzw. sehr wahrscheinlich), dass andere User auch gerade auf der DB arbeiten und recht intensive Vorgänge machen (Datenimport).

                              jedoch ist das reine order/limit-Query sofort da

                              Code:
                               WHERE t1.created > :createdFrom AND t2.created IS NULL
                              was ist hier createdFrom? mir sagt die doppelpunkt-syntax grade nichts, oder ist das nur als Platzhalter die die oben angesprochene Berechnung zu verstehen (NOW-15min)?

                              Zitat von jspit Beitrag anzeigen
                              Beim Test mit ähnlichen Daten (kleinerer Umfang) ist das hier
                              Code:
                              SELECT t1.created as max_created,t1.hostname,t1.mountpoint,t1.free,t1.`usage`
                              FROM diskfree t1
                              WHERE t1.created = (
                              SELECT MAX(t2.created)
                              FROM diskfree t2
                              WHERE t1.hostname = t2.hostname
                              AND t1.mountpoint = t2.mountpoint
                              )
                              schneller. Hatte immer im Hinterkopf subselects sind langsam.
                              frank-w Mich würde mal interessieren wie lange dies bei dir läuft.

                              Mit Zeitfenstern kann dies dann auch noch beschleunigt werden.
                              diese Variante hat gerade eben knapp 6 minuten gedauert also länger....wie gesagt, ich sehe nicht, ob evtl. nebenbei ein größerer Import parallel läuft. der Datenbank-server handled nebenbei auch noch eine größere Anwendung mit ~40 Tabellen mit ~750MB Datenbankgröße

                              edit:

                              habe jetzt mal das probiert:

                              Code:
                              SELECT t1.created,t1.hostname,t1.mountpoint,t1.free,t1.`usage`
                              FROM diskfree t1
                              where created > DATE_SUB(NOW(), INTERVAL 15 minute)
                              order by created
                              limit 50
                              ist sofort da...

                              mit join habe ich es jetzt so:

                              Code:
                              SELECT t1.id,t1.created,t1.hostname,t1.mountpoint,t1.free,t1.`usage`,t2.*
                              FROM diskfree t1
                              
                              LEFT JOIN diskfree t2 on (t2.hostname=t1.hostname) and (t2.mountpoint=t1.mountpoint) and (t2.created<t1.created) and (t2.created > DATE_SUB(NOW(), INTERVAL 30 minute))
                              
                              where t1.created > DATE_SUB(NOW(), INTERVAL 15 minute)
                              order by t1.created
                              limit 50
                              geht auch recht schnell

                              Kommentar

                              Lädt...
                              X