PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C#; Oracle Datenbank; Transection-Commit-Methode



AndreasT
29.09.2015, 15:02
Hallo,
ich habe eine Frage zum Thema c# und Commit-Methode beim Zugriff auf Oracle-Datenbanken.

Allgemeines:
Ich habe ein Tool mit C# geschrieben um auf einer Oracle-Datenbank Daten zu verändern: Insert; Update; Delete.
Mein Kollege hat bemängelt, dass die UNDO Tablespace sich stark vergrößern wenn mein Tool Daten aus der Datenbank löscht.
Ich habe eine Änderung durchgeführt und wollte einfach mal Rückmeldungen einholen. Danke in Vorraus!

Frage-1:
Wird beim verwenden der "ExecuteNonQuery() Methode" ein Commit auf der Datenbank durchgeführt oder nicht?

Frage-2:
Kann ich beim Verwenden der "OracleTransaction Methode" erst nach jedem z.B. 5-ten Lauf transaction.Commit() absetzen?
Mir ist an dieser Stelle der Syntax nicht klar!


Syntax alt:

OracleCommand delCmd;
try
{

delCmd = conn.CreateCommand();

// SQL for delete
delCmd.CommandText = cmdSQL_delete_table;
delCmd.ExecuteNonQuery();


}

Syntax neu:

OracleCommand delCmd;
OracleTransaction transaction=null;
try
{

delCmd = conn.CreateCommand();

// Start a local transaction
transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted );
// Assign transaction object for a pending local transaction
delCmd.Transaction = transaction;

// SQL for delete
delCmd.CommandText = cmdSQL_delete_table;
delCmd.ExecuteNonQuery();


// Commit Transaktion
transaction.Commit();
}

defo
29.09.2015, 17:50
Also vorab, ich habe keine Ahnung wie genau das bei C# läuft, habe von früher in Erinnerung, dass ich mich nie um die Transaktionen gekümmert habe. Das spräche schlicht und ergreifend für implizite Transaktionen, das ist mglw so quasi Standard.



Wird beim verwenden der "ExecuteNonQuery() Methode" ein Commit auf der Datenbank durchgeführt oder nicht?

Vermutlich ja, (also implizit) ich würde an der Stelle mal schauen, wie das Connectionseitig definiert ist. Ganz einfach auszuprobieren, sobald Du den Befehl abgesetzt hast, muss das "Ergebnis" für andere Sitzungen sichtbar werden.




Kann ich beim Verwenden der "OracleTransaction Methode" erst nach jedem z.B. 5-ten Lauf transaction.Commit() absetzen?
Mir ist an dieser Stelle der Syntax nicht klar!

Ich denke schon, wofür sollten die clientseitige Implementierung sonst gut sein?!

Fraglich wäre aber der Sinn dahinter. Transaktionen verwendet man, um logisch sinnvolle Arbeitsschritte zu bündeln und nach dem Motto "ganz oder gar nicht" durchführen oder zurückrollen zu können. Was also jeder 5. Lauf unter diesem Blickwinkel bedeutet, musst Du selber wissen.

Wenn ich neue Prozesse entwickele arbeite ich "ewig" ohne commit, geht irgendwas schief bzw. sind die Ergebnisse nicht wie gewünscht, mache ich ein rollback und überarbeitete meine Scripte. Aber das mache ich als Entwickler mit einem entsprechenden Tool, nicht mit einer Anwendung (obwohl es auch da manchmal nett wäre).
Ich nutze also die Fähigkeiten der DB, um immer wieder einen identischen (definierten) Ausgangspunkt erhalten zu können.
Der DB sollte es natürlich egal sein, welches Tool da ewig ohne Commit arbeitet.

Ansonsten gibt es viele Möglichkeit, wie man den Undotablespace flutet. Mehrfache insert/update (gleicher Rekord), scheinbar kleine deletes, leider cascading (man löscht nur ein Bankkonto und Millionen Buchungen und deren Indizes gehen mit- macht kein Sinn aber gutes Beispiel), aber auch ungünstige physikalische Eigenschaften des Datenmodells (PCTFree, Blob Handling, ...).

