information_schema
. innodb_trx
wird sagen Ihnen, wenn Sie in einer Transaktion sind innerhalb InnoDB. Wenn Sie noch nicht auf Tabellen zugegriffen oder explizit einen Lesesnapshot erstellt haben, befinden Sie sich nur in einer Transaktion in MySQL (die "Server-Schicht") und nicht in InnoDB (der "Speicher-Engine-Schicht").).
mysql> SELECT count(1) FROM information_schema.innodb_trx
-> WHERE trx_mysql_thread_id = CONNECTION_ID();
+----------+
| count(1) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
Okay, ich habe nicht vor, aber jetzt Ich habe eine Transaktion, und ...
mysql> SELECT count(1) FROM information_schema.innodb_trx
-> WHERE trx_mysql_thread_id = CONNECTION_ID();
+----------+
| count(1) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
... es gibt noch nichts für meine aktuellen CONNECTION_ID()
in innodb_trx.
Aber, wenn ich oder gerade von einer InnoDB-Tabelle schreiben lesen ...
mysql> SELECT COUNT(1) FROM t1;
+----------+
| COUNT(1) |
+----------+
| 301 |
+----------+
1 row in set (0.00 sec)
... jetzt kann ich meine Transaktion sehen, weil InnoDB sich dessen bewusst ist.
mysql> SELECT count(1) FROM information_schema.innodb_trx
-> WHERE trx_mysql_thread_id = CONNECTION_ID();
+----------+
| count(1) |
+----------+
| 1 |
+----------+
1 row in set (0.00 sec)
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
Lassen Sie uns sicherstellen, dass es weg ist ...
mysql> SELECT count(1) FROM information_schema.innodb_trx
-> WHERE trx_mysql_thread_id = CONNECTION_ID();
+----------+
| count(1) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
Nun sagen Sie den Server mit dem Speicher-Engine zu sagen, dass meine MVCC Ansicht beginnt jetzt, nicht später:
mysql> START TRANSACTION WITH CONSISTENT SNAPSHOT;
Query OK, 0 rows affected (0.00 sec)
Hinweis dass mir das eigentlich keinen "konsistenten" Schnappschuss gibt, wenn mein Isolationslevel es nicht erlaubt. Aber es reicht, dass InnoDB jetzt weiß, dass ich hier bin.
mysql> SELECT count(1) FROM information_schema.innodb_trx
-> WHERE trx_mysql_thread_id = CONNECTION_ID();
+----------+
| count(1) |
+----------+
| 1 |
+----------+
1 row in set (0.00 sec)
... und InnoDB erfährt sofort von der Transaktion.
Jetzt gibt es eine andere Möglichkeit zu bestimmen, ob Sie gerade in einer Transaktion sind. Oder, richtiger, ich sollte sagen, dass es eine andere Möglichkeit gibt zu bestimmen, dass Sie nicht in einer Transaktion gerade jetzt sind.
Ich verwende dies für gespeicherte Prozeduren, die innerhalb einer Transaktion ausgeführt werden müssen - der Aufrufer ist verantwortlich für das Starten und Festschreiben oder Zurückrollen, und die Prozedur wird nicht ausgeführt, wenn es keine aktive Transaktion gibt. Wie?
Die Prozedur ruft eine andere Prozedur auf, die im Hintergrund erfolgreich ist, wenn ich eine Transaktion habe, aber eine Ausnahme auslöst, wenn nicht. Wenn eine Prozedur eine zweite Prozedur aufruft und die zweite Prozedur eine Ausnahme auslöst, wird die erste Prozedur mit der gleichen Ausnahme beendet, es sei denn, die erste Prozedur hat eine HANDLER
installiert, um den Fehler abzufangen.
Also, wenn meine äußere Prozedur diese Prozedur aufruft, wenn es eine Transaktion aktiv ist, geschieht nichts, und die äußere Prozedur ausgeführt werden darf:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> CALL mysql.require_transaction;
Query OK, 0 rows affected (0.00 sec)
^^^ diese ^^^ ist, was ich in der Nähe zu tun der Anfang, innerhalb meiner gespeicherten Prozeduren, die nur ausgeführt werden müssen, wenn sie innerhalb einer Transaktion aufgerufen werden.
Kein Fehler, wir waren in einer Transaktion. Wenn es sich um eine andere Prozedur gehandelt hätte, hätte diese Prozedur einfach zur nächsten Anweisung weitergegangen.
Aber wenn wir meinen require_transaction
Prozedur aufrufen und wir sind nicht in einer Transaktion:
mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)
mysql> CALL mysql.require_transaction;
ERROR 1644 (42000): you must have an active database transaction before attempting
this operation
Ordentlich. Wir stürzen unseren Anrufer mit einer benutzerdefinierten Fehlermeldung ab. Wie?
DELIMITER $$
CREATE PROCEDURE `mysql`.`require_transaction`()
BEGIN
-- test the session's transactional status,
-- throwing an exception if we aren't in a transaction,
-- but finishing successfully if we are
DECLARE CONTINUE HANDLER
FOR 1305
SIGNAL SQLSTATE '42000'
SET MESSAGE_TEXT = 'you must have an active database transaction before attempting this operation';
SAVEPOINT `we created to be sure you were in a transaction`;
ROLLBACK TO SAVEPOINT `we created to be sure you were in a transaction`;
END $$
DELIMITER ;
Das hat für meine lange Zeit Abhilfe gewesen, was ich glaube, dass eine erhebliche Kontrolle bei der Gestaltung von MySQL zu sein - die offensichtlichen Unfähigkeit endgültig aus der SQL-Schnittstelle, um zu bestimmen, ob Sie sich gerade in einer Transaktion . Hier ist, warum dies funktioniert:
ein SAVEPOINT
anlegen und sofort rollen zurück, um es im Wesentlichen ein no-op. Solange es nicht schon einen aktiven Savepoint mit dem gleichen Namen gab, kein Schaden, kein Foul. Ich habe den sehr unwahrscheinlichen Namen we created to be sure you were in a transaction
für meine SAVEPOINT
verwendet.
Das Erstellen einer SAVEPOINT
kann nicht durchgeführt werden, wenn Sie nicht in einer Transaktion sind, aber dies tatsächlich im Hintergrund fehlschlägt.
Rollback zurück zu einem SAVEPOINT
, das nicht existiert, wird Fehler 1305, also wenn Sie nicht in einer Transaktion sind, wurde es nicht erstellt, und jetzt wird es nicht existieren, und es ist Ihr Fehler. Wenn Sie sich in einer Transaktion befinden, wird die SAVEPOINT
erstellt und dann freigegeben, Ihre Transaktion so belassen, wie es war.
mysql> ROLLBACK TO SAVEPOINT `we created to be sure you were in a transaction`;
ERROR 1305 (42000): SAVEPOINT we created to be sure you were in a transaction does not exist
mysql>
Hahahaha, das ist ein spiffy Hack. Jetzt sehen Sie, warum ich den Namen verwendet habe, den ich für meinen falschen Sicherungspunkt verwendet habe - "existiert nicht" wird an den Objektnamen angehängt, um die Fehlermeldung zu bilden.
auf MySQL 5.1, die nicht SIGNAL
hat, meine require_transaction
gespeicherte Prozedur beendet einfach mit dem nativen Fehler, die fast sinnvoll ... oder zumindest sinnvoll genug ist, dass jemand das DBA fragen wird kommen (me) was es bedeutet.
Um es schöner zu machen, in MySQL Server 5.5 und höher, fangen wir Fehler 1305 mit einem CONTINUE HANDLER
, mit dem wir unsere eigene benutzerdefinierte Fehlermeldung mit SIGNAL
setzen können.
Die Einstellung und dann das sofortige Zurückrollen zu einem Sicherungspunkt ist ein tragisch hackiger aber todsicherer Weg, um festzustellen, ob Sie in einer Transaktion sind.
Danke! SQL ist nicht auf interaktive Nutzung oder menschliche Fehler ausgerichtet (so ziemlich das Gleiche), oder? Da mein Anwendungsfall die interaktive Konsole ist, versuche "ein SAVEPOINT zu erstellen und zurück zu rollen" ist eine ziemlich gute Lösung für meine Bedürfnisse. (Vielleicht sollten Sie es oben als TL hinzufügen; DNR-Intro :-) – alexis
Ich nehme diesen Vorschlag unter Beratung, und überlege ein wenig Refactoring der Antwort. Vielen Dank. :) –