Laden...

Asynchrone Abfragen in Mysql

Erstellt von AtHeOS vor 18 Jahren Letzter Beitrag vor 18 Jahren 1.882 Views
A
AtHeOS Themenstarter:in
156 Beiträge seit 2005
vor 18 Jahren
Asynchrone Abfragen in Mysql

Mahlzeit,

Im Moment entwickle ich eine Applikation die aus diversen Datenquellen (Datenbanken,Steuerungen usw.) Daten asynchron erfaßt.

Soweit so gut. Das ganze läuft Multithreaded ab. Bei Oracle speichere ich die Verbindungsobjekte einfach in einem Hash und greife gleichzeitig auf die eine Verbindung hin z.B. 200 Gleichzeitige Abfragen auf das selbe Verbindungsobjekt. Jedoch bekomme ich bei Mysql immer einen Fehler wenn ich es auf diese Weise mache.

Aus diesem Grund wird bei jeder Anfrage eine neue Verbindung aufgebaut. Dies ist aber bei weitem zu unperformant da es mich jedes mal ca. 150 ms kostet.

Hat einer von euch eine Ahnung wie ich dieses Problem in den Griff bekomme ? Zusätzlich ist es notwendig das diese Applikation sehr performant ist.

Als Treiber wird der Original .net Treiber von Mysql.com benutzt.

Danke für eure Hilfe

354 Beiträge seit 2004
vor 18 Jahren

Es wäre natürlich interessant welche Fehlermeldung du da bekommst ... ?

.NET GUI - Die Community für grafische Oberflächen unter .NET
Jetzt kostenlos besorgen: .NET BlogBook
Norbert Eder
DasBackup

F
529 Beiträge seit 2003
vor 18 Jahren

Also, der Mysql-Treiber lockt immer die Verbindung, wenn ein DataReader gerade Daten von einem vorausgegangenen Command abhohlt. Dann wird die Verbindung erst wieder freigegeben, wenn der Reader auch geschlossen ist. Dieses Verhalten ist bei allen mir bekannten Datenbanktreibern mehr oder weniger feststellbar. (Mit weniger meine ich, dass es z.B. beim SQL-Base-Treiber meistens mit mehreren Kommandos pro Connections in unterschiedlichen Threads gekappt hat, aber eben nicht immer)

Du solltest einen Connection-Pool verwenden, der dir Datenbankverbindungen zur Verfügung stellt. Weiterhin läufst du dann auch nicht Gefahr, dass dir ein anderer deiner Threads die Datenbankverbindung schließt während du im einen Thread noch eine Abfrage ausführst.

Besuchen sie das VisualC++ - Forum

A
AtHeOS Themenstarter:in
156 Beiträge seit 2005
vor 18 Jahren

Danke für euche Antworten

@Franknstein
Das hört sich interessant an. Kannst du mir genaueres zu diesem Connection-Pool erzählen bzw ein kleines Beispiel posten ? Habe bis dato noch nicht damit gearbeitet.

Danke

F
529 Beiträge seit 2003
vor 18 Jahren

Ich habe mir mal einen solchen selbst geschrieben. Allerdings musst du einige Dinge beachten:
a) Alle Connections die du hohlst, musst du auch wieder immer zurückgeben. Sonst blockiert der Verbindungspool alle Threads die neue Verbindungen haben wollen. Timeouts, das die Verbindungen nach der Zeit x freigegeben werden, gibt es nicht.
b) Evtl muss du die Stelle auskommentieren, an der die Datenbankverbindung genullt wird, wenn sie dem Pool wieder übergeben wird. Dann musst du dich aber darum kümmern, dass sie automatisch neu erzeugt wird, wenn die Verbindung mal kaputt gehen sollte. (Die Klasse erzeugt nämlich immer eine neue Verbindung, wenn ein Verbindungsobjekt benötigt wird, was du ja nicht willst)
c) Du solltest es vermeiden, mehrere Verbindungen im selben Thread zu verwenden, wenn das maximale Verbindungslimit zu gering ist. Sonst kannst du das ganze Programm blockieren.

Warscheinlich mag es sinnvoll sein, diese Klasse lediglich als Anregung für einen eingenen Verbindungspool heranzuziehen und selbst einen Verbindungspool für deine Bedürfnisse zu entwickeln.

So und jetzt der Quelltext:


using System;
using System.Collections.Generic;
using System.Text;
using MySQLDriverCS;
using System.Threading;

namespace Chat.Server.Database
{
    class MySqlConnectionPool
    {
        MySQLConnection[] m_Connections = null;
        private bool m_HasFreeConnection = false;
        private Queue<int> m_FreeConnections = null;
        private AutoResetEvent m_Semaphore = null;
        private int m_MaxConnections = 5;
        private string m_ConnectionString = "Der Connectionstring";

        private MySqlConnectionPool()
        {
            this.m_Connections = new MySQLConnection[this.m_MaxConnections];

            this.m_Semaphore = new AutoResetEvent(false);
            this.m_FreeConnections = new Queue<int>();
            this.m_HasFreeConnection = true;

            this.m_HasFreeConnection = true;
            for (int i = 0; i < this.m_Connections.Length; i++)
            {
                this.m_FreeConnections.Enqueue(i);
            }
        }

        private static MySqlConnectionPool m_Instance = null;
        public static MySqlConnectionPool Instance
        {
            get
            {
                if (m_Instance == null)
                    m_Instance = new MySqlConnectionPool();

                return m_Instance;
            }
        }

        public MySQLConnection Get()
        {
            if (!this.m_HasFreeConnection)
                this.m_Semaphore.WaitOne();

            int Index = this.m_FreeConnections.Dequeue();
            this.m_Connections[Index] = new MySQLConnection(
                                     this.m_ConnectionString);
            this.m_Connections[Index].Open();
            this.m_Connections[Index].Database = "chatserver";

            if (this.m_FreeConnections.Count == 0)
                this.m_HasFreeConnection = false;

            return this.m_Connections[Index];
        }

        
        public void Return(MySQLConnection connection)
        {
            try
            {
                int index = this.IndexOf(connection);
                if (this.m_Connections[index].State != System.Data.ConnectionState.Closed)
                {
                    this.m_Connections[index].Close();
                    this.m_Connections[index].Dispose();
                }
                this.m_Connections[index] = null;
                this.m_FreeConnections.Enqueue(index);
                this.m_HasFreeConnection = true;
                this.m_Semaphore.Set();
            }
            catch (Exception)
            {
                if (connection.State != System.Data.ConnectionState.Closed)
                {
                    connection.Close();
                    connection.Dispose();
                    connection = null;
                }
                throw new Exception("Fehler: Die Datenbankverbindung existiert nicht in der Datenbankverbindungsliste");
            }
        }

        private int IndexOf(MySQLConnection con)
        {
            for (int i = 0; i < this.m_Connections.Length; i++)
            {
                if (this.m_Connections[i] == con)
                {
                    return i;
                }
            }
            throw new Exception("Das angegebene Element wurde nicht gefunden");
        }
    }
}

Bearbeitet:

Hier ist noch ein kleines Beispiel, wie die Klasse zu verwenden ist:


MySQLConnection con = null;
try
{
    con = MySqlConnectionPool.Instance.Get();
    MySQLCommand com = con.CreateCommand();
    com.CommandText = "DeinSQL";
    //Hier kannst du dann deinen DataReader/etc ausführen....   
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message, ex.StackTrace);
}
finally
{
    if(con != null)
    MySqlConnectionPool.Instance.Return(con);
}

Besuchen sie das VisualC++ - Forum