Als DB Admin muss man letztlich dafür sorgen, dass der Tablespace etwas "atmen" kann. Die Größe des Undo ist letztlich entscheidend dafür, wie lange eine DB kauen kann, ohne zu schlucken (oder essen ohne zu schlucken). Ob aus Nachlässigkeit oder Versehen, als Entwickler kann man ohne weiteres die gewohnten (vom Admin gesetzten) Grenzen sprengen. Ärgerlich ist dann am ehesten, wenn der ganze Kram über die Archivelogs auch noch ins Backup läuft.

Fazit, Du kannst mit einer 2. Session ganz einfach prüfen, was wie wann committed.
Schau Dir Deine Scripte gründlich an und frag Dich, ob weniger vielleicht mehr ist.

Ralf Jansen
29.09.2015, 20:24
Ich habe keine speziellen Oracle Erfahrungen aber dein Syntax ist im Allgemeinen schon problematisch. Du musst dich nicht nur darum kümmern wann commited wird sondern auch wann ein rollback stattfinden soll insbesondere im Fehlerfall. Wie überall in .Net bietet sich da die using Anweisung an die ein explizites Dispose auslöst und Resourcen freigibt. Bei DB Connections,Transactions heißt das das du einen sauberen Rollback bekommst wenn nötig und dein Connection wird frei und wandert zurück in den Connection Pool.

using (var cn = new OracleConnection(meinLieberConnectionString))
{
cn.Open();
using (var tr = cn.BeginTransaction(IsolationLevel.ReadCommitted))
{
using (var delCmd = new OracleCommand(cmdSQL_delete_table, cn, tr))
{
delCmd.ExecuteNonQuery();
}
tr.Commit();
}
}

defo
30.09.2015, 09:37
Du musst dich nicht nur darum kümmern wann commited wird sondern auch wann ein rollback stattfinden soll insbesondere im Fehlerfall.
Das ist natürlich sehr wichtig! Nicht geschlossene Transaktionen bei explizitem Transaktionhandling oder command / connection Leichen sind nicht nur ein Ressourcenproblem, sie können auch für Langzeitlocks auf den bearbeiteten Tabellen sorgen, sehr übel, je mehr Anwender, desto schlimmer.

AndreasT
30.09.2015, 11:42
Hallo gfoidl, danke für die Formatierung vom Code!

AndreasT
30.09.2015, 11:57
Hallo defo, vielen Dank für die Rückmeldung!

Wenn ich mit "ExecuteNonQuery() Methode" ein Befehl ausführe kommt er an. Die Änderung wird durchgeführt.
Ich weiß nicht wie Oracle damit umgeht: wird „Commt“ gemacht, wann wird er gemacht? was ist für die DB besser.

An dieser Stelle versuche ich mit C# auf Oracle zuzugreifen. Wenn man das Thema direkt auf der Datenbank behandelt mach man es anders:
1. macht eine Session auf,
2. macht die Änderungen
3. und dann irgendwann den Commit
4. dann weis ich es, dass es durch ist.


Noch mal Danke ich werde es noch mal genau analysieren und melde mich. Andreas

AndreasT
30.09.2015, 12:04
Genau so (oder änlich) habe ich es in meiner zweiten Version mit "BeginTransaktion" gemacht.
Ich wollte nur jetzt mein Code verbessern und tr.commit() nach Z.B. 5 Transaktionen durcführen.
Weil ich sehr viele Transaktionen durchführe ist die Idee "der DB das Leben einfacher zu machen" und tr.commit() nich nach jeder delCmd.ExecuteNonQuery() durchführen sondern seltener.

Danke!

defo
30.09.2015, 12:18
Weil ich sehr viele Transaktionen durchführe ist die Idee "der DB das Leben einfacher zu machen" und tr.commit() nich nach jeder delCmd.ExecuteNonQuery() durchführen sondern seltener.

Leichter machst Du es der DB so höchstens, wenn es nicht grade commits hagelt.
Angenommen, Du setzt die Deletes in einer Loop ab und das ergibt 100e Löschungen pro Sekunde, dann würde ich sagen ok, da kann man etwas commits "sparen". Ansonsten ist es egal. Der Fokus sollte hauptsächlich auf der Logik liegen. Wenn die Deletes umfangreicher sind und länger dauern (wegen cascade constraints oder so), dann ist es wurscht.
Falls Du wirklich sparen willst und es viele einzelne Deletes mit Abhängigkeiten sind, dann lieber weniger Massendeletes machen. Das hieße, man bestimmt Gesamtmengen je Table, die zu löschen sind und beginnt bei den unabhängigen Tabellen damit, blockweise zu löschen, alles in einer Transaktion.

Ralf Jansen
30.09.2015, 18:18
Ich wollte nur jetzt mein Code verbessern und tr.commit() nach Z.B. 5 Transaktionen durcführen.
Weil ich sehr viele Transaktionen durchführe ist die Idee "der DB das Leben einfacher zu machen" und tr.commit() nich nach jeder delCmd.ExecuteNonQuery() durchführen sondern seltener.

a.) Das wäre dann eine Transaktion nicht fünf. Der commit begrenzt die Transaktion. Wolltest du vielleicht von Sql Statements sprechen und nicht von Transaktionen?
b.) Wenn du die Deletes sinnvoll zu einer Transaktion zusammenfassen kannst wieso ist das dann nicht ein entsprechendes Delete Statement das 5 Löschungen ausführt? Wenn das verschiedene Tabellen sind aus denen du löscht und nicht zusammenfassbar ist wieso war es dann vorher nicht schon eine Transaktion?

Klingt für mich alles eher nach logischen Problemen (welche einzelnen Statements müßen aus Anwendungssicht eine unteilbare atomare Einheit bilden so das die DB immer in einem konsistenten Zustand ist auch wenn was schief geht) die zu klären wären und nicht nach technischen (wie spare ich Serverressouren) Probleme. Wenn das erste geklärt dann kann man sich fragen ob man Serverresourcen sparen wenn man leicht anders vorgeht (ohne die DB Konsistenz zu gefährden). Aber hier scheint mir ja schon das erste nicht sichergestellt.

Nebenbei. Wenn man bei gleicher Anzahl DB Änderungen weniger commits macht würde ich erwarten das etwas das UNDO heißt größer wird. Umso häufiger ich commite umso weniger Bedarf habe ich für einen Undo (oder ist meinen Annahme was der UNDO Tablespace tut falsch?). Die Anzahl commits zu reduzieren hört sich daher für mich kontraproduktiv an.

defo
01.10.2015, 08:20
Nebenbei. Wenn man bei gleicher Anzahl DB Änderungen weniger commits macht würde ich erwarten das etwas das UNDO heißt größer wird. Umso häufiger ich commite umso weniger Bedarf habe ich für einen Undo (oder ist meinen Annahme was der UNDO Tablespace tut falsch?). Die Anzahl commits zu reduzieren hört sich daher für mich kontraproduktiv an.
Nein, das ist glaub ich richtig so: Häufige Commits= wenig undo.

Die ganze Diskussion geht eh in die falsche Richtung. Wurde ja auch schon gesagt. Die commits sollten in erster Linie nach logischem Bedarf gesetzt werden.
Commits an sich benötigen minimal Zeit und das merkt man, wenn man viele macht (und es merken will, hab's noch nie gemessen)
Was dann kommt ist sehr vielschichtig und wahrscheinlich pauschal nicht zu beantworten, schon gar nicht mit meinem Halbwissen:
Es wird ja ein ganzer Block ins Undo übertragen, nicht nur 20byte von dem gelöschten Datensatz meiner Tabelle. (plus Indexänderungm ,,,)
Dann ist die Frage, wie einzelne Datensätze verteilt sind, viele in einem Block, jeder in einem anderen...
Im Extremfall habe ich 100 DS in einem Block, lösche alle mit separaten Transaktionen und der "gleiche" Block steht 100mal im Undo-natürlich mit 100 Änderungen.

AndreasT
14.10.2015, 16:40
Danke für die Rückmeldugen