Kapitel 14. Speicher-Engines und Tabellentypen
Zurück |  | Weiter

## Kapitel 14. Speicher-Engines und Tabellentypen

MySQL unterstützt mehrere Speicher-Engines für die Arbeit mit unterschiedlichen Tabellentypen. MySQL kennt Speicher-Engines sowohl für transaktionssichere als auch für nicht-transaktionssichere Tabellen:

- Mit `MyISAM` werden nicht-transaktionssichere Tabellen verwaltet. Dieser Tabellentyp kann Daten sehr schnell speichern und abrufen und bietet Volltext-Suchfähigkeiten. `MyISAM` wird in allen MySQL-Konfigurationen unterstützt und ist als Standard-Tabellentyp voreingestellt, sofern Sie in MySQL keinen anderen Default konfiguriert haben.

- Die Speicher-Engine `MEMORY` stellt Tabellen im Arbeitsspeicher zur Verfügung. Mit der Speicher-Egnine `MERGE` lassen sich mehrere identische `MyISAM`-Tabellen wie eine einzige behandeln. Wie`MyISAM` verwalten auch die Speicher-Engines `MEMORY` und `MERGE` nicht-transaktionssichere Tabellen. Beide sind ebenfalls standardmäßig in MySQL enthalten.

  Note: Die Speicher-Engine `MEMORY` hieß früher `HEAP`.

- Die Speicher-Engines `InnoDB` und `BDB` stellen transaktionssichere Tabellen zur Verfügung. `BDB` ist in den Binärdistributionen von MySQL-Max für Betriebssysteme, die dieses unterstützen, enthalten. Außerdem ist `InnoDB` Standardbestandteil aller MySQL 5.1-Binärdistributionen. In Quelldistributionen können Sie diese Engines aktivieren oder deaktivieren, indem Sie MySQL nach Ihren Wünschen konfigurieren.

- Die Speicher-Engine `EXAMPLE` ist ein „Sockel“-Modul, das eigentlich gar nichts tut. Sie können mit ihr zwar Tabellen anlegen, aber keine Daten speichern oder abrufen. Sie soll lediglich im MySQL-Quellcode als Beispiel zu dienen, um zu zeigen, wie man neue Speicher-Engines schreibt. Daher ist sie vor allem für Entwickler von Interesse.

- `NDB Cluster` wird von MySQL Cluster als Speicher-Engine zur Implementierung von Tabellen genutzt, die über viele Computer partitioniert sind. Sie steht in den MySQL-Max 5.1- Binärdistributionen zur Verfügung. Dieses Speicher-Engine wird zurzeit nur von Linux, Solaris und Mac OS X unterstützt. In zukünftigen MySQL-Releases soll sie auch auf anderen Plattformen, wie etwa Windows, unterstützt werden.

- Die Speicher-Engine `ARCHIVE` dient der Speicherung großer Datenmengen ohne Indizes mit einem sehr kleinen Speicherverbrauch.

- Die Speicher-Engine `CSV` speichert Daten in Textdateien in Form von kommagetrennten Werten.

- Die Speicher-Engine `BLACKHOLE` nimmt Daten entgegen, speichert sie jedoch nicht. Abfragen liefern immer eine leere Menge zurück.

- Die Speicher-Engine `FEDERATED` speichert Daten in einer Remote-Datenbank. Gegenwärtig funktioniert sie nur mit MySQL unter Verwendung der MySQL-C-Client-API. In zukünftigen Releases soll sie auch mit anderen Datenquellen mit anderen Treibern oder Client-Verbindungsmethoden funktionieren.

In diesem Kapitel werden die Speicher-Engines von MySQL beschrieben. Eine Ausnahme bildet die Engine `NDB Cluster`, die in Kapitel 16, MySQL Cluster behandelt wird.

Wenn Sie eine neue Tabelle anlegen, können Sie die zu verwendende Speicher-Engine angeben, indem Sie der `CREATE TABLE`-Anweisung die Tabellenoption `ENGINE` oder `TYPE` hinzufügen:

    CREATE TABLE t (i INT) ENGINE = INNODB;
    CREATE TABLE t (i INT) TYPE = MEMORY;

Der ältere Begriff `TYPE` wird aus Gründen der Abwärtskompatibilität noch als Synonym für `ENGINE` akzeptiert, doch `ENGINE` ist der aktuelle Begriff, während `TYPE` mittlerweile veraltet ist.

Wenn Sie die Option `ENGINE` oder `TYPE` weglassen, wird die Standard-Speicher-Engine verwendet. Normalerweise ist dies `MyISAM`, doch mit der Server-Startoption `--default-storage-engine` oder `--default-table-type` oder der Systemvariablen `storage_engine` oder `table_type` können Sie auch etwas anderes einstellen.

Wird MySQL unter Windows mit dem MySQL Configuration Wizard installiert, kann die Speicher-Engine `InnoDB` anstelle der standardmäßigen `MyISAM`-Engine gewählt werden. Siehe Abschnitt 2.3.5.6, „Der Dialog zur Datenbankverwendung“.

Um eine Typkonvertierung von Tabellen vorzunehmen, geben Sie in einer `ALTER TABLE`-Anweisung den neuen Typ an:

    ALTER TABLE t ENGINE = MYISAM;
    ALTER TABLE t TYPE = BDB;

Siehe Abschnitt 13.1.5, „`CREATE TABLE`“ und Abschnitt 13.1.2, „`ALTER TABLE`“.

Wenn Sie eine Speicher-Engine zu verwenden versuchen, die entweder gar nicht kompiliert oder zwar kompiliert, aber deaktiviert ist, legt MySQL stattdessen mit der Standard-Speicher-Engine eine Tabelle an, also normalerweise mit `MyISAM`. Dieses Verhalten ist praktisch, wenn Tabellen zwischen MySQL-Servern hin- und herkopiert werden, die verschiedene Speicher-Engines unterstützen. (So könnte beispielsweise in einer Replikation Ihr Masterserver aus Sicherheitsgründen transaktionssichere Speicher-Engines verwenden, während die Slaveserver aus Gründen der Schnelligkeit nur nicht-transaktionssichere einsetzen.)

Dass MySQL für Speicher-Engines, die nicht zur Verfügung stehen, automatisch die Standard-Speicher-Engine einsetzt, kann für Neulinge verwirrend sein. Es wird jedoch in einem solchen Fall immer eine Warnung ausgegeben.

Für neue Tabellen legt MySQL immer eine `.frm`-Datei zur Speicherung der Tabellen- und Spaltendefinitionen an. Der Index und die Daten der Tabelle können je nach Speicher-Engine in einer oder mehreren Dateien gespeichert sein. Der Server erstellt die `.frm`-Datei über der Ebene der Speicher-Engine. Einzelne Speicher-Engines legen zusätzliche Dateien an, die für die von ihnen verwalteten Tabellen erforderlich sind.

Eine Datenbank kann unterschiedliche Tabellentypen enthalten; die Tabellen müssen also nicht alle mit derselben Speicher-Engine angelegt werden.

Transaktionssichere Tabellen (TSTs) haben gegenüber den nicht-transaktionssicheren (NTSTs) mehrere Vorteile:

- Sie sind sicherer. Selbst wenn MySQL abstürzt oder Hardware-Probleme auftreten, bekommen Sie Ihre Daten auf jeden Fall zurück, sei es durch die automatische Wiederherstellung, sei es aus einer Sicherungskopie plus dem Transaktionslog.

- Sie können viele Anweisungen miteinander kombinieren und mit der `COMMIT`-Anweisung später alle gleichzeitig festschreiben (wenn Autocommit deaktiviert wurde).

- Sie können Ihre Änderungen mit einem `ROLLBACK` verwerfen (wenn Autocommit deaktiviert wurde).

- Schlägt ein Update fehl, werden alle Ihre Änderungen rückgängig gemacht. (Bei nicht-transaktionssicheren Tabellen sind alle einmal stattgefundenen Änderungen von Dauer.)

- Transaktionssichere Speicher-Engines bieten bessere Nebenläufigkeit für Tabellen, in denen gleichzeitig mit Lese-Operationen viele Änderungen stattfinden.

Sie können transaktionssichere und nicht-transaktionssichere Tabellen in derselben Anweisung verwenden, um aus beiden das Beste herauszuholen. Allerdings sollten Sie nicht bei ausgeschaltetem Autocommit verschiedene Speicher-Engines durcheinanderwürfeln, auch wenn MySQL mehrere transaktionssichere Engines unterstützt, um die bestmöglichen Ergebnisse zu erzielen. Denn wenn Sie die Engines vermischen, werden Änderungen an nicht-transaktionssicheren Tabellen weiterhin festgeschrieben und können nicht mehr zurückgerollt werden. Informationen zu diesem und anderen Problemen, die in Transaktionen mit einem Mix von Speicher-Engines auftreten können, finden Sie unter Abschnitt 13.4.1, „`BEGIN/COMMIT/ROLLBACK`“.

Nicht-transaktionssichere Tabellen haben mehrere Vorteile, die alle damit zusammenhängen, dass der Aufwand von Transaktionen entfällt:

- Viel schneller

- Weniger Speicherbedarf auf der Festplatte

- Weniger Arbeitsspeicherbedarf für Updates

## 14.1. Die `MyISAM`-Speicher-Engine

`MyISAM` ist die Standard-Speicher-Engine. Sie baut auf dem älteren `ISAM`-Code auf, hat aber viele praktische Erweiterungen. (Beachten Sie, dass MySQL 5.1 `ISAM` nicht mehr unterstützt.)

Jede `MyISAM`-Tabelle wird in drei Dateien auf der Festplatte gespeichert. Die Namen der Dateien beginnen mit dem Tabellennamen und haben eine Erweiterung, die den Dateityp angibt. Eine `.frm`-Datei speichert das Tabellenformat. Die Datendatei besitzt die Erweiterung `.MYD` (`MYData`). Die Indexdatei hat die Erweiterung `.MYI` (`MYIndex`).

Um ausdrücklich zu sagen, dass Sie eine `MyISAM`-Tabelle möchten, verwenden Sie die Tabellenoption `ENGINE`:

    CREATE TABLE t (i INT) ENGINE = MYISAM;

Der ältere Begriff `TYPE` wird aus Gründen der Abwärtskompatibilität noch als Synonym für `ENGINE` akzeptiert, doch `ENGINE` ist der aktuelle Begriff, während `TYPE` mittlerweile veraltet ist.

Normalerweise ist eine `ENGINE`-Angabe für die `MyISAM`-Speicher-Engine unnötig. `MyISAM` ist ohnehin die Standard-Engine, es sei denn, die Standardeinstellung wurden geändert. Um in solchen Situationen ganz sicherzugehen, dass `MyISAM` verwendet wird, sollten Sie explizit die Option `ENGINE` verwenden.

Zur Prüfung oder Reparatur von `MyISAM`-Tabellen setzen Sie den mysqlcheck-Client oder das Hilfsprogramm myisamchk ein. Überdies können Sie `MyISAM`-Tabellen mit myisampack komprimieren, dann belegen sie viel weniger Speicherplatz. Siehe auch Abschnitt 8.9, „mysqlcheck — Hilfsprogramm für die Wartung und Reparatur von Tabellen“, Abschnitt 5.10.4.1, „Benutzung von `myisamchk` für die Fehlerbeseitigung nach Abstürzen“, und Abschnitt 8.4, „myisampack — Erzeugung komprimierter, schreibgeschützter MyISAM Tabellen“.

Kennzeichen von `MyISAM`-Tabellen:

- Alle Daten werden mit dem niederwertigen Byte zuerst gespeichert. Dadurch werden die Daten maschinen- und betriebssystemunabhängig. Die einzigen Voraussetzungen für die binäre Portierbarkeit der Daten sind, dass der Computer vorzeichenbehaftete Integers (Zweierkomplement) und das IEEE-Fließkommaformat verwendet. Diese Voraussetzungen werden von den üblichen Computern meist erfüllt. Nur bei Embedded-Systemen mit ihren manchmal seltsamen Prozessoren ist die Binärkompatibilität nicht immer gegeben.

  Die Verarbeitungsgeschwindigkeit leidet nicht sonderlich, wenn das niederwertige Byte zuerst gespeichert wird. Die Bytes in einer Tabellenzeile werden normalerweise nicht ausgerichtet und es macht zeitmäßig kaum einen Unterschied, ob ein unausgerichtetes Byte in der Reihenfolge oder gegen die Reihenfolge gelesen wird. Darüber hinaus ist der Server-Code, der die Spaltenwerte abruft, im Vergleich zu anderem Code nicht zeitkritisch.

- Alle numerischen Schlüsselwerte werden mit dem höchstwertigen Byte zuerst gespeichert, um eine bessere Indexkompression zu ermöglichen.

- Große Dateien (bis zu 63 Bit Dateilänge) werden für Datei- und Betriebssysteme, auf denen große Dateien möglich sind, unterstützt.

- Eine `MyISAM`-Tabelle kann maximal 64 Indizes haben. Dies lässt sich jedoch durch Rekompilieren ändern: Ab der Version MySQL 5.1.4 können Sie den Build konfigurieren, indem Sie configure mit der Option `--with-max-indexes=`N`` aufrufen, wobei `N` die Höchstzahl der pro `MyISAM`-Tabelle zulässigen Indizes ist. `N` muss kleiner oder gleich 128 sein. In älteren Versionen als MySQL 5.1.4 müssen Sie die Quelle wechseln.

  Ein Index darf maximal 16 Spalten haben.

- Die Höchstlänge für Schlüssel beträgt 1000 Bytes. Auch dies lässt sich durch Wechseln der Quelle und Rekompilieren ändern. Ist ein Schlüssel länger als 250 Bytes wird ein größerer als der standardmäßig 1024 Bytes große Schlüsselblock verwendet.

- Werden Zeilen sortiert eingefügt (wie zum Beispiel mit einer `AUTO_INCREMENT`-Spalte), wird der Indexbaum aufgespalten, so dass der höchste Knoten nur einen Schlüssel enthält. So wird der Platz im Indexbaum besser ausgenutzt.

- Intern wird eine `AUTO_INCREMENT`-Spalte pro Tabelle unterstützt. `MyISAM` aktualisiert diese Spalte bei `INSERT`- und `UPDATE`-Operationen automatisch. Das macht `AUTO_INCREMENT`-Spalten schneller (um mindestens 10%). Werte am Anfang der Folge werden nach ihrer Löschung nicht wiederverwendet. (Wenn eine `AUTO_INCREMENT`-Spalte als die letzte Spalte eines Mehrspalten-Indizes definiert ist, werden gelöschte Werte vom Anfang einer Folge doch wiederverwendet.) Der `AUTO_INCREMENT`-Wert kann mit `ALTER TABLE` oder myisamchk zurückgesetzt werden.

- Zeilen mit dynamischer Größenanpassung werden bei einer Mischung von Lösch-, Änderungs- und Einfügeoperationen viel weniger stark fragmentiert, da aneinander grenzende gelöschte Blöcke automatisch zusammengefasst und Blöcke, deren Nachbarblock gelöscht werden, automatisch erweitert werden.

- Hat eine Tabelle in der Mitte der Datendatei keine freien Blöcke, können Sie neue Zeilen mit `INSERT` einfügen, während gleichzeitig andere Threads die Tabelle lesen. (Man nennt dies "nebenläufige Einfügeoperationen".) Wird eine Zeile gelöscht oder werden in eine Zeile dynamischer Länge mehr Daten geschrieben, als sie zuvor enthalten hatte, kann ein freier Block entstehen. Wenn alle freien Blöcke aufgebraucht (ausgefüllt) wurden, werden zukünftige Einfügeoperationen wieder nebenläufig. Siehe Abschnitt 7.3.3, „Gleichzeitige Einfügevorgänge“.

- Mit den Tabellenoptionen `DATA DIRECTORY` und `INDEX DIRECTORY` von `CREATE TABLE` können Sie die Daten- und die Indexdatei in unterschiedliche Verzeichnisse legen, um mehr Geschwindigkeit zu erzielen. Siehe Abschnitt 13.1.5, „`CREATE TABLE`“.

- `BLOB`- und `TEXT`-Spalten können indiziert werden.

- `NULL`-Werte sind in indizierten Spalten zulässig. Hierfür werden 0 bis 1 Byte pro Schlüssel gebraucht.

- Jede Zeichenspalte kann einen anderen Zeichensatz haben. Siehe Kapitel 10, Zeichensatz-Unterstützung.

- In der `MyISAM`-Indexdatei gibt es ein Flag, das anzeigt, ob die Tabelle ordentlich geschlossen wurde. Wenn mysqld mit der Option `--myisam-recover` gestartet wird, werden `MyISAM`-Tabellen beim Öffnen automatisch überprüft und repariert, wenn sie nicht richtig geschlossen wurden.

- myisamchk markiert Tabellen als geprüft, wenn Sie es mit der Option `--update-state` ausführen. myisamchk --fast prüft nur diejenigen Tabellen, die diese Markierung nicht tragen.

- myisamchk --analyze speichert Statistikdaten sowohl für Teilschlüssel als auch für vollständige Schlüssel.

- myisampack kann `BLOB`- und `VARCHAR`-Spalten packen.

`MyISAM` unterstützt auch folgende Funktionen:

- Unterstützung für einen echten `VARCHAR`-Typ; eine `VARCHAR`-Spalte beginnt mit einer Längenangabe, die in einem oder zwei Byte(s) gespeichert ist.

- Tabellen mit `VARCHAR`-Spalten können Zeilen mit fester oder dynamischer Länge haben.

- Die Summe der Längen der `VARCHAR`-und `CHAR`-Spalten in einer Tabelle kann bis zu 64KB betragen.

- Für `UNIQUE` kann ein berechneter Hash-Index verwendet werden. So ist `UNIQUE` für jede beliebige Spaltenkombination in einer Tabelle zulässig. (Allerdings können auf einem berechneten `UNIQUE`-Index keine Suchoperationen durchgeführt werden.)

Mehr zum Thema

- Ein spezielles Forum für die Speicher-Engine `MyISAM` finden Sie unter http://forums.mysql.com/list.php?21.

### 14.1.1. `MyISAM`-Startoptionen

Die folgenden Optionen von mysqld können verwendet werden, um das Verhalten von `MyISAM`-Tabellen zu ändern. Weitere Informationen finden Sie unter Abschnitt 5.2.1, „Befehlsoptionen für mysqld“.

- `--myisam-recover=`mode``

  Stellt den Modus für die automatische Wiederherstellung von abgestürzten `MyISAM`-Tabellen ein.

- `--delay-key-write=ALL`

  Zwischen den Schreibvorgängen werden die Schlüsselpuffer (Key-Buffer) für `MyISAM`-Tabellen nicht auf die Festplatte zurückgeschrieben.

  Note: Wenn Sie dies tun, sollten Sie auf `MyISAM`-Tabellen nicht von einem anderen Programm aus zugreifen (zum Beispiel von einem anderen MySQL-Server oder mit myisamchk), so lange die Tabellen in Gebrauch sind. Sonst riskieren Sie, dass der Index beschädigt wird. Diese Gefahr wird durch `--external-locking` nicht gebannt.

Die folgenden Systemvariablen beeinflussen das Verhalten von `MyISAM`-Tabellen. Weitere Informationen finden Sie unter Abschnitt 5.2.2, „Server-Systemvariablen“.

- `bulk_insert_buffer_size`

  Die Größe des bei der Optimierung von Massen-Einfügeoperationen verwendeten Tree-Caches. Hinweis: Dieser Wert gilt pro Thread!

- `myisam_max_sort_file_size`

  Zur Indexerstellung nicht die schnelle Index-Sortiermethode verwenden, wenn die temporäre Datei dadurch größer als dieser Wert würde. Hinweis: Dieser Parameter wird in Bytes angegeben.

- `myisam_sort_buffer_size`

  Stellt die Puffergröße für die Wiederherstellung von Tabellen ein.

Wenn Sie mysqld mit der Option `--myisam-recover` starten, wird die automatische Wiederherstellung aktiviert. Wenn der Server eine `MyISAM`-Tabelle öffnet, prüft er, ob sie als abgestürzt gekennzeichnet ist oder die Zählervariable für Öffnungen von 0 verschieden ist und Sie den Server ohne externe Sperren betreiben. Trifft eine dieser Bedingungen zu, so geschieht folgendes:

- Der Server überprüft die Tabelle auf Fehler.

- Wenn der Server einen Fehler findet, versucht er eine schnelle Tabellenreparatur (mit Sortierung, aber ohne Neuerzeugung der Datendatei).

- Scheitert die Reparatur wegen eines Fehlers in der Datendatei (zum Beispiel eines doppelten Schlüssels), versucht der Server erneut eine Reparatur, aber dieses Mal mit Neuerzeugung der Datendatei.

- Scheitert auch diese Reparatur, versucht es der Server noch einmal mit der alten Reparaturmethode (zeilenweises Schreiben der Daten ohne Sortierung). Dieses Verfahren müsste in der Lage sein, jeden Fehler zu beheben und braucht nur wenig Festplattenspeicher.

Wenn die Wiederherstellung nicht alle Zeilen aus den zuvor abgeschlossenen Anweisungen wiederherstellen kann und Sie im Wert der Option `--myisam-recover` nicht `FORCE` angegeben haben, bricht die automatische Reparatur ab und schreibt folgende Fehlermeldung in das Fehler-Log:

    Error: Couldn't repair table: test.g00pages

Wenn Sie `FORCE` angeben, wird stattdessen folgende Warnung in das Log geschrieben:

    Warning: Found 344 of 354 rows when repairing ./test/g00pages

Beachten Sie: Wenn die automatische Wiederherstellung den Wert `BACKUP` enthält, legt der Wiederherstellungsprozess Dateien an, deren Namen die Form ``tbl_name-datetime`.BAK` haben. Sie benötigen ein cron-Skript, das diese Dateien automatisch von den Datenbankverzeichnissen auf die Sicherungsmedien verschiebt.

### 14.1.2. Für Indizes benötigter Speicherplatz

`MyISAM`-Tabellen verwenden B-Baum-Indizes. Die Größe der Indexdatei, summiert über alle Schlüssel, lässt sich mit der Formel `(key_length+4)/0.67` ungefähr berechnen. Das gilt für den ungünstigsten Fall, dass alle Schlüssel in sortierter Reihenfolge eingefügt wurden und die Tabelle keine komprimierten Schlüssel besitzt.

String-Indizes werden Leerzeichen-komprimiert. Ist der erste Teil des Index ein String, so wird dieser zusätzlich präfixkomprimiert. Durch Leerzeichen-Kompression wird die Indexdatei kleiner als in den obigen Zahlen für den ungünstigsten Fall dargestellt, wenn eine Stringspalte viele Leerzeichen am Ende hat oder eine `VARCHAR`-Spalte ist, die nicht immer in voller Länge ausgenutzt wird. Eine Präfix-Kompression wird für Schlüssel eingesetzt, die mit einem String anfangen. Die Präfix-Kompression ist hilfreich, wenn mehrere Strings mit identischem Präfix vorhanden sind.

In `MyISAM`-Tabellen können Sie auch Zahlen präfixkomprimieren. Hierzu geben Sie beim Anlegen der Tabelle die Tabellenoption `PACK_KEYS=1` an. Das hilft, wenn Sie viele Integer-Schlüssel mit identischem Präfix haben, wenn die Zahlen mit dem höchstwertigen Byte zuerst gespeichert werden.

### 14.1.3. MyISAM-Tabellenformate

`MyISAM` unterstützt drei verschiedene Speicherformate. Zwei davon, das feste und das dynamische Format, werden automatisch anhand des verwendeten Spaltentyps gewählt. Das dritte, komprimierte Format kann nur mit der Utility myisampack angelegt werden.

Wenn Sie `CREATE TABLE` oder `ALTER TABLE` für eine Tabelle verwenden, die keine `BLOB`- oder `TEXT`-Spalten besitzt, können Sie mit der Tabellenoption `ROW_FORMAT` ein `FIXED`- oder `DYNAMIC`-Format erzwingen.

Um Tabellen zu dekomprimieren, geben Sie `ROW_FORMAT=DEFAULT` mit `ALTER TABLE` zusammen an.

Unter Abschnitt 13.1.5, „`CREATE TABLE`“ finden Sie Informationen über `ROW_FORMAT`.

#### 14.1.3.1. Kennzeichen statischer Tabellen (Tabellen fester Zeilenlänge)

Das statische (Festlängen-) Format ist für `MyISAM`-Tabellen voreingestellt. Es wird verwendet, wenn die Tabelle keine Spalten mit variabler Länge (`VARCHAR`-, `VARBINARY`-, `BLOB`- oder `TEXT`-Spalten) enthält. Jede Zeile wird dann mit einer festgelegten Anzahl von Bytes gespeichert.

Von den drei Speicherformaten, die `MyISAM` unterstützt, ist das statische Format das einfachste und sicherste (für Schäden am wenigsten anfällige). Außerdem bietet es wegen der Leichtigkeit, mit der die Zeilen der Datendatei auf der Festplatte gefunden werden, auch den schnellsten Festplattenzugriff. Um eine Zeile nach der Index-Zeilennummer nachzuschlagen, multiplizieren Sie die Nummer der Zeile mit ihrer Länge, um ihre Position zu ermitteln. Darüber hinaus ist es beim Durchsuchen einer Tabelle sehr einfach, mit jedem Festplattenzugriff eine konstante Anzahl Zeilen zu lesen.

Die Sicherheit erweist sich, wenn Ihr Computer abstürzt, während der MySQL-Server eine `MyISAM`-Datei mit festgelegtem Format schreibt. In einem solchen Fall kann myisamchk ganz leicht feststellen, wo die Zeilen beginnen und enden. Normalerweise ist es dadurch in der Lage, alle Zeilen außer der einen, die nur teilweise geschrieben wurde, zurückzugewinnen. Beachten Sie, dass `MyISAM`-Tabellenindizes immer anhand der Datenzeilen rekonstruiert werden können.

Kennzeichen von statischen Tabellen:

- `CHAR`-Spalten werden mit Leerzeichen bis zur Spaltenbreite aufgefüllt. `BINARY`-Spalten werden mit `0x00`-Bytes bis zur Spaltenbreite aufgefüllt.

- Sie sind sehr schnell.

- Sie lassen sich leicht zwischenspeichern.

- Sie sind nach einem Absturz einfach zu rekonstruieren, da sich die Datensätze an festen Positionen befinden.

- Eine Reorganisation der Tabellen ist nicht erforderlich, es sei denn, Sie löschen sehr viele Zeilen und möchten dem Betriebssystem den frei gewordenen Speicherplatz zurückgeben. Hierzu verwenden Sie `OPTIMIZE TABLE` oder myisamchk -r.

- Sie belegen normalerweise mehr Speicher als dynamische Tabellen.

#### 14.1.3.2. Kennzeichen dynamischer Tabellen

Ein dynamisches Speicherformat wird verwendet, wenn eine `MyISAM`-Tabelle Spalten variabler Länge enthält (`VARCHAR`-, `VARBINARY`-, `BLOB`- oder `TEXT`-Spalten), oder wenn die Tabelle mit der Tabellenoption `ROW_FORMAT=DYNAMIC` angelegt wurde.

Das dynamische Format ist ein wenig komplizierter als das statische, da jede Zeile einen Header mit einer Längenangabe besitzt. Eine Zeile kann fragmentiert (an nicht-benachbarten Orten gespeichert) werden, wenn sie aufgrund eines Updates länger wird.

Mit `OPTIMIZE TABLE` oder myisamchk -r lassen sich Tabellen defragmentieren. Wenn in einer Tabelle, die auch Spalten variabler Länge besitzt, Spalten mit festgelegter Länge vorliegen, die oft angesprochen oder geändert werden, so empfiehlt es sich, die Spalten mit variabler Länge in andere Tabellen auszulagern, um Fragmentierung zu verhindern.

Kennzeichen von dynamischen Tabellen:

- Alle String-Spalten sind dynamisch, außer jenen, deren Länge weniger als vier beträgt.

- Vor jeder Zeile steht eine Bitmap, die für String-Spalten angibt, welche Spalten den leeren String enthalten, und für numerische Spalten, welche Spalten den Wert null enthalten. Beachten Sie, dass dies keine Spalten mit `NULL`-Werten einbezieht. Wenn eine String-Spalte nach dem Entfernen angehängter Leerzeichen die Länge null hat oder eine numerischen Spalte den Wert Null hat, wird sie in der Bitmap markiert und nicht auf der Festplatte gespeichert. Nicht-leere Strings werden mit einem Längen-Byte plus dem String-Inhalt gespeichert.

- Sie benötigen normalerweise weniger Festplattenplatz als Festlängen-Tabellen.

- Jede Zeile belegt nur so viel Platz wie nötig. Doch wenn eine Zeile wächst, wird sie aufgespalten, was zu Fragmentierung führt. Wenn Sie zum Beispiel eine Zeile mit Daten aktualisieren, die ihre Länge anwachsen lassen, so wird sie fragmentiert. In diesem Fall kann es erforderlich sein, gelegentlich `OPTIMIZE TABLE` oder myisamchk -r auszuführen, um die Leistung zu verbessern. myisamchk -ei kann Ihnen Statistikdaten zu Ihrer Tabelle liefern.

- Sie sind nach einem Absturz schwerer zu rekonstruieren als Tabellen fester Länge, da die Zeilen unter Umständen in viele Stücke fragmentiert sind und Links (Fragmente) verlorengegangen sein könnten.

- Die erwartete Zeilenlänge für dynamische Zeilen wird mit folgendem Ausdruck berechnet:

      3
      + (number of columns + 7) / 8
      + (number of char columns)
      + (packed size of numeric columns)
      + (length of strings)
      + (number of NULL columns + 7) / 8

  Für jeden Link kommen 6 Bytes hinzu. Eine dynamische Zeile wird immer dann verknüpft (verlinkt), wenn ein Update sie verlängert. Da jeder neue Link mindestens 20 Bytes hat, passt die nächste Verlängerung wahrscheinlich noch in denselben Link mit hinein. Wenn nicht, wird ein neuer Link angelegt. Die Anzahl der Links lässt sich mit myisamchk -ed feststellen. Alle Links können mit `OPTIMIZE TABLE` oder myisamchk -r entfernt werden.

#### 14.1.3.3. Kennzeichen komprimierter Tabellen

Das komprimierte Speicherformat ist ein nur-lesbares Format, das mit myisampack angelegt wird. Komprimierte Tabellen lassen sich mit myisamchk auch wieder dekomprimieren.

Kennzeichen von komprimierten Tabellen:

- Sie belegen sehr wenig Platz auf der Festplatte. Dadurch wird der Speicherverbrauch minimiert, was bei der Verwendung langsamer Speichermedien (zum Beispiel CD-ROMs) praktisch ist.

- Da jede Zeile separat komprimiert wird, funktioniert der Zugriff mit geringem Aufwand. Der Header für eine Zeile belegt ein bis drei Bytes, abhängig von der längsten Zeile der Tabelle. Jede Spalte wird anders komprimiert und normalerweise gibt es für jede Spalte einen unterschiedlichen Huffman-Baum. Einige der Kompressionsarten sind:

  - Suffix-Kompression (Komprimierung von Leerzeichen am Ende).

  - Präfix-Kompression (Komprimierung von Leerzeichen am Anfang).

  - Zahlen mit dem Wert Null werden in einem Bit gespeichert.

  - Wenn Werte in einer Integerspalte einen kleinen Wertebereich haben, wird die Spalte im kleinstmöglichen Typ gespeichert. So kann zum Beispiel eine `BIGINT`-Spalte (acht Bytes) als `TINYINT`-Spalte (ein Byte) gespeichert werden, wenn alle ihre Werte zwischen `-128` und `127` betragen.

  - Hat eine Spalte nur eine kleine Menge möglicher Werte, wird ihr Datentyp in eine `ENUM` konvertiert.

  - Eine Spalte kann eine beliebige Kombination der oben beschriebenen Komprimierungen verwenden.

- Kann für Spalten fester oder dynamischer Länge verwendet werden.

### 14.1.4. MyISAM-Tabellenprobleme

Zwar wurde das von MySQL für die Datenspeicherung genutzte Dateiformat ausführlich getestet, aber es können immer Umstände auftreten, durch die Datenbanktabellen beschädigt werden. Im Folgenden wird erklärt, wie es dazu kommt und wie man damit umgeht.

#### 14.1.4.1. Beschädigte MyISAM-Tabellen

Obwohl das Tabellenformat `MyISAM` sehr zuverlässig ist (alle Änderungen, die eine SQL-Anweisung an einer Tabelle vornimmt, werden geschrieben, ehe die Anweisung zurückkehrt), können dennoch unter folgenden Umständen Tabellen beschädigt werden:

- Der mysqld-Prozess wird mitten in einem Schreibvorgang abgebrochen.

- Der Computer wird unerwartet heruntergefahren (zum Beispiel ausgeschaltet).

- Ein Hardware-Versagen.

- Während eine Tabelle gerade vom Server modifiziert wird, ändern Sie dieselbe Tabelle mit einem externen Programm (zum Beispiel myisamchk).

- Ein Software-Bug im Code von MySQL oder `MyISAM`.

Die folgenden Symptome sind typisch für eine beschädigte Tabelle:

- Beim Auswählen von Daten aus der Tabelle bekommen Sie folgende Fehlermeldung:

      Incorrect key file for table: '...'. Try to repair it

- Anfragen können Zeilen in der Tabelle nicht finden oder liefern unvollständige Ergebnisse zurück.

Mit der `CHECK TABLE`-Anweisung können Sie die Integrität einer `MyISAM`-Tabelle überprüfen und mit `REPAIR TABLE` können Sie sie reparieren, wenn sie beschädigt ist. Wenn mysqld nicht läuft, können Sie eine Tabelle auch mit dem Befehlmyisamchk prüfen oder reparieren Siehe hierzu auch Abschnitt 13.5.2.3, „`CHECK TABLE`“, Abschnitt 13.5.2.6, „`REPAIR TABLE`“ und Abschnitt 8.2, „myisamchk — Hilfsprogramm für die Tabellenwartung von MyISAM“.

Werden Ihre Tabellen häufig beschädigt, so sollten Sie nach den Ursachen forschen. Am wichtigsten ist es, festzustellen, ob die Tabelle infolge eines Server-Absturzes beschädigt wurde. Das können Sie leicht daran erkennen, dass im Fehler-Log eine `restarted mysqld`-Nachricht jüngeren Datums gespeichert ist. Wenn ja, dann wurde die Tabelle wahrscheinlich durch den Absturz des Servers beschädigt. Andernfalls kann der Schaden auch im normalen Betrieb aufgetreten sein. Das wäre dann ein Bug. Versuchen Sie in diesem Fall, einen reproduzierbaren Testfall zu erstellen, der das Problem demonstriert. Siehe auch Abschnitt A.4.2, „Was zu tun ist, wenn MySQL andauernd abstürzt“ und Abschnitt E.1.6, „Erzeugen eines Testfalls, wenn Sie Tabellenbeschädigung feststellen“.

#### 14.1.4.2. Client benutzt Tabelle oder hat sie nicht korrekt geschlossen

Jede `MyISAM`-Indexdatei (`.MYI`-Datei) besitzt im Header einen Zähler, an dem sich erkennen lässt, ob eine Tabelle ordnungsgemäß geschlossen wurde. Liefert `CHECK TABLE` oder myisamchk folgende Warnung, so bedeutet dies, dass der Zähler nicht mehr synchron läuft:

    clients are using or haven't closed the table properly

Diese Warnung bedeutet zwar nicht unbedingt, dass die Tabelle beschädigt ist, aber Sie sollten zumindest eine Überprüfung vornehmen.

Der Zähler funktioniert folgendermaßen:

- Wenn eine Tabelle in MySQL zum ersten Mal geändert wird, wird ein Zähler im Header der Indexdateien inkrementiert.

- Bei nachfolgenden Änderungen bleibt der Zähler gleich.

- Wenn die letzte Instanz einer Tabelle geschlossen wird (wegen einer `FLUSH TABLES`-Operation oder weil im Tabellen-Cache kein Platz mehr ist), wird der Zähler dekrementiert, wenn die Tabelle an irgendeinem Punkt geändert wurde.

- Wenn Sie die Tabelle reparieren oder prüfen und für gut befinden, wird der Zähler wieder auf Null zurückgesetzt.

- Um Abstimmungsprobleme mit anderen Prozessen zu verhindern, welche die Tabelle ebenfalls überprüfen, wird der Zähler beim Schließen nicht dekrementiert, wenn er den Wert null hatte.

Anders ausgedrückt: Der Zähler kann unter folgenden Bedingungen nicht mehr synchron sein:

- Eine `MyISAM`-Tabelle wird ohne vorheriges `LOCK TABLES` und `FLUSH TABLES` kopiert.

- MySQL ist zwischen einer Änderung und dem endgültigen Schließen der Tabelle abgestürzt. (Beachten Sie, dass auch in diesem Fall die Tabelle immer noch in Ordnung sein kann, da MySQL zwischen zwei Anweisungen immer alles schreibt.)

- Eine Tabelle wurde von myisamchk --recover oder myisamchk --update-state geändert, während sie gleichzeitig von mysqld benutzt wurde.

- Mehrere mysqld-Server benutzen die Tabelle und einer von ihnen hat `REPAIR TABLE` oder `CHECK TABLE` auf ihr ausgeführt, während die anderen Server gerade auf sie zugriffen. In diesem Fall ist die Verwendung von `CHECK TABLE` sicher, obwohl Sie vielleicht von anderen Servern eine Warnung bekommen. `REPAIR TABLE` sollten Sie allerdings vermeiden, denn wenn ein Server die Datendatei durch eine neue ersetzt, können die anderen Server dies nicht wissen.

  Normalerweise sollte man ein Data Directory nicht mit mehreren Servern gemeinsam nutzen. Weitere Hinweise finden Sie unter Abschnitt 5.13, „Mehrere MySQL-Server auf derselben Maschine laufen lassen“.

## 14.2. `InnoDB`-Tabellen

### 14.2.1. Überblick über InnoDB-Tabellen

Mit `InnoDB` verfügt MySQL über eine transaktionssichere (`ACID`-konforme) Speicher-Engine mit Commit-, Rollback- und Datenwiederherstellungsfähigkeiten. `InnoDB` beherrscht sowohl Zeilensperren als auch, ähnlich wie Oracle, eine konsistente Leseoperation ohne Sperren für `SELECT`-Anweisungen. Diese Features verbessern die Mehrbenutzertauglichkeit und die Leistung. `InnoDB` benötigt keine Sperreneskalation da Zeilensperren sehr wenig Platz beanspruchen. Außerdem unterstützt `InnoDB` die `FOREIGN KEY`-Constraints. Sie können `InnoDB`-Tabellen nach Belieben mit Tabellen aus anderen MySQL-Speicher-Engines mischen, sogar in ein- und derselben Anweisung.

`InnoDB` wurde für maximale Leistung bei der Verarbeitung großer Datenmengen ausgelegt. Es gibt wohl keine andere festplattengestützte Speicher-Engine für relationale Datenbanken, die so effizient mit der CPU umgeht.

Die voll in den MySQL-Server integrierte `InnoDB`-Speicher-Engine hat ihren eigenen Bufferpool zur Speicherung von Daten und Indizes im Hauptspeicher. `InnoDB` speichert ihre Tabellen und Indizes in einem Tablespace, der aus mehreren Dateien (oder Festplattenpartitionen) bestehen kann. Darin unterscheidet sie sich beispielsweise von `MyISAM`, wo jede Tabelle in separaten Dateien untergebracht wird. `InnoDB`-Tabellen können beliebig groß sein, selbst auf Betriebssystemen, deren Dateigröße auf 2GB beschränkt ist.

`InnoDB` ist standardmäßig in Binärdistributionen enthalten. Der Windows Essentials-Installer macht `InnoDB` auf Windows zur Standard-Speicher-Engine für MySQL.

`InnoDB` wird in einer Vielzahl großer Produktionsdatenbanken eingesetzt, die hohe Anforderungen an die Leistung stellen. Die bekannte Internet-Newssite Slashdot.org läuft mit `InnoDB`. Mytrix speichert mehr als 1TB Daten in `InnoDB` und eine andere Site verarbeitet durchschnittlich 800 Inserts/Updates pro Sekunde auf `InnoDB`.

`InnoDB` unterliegt derselben GNU GPL License Version 2 (von Juni 1991) wie MySQL. Weitere Informationen über MySQL-Lizenzen finden Sie unter http://www.mysql.com/company/legal/licensing/.

Mehr zum Thema

- Ein spezielles `InnoDB`-Forum finden Sie unter http://forums.mysql.com/list.php?22.

### 14.2.2. Kontaktinformationen

Mit Innobase Oy, dem Produzenten der `InnoDB`-Engine können Sie wie folgt Kontakt aufnehmen:

          Web site: http://www.innodb.com/
          Email: <sales@innodb.com>
          Phone: +358-9-6969 3250 (office)
          +358-40-5617367 (mobile)

          Innobase Oy Inc.
          World Trade Center Helsinki
          Aleksanterinkatu 17
          P.O.Box 800
          00101 Helsinki
          Finland

### 14.2.3. Konfiguration

Die Speicher-Engine `InnoDB` ist nach Voreinstellung aktiv. Wenn Sie keine `InnoDB`-Tabellen verwenden möchten, setzen Sie die Option `skip-innodb` in Ihre MySQL-Optionsdatei.

Hinweis: Mit `InnoDB` verfügt MySQL über eine transaktionssichere (`ACID`-konforme) Speicher-Engine mit Commit-, Rollback- und Datenwiederherstellungsfähigkeiten. Diese Fähigkeiten stehen jedoch nur dann zur Verfügung, wenn auch das zugrunde liegende Betriebssystem und die Hardware vorschriftsgemäß arbeiten.. Viele Betriebssysteme oder Festplatten-Subsysteme verzögern Schreiboperationen oder ordnen sie anders an, um die Leistung zu verbessern. Auf manchen Betriebssystemen kann sogar der Systemaufruf selbst, der eigentlich warten sollte, bis alle noch ungespeicherten Daten einer Datei auf die Platte zurückgeschrieben wurden — `fsync()` — bereits zurückkehren, ehe die Daten in einen dauerhaften Speicher geschrieben wurden. So kann zum Beispiel ein Betriebssystemabsturz oder ein Stromausfall Daten, die gerade erst committet wurden, zerstören, oder im schlimmsten Fall sogar die Datenbank schädigen, indem Schreiboperationen in die verkehrte Reihenfolge gestellt werden. Wenn Ihnen an der Integrität Ihrer Daten etwas liegt, sollten Sie das Verhalten bei Stromausfall testen, ehe Sie etwas in die Produktionsumgebung einführen. Auf Mac OS X 10.3 und höher verwendet `InnoDB` eine spezielle `fcntl()`-Methode, um Dateien auf die Festplatte zurückzuschreiben. Unter Linux empfiehlt es sich, den Write-Back-Cache zu deaktivieren.

Auf ATAPI-Festplatten kann es funktionieren, mit einem Befehl wie `hdparm -W0 /dev/hda` den Write-Back-Cache zu deaktivieren. Vorsicht! Manche Treiber oder Festplattencontroller sind nicht in der Lage, den Write-Back-Cache zu deaktivieren.

Zwei wichtige, von `InnoDB` verwalteten Festplattenressourcen sind die Tablespace-Datendateien und die Logdateien.

Hinweis: Wenn Sie Konfigurationsoptionen für `InnoDB` angeben, erzeugt MySQL eine sich selbst erweiternde, 10MB große Datendatei namens `ibdata1` und zwei 5MB große Logdateien namens `ib_logfile0` und `ib_logfile1` im MySQL Data Directory. Um eine gute Performance zu erzielen, sollten Sie explizit die in den folgenden Beispielen erwähnten `InnoDB`-Parameter setzen, allerdings natürlich angepasst an Ihre Hardware und Systemanforderungen.

Die folgenden Beispiele haben nur repräsentativen Charakter. In Abschnitt 14.2.4, „`InnoDB`: Startoptionen und Systemvariablen“ erfahren Sie mehr über die Konfigurationsparameter für `InnoDB`.

Die `InnoDB`-Tablespace-Dateien richten Sie ein, indem Sie die Option `innodb_data_file_path` im `[mysqld]`-Abschnitt der Optionsdatei `my.cnf` einstellen. Auf Windows verwenden Sie stattdessen `my.ini`. Der `innodb_data_file_path` sollte eine Liste mit einer oder mehreren Datendatei-Spezifikationen sein. Mehrere Datendateien werden durch Semikola (‘`;`’) getrennt:

    innodb_data_file_path=datafile_spec1[;datafile_spec2]...

Eine Einstellung, die explizit einen Tablespace mit den Standardmerkmalen anlegt, wäre beispielsweise:

    [mysqld]
    innodb_data_file_path=ibdata1:10M:autoextend

Diese Einstellung konfiguriert eine einzige 10MB große Datendatei namens `ibdata1`, die sich selbsttätig erweitert. Da kein Verzeichnis vorgegeben ist, legt `InnoDB` sie im MySQL Data Directory an.

Größen geben Sie an, indem Sie das Suffix `M` für MB oder `G` für GB verwenden.

Ein Tablespace mit einer 50MB großen Datendatei fester Größe namens `ibdata1` und einer 50MB großen, selbsterweiternden Datei namens `ibdata2` im Data Directory kann folgendermaßen konfiguriert werden:

    [mysqld]
    innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend

Zur vollständigen Syntax einer Datendateispezifikation gehören Dateiname, Größe und mehrere optionale Attribute:

    file_name:file_size[:autoextend[:max:max_file_size]]

`autoextend` und die folgenden Attribute können nur für die letzte Datendatei auf der Zeile `innodb_data_file_path` verwendet werden.

Wenn Sie die Option `autoextend` für die letzte Datendatei angeben, erweitert `InnoDB` diese Datei, sobald sie im Tablespace nicht mehr genug freien Platz hat, in Inkrementierungsschritten, die auf 8MB voreingestellt sind. Diese Einstellung kann in der Systemvariablen `innodb_autoextend_increment` geändert werden.

Wenn die Platte vollläuft, müssen Sie eine andere Datendatei oder Festplatte hinzufügen. Eine Anleitung zur Rekonfiguration vorhandener Tablespaces finden Sie in Abschnitt 14.2.7, „Hinzufügen und Entfernen von InnoDB-Daten- und -Logdateien“.

Da `InnoDB` die maximale Dateigröße des Dateisystems nicht kennt, müssen Sie aufpassen, wenn Ihr Dateisystem nur einen relativ kleinen Wert wie etwa 2GB zulässt. Eine Maximalgröße für eine selbsterweiternde Datendatei geben Sie mit dem Attribut `max` an. In der folgenden Konfiguration kann `ibdata1` auf bis zu 500MB anwachsen:

    [mysqld]
    innodb_data_file_path=ibdata1:10M:autoextend:max:500M

`InnoDB` legt Ttablespace-Dateien standardmäßig im MySQL Data Directory an. Um ein anderes Verzeichnis anzugeben, verwenden Sie die Option `innodb_data_home_dir`. Wenn sie zum Beispiel zwei Dateien namens `ibdata1` und `ibdata2` im Verzeichnis `/ibdata` anlegen möchten, müssen Sie `InnoDB` wie folgt konfigurieren:

    [mysqld]
    innodb_data_home_dir = /ibdata
    innodb_data_file_path=ibdata1:50M;ibdata2:50M:autoextend

Hinweis: Da `InnoDB` keine Verzeichnisse erstellt, müssen Sie dafür sorgen, dass `/ibdata` existiert, ehe Sie den Server starten. Das gilt auch für Logdateiverzeichnisse, die Sie konfigurieren. Mit dem Unix- oder DOS-Befehl `mkdir` können Sie alle erforderlichen Verzeichnisse anlegen.

`InnoDB` bildet Verzeichnispfade für Datendateien, indem es den Wert von `innodb_data_home_dir` mit dem Namen der Datendatei verkettet, und wenn nötig ein Pfadtrennzeichen (Schrägstrich oder Backslash) zwischen die Werte setzt. Wenn die Option `innodb_data_home_dir` in `my.cnf` gar nicht auftaucht, ist der Standardwert das „Punkt“-Verzeichnis `./`, also das MySQL Data Directory. (Der MySQL-Server macht das Data Directory zum aktuellen Arbeitsverzeichnis, wenn er seine Arbeit aufnimmt.)

Wenn Sie `innodb_data_home_dir` als leeren String angeben, können Sie absolute Pfade für die Datendateien angeben, die im Wert von `innodb_data_file_path` aufgeführt sind. Das folgende Beispiel ist äquivalent zu dem vorherigen:

    [mysqld]
    innodb_data_home_dir =
    innodb_data_file_path=/ibdata/ibdata1:50M;/ibdata/ibdata2:50M:autoextend

Ein einfaches `my.cnf`-Beispiel. Angenommen, Sie haben einen Computer mit 128MB Arbeitsspeicher und einer Festplatte. Das folgende Beispiel zeigt mögliche Konfigurationsparameter in `my.cnf` oder `my.ini` für `InnoDB`, einschließlich des `autoextend`-Attributs. Das Beispiel passt zu den meisten Unix- und Windows-Benutzern, die ihre `InnoDB`-Datendateien und -Logdateien nicht auf mehrere Festplatten verteilen möchten. Es legt eine selbsterweiternde Datendatei namens `ibdata1` und zwei `InnoDB`-Logdateien namens `ib_logfile0` und `ib_logfile1` im MySQL Data Directory an. Außerdem wird die kleine, archivierte `InnoDB`-Logdatei `ib_arch_log_0000000000`, die `InnoDB` automatisch erstellt, ins Data Directory gespeichert.

    [mysqld]
    # Hier können Sie Ihre übrigen MySQL-Server-Optionen angeben
    # ...
    # Datendateien müssen Daten und Indizes speichern können.
    # Achten Sie auf ausreichend freien Plattenplatz.
    innodb_data_file_path = ibdata1:10M:autoextend
    #
    # Bufferpool-Größe wird auf 50-80% vom Arbeitsspeicher eingestellt
    innodb_buffer_pool_size=70M
    innodb_additional_mem_pool_size=10M
    #
    # Logdateigröße wird auf 25% der Bufferpool-Größe eingestellt
    innodb_log_file_size=20M
    innodb_log_buffer_size=8M
    #
    innodb_flush_log_at_trx_commit=1

Achten Sie darauf, dass der MySQL-Server die richtigen Berechtigungen hat, um Dateien im Data Directory anlegen zu können. Generell benötigt der Server Zugriffsrecht auf jedes Verzeichnis, in dem er Daten- oder Logdateien anlegen soll.

Beachten Sie, dass Datendateien in manchen Dateisystemen höchstens 2GB groß sein dürfen. Die kombinierte Größe aller Logdateien muss unter 4GB liegen und die kombinierte Größe der Datendateien mindestens 10MB betragen.

Wenn Sie zum ersten Mal einen `InnoDB`-Tablespace anlegen, starten Sie den MySQL-Server am besten von der Kommandozeile. Da `InnoDB` dann Informationen über die Datenbankerstellung auf dem Bildschirm ausgibt, können Sie sehen, was geschieht. Wenn beispielsweise auf Windows mysqld-max im Verzeichnis `C:\Program Files\MySQL\MySQL Server 5.1\bin` liegt, können Sie folgendermaßen starten:

    C:\> "C:\Program Files\MySQL\MySQL Server 5.1\bin\mysqld-max" --console

Wenn sie keine Serverausgabe an den Bildschirm schicken, müssen Sie im Fehlerlog nachschauen, welche Meldungen `InnoDB` beim Hochfahren ausgibt.

Unter Abschnitt 14.2.5, „InnoDB-Tablespace erzeugen“ sehen Sie ein Beispiel dafür, welche Informationen `InnoDB` anzeigt.

Die `InnoDB`-Optionen können Sie in die `[mysqld]`-Gruppe einer beliebigen Optionsdatei legen, die Ihr Server beim Hochfahren liest. Speicherorte für Optionsdateien werden in Abschnitt 4.3.2, „my.cnf-Optionsdateien“ beschrieben.

Wenn Sie MySQL auf Windows mit dem Installations- und Konfigurationsassistenten installiert haben, liegen die Optionen in der `my.ini`-Datei in Ihrem MySQL-Installationsverzeichnis. Siehe Abschnitt 2.3.5.14, „Speicherort der Datei `my.ini`“.

Wenn Ihr PC einen Bootloader nutzt und `C:` nicht das Bootverzeichnis ist, haben Sie keine andere Möglichkeit, als die `my.ini`-Datei in Ihrem Windows-Verzeichnis zu benutzen (in der Regel `C:\WINDOWS` oder `C:\WINNT`). Verwenden Sie den Befehl `SET` auf der Kommandozeile eines Konsolenfensters, um den Wert von `WINDIR` auszugeben:

    C:\> SET WINDIR
    windir=C:\WINDOWS

Wenn Sie sicherstellen möchten, dass mysqld-Optionen nur aus einer bestimmten Datei liest, verwenden Sie `--defaults-option` beim Serverstart als erste Option auf der Kommandozeile:

    mysqld --defaults-file=your_path_to_my_cnf

Ein fortgeschrittenes`my.cnf`-Beispiel. Angenommen, Sie haben einen Linux-Computer mit 2GB RAM und drei 60GB-Festplatten mit den Verzeichnispfaden `/`, `/dr2` und `/dr3`. Das folgende Beispiel zeigt, welche Konfigurationsparameter man in `my.cnf` für `InnoDB` setzen könnte.

    [mysqld]
    # Hier können Sie Ihre übrigen MySQL-Server-Optionen angeben
    # ...
    innodb_data_home_dir =
    #
    # Datendateien müssen Daten und Indizes speichern können.
    innodb_data_file_path = /ibdata/ibdata1:2000M;/dr2/ibdata/ibdata2:2000M:autoextend
    #
    # Setzen Sie die Bufferpool-Größe auf 50-80% des Arbeitsspeichers,
    # aber achten Sie darauf, dass für Linux x86 die gesamte Speichernutzung < 2GB ist.
    innodb_buffer_pool_size=1G
    innodb_additional_mem_pool_size=20M
    innodb_log_group_home_dir = /dr3/iblogs
    #
    innodb_log_files_in_group = 2
    #
    # Setzen Sie die Logdateigröße auf circa 25% der Bufferpool-Größe
    innodb_log_file_size=250M
    innodb_log_buffer_size=8M
    #
    innodb_flush_log_at_trx_commit=1
    innodb_lock_wait_timeout=50
    #
    # Kommentieren Sie die nächsten Zeilen aus, wenn Sie sie nutzen möchten
    #innodb_thread_concurrency=5

In manchen Fällen steigt die Datenbank-Performance, wenn nicht alle Daten auf derselben physikalischen Platte liegen. Oft ist es gut für die Performance, wenn die Logdateien auf einer anderen Festplatte liegen. Das Beispiel zeigt, wie das geht: Es speichert zwei Dateien auf verschiedenen Festplatten und legt die Logdateien auf eine dritte Platte. `InnoDB` füllt den Tablespace beginnend mit der ersten Datendatei. Für einen schnelleren Zugriff können Sie auch rohe Festplattenpartitionen (Raw Devices) als `InnoDB`-Datendateien verwenden. Siehe Abschnitt 14.2.3.2, „Verwendung von Raw Devices für den Shared Tablespace“.

Warnung: Auf 32-Bit GNU/Linux x86 dürfen Sie die Arbeitsspeichernutzung nicht zu hoch einstellen. Wenn `glibc` den Prozess-Heap über die Thread-Stacks hinauswachsen lässt, stürzt der Server ab. Wenn der Wert des folgenden Ausdrucks 2GB erreicht oder übersteigt, ist Gefahr in Verzug:

    innodb_buffer_pool_size
    + key_buffer_size
    + max_connections*(sort_buffer_size+read_buffer_size+binlog_cache_size)
    + max_connections*2MB

Jeder Thread benutzt einen Stack (oft 2MB, aber in den Binaries von MySQL AB nur 256KB) und im schlimmsten Fall auch `sort_buffer_size + read_buffer_size` zusätzlichen Arbeitsspeicher.

Indem Sie MySQL selbst kompilieren, können Sie bis zu 64GB physikalischen Speicher in 32-Bit Windows verwenden. Siehe Beschreibung von `innodb_buffer_pool_awe_mem_mb` in Abschnitt 14.2.4, „`InnoDB`: Startoptionen und Systemvariablen“.

Wie werden die anderen mysqld-Serverparameter eingestellt? Die folgenden Werte sind typisch und eignen sich für die meisten Nutzer:

    [mysqld]
    skip-external-locking
    max_connections=200
    read_buffer_size=1M
    sort_buffer_size=1M
    #
    # Setzen Sie key_buffer auf 5 - 50% Ihres Arbeitsspeichers, je nachdem, wie
    # oft Sie MyISAM-Tabellen benutzen, aber behalten Sie key_buffer_size + InnoDB
    # buffer pool size < 80% Ihres Arbeitsspeichers
    key_buffer_size=value

#### 14.2.3.1. Verwendung von Tabellen-Tablespaces (ein Tablespace pro Tabelle)

Sie können jede `InnoDB`-Tabelle und ihre Indizes in ihrer eigenen Datei speichern. Dieses Feature nennt man „Multi-Tablespaces“ da im Endeffekt jede Tabelle ihren eigenen Tablespace bekommt.

Multi-Tablespaces sind praktisch für Benutzer, die bestimmte Tabellen auf separate physikalische Platten verlagern oder Backups einzelner Tabellen rasch wiederherstellen möchten, ohne die Arbeit mit den übrigen `InnoDB`-Tabellen zu unterbrechen.

Aktivieren Sie Multi-Tablespaces mit folgender Zeile im `[mysqld]`-Abschnitt von `my.cnf`:

    [mysqld]
    innodb_file_per_table

Nach dem Server-Neustart speichert `InnoDB` jede neu erzeugte Tabelle in einer eigenen Datei ``tbl_name`.ibd` in dem Datenbankverzeichnis, zu dem die Tabelle gehört. Das ähnelt dem Vorgehen der `MyISAM`-Speicher-Engine, doch diese spaltet die Tabellen in eine Datendatei ``tbl_name`.MYD` und eine Indexdatei ``tbl_name`.MYI`. Mit `InnoDB` werden Daten und Indizes gemeinsam in der`.ibd`-Datei gespeichert. Die ``tbl_name`.frm`-Datei wird wie üblich angelegt.

Wenn Sie die `innodb_file_per_table`-Zeile aus `my.cnf` löschen und den Server neu starten, erzeugt `InnoDB` die Tabellen wieder in Shared Tablespace-Dateien.

`innodb_file_per_table` wirkt sich nur auf eine einzelne Tabellenerzeugung aus, und beeinflusst nicht den Zugriff auf bestehende Tabellen. Wenn Sie den Server mit dieser Option starten, werden neue Tabellen mit `.ibd`-Dateien angelegt, aber alte liegen immer noch im Shared Tablespace. Entfernen Sie die Option wieder und starten dann den Server neu, werden die neuen Tabellen im Shared Tablespace angelegt, aber Tabellen, die mit Multi-Tablespaces angelegt wurden, bleiben weiterhin zugänglich.

`InnoDB` benötigt immer den Shared Tablespace, da es sein internes Data Dictionary und seine Undo-Logs dort speichert. Die `.ibd`-Dateien reichen `InnoDB` zum Funktionieren nicht aus.

Hinweis: Sie können `.ibd`-Dateien genau wie `MyISAM`-Tabellendateien nach Belieben zwischen Datenbankverzeichnissen hin- und herschieben, weil die im Shared Tablespace von `InnoDB` gespeicherte Tabellendefinition den Datenbanknamen enthält und weil `InnoDB` die Konsistenz von Transaktions-IDs und Lognummern beibehalten muss.

Um eine `.ibd`-Datei und die zugehörige Tabelle von einer Datenbank in eine andere zu verlagern, verwenden Sie eine `RENAME TABLE`-Anweisung:

    RENAME TABLE db1.tbl_name TO db2.tbl_name;

Wenn Sie über ein „sauberes“ Backup einer `.ibd`-Datei verfügen, können Sie diese in ihrer angestammten MySQL-Installation folgendermaßen wiederherstellen:

1. Geben Sie folgende `ALTER TABLE`-Anweisung:

       ALTER TABLE tbl_name DISCARD TABLESPACE;

   Vorsicht: Diese Anweisung löscht die aktuelle `.ibd`-Datei.

2. Speichern Sie die `.ibd`-Backup-Datei zurück in das richtige Datenbankverzeichnis.

3. Geben Sie folgende `ALTER TABLE`-Anweisung:

       ALTER TABLE tbl_name IMPORT TABLESPACE;

Ein „sauberes“ Backup einer `.ibd`-Datei bedeutet in diesem Zusammenhang:

- Keine schwebenden Transaktionen in der `.ibd`-Datei.

- Keine noch nicht zusammengeführten Insert-Puffer-Einträge in der `.ibd`-Datei.

- Purge hat alle zum Löschen vorgemerkten Indexeinträge aus der `.ibd`-Datei entfernt.

- mysqld hat alle geänderten Seiten der `.ibd`-Datei aus dem Bufferpool in die Datei zurückgeschrieben.

Mit folgender Methode können Sie eine saubere `.ibd`-Backup-Datei anlegen:

1. Beenden Sie alle Aktivitäten des mysqld-Servers und schreiben Sie alle Transaktionen fest.

2. Warten Sie, bis `SHOW ENGINE INNODB STATUS` anzeigt, dass keine Transaktionen mehr in der Datenbank aktiv sind und der Status des Haupt-Threads von `InnoDB` den Wert `Waiting for server activity` angenommen hat. Dann können Sie die `.ibd`-Datei kopieren.

Eine andere Möglichkeit, an eine saubere Kopie einer `.ibd`-Datei zu kommen, ist die Verwendung eines kommerziellen InnoDB Hot Backup-Tools:

1. Legen Sie mit InnoDB Hot Backup ein Backup der `InnoDB`-Installation an.

2. Starten Sie einen zweiten mysqld-Server auf dem Backup und lassen Sie ihn die `.ibd`-Dateien im Backup säubern.

#### 14.2.3.2. Verwendung von Raw Devices für den Shared Tablespace

Sie können auch rohe Festplattenpartitionen für die Datendateien im Shared Tablespace verwenden. So können sie ungepufferte E/A-Zugriffe ohne Dateisystem-Overhead auf Windows und einigen Unix-Systemen implementieren und die Performance dadurch steigern.

Wenn Sie eine neue Datendatei anlegen, müssen Sie in `innodb_data_file_path` das Schlüsselwort `newraw` direkt hinter die Größe der Datendatei setzen. Die Partition muss mindestens die angegebene Größe haben. Beachten Sie, dass 1MB in `InnoDB` 1024 × 1024 Bytes sind, während 1MB in Festplattenspezifikationen normalerweise 1.000.000 Bytes bedeutet.

    [mysqld]
    innodb_data_home_dir=
    innodb_data_file_path=/dev/hdd1:3Gnewraw;/dev/hdd2:2Gnewraw

Wenn Sie den Server das nächste Mal starten, bemerkt `InnoDB` das Schlüsselwort `newraw` und initialisiert die neue Partition. Sie dürfen jetzt aber noch keine `InnoDB`-Tabellen ändern oder anlegen, sonst reinitialisiert `InnoDB` beim nächsten Serverstart die Partition und Ihre Änderungen gehen verloren. (Als Sicherheitsmaßnahme hindert `InnoDB` die Benutzer daran, irgendwelche Daten zu ändern, wenn eine Partition mit `newraw` definiert wurde.)

Nachdem `InnoDB` die neue Partition initialisiert hat, halten Sie den Server an und ändern `newraw` in der Datendateispezifikation in `raw` um:

    [mysqld]
    innodb_data_home_dir=
    innodb_data_file_path=/dev/hdd1:5Graw;/dev/hdd2:2Graw

Wenn Sie nun den Server erneut starten, erlaubt `InnoDB` auch Änderungen.

Auf Windows können Sie eine Festplattenpartition folgendermaßen als Datendatei zuweisen:

    [mysqld]
    innodb_data_home_dir=
    innodb_data_file_path=//./D::10Gnewraw

Das `//./` entspricht beim Zugriff auf physikalische Platten der Windows-Syntax `\\.\`.

Wenn Sie rohe Festplattenpartitionen benutzen, achten Sie bitte darauf, dass ihre Berechtigungseinstellungen dem vom MySQL-Server benutzten Konto auch Lese- und Schreiboperationen gestatten.

### 14.2.4. `InnoDB`: Startoptionen und Systemvariablen

Dieser Abschnitt beschreibt die Befehlsoptionen und Systemvariablen für `InnoDB`. Systemvariablen, die true oder false sein können, werden beim Serverstart entweder durch Nennung ihres Namens aktiviert oder mit dem Präfix `skip-` deaktiviert. Um beispielsweise `InnoDB`-Prüfsummen ein- oder auszuschalten, verwenden Sie `--innodb_checksums` oder `--skip-innodb_checksums` auf der Kommandozeile, oder `innodb_checksums` oder `skip-innodb_checksums` in einer Optionsdatei. Systemvariablen, die einen numerischen Wert annehmen, können als `--`var_name`=`value`` auf der Kommandozeile oder als ``var_name`=`value`` in Optionsdateien angegeben werden. Weitere Informationen über die Angabe von Optionen und Systemvariablen finden Sie unter Abschnitt 4.3, „Angabe von Programmoptionen“. Viele der Systemvariablen können zur Laufzeit geändert werden (siehe Abschnitt 5.2.3.2, „Dynamische Systemvariablen“).

`InnoDB`-Befehlsoptionen:

- `--innodb`

  Aktiviert die `InnoDB`-Speicher-Engine, wenn der Server mit `InnoDB`-Unterstützung kompiliert wurde. Mit `--skip-innodb` können Sie `InnoDB` deaktivieren.

- `--innodb_status_file`

  Veranlasst `InnoDB`, eine Datei namens ``<datadir>`/innodb_status.`<pid>`` im MySQL Data Directory anzulegen. `InnoDB` schreibt in regelmäßigen Abständen die Ausgabe von `SHOW ENGINE INNODB STATUS` in diese Datei.

`InnoDB`-Systemvariablen:

- `innodb_additional_mem_pool_size`

  Die Größe des von `InnoDB` zum Speichern von Data Dictionary-Informationen und anderen internen Datenstrukturen verwendeten Arbeitsspeicherpools in Bytes. Je mehr Tabellen Ihre Anwendung hat, umso mehr Arbeitsspeicher müssen Sie hier zuweisen. Wenn `InnoDB` in diesem Pool der Speicher ausgeht, beginnt es, Arbeitsspeicher vom Betriebssystem abzuzweigen, und gibt Warnmeldungen in das MySQL-Fehlerlog aus. Der Standardwert beträgt 1MB.

- `innodb_autoextend_increment`

  In Inkrementen dieser Größe (in MB) wächst ein selbsterweiternder Tablespace, wenn er vollläuft. Der Standardwert beträgt 8.

- `innodb_buffer_pool_awe_mem_mb`

  Die Größe des Bufferpools (in MB), wenn er im AWE-Speicher liegt. Dies gilt jedoch nur für 32-Bit Windows. Wenn Ihr 32-Bit Windows-Betriebssystem über die so genannten „Address Windowing Extensions“ mehr als 4GB Arbeitsspeichergröße unterstützt, können Sie den `InnoDB`-Bufferpool im physikalischen AWE-Speicher mit dieser Variablen zuweisen. Der größtmögliche Wert der Variablen beträgt 63000. Ist er größer als 0, ist `innodb_buffer_pool_size` das Fenster im 32-Bit-Adressraum von mysqld, wobei `InnoDB` diesen AWE-Arbeitsspeicher abbildet. Ein guter Wert für `innodb_buffer_pool_size` ist 500MB.

  Um den AWE-Speicher nutzen zu können, müssen Sie MySQL neu kompilieren. Welche Projekteinstellungen derzeit dazu erforderlich sind, entnehmen Sie bitte der Quelldatei `storage/innobase/os/os0proj.c`.

- `innodb_buffer_pool_size`

  Die Größe des Arbeitsspeicherpuffers in Bytes, den `InnoDB` zum Zwischenspeichern der Daten und Indizes seiner Tabellen benutzt. Je größer Sie diesen Wert einstellen, umso weniger Festplattenzugriffe sind für den Zugriff auf die Tabellendaten erforderlich. Für einen dedizierten Datenbankserver können Sie dies auf 80% des physikalischen Arbeitsspeichers heraufsetzen. Machen Sie ihn jedoch nicht zu groß, da ein Wettlauf um den physikalischen Speicher das Betriebssystem zum Paging veranlassen kann.

- `innodb_checksums`

  `InnoDB` kann für alle von der Platte gelesenen Seiten eine Prüfsummenvalidierung verwenden, um eine zusätzliche Fehlertoleranz gegenüber Schäden an Hardware oder Datendateien zu gewährleisten. Diese Validierung ist standardmäßig eingeschaltet. Doch in einigen wenigen Fällen (zum Beispiel bei der Ausführung von Benchmarks) ist diese zusätzliche Sicherheitsvorkehrung überflüssig und kann mit `--skip-innodb-checksums` deaktiviert werden.

- `innodb_commit_concurrency`

  Die Anzahl der Threads, die gleichzeitig committen können. Der Wert 0 schaltet die Nebenläufigkeitssteuerung aus.

- `innodb_concurrency_tickets`

  Wie viele Threads gleichzeitig auf `InnoDB` zugreifen können, hängt von der Einstellung der `innodb_thread_concurrency`-Variablen ab. Ein Thread wird in eine Schlange gestellt, wenn er auf `InnoDB` zugreifen möchte und das Nebenläufigkeitslimit bereits erreicht ist. Wenn einem Thread der Eintritt in `InnoDB` erlaubt wird, bekommt eine Anzahl von „Freifahrscheine“, die der Anzahl der `innodb_concurrency_tickets` entspricht und kann `InnoDB` so lange nach Belieben betreten und verlassen, bis seine Freifahrscheine aufgebraucht sind. Danach wird der Thread wieder einer Prüfung unterzogen (und eventuell in die Schlange gestellt), wenn er das nächste Mal in `InnoDB` eintreten möchte.

- `innodb_data_file_path`

  Die Pfade und Größen der einzelnen Datendateien. Der vollständige Verzeichnispfad zu den Datendateien entsteht, wenn `innodb_data_home_dir` mit den einzelnen, hier angegebenen Pfaden verkettet wird. Die Dateigrößen werden in MB oder GB (1024MB) durch Anfügen von `M` oder `G` an den Größenwert angegeben. Die Summe der Dateigrößen muss mindestens 10MB betragen. Wenn Sie keinen `innodb_data_file_path` angeben, wird standardmäßig eine einzige, selbsterweiternde, 10MB große Datendatei namens `ibdata1` erzeugt. Wie groß die einzelnen Dateien werden können, entscheidet Ihr Betriebssystem. Auf Systemen, die große Dateien unterstützen, können Sie die Dateigröße auf mehr als 4GB setzen. Sie können auch rohe Festplattenpartitionen als Datendateien einsetzen. Siehe Abschnitt 14.2.3.2, „Verwendung von Raw Devices für den Shared Tablespace“.

- `innodb_data_home_dir`

  Der gemeinsame Teil des Verzeichnispfads für alle `InnoDB`-Datendateien. Wenn Sie diesen Wert nicht einstellen, ist das MySQL Data Directory das Ziel. Geben Sie hier einen leeren String an, so können Sie in `innodb_data_file_path` absolute Dateipfade verwenden.

- `innodb_doublewrite`

  Nach Voreinstellung speichert `InnoDB` alle Daten zweimal, nämlich zuerst in den Doublewrite-Puffer und dann in die eigentlichen Datendateien. Diese Variable ist standardmäßig eingeschaltet, kann aber mit der Option `--skip-innodb_doublewrite` ausgeschaltet werden, wenn Sie Benchmarks ausführen oder Ihnen eine Top-Performance so wichtig ist, dass Sie sich für Datenintegrität und mögliche Systemabstürze weniger interessieren.

- `innodb_fast_shutdown`

  Wenn Sie diese Variable auf 0 setzen, führt `InnoDB` vor dem Herunterfahren eine vollständige Purge-Operation und Verschmelzung der Insert-Puffer durch. Diese Operationen können Minuten oder im Extremfall sogar Stunden in Anspruch nehmen. Setzen Sie diese Variable auf 1, übergeht `InnoDB` beim Herunterfahren diese Operationen. Der Standardwert ist 1. Wenn Sie ihn auf 2 setzen, leert `InnoDB` nur die Logs und fährt dann kalt herunter, wie bei einem Absturz von MySQL. Es geht zwar keine committete Transaktion verloren, aber nach dem nächsten Hochfahren wird eine Wiederherstellung gefahren. Den Wert 2 können Sie nicht auf NetWare verwenden.

- `innodb_file_io_threads`

  Die Anzahl der Dateizugriffs-Threads in `InnoDB`. Normalerweise kann man den Standardwert 4 beibehalten, aber auf Windows kann eine größere Zahl die Festplattenzugriffe günstig beeinflussen. Auf Unix bleibt eine Erhöhung dieses Werts ohne Wirkung, da `InnoDB` immer den Standardwert verwendet.

- `innodb_file_per_table`

  Wenn diese Variable eingeschaltet ist, erzeugt `InnoDB` jede neue Tabelle mit ihrer eigenen `.ibd`-Datei zum Speichern von Daten und Indizes, anstatt im Shared Tablespace. Nach Voreinstellung werden die Tabellen im Shared Tablespace angelegt. Siehe Abschnitt 14.2.3.1, „Verwendung von Tabellen-Tablespaces (ein Tablespace pro Tabelle)“.

- `innodb_flush_log_at_trx_commit`

  Hat `innodb_flush_log_at_trx_commit` den Wert 0, wird einmal pro Sekunde der Logpuffer in die Logdatei geschrieben und diese auf die Festplatte zurückgespeichert, doch beim Committen einer Transaktion wird nichts veranlasst. Ist der Wert 1 (der Standard), wird bei jedem Commit der Logpuffer in die Logdatei und diese auf die Festplatte geschrieben. Ist der Wert 2, wird der Puffer bei jedem Commit in die Datei übertragen, aber diese nicht beim Commit, sondern einmal pro Sekunde auf die Festplatte zurückgeschrieben. Beachten Sie jedoch, dass dieser Schreibvorgang aus Gründen der Prozessplanung in Wirklichkeit nicht unbedingt exakt einmal pro Sekunde stattfindet.

  Der Standardwert dieser Variablen, nämlich 1, ist für die ACID-Fähigkeit erforderlich. Ein anderer Wert als 1 kann zwar die Performance steigern, aber um den Preis, dass Sie bei einem Systemabsturz eine Sekunde an Transaktionen verlieren. Ist der Wert 0, kann jeder Absturz des mysqld-Prozesses die Transaktionen der letzten Sekunde ausradieren. Ist der Wert 2, würde dieser Datenverlust bei einem Betriebssystemabsturz oder Stromausfall eintreten. Da allerdings die Wiederherstellungsfunktion von `InnoDB` nicht beeinträchtigt wird, würde die Crash-Recovery unabhängig vom Wert dieser Variablen funktionieren. Beachten Sie jedoch, dass viele Betriebssysteme und einige Festplatten die Flush-to-Disk-Operation irreführen, indem sie mysqld weismachen, dass die Daten bereits auf die Festplatte geschrieben wurden, auch wenn das nicht der Fall ist. Die Dauerhaftigkeit von Transaktionen ist also selbst mit der Einstellung 1 nicht gewährleistet, und im schlimmsten Falle kann ein Stromausfall sogar die `InnoDB`-Datenbank beschädigen. Ein batteriegestützter Festplatten-Cache im SCSI-Festplattencontroller oder in der Festplatte selbst kann das Zurückschreiben von Dateien auf die Festplatte beschleunigen und die Operation sicherer machen. Außerdem können Sie mit dem Unix-Befehl hdparm das Caching von Festplatten-Schreibvorgängen in Hardware-Caches deaktivieren oder einen anderen Hardware-spezifischen Befehl verwenden.

- `innodb_flush_method`

  Die Standardeinstellung `fdatasync` sorgt dafür, dass `InnoDB` Daten- und Logdateien mit `fsync()` auf die Festplatte schreibt. Die Einstellung `O_DSYNC` lässt `InnoDB` Logdateien mit `O_SYNC` öffnen und auf die Festplatte schreiben, aber für Datendateien `fsync()` verwenden. Die (auf einigen GNU/Linux-Versionen mögliche) Einstellung `O_DIRECT` veranlasst `InnoDB`, Datendateien mit `O_DIRECT` zu öffnen und Daten- und Logdateien mit `fsync()` auf die Festplatte schreiben. Beachten Sie, dass `InnoDB` die Funktion `fsync()` anstelle von `fdatasync()` benutzt und `O_DSYNC` nicht standardmäßig einsetzt, weil dies mit vielen Unix-Varianten bereits zu Problemen geführt hat. Diese Variable ist nur für Unix relevant. Auf Windows wird immer und unabänderlich `async_unbuffered` zum Zurückschreiben von Daten auf die Festplatte verwendet.

- `innodb_force_recovery`

  Der Crash-Recovery-Modus. Warnung: Diese Variable sollte nur im Notall, wenn Tabellen aus einer beschädigten Datenbank gesichert werden sollen, auf 0 gesetzt werden! Die Bedeutung der möglichen Werte 1 bis 6 wird in Abschnitt 14.2.8.1, „Erzwingen einer `InnoDB`-Wiederherstellung (Recovery)“ erläutert. Als Sicherheitsmaßnahme verhindert `InnoDB` alle Änderungen an Daten, wenn diese Variable größer als 0 ist.

- `innodb_lock_wait_timeout`

  Gibt an, wie viele Sekunden eine `InnoDB`-Transaktion auf eine Sperre wartet, ehe sie zurückgerollt wird. `InnoDB` entdeckt automatisch Transaktions-Deadlocks in seiner eigenen Sperrentabelle und macht dann die Transaktion rückgängig. `InnoDB` erkennt Sperren, die mit der `LOCK TABLES`-Anweisung gesetzt wurden. Die Standardeinstellung beträgt 50 Sekunden.

  Hinweis: Die größtmögliche Dauerhaftigkeit und Konsistenz in einer Replikationsumgebung mit `InnoDB` und Transaktionen erzielen Sie, wenn Sie in der `my.cnf`-Datei Ihres Masterservers `innodb_flush_log_at_trx_commit=1` und `sync_binlog=1` einstellen.

- `innodb_locks_unsafe_for_binlog`

  Diese Variable steuert Next-Key-Locking in `InnoDB`-Suchoperationen und Index-Scans. Standardmäßig ist diese Variable 0 (deaktiviert) und das Next-Key-Locking somit eingeschaltet.

  Normalerweise benutzt `InnoDB` einen Algorithmus namens Next-Key-Locking. Zeilensperren funktionieren in `InnoDB` folgendermaßen: Wenn ein Tabellenindex durchsucht oder gescannt wird, errichtet `InnoDB` Shared oder exklusive Sperren auf allen gefundenen Indexeinträgen. Somit sind die Zeilensperren in Wirklichkeit Sperren auf Indexeinträgen. Diese Sperren betreffen auch die „Lücke“, die den Indexeinträgen vorausgeht. Wenn ein Benutzer eine Shared oder exklusive Sperre auf Eintrag R eines Index hat, können andere Benutzer keine neuen Indexeinträge unmittelbar vor R in diesen Index einfügen. Ist diese Variable eingeschaltet, wird Next-Key-Locking von `InnoDB` nicht nicht in Suchoperationen oder Index-Scans verwendet, wohl aber zur Sicherung von Fremdschlüssel-Constraints und Prüfung auf Schlüsselduplikate. Das Einschalten dieser Variablen kann Phantomprobleme verursachen: Angenommen, Sie möchten alle Kinder der `child`-Tabelle, die einen Identifier-Wert größer 100 haben, lesen und sperren, da Sie vorhaben, in den ausgewählten Zeilen später eine Spalte zu ändern:

      SELECT * FROM child WHERE id > 100 FOR UPDATE;

  Nehmen wir weiterhin an, auf der Spalte `id` ist ein Index definiert. Die Anfrage scannt diesen Index ab dem ersten Eintrag, in dem `id` größer als 100 ist. Wenn die auf den Indexeinträgen errichteten Sperren Einfügungen in den Lücken nicht ausschließen, kann ein anderer Client eine neue Zeile in die Tabelle einfügen. Wenn Sie dasselbe `SELECT` in derselben Transaktion ausführen, sehen Sie in der Ergebnismenge eine neue Zeile. Das führt auch dazu, dass `InnoDB` bei Einfügung neuer Elemente in die Datenbank keine Serialisierbarkeit garantieren kann. Folglich gewährleistet `InnoDB` bei Einschaltung dieser Variablen maximal die Isolationsebene `READ COMMITTED`. (Die Konfliktserialisierbarkeit ist aber nach wie vor garantiert.)

  Die Einschaltung dieser Variablen hat noch einen Zusatzeffekt: `InnoDB` sperrt in einem `UPDATE` oder `DELETE` nur die Zeilen, die aktualisiert bzw. gelöscht werden. Dadurch werden Deadlocks zwar sehr unwahrscheinlich, können aber immer noch auftreten. Beachten Sie, dass das Einschalten dieser Variablen nach wie vor nicht erlaubt, dass `UPDATE` andere, ähnliche Operationen (wie etwa ein anderes `UPDATE`) übernimmt, selbst dann nicht, wenn die beiden Operationen unterschiedliche Zeilen betreffen. Betrachten Sie das nächste Beispiel, das mit folgender Tabelle beginnt:

      CREATE TABLE A(A INT NOT NULL, B INT) ENGINE = InnoDB;
      INSERT INTO A VALUES (1,2),(2,3),(3,2),(4,3),(5,2);
      COMMIT;

  Angenommen, ein Client führt folgende Anweisungen aus:

      SET AUTOCOMMIT = 0;
      UPDATE A SET B = 5 WHERE B = 3;

  Nehmen wir weiterhin an, dass danach ein anderer Client diese Anweisungen ausführt:

      SET AUTOCOMMIT = 0;
      UPDATE A SET B = 4 WHERE B = 2;

  In diesem Fall muss das zweite `UPDATE` auf ein Commit oder Rollback des ersten warten. Das erste `UPDATE` besitzt eine exklusive Sperre auf Zeile (2,3) und das zweite `UPDATE` versucht, während es die Zeilen scannt, für dieselbe Zeile ebenfalls eine Sperre zu erwerben, die es jedoch nicht bekommt. Das liegt daran, dass das zweite `UPDATE` zuerst eine exklusive Sperre auf einer Zeile erwirbt und dann feststellt ob diese Zeile zur Ergebnismenge gehört. Wenn nicht, gibt es die überflüssige Sperre wieder frei, sofern die Variable `innodb_locks_unsafe_for_binlog` eingeschaltet ist.

  Also führt `InnoDB` das `UPDATE` Nummer eins folgendermaßen aus:

      x-lock(1,2)
      unlock(1,2)
      x-lock(2,3)
      update(2,3) to (2,5)
      x-lock(3,2)
      unlock(3,2)
      x-lock(4,3)
      update(4,3) to (4,5)
      x-lock(5,2)
      unlock(5,2)

  Das zweite `UPDATE` führt `InnoDB` so aus:

      x-lock(1,2)
      update(1,2) to (1,4)
      x-lock(2,3) - wait for query one to commit or rollback

- `innodb_log_arch_dir`

  Das Verzeichnis, in dem vollgeschriebene Logdateien archiviert werden, sofern die Archivierung von Logs eingeschaltet ist. Wenn ja, so sollte diese Variable auf denselben Wert wie `innodb_log_group_home_dir` gesetzt werden. Das ist jedoch nicht obligatorisch.

- `innodb_log_archive`

  Gibt an, ob `InnoDB`-Archivdateien protokolliert werden sollen. Diese Variable ist nur aus historischen Gründen noch vorhanden, wird aber nicht benutzt. Da MySQL die Backup-Recovery anhand seiner eigenen Logdateien durchführt, gibt es keinen Anlass, `InnoDB`-Logdateien zu archivieren. Die Variable hat den Standardwert 0.

- `innodb_log_buffer_size`

  Gibt in Bytes die Größe des Puffers an, den `InnoDB` benutzt, um Logdateien auf die Platte zu schreiben. Werte von 1MB bis 8MB sind hier annehmbar. Der Standardwert ist 1MB. Wenn Sie einen großen Logpuffer haben, können umfangreiche Transaktionen zuende laufen, ohne dass das Log vor dem Committen auf die Festplatte zurückgeschrieben werden muss. In einer Umgebung mit großen Transaktionen können Sie also Festplattenzugriffe reduzieren, indem Sie den Logpuffer vergrößern.

- `innodb_log_file_size`

  Die Größe jeder Logdatei einer Loggruppe in Bytes. Die kombinierte Größe der Logdateien muss auf 32-Bit-Rechnren weniger als 4GB sein. Der Standard ist 5MB. Annehmbar sind Werte zwischen 1MB und 1/`N`-tel der Größe des Bufferpools, wobei `N` die Anzahl der Dateien in einer Loggruppe ist. Je größer der Wert, umso weniger Checkpoint-Flushing ist im Bufferpool erforderlich, was wiederum Plattenzugriffe spart. Allerdings haben große Logdateien auch zur Folge, dass die Recovery nach einem Absturz langsamer läuft.

- `innodb_log_files_in_group`

  Die Anzahl der Logdateien in der Loggruppe. `InnoDB` benutzt die Dateien in zirkulärer Weise. Der (empfehlenswerte) Standardwert ist 2.

- `innodb_log_group_home_dir`

  Der Verzeichnispfad zu den `InnoDB`-Logdateien. Er muss denselben Wert haben wie `innodb_log_arch_dir`. Wenn Sie keine `InnoDB`-Logvariablen angeben, werden nach Voreinstellung zwei 5MB große Dateien namens `ib_logfile0` und `ib_logfile1` im MySQL Data Directory angelegt.

- `innodb_max_dirty_pages_pct`

  Ein Integer von 0 bis 100. Der Standardwert ist 90. Der Haupt-Thread in `InnoDB` versucht, Seiten aus dem Bufferpool derart zu schreiben, dass der Prozentsatz von noch nicht geschriebenen Seiten diesen Wert nicht übersteigt.

- `innodb_max_purge_lag`

  Diese Variable gibt an, wie lange `INSERT`-, `UPDATE`- und `DELETE`-Operationen aufgeschoben werden, wenn die Purge-Operationen hinterher hinken (siehe Abschnitt 14.2.12, „Implementierung der Multiversionierung“). Der Standardwert ist 0 (keine Verzögerungen).

  Das Transaktionssystem von `InnoDB` pflegt eine Liste von Transaktionen, die Indexeinträge anhand von `UPDATE`- oder `DELETE`-Operationen zum Löschen vorgemerkt haben. Die Länge dieser Liste sei `purge_lag`. Wenn `purge_lag` den Wert `innodb_max_purge_lag` überschreitet, wird jede `INSERT`-, `UPDATE`- und `DELETE`-Operation um ((`purge_lag`/`innodb_max_purge_lag`)×10)–5 Millisekunden aufgeschoben. Diese Verzögerung wird alle zehn Sekunden am Anfang eines Purge-Batch berechnet. Die Operationen werden nicht aufgeschoben, wenn Purge nicht laufen kann, weil eine alte Consistent Read View die zu bereinigenden Zeilen sehen könnte.

  Eine typische Einstellung für eine problematische Arbeitslast wäre 1 Million, wenn die Transaktionen nur etwa 100 Bytes klein sind und wir 100MB unbereinigte Zeilen in unseren Tabellen gestatten können.

- `innodb_mirrored_log_groups`

  Gibt an, wie viele identische Kopien von Loggruppen wir für die Datenbank bewahren. Zurzeit sollte dies auf 1 gesetzt werden.

- `innodb_open_files`

  Diese Variable ist nur dann von Belang, wenn Sie Multi-Tablespaces in `InnoDB` benutzen. Sie gibt an, wie viele `.ibd`-Dateien `InnoDB` höchstens gleichzeitig offen halten kann. Der Mindestwert ist 10 und der Standardwert 300.

  Die für `.ibd`-Dateien verwendeten Dateideskriptoren sind ausschließlich für `InnoDB` da. Sie haben nichts mit der Serveroption `--open-files-limit` zu tun und beeinflussen nicht die Arbeit des Tabellen-Caches.

- `innodb_support_xa`

  Die Einstellung `ON` oder 1 (der Standard) schaltet die `InnoDB`-Unterstützung für zweiphasigen Commit in XA-Transaktionen ein. Die Aktivierung von `innodb_support_xa` verursacht eine zusätzliche Schreiboperation auf der Platte zur Vorbereitung der Transaktion. Wenn Sie sich für XA nicht interessieren, können Sie diese Variable mit der Einstellung `OFF` oder 0 deaktivieren, was die Schreibvorgänge auf der Festplatte reduziert und die `InnoDB`-Performance erhöht.

- `innodb_sync_spin_loops`

  Gibt an, wie oft ein Thread auf die Freigabe eines `InnoDB`-Mutex wartet, ehe er suspendiert wird.

- `innodb_table_locks`

  `InnoDB` beachtet `LOCK TABLES`; MySQL kehrt von `LOCK TABLE .. WRITE` erst zurück, wenn alle anderen Threads alle ihre Sperren auf der Tabelle freigegeben haben. Der Standardwert 1 bedeutet, dass `LOCK TABLES` `InnoDB` veranlasst, eine Tabelle intern zu sperren. In Anwendungen mit `AUTOCOMMIT=1` können die internen Tabellensperren von `InnoDB` Deadlocks verursachen. Sie können `innodb_table_locks=0` in der Datei der Serveroptionen einstellen, um dieses Problem zu beheben.

- `innodb_thread_concurrency`

  `InnoDB` versucht, die Anzahl der nebenläufigen Betriebssystem-Threads innerhalb von `InnoDB` kleiner oder gleich dem in dieser Variablen festgelegten Höchstwert zu halten. Wenn Sie Performance-Probleme haben und `SHOW ENGINE INNODB STATUS` zeigt, dass viele Threads auf Semaphoren warten, haben Sie es vielleicht mit Thread-„Überlastung“ zu tun. In diesem Fall setzen sie diese Variable herunter oder herauf. Wenn Ihr Computer über viele Prozessoren und Festplatten verfügt, können Sie diesen Wert heraufsetzen, um Ihre Ressourcen besser auszunutzen. Ein empfehlenswerter Wert ist die Summe der Prozessoren und Festplatten, über die Ihr System verfügt. Beträgt der Wert 500 oder mehr, wird die Nebenläufigkeitsprüfung deaktiviert. Der Standardwert ist 20 und die Nebenläufigkeitsprüfung wird deaktiviert, wenn er auf größer oder gleich 20 eingestellt wird.

- `innodb_thread_sleep_delay`

  Gibt in Mikrosekunden an, wie lange `InnoDB`-Threads schlafen, bevor sie in die `InnoDB`-Schlange eintreten. Der Standardwert ist 10.000. Der Wert 0 schaltet den Schlaf aus.

- `sync_binlog`

  Hat diese Variable einen positiven Wert, synchronisiert der MySQL-Server sein Binärlog nach jedem `sync_binlog`ten Schreibvorgang mittels `fdatasync()` auf die Festplatte. Im Autocommit-Modus entsteht pro Anweisung und ansonsten pro Transaktion ein Eintrag ins Binärlog. Der Standardwert 0 veranlasst keine Festplatten-Synchronisierung. Der Wert 1 ist am sichersten, da bei einem Absturz nur maximal eine Anweisung/Transaktion aus dem Binärlog verloren geht. Er ist aber auch am langsamsten (sofern nicht die Festplatte einen batteriegestützten Cache hat; dies würde die Synchronisierung sehr schnell machen).

### 14.2.5. InnoDB-Tablespace erzeugen

Angenommen, Sie haben MySQL installiert und die notwendigen Konfigurationsparameter für `InnoDB` in die Konfigurationsdatei geschrieben. Bevor Sie MySQL nun starten, müssen Sie überprüfen, ob die Verzeichnisse vorhanden sind, die Sie für `InnoDB`-Daten- und Logdateien angegeben haben, und ob der MySQL-Server Zugriffsrechte für diese Verzeichnisse hat. `InnoDB` legt keine Verzeichnisse, sondern nur Dateien an. Prüfen Sie außerdem, ob Sie genug Platz auf der Festplatte haben, um die Daten- und Logdateien zu speichern.

Wenn Sie Ihren MySQL-Server mit eingeschaltetem `InnoDB` starten, führen Sie mysqld am besten auf der Kommandozeile und nicht mit dem Wrapper mysqld_safe oder als Windows-Dienst aus. Dann können Sie nämlich die Ausgabe von mysqld sehen und erkennen, was passiert. Auf Unix rufen Sie einfach nur mysqld auf und auf Windows verwenden Sie dafür die `--console`-Option.

Wenn Sie MySQL-Server zum ersten Mal starten, nachdem Sie `InnoDB` in Ihrer Optionsdatei konfiguriert haben, erzeugt `InnoDB` Ihre Daten- und Logdateien und gibt in etwa folgendes aus:

    InnoDB: The first specified datafile /home/heikki/data/ibdata1
    did not exist:
    InnoDB: a new database to be created!
    InnoDB: Setting file /home/heikki/data/ibdata1 size to 134217728
    InnoDB: Database physically writes the file full: wait...
    InnoDB: datafile /home/heikki/data/ibdata2 did not exist:
    new to be created
    InnoDB: Setting file /home/heikki/data/ibdata2 size to 262144000
    InnoDB: Database physically writes the file full: wait...
    InnoDB: Log file /home/heikki/data/logs/ib_logfile0 did not exist:
    new to be created
    InnoDB: Setting log file /home/heikki/data/logs/ib_logfile0 size
    to 5242880
    InnoDB: Log file /home/heikki/data/logs/ib_logfile1 did not exist:
    new to be created
    InnoDB: Setting log file /home/heikki/data/logs/ib_logfile1 size
    to 5242880
    InnoDB: Doublewrite buffer not found: creating new
    InnoDB: Doublewrite buffer created
    InnoDB: Creating foreign key constraint system tables
    InnoDB: Foreign key constraint system tables created
    InnoDB: Started
    mysqld: ready for connections

Jetzt hat `InnoDB` seinen Tablespace und seine Logdateien initialisiert. Sie können mit dem MySQL-Server über die üblichen MySQL-Clientprogramme wie mysql Verbindung aufnehmen. Wenn Sie den MySQL-Server mit mysqladmin shutdown herunterfahren, wird folgendes ausgegeben:

    010321 18:33:34  mysqld: Normal shutdown
    010321 18:33:34  mysqld: Shutdown Complete
    InnoDB: Starting shutdown...
    InnoDB: Shutdown completed

Sie können die Datendatei und Logverzeichnisse anschauen und die darin erstellten Dateien erkennen. Das Logverzeichnis enthält außerdem eine kleine Datei namens `ib_arch_log_0000000000`, die infolge der Datenbankerstellung entstanden ist, nachdem `InnoDB` die Logarchivierung abgeschaltet hat. Wenn MySQL erneut gestartet wird, sind die Daten- und Logdateien bereits vorhanden, sodass die Ausgabe viel knapper ausfällt:

    InnoDB: Started
    mysqld: ready for connections

Wenn Sie die `innodb_file_per_table`-Option in `my.cnf` hinzufügen, speichert `InnoDB` jede Tabelle in einer eigenen `.ibd`-Datei in demselben MySQL-Datenbankverzeichnis, in dem auch die `.frm`-Datei angelegt wurde. Siehe Abschnitt 14.2.3.1, „Verwendung von Tabellen-Tablespaces (ein Tablespace pro Tabelle)“.

#### 14.2.5.1. Falls etwas bei der Datenbankerzeugung schiefgeht

Wenn `InnoDB` während einer Dateioperation einen Betriebssystemfehler ausgibt, hat das Problem in der Regel eine der folgenden Ursachen:

- Sie haben das `InnoDB`-Datendatei- oder Logverzeichnis nicht angelegt.

- mysqld verfügt nicht über die notwendigen Berechtigungen, um Dateien in diesen Verzeichnissen anlegen zu können.

- mysqld kann die richtige `my.cnf`- oder `my.ini`-Optionsdatei nicht lesen und weiß deshalb nicht, welche Optionen Sie angegeben haben.

- Die Festplatte ist voll oder das Plattenkontingent überschritten.

- Da Sie ein Unterverzeichnis angelegt haben, das genauso heißt wie die Datendatei, kann der Name nicht als Dateiname verwendet werden..

- Ein Syntaxfehler hat sich in den Wert von `innodb_data_home_dir` oder `innodb_data_file_path` eingeschlichen.

Wenn `InnoDB` beim Versuch scheitert, seinen Tablespace oder seine Logdateien zu initialisieren, sollten Sie alle von `InnoDB` erzeugten Dateien löschen. Dazu gehören alle `ibdata`-Dateien und alle `ib_logfile`-Dateien. Falls Sie bereits `InnoDB`-Tabellen angelegt haben, müssen Sie auch die `.frm`-Dateien (und, wenn Sie Multi-Tablespaces benutzen, die `.ibd`-Dateien) aus den MySQL-Datenbankverzeichnissen löschen. Danach können Sie erneut versuchen, die `InnoDB`-Datenbank anzulegen. Am besten starten Sie den MySQL-Server von einer Kommandozeile, um erkennen zu können, was geschieht.

### 14.2.6. `InnoDB`-Tabellen erzeugen

Um eine `InnoDB`-Tabelle zu erzeugen, müssen Sie die `ENGINE = InnoDB`-Option in der `CREATE TABLE`-Anweisung angeben:

    CREATE TABLE customers (a INT, b CHAR (20), INDEX (a)) ENGINE=InnoDB;

Der ältere Begriff `TYPE` wird als Synonym für `ENGINE` aus Gründen der Abwärtskompatibilität zwar weiter unterstützt, aber `ENGINE` ist der bessere Begriff, während `TYPE` veraltet ist.

Die Anweisung erzeugt eine Tabelle und einen Index auf der Spalte `a` im `InnoDB`-Tablespace, der aus den in `my.cnf` angegebenen Datendateien besteht. Außerdem legt MySQL die Datei `customers.frm` im Verzeichnis `test` an, das ein Unterverzeichnis des MySQL-Datenbankverzeichnisses ist. Intern fügt `InnoDB` in das Data Dictionary einen Eintrag für diese Tabelle ein, der den Datenbanknamen enthält. Wenn beispielsweise `test` die Datenbank ist, in der die `customers`-Tabelle angelegt wurde, heißt der Eintrag `'test/customers'`. Das bedeutet, dass Sie in einer anderen Datenbank eine gleichnamige `customers`-Tabelle anlegen können, ohne dass es in `InnoDB` zu Namenskonflikten kommt.

Wieviel Platz im `InnoDB`-Tablespace noch frei ist, sagt Ihnen eine `SHOW TABLE STATUS`-Anweisung für irgendeine `InnoDB`-Tabelle. Der freie Platz im Tablespace wird im Abschnitt `Comment` der Ausgabe von `SHOW TABLE STATUS` angezeigt, beispielsweise:

    SHOW TABLE STATUS FROM test LIKE 'customers'

Beachten Sie, dass die Statistikdaten, die `SHOW` für `InnoDB`-Tabellen anzeigt, nur Näherungswerte sind. Sie werden für die SQL-Optimierung eingesetzt. Doch die reservierten Größen für Tabellen und Indizes in Bytes werden präzise angegeben.

#### 14.2.6.1. Benutzung von Transaktionen in `InnoDB` mit verschiedenen APIs

Standardmäßig startet jeder Client, der sich mit dem MySQL-Server verbindet, mit eingeschaltetem Autocommit-Modus. Das bedeutet, dass jede SQL-Anweisung direkt bei der Ausführung automatisch in der Datenbank festgeschrieben wird. Um Transaktionen zu verwenden, die aus mehreren Anweisungen bestehen, schalten Sie Autocommit mit der SQL-Anweisung `SET AUTOCOMMIT = 0` aus und verwenden `COMMIT` und `ROLLBACK`, um Ihre Transaktionen zu committen oder zurückzurollen. Wenn Sie Autocommit eingeschaltet lassen möchten, können Sie Ihre Transaktionen zwischen `START TRANSACTION` und entweder `COMMIT` oder `ROLLBACK` einschließen. Das folgende Beispiel zeigt zwei Transaktionen: Die erste wird committet und die zweite zurückgerollt.

    shell> mysql test

    mysql> CREATE TABLE CUSTOMER (A INT, B CHAR (20), INDEX (A))
        -> ENGINE=InnoDB;
    Query OK, 0 rows affected (0.00 sec)
    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)
    mysql> INSERT INTO CUSTOMER VALUES (10, 'Heikki');
    Query OK, 1 row affected (0.00 sec)
    mysql> COMMIT;
    Query OK, 0 rows affected (0.00 sec)
    mysql> SET AUTOCOMMIT=0;
    Query OK, 0 rows affected (0.00 sec)
    mysql> INSERT INTO CUSTOMER VALUES (15, 'John');
    Query OK, 1 row affected (0.00 sec)
    mysql> ROLLBACK;
    Query OK, 0 rows affected (0.00 sec)
    mysql> SELECT * FROM CUSTOMER;
    +------+--------+
    | A    | B      |
    +------+--------+
    |   10 | Heikki |
    +------+--------+
    1 row in set (0.00 sec)
    mysql>

In APIs wie PHP, Perl DBI, JDBC, ODBC oder der Standardschnittstelle für C-Aufrufe von MySQL können Sie Anweisungen zur Transaktionssteuerung wie etwa `COMMIT` wie alle anderen SQL-Anweisungen, beispielsweise `SELECT` oder `INSERT`, als Strings an den MySQL-Server senden. Manche APIs bieten auch eigene, spezielle Transaktionsfunktionen oder -methoden für Commit und Rollback.

#### 14.2.6.2. MyISAM-Tabellen in InnoDB-Tabellen umwandeln

Wichtig: Bitte konvertieren Sie keine Systemtabellen von MySQL in der `mysql`-Datenbank (wie etwa `user` oder `host`) in den Typ `InnoDB`. Diese Operation wird nicht unterstützt. Die Systemtabellen müssen immer den Typ `MyISAM` haben.

Wenn Sie alle Tabellen (außer den Systemtabellen) als `InnoDB`-Tabellen anlegen möchten, fügen Sie einfach die Zeile `default-storage-engine=innodb` in den `[mysqld]`-Abschnitt Ihrer Serveroptionsdatei ein.

`InnoDB` kennt im Gegensatz zu `MyISAM` keine spezielle Optimierung für eine separate Indexerstellung. Daher lohnt es sich nicht, zuerst die Tabellen zu exportieren und importieren und danach die Indizes anzulegen. Der schnellste Weg, eine Tabelle in `InnoDB` zu ändern, besteht darin, die Einfügeoperationen direkt auf der `InnoDB`-Tabelle vorzunehmen. Verwenden Sie also `ALTER TABLE … ENGINE=INNODB` oder erzeugen Sie eine leere `InnoDB`-Tabelle mit denselben Definitionen und fügen Sie darin die Zeilen mit `INSERT INTO … SELECT * FROM …` ein.

Wenn Sie `UNIQUE`-Constraints auf Sekundärschlüsseln haben, können Sie einen Tabellenimport beschleunigen, indem Sie die Eindeutigkeitsprüfungen während der Importoperation vorübergehend abschalten:

    SET UNIQUE_CHECKS=0;
    ... import operation ...
    SET UNIQUE_CHECKS=1;

Bei großen Tabellen spart das eine Menge Festplattenzugriffe, da `InnoDB` dann seinen Insert-Puffer nutzen kann, um die Sekundärindexeinträge in einer Batch-Operation zu schreiben.

Um eine bessere Kontrolle über den Einfügungsprozess zu haben, ist es manchmal besser, Daten einer großen Tabelle in Stücken einzufügen:

    INSERT INTO newtable SELECT * FROM oldtable
       WHERE yourkey > something AND yourkey <= somethingelse;

Wenn alle Datensätze eingefügt wurden, können Sie die Tabellen umbenennen.

Während der Konvertierung großer Tabellen sollten Sie den `InnoDB`-Bufferpool vergrößern, damit weniger Plattenzugriffe erforderlich sind. Reservieren Sie jedoch nicht mehr als 80% des physikalischen Arbeitsspeichers. Sie können auch die `InnoDB`-Logdateien vergrößern.

Achten Sie darauf, dass der Tablespace nicht vollläuft: `InnoDB`-Tabellen brauchen auf der Festplatte viel mehr Speicherplatz als `MyISAM`-Tabellen. Wenn einer `ALTER TABLE`-Operation der Platz ausgeht, startet sie ein Rollback, und das kann Stunden dauern, wenn es an die Festplatte gebunden ist. Bei Inserts nutzt `InnoDB` den Insert-Puffer, um Sekundärindexeinträge in Batches mit dem Index zusammenzuführen. Das spart viele Schreiboperationen auf der Festplatte. Da für den Rollback kein solcher Mechanismus existiert, kann dieser 30-mal länger als die Einfügeoperationen dauern.

Wenn ein Rollback-Prozess außer Kontrolle gerät und Sie keine wertvollen Daten in Ihrer Datenbank haben, ist es manchmal ratsam, den Datenbankprozess anzuhalten, anstatt Millionen von Lese/Schreiboperationen auf der Festplatte abzuwarten. Den gesamten Vorgang können Sie unter Abschnitt 14.2.8.1, „Erzwingen einer `InnoDB`-Wiederherstellung (Recovery)“ nachlesen.

#### 14.2.6.3. Wie eine Auto-Increment-Spalte in InnoDB funktioniert

Wenn Sie eine `AUTO_INCREMENT`-Spalte für eine `InnoDB`-Tabelle definieren, enthält der Tabellen-Handle im `InnoDB`-Data Dictionary einen speziellen Zähler namens Auto-Increment-Zähler, der zur Zuweisung neuer Werte zu dieser Spalte benutzt wird. Dieser Zähler wird nur im Arbeitsspeicher und nicht auf der Festplatte gespeichert.

`InnoDB` initialisiert den Auto-Increment-Zähler für eine Tabelle `T`, die eine `AUTO_INCREMENT`-Spalte namens `ai_col` enthält, wie folgt: Nach dem Serverstart führt `InnoDB` für die erste Einfügung in die Tabelle `T` das Äquivalent folgender Anweisung aus:

    SELECT MAX(ai_col) FROM T FOR UPDATE;

`InnoDB` inkrementiert den von der Anweisung abgerufenen Wert um eins und weist ihn der Spalte und dem Auto-Increment-Zähler für die Tabelle zu. Ist die Tabelle leer, verwendet `InnoDB` den Wert `1`. Wenn ein Benutzer eine `SHOW TABLE STATUS`-Anweisung gibt, die Ausgabe für die Tabelle `T` anzeigt, und der Auto-Increment-Zähler noch nicht initialisiert wurde, nimmt `InnoDB` die Initialisierung vor, inkrementiert aber nicht den Wert und speichert ihn nicht für spätere Einfügeoperationen. Beachten Sie, dass für diese Initialisierung eine normale Schreiboperation mit exklusiver Sperre auf der Tabelle ausgeführt wird und die Sperre bis zum Ende der Transaktion aufrecht erhalten bleibt.

Genauso geht `InnoDB` vor, wenn ein Auto-Increment-Zähler für eine neu erzeugte Tabelle initialisiert wird.

Wenn ein Auto-Increment-Zähler initialisiert wurde und ein Benutzer nicht ausdrücklich einen Wert für eine `AUTO_INCREMENT`-Spalte angibt, inkrementiert `InnoDB` den Zähler um eins und weist der Spalte den neuen Wert zu. Wenn der Benutzer eine Zeile einfügt, die den Spaltenwert explizit angibt, und dieser Wert größer als der aktuelle Zähler ist, wird der Zähler auf den angegebenen Spaltenwert gesetzt.

Eventuell treten in der Abfolge der Werte einer `AUTO_INCREMENT`-Spalte Lücken auf, wenn Sie Transaktionen zurückrollen, die Nummern mithilfe des Zählers zugewiesen haben.

Wenn der Benutzer in einem `INSERT` den Wert `NULL` oder `0` für die `AUTO_INCREMENT`-Spalte angibt, behandelt `InnoDB` die betreffende Zeile, als wäre überhaupt kein Wert vorhanden, und generiert einen neuen Wert für sie.

Das Verhalten des Auto-Increment-Mechanismus ist nicht definiert, wenn ein Benutzer der Spalte einen negativen Wert zuweist, oder wenn der Wert die größte Ganzzahl übersteigt, die in dem angegebenen Integertyp gespeichert werden kann.

`InnoDB` benutzt für den Zugriff auf den Auto-Increment-Zähler eine spezielle `AUTO-INC`-Tabellensperre, die bis zum Ende der laufenden SQL-Anweisung (nicht der Transaktion) gehalten wird. Die spezielle Strategie zum Aufheben der Sperre wurde eingefügt, um die Nebenläufigkeit von Einfügeoperationen in Tabellen mit `AUTO_INCREMENT`-Spalten zu verbessern. Es können nicht zwei Transaktionen zugleich auf derselben Tabelle eine `AUTO-INC`-Sperre halten.

`InnoDB` nutzt den speicherresidenten Auto-Increment-Zähler so lange, wie der Server läuft. Wird der Server angehalten und neu gestartet, initialisiert `InnoDB` den Zähler bei der ersten `INSERT`-Operation auf der Tabelle neu, wie oben bereits beschrieben.

`InnoDB` unterstützt die Tabellenoption `AUTO_INCREMENT = `N`` in `CREATE TABLE` und `ALTER TABLE`-Anweisungen, um den Anfangswert des Zählers einzustellen oder seinen laufenden Wert zu ändern. Die Wirkung dieser Option wird durch einen Neustart des Servers aufgehoben, und zwar aus Gründen, die bereits weiter oben dargestellt wurden.

#### 14.2.6.4. Fremdschlüssel-Beschränkungen

`InnoDB` unterstützt auch Fremdschlüssel-Constraints, die in `InnoDB` mit folgender Syntax definiert werden:

    [CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, ...)
        REFERENCES tbl_name (index_col_name, ...)
        [ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
        [ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION}]

Für Fremdschlüsseldefinitionen gelten folgende Bedingungen:

- Beide Tabellen müssen `InnoDB`-Tabellen sein und dürfen keine `TEMPORARY`-Tabellen sein.

- In der referenzierenden Tabelle muss ein Index bestehen, in dem die Fremdschlüsselspalten als erste Spalten in derselben Reihenfolge aufgeführt sind. Ein solcher Index wird automatisch auf der referenzierenden Tabelle angelegt, wenn er noch nicht existiert.

- In der referenzierten Tabelle muss ein Index bestehen, in dem die referenzierten Spalten als erste Spalten in derselben Reihenfolge aufgeführt sind.

- Index-Präfixe auf Fremdschlüsselspalten werden nicht unterstützt. Dies hat unter anderem zur Folge, dass `BLOB`- und `TEXT`-Spalten nicht in einen Fremdschlüssel eingebunden werden können, da Indizes auf diesen Spalten immer ein Längenpräfix haben müssen.

- Wenn die `CONSTRAINT `symbol``-Klausel verwendet wird, muss der `symbol`-Wert in der ganzen Datenbank einzigartig sein. Ist die Klausel nicht angegeben, erstellt `InnoDB` den Namen automatisch.

`InnoDB` weist jede `INSERT`- oder `UPDATE`-Operation zurück, die versucht, einen Fremdschlüsselwert in einer Kindtabelle anzulegen, wenn kein passender Schlüsselwert in der Elterntabelle vorhanden ist. Was `InnoDB` mit einer `INSERT`- oder `UPDATE`-Operation anfängt, die versucht, in der Elterntabelle einen Schlüsselwert zu ändern oder zu löschen, zu dem in der Kindtabelle passende Zeilen vorhanden sind, hängt davon ab, welche Referenzaktion in den Teilklauseln `ON UPDATE` und `ON DELETE` der `FOREIGN KEY`-Klausel angegeben ist. Wenn der Benutzer versucht, in der Elterntabelle eine Zeile zu ändern oder zu löschen, zu der in der Kindtabelle eine oder mehr passende Zeilen vorhanden sind, bietet `InnoDB` fünf mögliche Optionen:

- `CASCADE`: Bei Löschung/Änderung einer Zeile der Elterntabelle werden automatisch die zugehörigen Zeilen der Kindtabelle auch gelöscht oder geändert. Es gibt sowohl `ON DELETE CASCADE` als auch `ON UPDATE CASCADE`. Zwischen zwei Tabellen sollten Sie bitte nicht mehrere `ON UPDATE CASCADE`-Klauseln definieren, die auf derselben Spalte der Eltern- oder Kindtabelle arbeiten.

- `SET NULL`: Bei Löschung/Änderung einer Zeile der Elterntabelle werden automatisch die zugehörigen Fremdschlüsselspalten der Kindtabelle auf `NULL` gesetzt. Das gilt nur, wenn die Fremdschlüsselspalten nicht als `NOT NULL` definiert sind. Sowohl `ON DELETE SET NULL` als auch `ON UPDATE SET NULL` wird unterstützt.

- `NO ACTION`: Im Standard-SQL bedeutet `NO ACTION` tatsächlich keine Aktion in dem Sinne, dass jeder Versuch, einen Primärschlüssel zu löschen oder zu ändern, unterbunden wird, wenn es dazu einen Fremschlüsselwert in der referenzierten Tabelle gibt. `InnoDB` weist dann die Lösch- oder Änderungsoperation auf der Elterntabelle zurück.

- `RESTRICT` weist die Lösch- oder Änderungsoperation auf der Elterntabelle zurück. `NO ACTION` und `RESTRICT` sind dasselbe wie ein Auslassen der `ON DELETE`- oder `ON UPDATE`-Klausel. (Manche Datenbanksysteme kennen verzögerte Prüfungen (deferred checks), zu denen auch `NO ACTION` gehört. Da in MySQL Fremdschlüssel-Constraints jedoch sofort geprüft werden, sind `NO ACTION` und `RESTRICT` hier dasselbe.)

- `SET DEFAULT`: Diese Aktion wird zwar vom Parser anerkannt, aber `InnoDB` weist Tabellendefinitionen mit `ON DELETE SET DEFAULT`- oder `ON UPDATE SET DEFAULT`-Klauseln zurück.

Beachten Sie, dass `InnoDB` Fremdschlüsselreferenzen in einer Tabelle unterstützt. In solchen Fällen sind „Datensätze der Kindtabelle“ in Wirklichkeit abhängige Datensätze in derselben Tabelle.

Da `InnoDB` Indizes auf Fremdschlüsseln und referenzierten Schlüsseln verlangt, können Fremdschlüsselprüfungen schnell durchgeführt werden und erfordern keinen Tabellen-Scan. Der Index auf den Fremdschlüsseln wird automatisch angelegt. Das war in manchen älteren Versionen anders, wo Indizes explizit angelegt werden mussten, da sonst keine Fremdschlüssel-Constraints angelegt werden konnten.

Zusammengehörige Spalten im Fremdschlüssel und referenzierten Schlüssel müssen in `InnoDB` ähnliche Datentypen haben, damit sie sich ohne Typkonvertierung vergleichen lassen. Die Größe und das Vorzeichen von Integer-Typen müssen gleich sein. Die Länge von String-Typen muss nicht unbedingt identisch sein. Wenn Sie `SET NULL` verlangen, dürfen die Spalten der Kindtabelle nicht als `NOT NULL` deklariert sein.

Wenn MySQL die Fehlernummer 1005 aus einer `CREATE TABLE`-Anweisung meldet und die Fehlermeldung sich auf Fehlernummer 150 bezieht, schlug die Tabellenerstellung fehl, weil ein Fremdschlüssel-Constraint nicht wohlgeformt war. Wenn ein `ALTER TABLE` scheitert und auf Fehlernummer 150 verweist, bedeutet dies, dass eine Fremdschlüsseldefinition für die geänderte Tabelle nicht korrekt war. Die Anweisung `SHOW ENGINE INNODB STATUS` zeigt eine detaillierte Erklärung des letzten `InnoDB`-Fremdschlüsselfehlers im Server an.

Hinweis: `InnoDB` prüft Fremdschlüssel-Constraints nicht auf Fremdschlüsseln oder referenzierten Schlüsseln, die eine `NULL`-Spalte enthalten.

Hinweis: Trigger werden von kaskadierenden Fremdschlüsselaktionen derzeit nicht aktiviert.

Abweichung von SQL-Standards: Wenn mehrere Zeilen in der Elterntabelle denselben Referenzschlüsselwert haben verhält sich `InnoDB` bei Fremdschlüsselprüfungen so, als würden die anderen Zeilen der Elterntabelle, also die mit demselben Schlüsselwert, gar nicht vorhanden. Wenn Sie beispielsweise einen `RESTRICT`-Typ-Constraint definiert haben und es eine Kindzeile mit mehreren Elternzeilen gibt, verbietet `InnoDB` das Löschen irgendeiner dieser Elternzeilen.

`InnoDB` führt kaskadierende Operationen mit einem Depth-First-Algorithmus durch, beruhend auf Einträgen in den Indizes, die zu den Fremdschlüssel-Constraints gehören.

Abweichung von SQL-Standards: Ein `FOREIGN KEY`-Constraint, der einen Nicht-`UNIQUE`-Schlüssel referenziert, ist nicht Standard-SQL, sondern eine `InnoDB`-Erweiterung dieses Standards.

Abweichung von SQL-Standards: Wenn `ON UPDATE CASCADE` oder `ON UPDATE SET NULL` rekursiv dieselbe Tabelle ändert, die zuvor während der kaskadierenden Änderung auch bereits aktualisiert wurde, verhält es sich wie `RESTRICT`. Sie können also keine rückbezüglichen `ON UPDATE CASCADE`- oder `ON UPDATE SET NULL`-Operationen ausführen. Dadurch sollen Endlosschleifen wegen kaskadierender Updates verhindert werden. Andererseits ist ein rückbezügliches `ON DELETE SET NULL` jedoch möglich, nämlich als rückbezügliches `ON DELETE CASCADE`. Die maximale Schachtelungstiefe von kaskadierenden Operationen beträgt 15 Ebenen.

Abweichung von SQL-Standards: Wie immer in MySQL prüft `InnoDB` für jede SQL-Anweisung, die viele Zeilen einfügt, löscht oder ändert, die `UNIQUE`- und `FOREIGN KEY`-Constraints zeilenweise. Nach dem SQL-Standard sollte eigentlich eine verzögerte Prüfung vorgenommen werden, bei der Constraints erst nach der Verarbeitung der gesamten SQL-Anweisung geprüft werden. Bis die verzögerte Constraint-Prüfung auch in `InnoDB` implementiert ist, werden einige Dinge nicht möglich sein, etwa das Löschen eines Datensatzes, der sich über einen Fremdschlüssel auf sich selbst bezieht.

In dem folgenden einfachen Beispiel sind `parent`- und `child`-Tabellen über einen Einzelspalten-Fremdschlüssel verbunden:

    CREATE TABLE parent (id INT NOT NULL,
                        PRIMARY KEY (id)
    ) ENGINE=INNODB;
    CREATE TABLE child (id INT, parent_id INT,
                       INDEX par_ind (parent_id),
                       FOREIGN KEY (parent_id) REFERENCES parent(id)
                         ON DELETE CASCADE
    ) ENGINE=INNODB;

Das folgende, komplexere Beispiel zeigt eine `product_order`-Tabelle, die Fremdschlüssel für zwei andere Tabellen besitzt. Ein Fremdschlüssel referenziert einen Zwei-Spalten-Index in der `product`-Tabelle und der andere einen Ein-Spalten-Index in der `customer`-Tabelle:

    CREATE TABLE product (category INT NOT NULL, id INT NOT NULL,
                          price DECIMAL,
                          PRIMARY KEY(category, id)) ENGINE=INNODB;
    CREATE TABLE customer (id INT NOT NULL,
                          PRIMARY KEY (id)) ENGINE=INNODB;
    CREATE TABLE product_order (no INT NOT NULL AUTO_INCREMENT,
                          product_category INT NOT NULL,
                          product_id INT NOT NULL,
                          customer_id INT NOT NULL,
                          PRIMARY KEY(no),
                          INDEX (product_category, product_id),
                          FOREIGN KEY (product_category, product_id)
                            REFERENCES product(category, id)
                            ON UPDATE CASCADE ON DELETE RESTRICT,
                          INDEX (customer_id),
                          FOREIGN KEY (customer_id)
                            REFERENCES customer(id)) ENGINE=INNODB;

`InnoDB` ermöglicht es, mit `ALTER TABLE` einer Tabelle einen Fremdschlüssel-Constraint hinzuzufügen:

    ALTER TABLE tbl_name
        ADD [CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, ...)
        REFERENCES tbl_name (index_col_name, ...)
        [ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION}]
        [ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION}]

Bitte legen Sie immer zuerst die erforderlichen Indizes an. Sie können einer Tabelle mit `ALTER TABLE` auch einen rückbezüglichen Fremdschlüssel-Constraint hinzufügen.

`InnoDB` ermöglicht es überdies, mit `ALTER TABLE` Fremdschlüssel zu löschen:

    ALTER TABLE tbl_name DROP FOREIGN KEY fk_symbol;

Wenn die `FOREIGN KEY`-Klausel bei der Erzeugung des Fremdschlüssels einen `CONSTRAINT`-Namen enthielt, können Sie beim Löschen dieses Fremdschlüssels denselben Namen nennen. Ansonsten wird beim Anlegen des Fremdschlüssels intern ein `fk_symbol`-Wert generiert. Um zum Löschen eines Fremdschlüssels diesen Symbolwert zu ermitteln, dient die `SHOW CREATE TABLE`-Anweisung. Ein Beispiel:

    mysql> SHOW CREATE TABLE ibtest11c\G
    *************************** 1. row ***************************
           Table: ibtest11c
    Create Table: CREATE TABLE `ibtest11c` (
      `A` int(11) NOT NULL auto_increment,
      `D` int(11) NOT NULL default '0',
      `B` varchar(200) NOT NULL default '',
      `C` varchar(175) default NULL,
      PRIMARY KEY  (`A`,`D`,`B`),
      KEY `B` (`B`,`C`),
      KEY `C` (`C`),
      CONSTRAINT `0_38775` FOREIGN KEY (`A`, `D`)
    REFERENCES `ibtest11a` (`A`, `D`)
    ON DELETE CASCADE ON UPDATE CASCADE,
      CONSTRAINT `0_38776` FOREIGN KEY (`B`, `C`)
    REFERENCES `ibtest11a` (`B`, `C`)
    ON DELETE CASCADE ON UPDATE CASCADE
    ) ENGINE=INNODB CHARSET=latin1
    1 row in set (0.01 sec)

    mysql> ALTER TABLE ibtest11c DROP FOREIGN KEY `0_38775`;

Sie können einen Fremdschlüssel nicht in separaten Klauseln derselben `ALTER TABLE`-Anweisung anlegen und löschen. Hierzu sind zwei getrennte Anweisungen erforderlich.

Der Parser von `InnoDB` gestattet es, für Tabellen- und Spaltenbezeichner in einer `FOREIGN KEY … REFERENCES …`-Klausel Backticks als Anführungszeichen zu verwenden. (Alternativ können doppelte Anführungszeichen gesetzt werden, wenn der SQL-Modus `ANSI_QUOTES` SQL eingeschaltet ist.) Außerdem berücksichtigt der `InnoDB`-Parser die Einstellung der Systemvariablen `lower_case_table_names`.

`InnoDB` gibt die Fremdschlüsseldefinitionen einer Tabelle im Rahmen der `SHOW CREATE TABLE`-Anweisung zurück:

    SHOW CREATE TABLE tbl_name;

mysqldump erstellt ebenfalls die korrekten Definitionen der Tabellen in der Dump-Datei und vergisst dabei auch die Fremdschlüssel nicht.

Die Fremdschlüssel-Constraints einer Tabelle können wie folgt angezeigt werden:

    SHOW TABLE STATUS FROM db_name LIKE 'tbl_name';

Die Fremdschlüssel-Constraints stehen in der `Comment`-Spalte der Ausgabe.

Bei den Fremdschlüsselprüfungen errichtet `InnoDB` Shared-Sperren auf Zeilenebene auf den relevanten Datensätzen der Kind- oder Elterntabellen. `InnoDB` prüft die Fremdschlüssel-Constraints sofort und nicht erst beim Committen der Transaktion.

Damit Dump-Dateien, die Fremdschlüsselbeziehungen haben, leichter geladen werden können bindet mysqldump automatisch in die Dump-Ausgabe eine Anweisung ein, die `FOREIGN_KEY_CHECKS` auf 0 setzt. So entstehen keine Probleme mit Tabellen, die beim Neuladen der Dump-Dateien eigentlich in einer bestimmten Reihenfolge geladen werden müssen. Diese Variable kann auch manuell gesetzt werden:

    mysql> SET FOREIGN_KEY_CHECKS = 0;
    mysql> SOURCE dump_file_name;
    mysql> SET FOREIGN_KEY_CHECKS = 1;

So können Sie die Tabellen in beliebiger Reihenfolge importieren, wenn die Dump-Datei Tabellen enthält, die eigentlich gemäß ihrer Fremdschlüssel nicht richtig geordnet sind. Außerdem wird der Import beschleunigt. Auch in Fällen, wo Sie Fremdschlüssel-Constraints in `LOAD DATA`- und `ALTER TABLE`-Operationen ignorieren möchten, kann es sinnvoll sein, `FOREIGN_KEY_CHECKS` auf 0 zu setzen.

Mit `InnoDB` können Sie eine durch einen `FOREIGN KEY`-Constraint referenzierte Tabelle nur löschen, wenn `SET FOREIGN_KEY_CHECKS=0` eingestellt wurde. Wenn Sie eine Tabelle löschen, werden die in ihrer Erzeugungsanweisung definierten Constraints mit gelöscht.

Wenn Sie eine gelöschte Tabelle wiederherstellen, muss ihre Definition zu den auf sie bezogenen Fremdschlüssel-Constraints passen. Sie muss die richtigen Spaltennamen und -typen sowie Indizes auf den referenzierten Schlüsseln haben. Ist dies nicht der Fall, gibt MySQL die Fehlernummer 1005 zurück und nennt die Fehlernummer 150 in der Fehlermeldung.

#### 14.2.6.5. `InnoDB` und MySQL-Replikation

In MySQL funktioniert die Replikation mit `InnoDB`-Tabellen wie mit `MyISAM`-Tabellen. Es sind auch Replikationsumgebungen möglich, bei denen der Slave eine andere Speicher-Engine als der Master verwendet. So können Sie beispielsweise Änderungen, die auf dem Master an einer `InnoDB`-Tabelle vorgenommen werden, in einer `MyISAM`-Tabelle auf dem Slave replizieren.

Um einen neuen Slave für den Master einzurichten, müssen Sie den `InnoDB`-Tablespace und die Logdateien sowie die `.frm`-Dateien der `InnoDB`-Tabellen kopieren und diese Kopien auf den Slave übertragen. Wenn die Variable `innodb_file_per_table` eingeschaltet ist, müssen auch die `.ibd`-Dateien kopiert werden. Wie dies genau geht, erfahren Sie unter Abschnitt 14.2.8, „Sichern und Wiederherstellen einer `InnoDB`-Datenbank“.

Wenn Sie den Master oder einen vorhandenen Slave herunterfahren, können Sie ein kaltes Backup des `InnoDB`-Tablespace und der Logdateien erstellen und damit einen Slave einrichten. Um einen neuen Slave aufzusetzen, ohne gleich den Server herunterzufahren, können Sie das kommerzielle (also kostenpflichtige) Tool `InnoDB Hot Backup` verwenden.

Die `InnoDB`-Replikation wird mit der `LOAD TABLE FROM MASTER`-Anweisung eingerichtet, die allerdings nur mit `MyISAM`-Tabellen funktioniert. Dafür gibt es jedoch zwei Workarounds:

- Sie können einen Tabellen-Dump auf dem Master vornehmen und die Dump-Datei in den Slave importieren.

- Sie können auf dem Master `ALTER TABLE `tbl_name` ENGINE=MyISAM` einstellen, bevor Sie die Replikation mit `LOAD TABLE `tbl_name` FROM MASTER` starten, und danach die Master-Tabelle mit `ALTER TABLE` wieder in `InnoDB` konvertieren. Tun Sie das aber nicht mit Tabellen, die Fremdschlüsseldefinitionen haben, da diese Definitionen sonst verlorengehen.

Transaktionen, die auf dem Master scheitern, haben auf die Replikation keinerlei Auswirkungen. Die Replikation beruht in MySQL auf dem Binärlog, wo die SQL-Anweisungen, die Daten modifizieren, festgehalten werden. Eine Transaktion, die scheitert (beispielsweise wegen Verstoß gegen einen Fremdschlüssel-Constraint oder weil sie zurückgerollt wird), gelangt gar nicht ins Binärlog und ergo auch nicht auf die Slaves. Siehe Abschnitt 13.4.1, „`BEGIN/COMMIT/ROLLBACK`“.

### 14.2.7. Hinzufügen und Entfernen von InnoDB-Daten- und -Logdateien

Dieser Abschnitt beschreibt, was Sie tun können, wenn Ihr `InnoDB`-Tablespace nicht mehr genug Platz hat oder Sie die Größe der Logdateien ändern möchten.

Am einfachsten lässt sich der `InnoDB`-Tablespace vergrößern, wenn er von Anfang an als selbsterweiternd konfiguriert wird. Hierzu geben Sie das `autoextend`-Attribut für die letzte Datendatei in der Tablespace-Definition an. Dann lässt `InnoDB` diese Datei automatisch in Inkrementen von 8MB anwachsen, wenn ihr der Platz ausgeht. Die Inkrement-Größe kann mit der Systemvariablen `innodb_autoextend_increment` in MB eingestellt werden.

Sie können Ihren Tablespace jedoch auch vergrößern, indem Sie eine weitere Datendatei hinzufügen. Hierzu müssen sie den MySQL-Server herunterfahren, der Tablespace-Konfiguration am Ende von `innodb_data_file_path` eine neue Datendatei hinzufügen, und den Server wieder neu starten.

Wenn die letzte Datendatei mit dem Schlüsselwort `autoextend` definiert worden ist, müssen Sie bei der Rekonfiguration des Tablespaces berücksichtigen, auf welches Maß diese letzte Datendatei angewachsen ist. Ermitteln Sie die Größe der Datendatei, runden Sie sie auf das nächste Vielfache von 1024 × 1024 bytes (= 1MB) ab und geben Sie diesen gerundeten Wert explizit in `innodb_data_file_path` an. Dann können Sie eine weitere Datendatei hinzufügen. Denken Sie daran, dass Sie nur die letzte Datendatei in `innodb_data_file_path` als selbsterweiternd definieren können.

Nehmen wir beispielsweise an, der Tablespace hat nur eine selbsterweiternde Datendatei namens `ibdata1`:

    innodb_data_home_dir =
    innodb_data_file_path = /ibdata/ibdata1:10M:autoextend

Diese Datei sei nun mit der Zeit auf 988MB angewachsen. Hier sehen Sie die Konfigurationszeile, nachdem die ursprüngliche Datendatei als nicht mehr selbsterweitend definiert und eine neue, selbsterweiternde Datendatei hinzugefügt wurde:

    innodb_data_home_dir =
    innodb_data_file_path = /ibdata/ibdata1:988M;/disk2/ibdata2:50M:autoextend

Wenn Sie der Tablespace-Konfiguration eine neue Datei hinzufügen, achten Sie darauf, dass diese noch nicht existiert. `InnoDB` wird die Datei erzeugen und initialisieren, wenn der Server neu gestartet wird.

Gegenwärtig ist es nicht möglich, eine Datendatei aus dem Tablespace zu löschen. Wenn Sie Ihren Tablespace verkleinern möchten, gehen Sie folgendermaßen vor:

1. Mit mysqldump erstellen Sie einen Dump aller `InnoDB`-Tabellen.

2. Halten Sie den Server an.

3. Löschen Sie alle vorhandenen Tablespace-Dateien.

4. Konfigurieren Sie einen neuen Tablespace.

5. Starten Sie den Server neu.

6. Importieren Sie die Dump-Dateien.

Wenn Sie die Anzahl oder Größe Ihrer `InnoDB`-Logdateien ändern möchten, halten Sie den MySQL-Server an und achten darauf, dass er ohne Fehler herunterfährt (um zu gewährleisten, dass in den Logs keine Daten unvollendeter Transaktionen hängen bleiben). Dann kopieren Sie die Logdateien an einen sicheren Ort, nur für den Fall, dass beim Herunterfahren etwas schiefgeht und Sie den Tablespace wiederherstellen müssen. Löschen Sie die alten Logdateien aus dem Logdateiverzeichnis, ändern Sie die Logdateikonfiguration in `my.cnf` und starten Sie den MySQL-Server neu. mysqld erkennt beim Hochfahren, dass keine Logdateien vorhanden sind, und teilt Ihnen mit, dass neue angelegt werden.

### 14.2.8. Sichern und Wiederherstellen einer `InnoDB`-Datenbank

Der Schlüssel zu einem sicheren Datenbankmanagement sind regelmäßige Backups.

InnoDB Hot Backup ist ein Online-Backup-Tool mit dem Sie eine `InnoDB`-Datenbank bei laufendem Betrieb sichern können. InnoDB Hot Backup verlangt nicht, dass Sie die Datenbank herunterfahren, setzt keine Sperren und stört nicht die normale Datenbankverarbeitung. InnoDB Hot Backup ist ein kostenpflichtiges (kommerzielles) Add-on, das pro Jahr und pro MySQL-Server-Computer €390 kostet. Genauere Informationen und Screenshots finden Sie unter InnoDB Hot Backup home page.

Wenn Sie in der Lage sind, Ihren MySQL-Server herunterzufahren, können Sie auch ein Binär-Backup aller Dateien erstellen, die `InnoDB` zur Verwaltung seiner Tabellen benötigt. Gehen Sie folgendermaßen vor:

1. Fahren Sie den MySQL-Server herunter und achten Sie darauf, dass dabei keine Fehler auftreten.

2. Kopieren Sie alle Datendateien (`ibdata`-Dateien und `.ibd`-Dateien) an einen sicheren Ort.

3. Kopieren Sie alle `ib_logfile`-Dateien an einen sicheren Ort.

4. Kopieren Sie Ihre `my.cnf`-Konfigurationsdatei(en) an einen sicheren Ort.

5. Kopieren Sie alle `.frm`-Dateien für Ihre `InnoDB`-Tabellen an einen sicheren Ort.

Da die Replikation mit `InnoDB`-Tabellen funktioniert, können Sie die Replikationsfähigkeiten von MySQL nutzen, um in Hochverfügbarkeitsumgebungen eine Kopie Ihrer Datenbank zu halten.

Zusätzlich zu den Binär-Backups sollten Sie auch regelmäßig mit mysqldump Dump-Kopien der Tabellen anlegen, da eine Binärdatei beschädigt werden kann, ohne dass man es merkt. Dump-Dateien hingegen werde in Textdateien gespeichert, die für Menschen lesbar sind. So lassen sich Schäden leichter erkennen. Außerdem ist die Gefahr einer ernsten Datenkorruption geringer, da das Format einfacher ist. mysqldump besitzt zudem die `--single-transaction`-Option, mit der Sie konsistente Snapshots anlegen können, ohne andere Clients auszusperren.

Um überhaupt in der Lage zu sein, eine `InnoDB`-Datenbank aus dem Binär-Backup wiederherzustellen, müssen Sie den MySQL-Server mit eingeschaltetem Binär-Logging betreiben. Dann können Sie das Binärlog auf die Datenbanksicherung übertragen, um eine Point-in-Time-Recovery zu fahren:

    mysqlbinlog yourhostname-bin.123 | mysql

Um den MySQL-Server nach einem Absturz wiederherzustellen, müssen Sie ihn lediglich neu starten. `InnoDB` schaut automatisch in die Logs und versetzt die Datenbank wieder in den aktuellen Zustand (Roll-forward). `InnoDB` rollt unbestätigte Transaktionen, die zum Zeitpunkt des Absturzes anhängig waren, automatisch zurück. Während der Wiederherstellung zeigt mysqld so etwas wie dieses an:

    InnoDB: Database was not shut down normally.
    InnoDB: Starting recovery from log files...
    InnoDB: Starting log scan based on checkpoint at
    InnoDB: log sequence number 0 13674004
    InnoDB: Doing recovery: scanned up to log sequence number 0 13739520
    InnoDB: Doing recovery: scanned up to log sequence number 0 13805056
    InnoDB: Doing recovery: scanned up to log sequence number 0 13870592
    InnoDB: Doing recovery: scanned up to log sequence number 0 13936128
    ...
    InnoDB: Doing recovery: scanned up to log sequence number 0 20555264
    InnoDB: Doing recovery: scanned up to log sequence number 0 20620800
    InnoDB: Doing recovery: scanned up to log sequence number 0 20664692
    InnoDB: 1 uncommitted transaction(s) which must be rolled back
    InnoDB: Starting rollback of uncommitted transactions
    InnoDB: Rolling back trx no 16745
    InnoDB: Rolling back of trx no 16745 completed
    InnoDB: Rollback of uncommitted transactions completed
    InnoDB: Starting an apply batch of log records to the database...
    InnoDB: Apply batch completed
    InnoDB: Started
    mysqld: ready for connections

Wenn Ihre Datenbank beschädigt wurde oder Ihre Festplatte abstürzt, müssen Sie ein Backup zur Wiederherstellung verwenden. Sind Schäden aufgetreten, müssen Sie ein Backup suchen, das unbeschädigt ist. Nach der Wiederherstellung der Sicherungsdateien aus dem Backup müssen Sie mit mysqlbinlog und mysql die Änderungen, die nach dem Backup eingetreten sind, aus den Binärlogs anwenden.

In manchen Fällen, in denen Daten beschädigt wurden, reicht es aus, die beschädigten Tabellen zu dumpen, zu löschen und neu zu erzeugen. Mit der SQL-Anweisung `CHECK TABLE` finden Sie die meisten Schäden heraus, allerdings nicht jede nur denkbare Art von Datenkorruption. Der `innodb_tablespace_monitor` prüft die Integrität des Dateiraum-Managements in den Tablespace-Dateien.

Manchmal ist ein scheinbarer Datenbankschaden in Wirklichkeit ein Schaden, den das Betriebssystem an seinem eigenen Datei-Cache verursacht hat, während die Daten auf der Festplatte nach wie vor in Ordnung sind. Am besten versuchen Sie als Erstes, den Computer neu zu starten. So können Sie Fehler eliminieren, die nur scheinbar einen Schaden an Datenbankseiten verursachten.

#### 14.2.8.1. Erzwingen einer `InnoDB`-Wiederherstellung (Recovery)

Wenn Schäden an Datenbankseiten vorhanden sind, sollten Sie Ihre Tabellen mit `SELECT INTO OUTFILE` dumpen. Normalerweise sind die meisten auf diese Weise geretteten Daten intakt. Trotzdem kann der Schaden dazu führen, dass `SELECT * FROM `tbl_name``-Anweisungen oder `InnoDB`-Hintergrundoperationen abstürzen oder sich durchsetzen oder gar die Roll-forward-Recovery von `InnoDB` abstürzen lassen. Sie können einen Neustart von `InnoDB` erzwingen und gleichzeitig die Hintergrundoperationen anhalten, sodass ein Tabellen-Dump möglich ist. Zum Beispiel könnten Sie dem Abschnitt `[mysqld]` Ihrer Optionsdatei vor dem Server-Neustart folgende Zeile hinzufügen:

    [mysqld]
    innodb_force_recovery = 4

Weiter unten erfahren Sie, welche von null verschiedenen Werte `innodb_force_recovery` annehmen kann. Größere Werte schließen alle Sicherheitsmaßnahmen der kleineren Werte mit ein. Wenn Sie in der Lage sind, Ihre Tabellen mit einem Optionswert von höchstens 4 zu dumpen, können Sie davon ausgehen, dass nur wenige Daten auf einzelnen beschädigten Seiten verloren gegangen sind. Der Wert 6 ist schon drastischer: Hier bleiben Datenbankseiten in einem obsoleten Zustand zurück, was zu zusätzlichen Schäden in B-Bäumen und anderen Datenbankstrukturen führen kann.

- `1` (`SRV_FORCE_IGNORE_CORRUPT`)

  Server soll auch dann weiter laufen, wenn er eine beschädigte Seite entdeckt. Versuchen Sie, `SELECT * FROM `tbl_name`` beschädigte Indexeinträge und Seiten überspringen zu lassen, da dies den Tabellen-Dump erleichtert.

- `2` (`SRV_FORCE_NO_BACKGROUND`)

  Unterbindet den Haupt-Thread. Wenn während der Purge-Operation ein Absturz passiert, wird dieser Recovery-Wert das verhindern.

- `3` (`SRV_FORCE_NO_TRX_UNDO`)

  Nach der Recovery keine Transaktionen zurückrollen.

- `4` (`SRV_FORCE_NO_IBUF_MERGE`)

  Verhindert auch Merge-Operationen auf Insert-Puffern. Wenn diese einen Absturz herbeiführen würden werden sie nicht durchgeführt. Es wird keine Tabellenstatistik berechnet.

- `5` (`SRV_FORCE_NO_UNDO_LOG_SCAN`)

  Beim Starten der Datenbank nicht in die Undo-Logs schauen: `InnoDB` behandelt dann auch unvollständige Transaktionen als abgeschlossen.

- `6` (`SRV_FORCE_NO_LOG_REDO`)

  Keinen Roll-forward der Logs in Verbindung mit der Recovery durchführen.

Selbst bei einer erzwungenen Recovery können Sie Tabellen mit `SELECT` dumpen oder sie mit `DROP` löschen oder mit `CREATE` anlegen. Wenn Sie wissen, dass eine bestimmte Tabelle beim Zurückrollen einen Absturz verursacht, können Sie sie löschen. Oder Sie verwenden diese Option, um ein Rollback anzuhalten, das wegen eines gescheiterten Massen-Imports oder einer fehlgeschlagenen `ALTER TABLE`-Operation außer Kontrolle geraten ist. Sie können den mysqld-Prozess anhalten und `innodb_force_recovery` auf `3` setzen, um die Datenbank ohne den Rollback wieder ans Laufen zu bringen, und dann mit `DROP` die Tabelle löschen, die den Endlos-Rollback verursacht hatte.

Die Datenbank darf auf keine andere Weise mit einem von null verschiedenen `innodb_force_recovery`-Wert benutzt werden. Zur Sicherheit hindert `InnoDB` die Benutzer an `INSERT`-, `UPDATE`- und `DELETE`-Operationen, wenn `innodb_force_recovery` größer als 0 ist.

#### 14.2.8.2. Checkpoints

`InnoDB` implementiert einen „fuzzy“ Checkpoint-Mechanismus. `InnoDB` schreibt Datenbankseiten, die geändert wurden, in kleinen Batches aus dem Bufferpool auf die Platte. Es ist nicht nötig, den Bufferpool in einem einzigen, großen Batch auf die Platte zu schreiben, da dies in der Praxis dazu führen würde, dass Benutzer während des Checkpointing-Prozesses keine SQL-Anweisungen erteilen können.

Bei einer Recovery sucht `InnoDB` ein Checkpoint-Label in den Logdateien, da es weiß, dass alle Datenbankmodifikationen, die diesem Label vorausgingen, im Disk-Image der Datenbank vorhanden sind. Dann scannt `InnoDB` die Logdateien von dem Checkpoint aus nach vorne durch und wendet die dort protokollierten Modifikationen auf die Datenbank an.

`InnoDB` schreibt Daten in rotierender Weise in seine Logdateien. Alle committeten Modifikationen, die dazu führen, dass Datenbankseiten im Bufferpool von den Images auf der Festplatte abweichen, müssen in den Logdateien vorhanden sein, falls `InnoDB` einmal eine Recovery durchführen muss. Wenn `InnoDB` eine Logdatei benutzt, muss es also dafür sorgen, dass die Datenbankseiten-Images auf der Platte die in der für die Recovery verwendeten Logdatei protokollierten Modifikationen enthalten. Mit anderen Worten muss `InnoDB` einen Checkpoint anlegen, wozu häufig modifizierte Datenbankseiten auf die Platte zurückgeschrieben werden müssen.

Dies erklärt auch, warum sehr große Logdateien die Plattenzugriffe beim Checkpointing reduzieren. Oft ist es sinnvoll, die Gesamtgröße der Logdateien auf die Größe des Bufferpools oder sogar einen noch höheren Wert einzustellen. Der Nachteil großer Logdateien: Die Recovery kann dann länger dauern, da mehr Informationen in die Datenbank eingebracht werden müssen.

### 14.2.9. Eine InnoDB-Datenbank auf eine andere Maschine verschieben

Auf Windows speichert `InnoDB` die Namen von Datenbanken und Tabellen intern immer in Kleinbuchstaben. Um Datenbanken in einem Binärformat von Unix auf Windows oder von Windows auf Unix zu verlagern, sollten Sie alle Tabellen- und Datenbanknamen auf Kleinschrift umstellen. Dies können Sie ganz bequem erreichen, indem Sie dem `[mysqld]`-Abschnitt Ihrer `my.cnf`- oder `my.ini`-Datei folgende Zeile hinzufügen, bevor Sie irgendwelche Datenbanken oder Tabellen erzeugen:

    [mysqld]
    lower_case_table_names=1

Wie `MyISAM`-Datendateien sind auch die Daten- und Logdateien von `InnoDB` auf allen Plattformen, die dasselbe Fließkommazahlenformat haben, binär kompatibel. Sie können eine `InnoDB`-Datenbank einfach verschieben, indem Sie alle in Abschnitt 14.2.8, „Sichern und Wiederherstellen einer `InnoDB`-Datenbank“ aufgelisteten relevanten Dateien kopieren. Wenn die Fließkommaformate unterschiedlich sind, aber keine `FLOAT`- oder `DOUBLE`-Datentypen in den Tabellen verwendet werden, ist die Prozedur dieselbe: Kopieren Sie einfach die relevanten Dateien. Sind aber sowohl die Formate unterschiedlich, als auch Fließkommawerte in den Tabellen vorhanden, so müssen Sie Ihre Tabellen mit mysqldump auf der einen Maschine dumpen und dann diese Dump-Dateien auf die andere Maschine übertragen.

Eine bessere Performance erzielen Sie, wenn Sie den Autocommit-Modus beim Datenimport abschalten, immer vorausgesetzt, der Tablespace hat genug Platz für das große Rollback-Segment, das Import-Transaktionen generieren. Den Commit-Befehl geben Sie nachträglich, wenn Sie eine Tabelle oder ein Tabellensegment importiert haben.

### 14.2.10. InnoDB-Transaktionsmodell

Das Transaktionsmodell von `InnoDB` soll die besten Eigenschaften einer Multiversionierungsdatenbank mit dem traditionellen zweiphasigen Sperren verbinden. `InnoDB` errichtet Sperren auf Zeilenebene und führt Anfragen standardmäßig als nicht-sperrende, konsistente Leseoperationen nach Art von Oracle aus. Die Sperrentabelle in `InnoDB` wird so platzsparend gespeichert, dass keine Sperreneskalation erforderlich ist: Einzelne Zeilen oder beliebige Teile der Zeilen in der Datenbank können von mehreren Benutzern gesperrt werden, ohne dass `InnoDB` der Speicherplatz ausgeht.

#### 14.2.10.1. `InnoDB`-Sperrmodi

`InnoDB` implementiert zwei Arten von Standard-Zeilensperren:

- Eine Shared-Sperre (`S`) erlaubt einer Transaktion, eine Zeile (ein Tupel) zu lesen.

- Eine exklusive (`X`) Sperre erlaubt einer Transaktion, eine Zeile zu ändern oder zu löschen.

Wenn die Transaktion `T1` eine Shared-Sperre (`S`) auf dem Tupel `t` hält, dann

- kann einer Forderung einer anderen Transaktion `T2` nach einer `S`-Sperre auf `t` sofort stattgegeben werden. Danach halten sowohl `T1` als auch `T2` eine `S`-Sperre auf `t`.

- kann einer Forderung einer anderen Transaktion `T2` nach einer `X`-Sperre auf `t` nicht sofort stattgegeben werden.

Wenn eine Transaktion `T1` eine exklusive (`X`) Sperre auf Tupel `t` hält, kann einer Forderung einer anderen Transaktion `T2` nach einer Sperre egal welchen Typs auf `t` nicht sofort stattgegeben werden. Transaktion `T2` muss warten, bis Transaktion `T1` ihre Sperre auf Tupel `t` wieder freigegeben hat.

Außerdem unterstützt `InnoDB` Multigranuläre Sperren: So können Zeilen und Tabellensperren koexistieren. Um Sperren auf mehreren Granularitätsebenen in die Praxis umzusetzen, gibt es die so genannten Intention Locks. Mithilfe dieser intendierten Sperren kann eine Transaktion anzeigen, welchen Sperrentyp (Shared oder exklusiv) sie später auf einer Zeile einer Tabelle benötigt. In `InnoDB` werden zwei Arten von Intention Locks verwendet (wir gehen davon aus, dass Transaktion `T` eine Sperre des angegebenen Typs auf Tabelle `R`) angefordert hat:

- Intention Shared (`IS`): Transaktion `T` will `S`-Sperren einzelner Zeilen von Tabelle `R` erwerben.

- Intention Exclusive (`IX`): Transaktion `T` will `X`-Sperren auf diesen Zeilen erwerben.

Das Protokoll für Intention Locking ist wie folgt:

- Bevor eine Transaktion eine `S`-Sperre auf einer gegebenen Zeile errichten kann, muss sie eine `IS`- oder stärkere Sperre auf der Tabelle, zu der die Zeile gehört, erwerben.

- Bevor eine Transaktion eine `X`-Sperre auf einer gegebenen Zeile errichten kann, muss sie eine `IX`-Sperre auf der Tabelle, zu der die Zeile gehört, erwerben.

Diese Regeln lassen sich gut in einer Matrix der Sperrtypenkompatibilität zusammenfassen:

| `X` | `IX` | `S` | `IS`
`X` | Konflikt | Konflikt | Konflikt | Konflikt
`IX` | Konflikt | Kompatibel | Konflikt | Kompatibel
`S` | Konflikt | Konflikt | Kompatibel | Kompatibel
`IS` | Konflikt | Kompatibel | Kompatibel | Kompatibel

Der Sperranforderung einer Transaktion wird stattgegeben, wenn die Sperre mit den bereits vorhandenen Sperren kompatibel ist. Ihr wird nicht stattgegeben, wenn die Sperre mit den vorhandenen in Konflikt treten würde. Dann muss die Transaktion abwarten, bis die andere Sperre, die den Konflikt verursachen würde, freigegeben wurde. Wenn eine Sperranforderung mit einer vorhandenen Sperre in Konflikt tritt und nicht gewährt werden kann, weil sie einen Deadlock verursachen würde, wird ein Fehler ausgelöst.

Somit können Intention Locks nichts blockieren, außer vielleicht Sperranforderungen für ganze Tabellen (beispielsweise `LOCK TABLES … WRITE`). `IX` und `IS` sollen vor allem anzeigen, dass jemand eine Tabellenzeile sperrt oder sperren wird.

Das folgende Beispiel zeigt, wie ein Fehler ausgelöst wird, wenn eine Sperranforderung einen Deadlock zur Folge haben würde. An dem Beispiel sind zwei Clients beteiligt, nämlich A und B.

Zuerst legt Client A eine Tabelle mit einer Zeile an und beginnt dann eine Transaktion. Inmitten der Transaktion erwirbt A eine `S`-Sperre auf der Zeile, indem er im Shared-Modus ein Select auf ihr ausführt:

    mysql> CREATE TABLE t (i INT) ENGINE = InnoDB;
    Query OK, 0 rows affected (1.07 sec)

    mysql> INSERT INTO t (i) VALUES(1);
    Query OK, 1 row affected (0.09 sec)

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)

    mysql> SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE;
    +------+
    | i    |
    +------+
    |    1 |
    +------+
    1 row in set (0.10 sec)

Dann beginnt Client B eine Transaktion und versucht, die Zeile aus der Tabelle zu löschen:

    mysql> START TRANSACTION;
    Query OK, 0 rows affected (0.00 sec)

    mysql> DELETE FROM t WHERE i = 1;

Die Löschoperation macht eine `X`-Sperre erforderlich. Diese kann jedoch nicht erteilt werden, da sie mit der `S`-Sperre von Client A inkompatibel ist. So wird der Request in die Schlange der Sperranforderungen auf diese Zeile gestellt und Client B wird blockiert.

Endlich versucht Client A ebenfalls, die Zeile aus der Tabelle zu löschen:

    mysql> DELETE FROM t WHERE i = 1;
    ERROR 1213 (40001): Deadlock found when trying to get lock;
    try restarting transaction

Hier tritt der Deadlock auf, da Client A eine `X`-Sperre benötigt, um die Zeile sperren zu können. Diese Sperre kann jedoch nicht gewährt werden, da Client B schon vorher eine `X`-Sperre angefordert hatte, und nur noch darauf wartet, dass A seine `S`-Sperre freigibt. Doch auch die `S`-Sperre von A kann nicht auf eine `X`-Sperre hochgesetzt werden, da B schon vorher eine `X`-Sperre angefordert hatte. Infolgedessen meldet `InnoDB` dem Client A einen Fehler und gibt dessen Sperre frei. Jetzt kann der Sperranforderung von Client B stattgegeben werden und B löscht die Zeile aus der Tabelle.

#### 14.2.10.2. `InnoDB` und `AUTOCOMMIT`

In `InnoDB` findet jegliche Benutzeraktivität in einer Transaktion statt. Wenn der Autocommit-Modus eingeschaltet ist, bildet jede SQL-Anweisung eine eigene Transaktion. Standardmäßig ist dieser Modus immer aktiv, wenn MySQL hochfährt.

Wird Autocommit mit `SET AUTOCOMMIT = 0` ausgeschaltet, gehen wir davon aus, dass der Benutzer immer eine Transaktion offen hat. Eine `COMMIT`- oder `ROLLBACK`-Anweisung beendet die laufende Transaktion und eine neue beginnt. `COMMIT` bedeutet, dass die Änderungen, die in der aktuellen Transaktion vorgenommen wurden, in der Datenbank dauerhaft festgeschrieben und für andere Benutzer sichtbar gemacht werden. Eine `ROLLBACK`-Anweisung hingegen macht alle Änderungen in der aktuellen Transaktion rückgängig. Beide Anweisungen geben auch alle `InnoDB`-Sperren frei, die in der Transaktion gesetzt wurden.

Wenn auf einer Verbindung Autocommit eingeschaltet ist, kann der Benutzer dennoch Transaktionen aus mehreren Anweisungen ausführen, wenn er sie mit einem expliziten `START TRANSACTION` oder `BEGIN` einleitet und mit `COMMIT` oder `ROLLBACK` beendet.

#### 14.2.10.3. `InnoDB` und `TRANSACTION ISOLATION LEVEL`

Was die Transaktionsisolationsebenen von SQL:1992 angeht, so hat `InnoDB` die Standardeinstellung `REPEATABLE READ`. `InnoDB` bietet alle vier Transaktionsisolationsebenen des SQL-Standards. Die Standardisolationsebene können Sie mit der `--transaction-isolation`-Option auf der Kommandozeile oder in einer Optionsdatei einstellen. So können Sie beispielsweise im `[mysqld]`-Abschnitt einer Optionsdatei folgendes eintragen:

    [mysqld]
    transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED
                             | REPEATABLE-READ | SERIALIZABLE}

Ein Benutzer kann die Isolationsebene für eine einzelne Session oder alle neuen Verbindungen mit der `SET TRANSACTION`-Anweisung einstellen, die folgende Syntax hat:

    SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL
                           {READ UNCOMMITTED | READ COMMITTED
                            | REPEATABLE READ | SERIALIZABLE}

Beachten Sie, dass in den Namen der Ebenen in der `--transaction-isolation`-Option Bindestriche verwendet werden, aber nicht in der `SET TRANSACTION`-Anweisung.

Standardmäßig wird die Isolationsebene immer für die nächste (noch nicht begonnene) Transaktion eingestellt. Mit dem Schlüsselwort `GLOBAL` stellt die Anweisung die Standard-Transaktionsebene global für alle neuen Verbindungen ein, die ab diesem Punkt aufgebaut werden (aber nicht für die schon bestehenden Verbindungen). Um dies zu tun, benötigen Sie die `SUPER`-Berechtigung. Das Schlüsselwort `SESSION` stellt die Standard-Transaktionsebene für alle zukünftigen Transaktionen auf aktuellen Verbindung ein.

Jeder Client kann die Session-Isolationsebene (sogar inmitten einer Transaktion), oder die Isolationsebene der nächsten Transaktion haben.

Ob die Transaktionsisolationsebene global oder für die einzelne Session eingestellt wurde, können Sie anhand der Systemvariablen `tx_isolation` mit folgenden Anweisungen ermitteln:

    SELECT @@global.tx_isolation;
    SELECT @@tx_isolation;

Für Zeilensperren verwendet `InnoDB` Next-Key-Locking. Das bedeutet, dass `InnoDB` zusätzlich zu den Indexeinträgen auch die „Lücke“ vor einem Indexeintrag sperrt, um zu verhindern, dass andere Benutzer gerade dort etwas einfügen. Eine Next-Key-Sperre ist eine Sperre, die nicht nur den Indexeintrag, sondern auch die Lücke vor diesem blockiert. Eine Gap-Sperre ist eine Sperre, die lediglich die Lücke vor einem Indexeintrag betrifft.

Im Folgenden werden die Isolationsebenen in `InnoDB` genauer erläutert:

- `READ UNCOMMITTED`

  `SELECT`-Anweisungen werden ohne Sperren ausgeführt, wobei es jedoch möglich ist, dass eine ältere Version eines Eintrags verwendet wird. Also sind Leseoperationen mit dieser Isolationsebene nicht konsistent (man bezeichnet dies auch als „Dirty Read“). Ansonsten funktioniert diese Isolationsebene wie `READ COMMITTED`.

- `READ COMMITTED`

  Eine Oracle-ähnliche Isolationsebene. Alle `SELECT … FOR UPDATE`- und `SELECT … LOCK IN SHARE MODE`-Anweisungen sperren nur die Indexeinträge und nicht die Lücken davor. So können neben den gesperrten Einträgen nach Belieben neue Datensätze eingefügt werden. `UPDATE`- und `DELETE`-Anweisungen, die einen eindeutigen Index mit einer eindeutigen Suchbedingung verwenden, können nur den gefundenen Indexeintrag, aber nicht die Lücke davor, sperren. In Bereichs-`UPDATE`s und `DELETE`s muss `InnoDB` Next-Key- oder Gap-Sperren setzen und Einfügungen anderer Benutzer in die Lücken, die in dem Bereich liegen, blockieren. Dies ist erforderlich, da die Replikation und Recovery von MysQL nur funktioniert, wenn keine „Phantomzeilen“ vorhanden sind.

  Konsistente Leseoperationen verhalten sich wie in Oracle: Jede konsistente Leseoperation, sogar innerhalb derselben Transaktion, setzt und liest ihren eigenen, frischen Snapshot. Siehe Abschnitt 14.2.10.4, „Konsistentes Lesen“.

- `REPEATABLE READ`

  Die Standardisolationsebene von `InnoDB`. `SELECT … FOR UPDATE`-, `SELECT … LOCK IN SHARE MODE`-, `UPDATE`- und `DELETE`-Anweisungen, die einen eindeutigen Index mit einer eindeutigen Suchbedingung verwenden, sperren nur den gefundenen Indexeintrag, aber nicht die Lücke davor. Für andere Suchbedingungen setzen diese Operationen Next-Key-Locking ein oder sperren den durchsuchten Indexbereich mit Next-Key- oder Gap-Sperren und blockieren dadurch Einfügungen anderer Benutzer.

  In konsistenten Leseoperationen gibt es einen wichtigen Unterschied zur Isolationsebene `READ COMMITTED`: Alle konsistenten Leseoperationen innerhalb derselben Transaktion lesen denselben Snapshot, der von der ersten Leseoperation eingerichtet wurde. Daraus folgt: Wenn Sie mehrere einfache `SELECT`-Anweisungen innerhalb derselben Transaktion erteilen, sind diese `SELECT`-Anweisungen auch untereinander konsistent. Siehe Abschnitt 14.2.10.4, „Konsistentes Lesen“.

- `SERIALIZABLE`

  Wie `REPEATABLE READ`, nur dass `InnoDB` implizit alle einfachen `SELECT`-Anweisungen zu `SELECT … LOCK IN SHARE MODE` committet.

#### 14.2.10.4. Konsistentes Lesen

Bei einer konsistenten Leseoperation verwendet `InnoDB` Multiversionierung, um einer Anfrage einen Snapshot der Datenbank für einen bestimmten Zeitpunkt (Point-in-Time) zu präsentieren. Die Anfrage erkennt die Änderungen, die Transaktionen vor diesem Zeitpunkt vorgenommen hatten, aber keine Änderungen späterer oder noch unvollendeter Transaktionen. Die Ausnahme: Änderungen von früheren Anweisungen in derselben Transaktion sind für die Anweisung sichtbar.

Wenn Sie die Standardisolationsebene `REPEATABLE READ` benutzen, lesen alle konsistenten Leseoperationen innerhalb derselben Transaktion den Snapshot, der durch die erste Leseoperation in dieser Transaktion erzeugt wurde. Einen frischeren Snapshot für Ihre Anfragen erhalten Sie, indem Sie die laufende Transaktion committen und danach neue Anfragen absetzen.

Konsistentes Lesen ist der Standardmodus, in dem `InnoDB` `SELECT`-Anfragen auf den Isolationsebenenen `READ COMMITTED` und `REPEATABLE READ` verarbeitet. Konsistentes Lesen errichtet keine Sperren auf den benutzten Tabellen, sodass andere Benutzer diese Tabellen nach Belieben modifizieren können, während eine konsistente Leseoperation auf ihnen abläuft.

Beachten Sie, dass konsistente Leseoperationen nicht mit `DROP TABLE` und `ALTER TABLE` funktionieren. Mit `DROP TABLE` nicht, weil MySQL eine Tabelle, die gelöscht wurde, nicht mehr benutzen kann und `InnoDB` sie zerstört, und mit `ALTER TABLE` nicht, weil dieser Befehl innerhalb einer Transaktion ausgeführt wird, die eine neue Tabelle anlegt und Zeilen aus der alten Tabelle in sie einfügt. Wenn Sie die konsistente Leseoperation neu ausführen, kann sie die Zeilen der neuen Tabelle nicht erkennen, da sie in einer Transaktion eingefügt wurden, die in dem von der konsistenten Leseoperation verwendeten Snapshot nicht sichtbar war.

#### 14.2.10.5. Lesevorgänge sperren

In manchen Fällen ist eine konsistente Leseoperation nicht das Richtige. Vielleicht möchten Sie ja eine neue Zeile in Ihre `child`-Tabelle einfügen und dabei sicherstellen, dass das Kind in der Tabelle `parent` auch wirklich ein Elternteil hat. Das folgende Beispiel zeigt, wie Sie referenzielle Integrität in Ihren Anwendungscode implementieren können.

Angenommen, Sie verwenden eine konsistente Leseoperation, um die Tabelle `parent` zu lesen, und sehen auch tatsächlich die Elternzeile des Kindes in der Tabelle. Können Sie die Kindzeile nun auch beruhigt in die Tabelle `child` einfügen? Nein, denn es kann ja sein, dass irgendein anderer Benutzer zwischenzeitlich die Elternzeile aus der Tabelle `parent` gelöscht hat, ohne dass Sie es merkten.

Die Lösung: Sie führen das `SELECT` in dem Sperrmodus `LOCK IN SHARE MODE` aus:

    SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;

Wird eine Leseoperation im Share-Modus ausgeführt, so liest sie die aktuellsten verfügbaren Daten und errichtet eine Shared-Sperre auf den Zeilen, die sie liest. Diese verhindert, dass andere Benutzer die Zeile ändern oder löschen. Wenn die Daten zu einer noch unvollendeten Transaktion einer anderen Clientverbindung gehören, warten wir, bis die Transaktion committet ist. wenn wir gesehen haben, dass die obige Anfrage den Parent `'Jones'` zurückgibt, können wir unseren Eintrag beruhigt in die `child`-Tabelle einfügen und unsere Transaktion committen.

Betrachten wir ein anderes Beispiel: Wir haben in der Tabelle `child_codes` ein Zählerfeld eines Integer-Typs, das wir dazu benutzen, jedem Kind, das der `child`-Tabelle hinzugefügt wird, eine eindeutige Kennnummer zu geben. Da wäre es natürlich keine gute Idee, den Wert des Zählers mit einer konsistenten Leseoperation oder im Shared-Modus zu lesen, da zwei Datenbanknutzer dann vielleicht denselben Zählerwert sehen und einen Fehler wegen Schlüsselduplikaten auslösen, sofern sie versuchen, Kindeinträge mit derselben Nummer in die Tabelle einzufügen.

Hier ist `LOCK IN SHARE MODE` keine gute Lösung. Denn wenn zwei Benutzer gleichzeitig den Zähler lesen, könnte mindestens einer von ihnen in einen Deadlock geraten, wenn er versucht, den Zähler zu aktualisieren.

Hier haben Sie zwei gute Möglichkeiten, das Lesen und Inkrementieren des Zählers zu implementieren: (1) Sie inkrementierten zuerst den Zähler um 1 und führen erst dann die Leseoperation durch, oder (2) Sie lesen den Zähler zuerst im Sperrmodus `FOR UPDATE` und inkrementieren ihn danach. Der zweite Ansatz kann folgendermaßen implementiert werden:

    SELECT counter_field FROM child_codes FOR UPDATE;
    UPDATE child_codes SET counter_field = counter_field + 1;

Ein `SELECT … FOR UPDATE` liest die neuesten verfügbaren Daten und errichtet eine exklusive Sperre auf jeder Zeile, die es liest. Somit setzt es dieselben Sperren, die auch ein Searched SQL `UPDATE` auf den Zeilen erwerben würde.

Die obige Beschreibung ist nur ein Beispiel dafür, wie `SELECT … FOR UPDATE` funktioniert. In MySQL können Sie einen eindeutigen Identifier grundsätzlich mit nur einem einzigen Tabellenzugriff generieren:

    UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1);
    SELECT LAST_INSERT_ID();

Die `SELECT`-Anweisung ruft nur die Identifier-Information ab (die für die aktuelle Verbindung spezifisch ist). Sie greift auf keine Tabellen zu.

Sperren von `IN SHARE MODE`- und `FOR UPDATE`-Leseoperationen werden freigegeben, wenn die Transaktion committet oder zurückgerollt wird.

#### 14.2.10.6. Nächsten Schlüssel sperren: Wie das Phantom-Problem vermieden wird

In Zeilensperren verwendet `InnoDB` einen Algorithmus namens Next-Key-Locking. `InnoDB` sperrt Zeilen so, dass es beim Durchsuchen oder Scannen eines Tabellenindex Shared- oder exklusive Sperren auf den gefundenen Indexeinträgen errichtet. Also sind die Zeilensperren in Wirklichkeit Indexeintragssperren.

Die Sperren, die `InnoDB` auf Indexeinträgen errichtet, betreffen auch die „Lücke“ vor den Einträgen. Wenn ein Benutzer eine Shared- oder exklusive Sperre auf Eintrag `R` eines Index hält, kann kein anderer Benutzer unmittelbar vor `R` einen Eintrag in die Indexreihenfolge einfügen. Durch dieses so genannte „Gap-Locking“ wird das „Phantom-Problem“ gelöst. Angenommen, Sie wollten alle Kinder der `child`-Tabelle lesen und sperren, deren Identifier-Wert größer als 100 ist, um später eine Spalte in den ausgewählten Zeilen zu ändern:

    SELECT * FROM child WHERE id > 100 FOR UPDATE;

Angenommen, Sie haben einen Index auf der Spalte `id`. Die Anfrage scannt diesen Index ab dem ersten Eintrag, dessen `id` größer als 100 ist. Wenn die Sperren auf den Indexeinträgen Einfügungen in die Lücken nicht verhindern würden, könnte in der Zwischenzeit eine neue Zeile in die Tabelle eingefügt werden. Wenn Sie dasselbe `SELECT` in derselben Transaktion ein zweites Mal ausführten, hätten Sie plötzlich eine zusätzliche Zeile in der Ergebnismenge. Das verstößt aber gegen das Isolationsprinzip von Transaktionen: Eine Transaktion muss so ablaufen, dass sich die Daten, die sie liest, während ihrer Laufzeit nicht ändern können. Wenn wir eine Zeilenmenge als ein Datenelement betrachten, würde die neue „Phantomzeile“ dieses Isolationsprinzip verletzen.

Wenn `InnoDB` einen Index scannt, kann es auch die Lücke hinter dem letzten Eintrag im Index sperren. Genau dies geschieht auch im obigen Beispiel: Die von `InnoDB` gesetzten Sperren verhindern jede Einfügung in die Tabelle an Stellen, deren `id` größer als 100 ist.

Mit Next-Key-Locking können Sie eine Eindeutigkeitsprüfung in Ihrer Anwendung implementieren. Wenn Sie Ihre Daten im Share-Modus lesen und kein Duplikat der Zeile sehen, die Sie einfügen möchten, dann können Sie die Einfügung beruhigt ausführen und wissen, dass die Next-Key-Sperre auf dem Nachfolger Ihrer Zeile verhindert, dass in der Zwischenzeit jemand anders ein Duplikat Ihrer Zeile einfügt.

#### 14.2.10.7. Ein Beispiel, wie konsistentes Lesen bei InnoDB funktioniert

Angenommen, Sie verwenden die Standardisolationsebene `REPEATABLE READ`. Wenn Sie eine konsistente Leseoperation ausführen (also eine normale `SELECT`-Anweisung), weist `InnoDB` Ihrer Transaktion einen Zeitpunkt zu, an dem Ihre Anfrage die Datenbank sieht. Wenn eine andere Transaktion nach diesem Zeitpunkt eine Zeile löscht und committet, können Sie dies nicht sehen. Einfügungen und Änderungen werden genauso behandelt.

Sie können Ihren Zeitpunkt aktualisieren, indem Sie Ihre Transaktion committen und eine neue `SELECT`-Anfrage starten.

Dies bezeichnet man als Nebenläufigkeitssteuerung mit Multiversionierung.

                   User A                 User B

               SET AUTOCOMMIT=0;      SET AUTOCOMMIT=0;
    time
    |          SELECT * FROM t;
    |          empty set
    |                                 INSERT INTO t VALUES (1, 2);
    |
    v          SELECT * FROM t;
               empty set
                                      COMMIT;

               SELECT * FROM t;
               empty set

               COMMIT;

               SELECT * FROM t;
               ---------------------
               |    1    |    2    |
               ---------------------
               1 row in set

In diesem Beispiel sieht Benutzer A die von Benutzer B eingefügte Zeile erst dann, wenn sowohl B als auch A ihre Operationen committet haben, sodass der Zeitpunkt sich auf die Zeit nach dem Commit von B verschiebt.

Wenn Sie den „frischsten“ Zustand der Datenbank sehen möchten, stellen Sie entweder die Isolationsebene `READ COMMITTED` oder einen Locking-Read ein:

    SELECT * FROM t LOCK IN SHARE MODE;

#### 14.2.10.8. Sperren, die in InnoDB durch unterschiedliche SQL-Anweisungen gesetzt werden

Ein Locking Read, ein `UPDATE` oder ein `DELETE` errichten normalerweise Zeilensperren auf jedem Indexeintrag, der bei der Verarbeitung der SQL-Anfrage gescannt wird. Ob irgendwelche `WHERE`-Bedingungen in der Anfrage die Zeile eigentlich ausschließen würden, spielt keine Rolle. `InnoDB` merkt sich nicht die genaue `WHERE`-Bedigungn, sondern weiß nur, welche Indexbereiche gescannt werden. Die Sperren auf den Einträgen sind normalerweise Next-Key-Sperren, die auch Einfügungen in die „Lücke“ vor dem Eintrag blockieren.

Wenn exklusive Sperren errichtet werden, ruft `InnoDB` immer auch den geclusterten Indexeintrag ab und sperrt ihn.

Wenn Sie nicht die geeigneten Indizes für Ihre Anweisung haben und MySQL die gesamte Tabelle scannen muss, um die Anfrage zu verarbeiten, werden alle Tabellenzeilen und somit auch alle Einfügeoperationen anderer Benutzer auf der Tabelle gesperrt. Es ist also wichtig, gute Indizes anzulegen, damit Ihre Anfragen nicht unnötig viele Zeilen scannen müssen.

`InnoDB` setzt bestimmte Arten von Sperren wie folgt:

- `SELECT … FROM` ist ein konsistenter Lesevorgang, der einen Snapshot der Datenbank betrachtet und nur dann Sperren errichtet, wenn die Transaktionsisolationsebene `SERIALIZABLE` ist. In diesem Fall werden Shared Next-Key-Sperren auf die gefundenen Indexeinträge gesetzt.

- `SELECT … FROM … LOCK IN SHARE MODE` setzt Shared Next-Key-Sperren auf alle Indexeinträge, die der Lesevorgang findet.

- `SELECT … FROM … FOR UPDATE` setzt exklusive Next-Key-Sperren auf alle Indexeinträge, die der Lesevorgang findet.

- `INSERT INTO … VALUES (…)` setzt eine exklusive Sperre auf die eingefügte Zeile. Beachten Sie, dass diese Sperre keine Next-Key-Sperre ist und daher andere Benutzer nicht daran hindert, in die Lücke vor der neuen Zeile etwas einzufügen. Wenn ein Fehler wegen Schlüsselduplikaten auftritt, wird eine Shared-Sperre auf dem Duplikat-Indexeintrag gesetzt.

- Bei der Initialisierung einer zuvor angegebenen `AUTO_INCREMENT`-Spalte auf einer Tabelle setzt `InnoDB` eine exklusive Sperre auf das Ende des mit dieser `AUTO_INCREMENT`-Spalte verbundenen Index. Für den Zugriff auf den Auto-Increment-Zähler nutzt `InnoDB` einen bestimmten Tabellensperrmodus namens `AUTO-INC`, mit dem die Sperre nur bis zum Ende der aktuellen SQL-Anweisung und nicht der gesamten Transaktion aufrecht erhalten bleibt. Siehe Abschnitt 14.2.10.2, „`InnoDB` und `AUTOCOMMIT`“.

  `InnoDB` ruft den Wert einer initialisierten `AUTO_INCREMENT`-Spalte ab, ohne Sperren zu setzen.

- `INSERT INTO T SELECT … FROM S WHERE …` errichtet eine exklusive Sperre (keine Next-Key-Sperre) auf jeder in `T` eingefügten Zeile. `InnoDB` erwirbt Shared Next-Key-Sperren auf `S`, wenn nicht `innodb_locks_unsafe_for_binlog` aktiviert ist. In diesem Fall durchsucht es `S` mit einer konsistenten Leseoperation. `InnoDB` muss im zweiten Fall Sperren setzen: Bei einer Roll-forward-Recovery von einem Backup muss jede SQL-Anweisung in genau derselben Weise wie ursprünglich ausgeführt werden.

- `CREATE TABLE … SELECT …` führt das `SELECT` als konsistente Leseoperation oder mit Shared-Sperren durch, wie oben beschrieben.

- `REPLACE` wird wie ein Insert ausgeführt, wenn keine Konflikte auf einem eindeutigen Schlüssel entstehen. Andernfalls wird eine exklusive Next-Key-Sperre auf der zu aktualisierenden Zeile errichtet.

- `UPDATE … WHERE …` setzt eine exklusive Next-Key-Sperre auf jeden bei der Suche gefundenen Datensatz.

- `DELETE FROM … WHERE …` setzt eine exklusive Next-Key-Sperre auf jeden bei der Suche gefundenen Datensatz.

- Wenn ein `FOREIGN KEY`-Constraint auf einer Tabelle definiert ist, setzt jedes Insert, Update oder Delete, bei dem die Constraint-Bedingung geprüft werden muss, Shared-Zeilensperren auf die betrachteten Datensätze, um den Constraint zu überprüfen. `InnoDB` setzt diese Sperren auch dann, wenn der Constraint versagt.

- `LOCK TABLES` setzt Tabellensperren, allerdings auf der MySQL-Ebene, die höher als die `InnoDB`-Ebene liegt. `InnoDB` weiß von den Tabellensperren, wenn `innodb_table_locks=1` (die Standardeinstellung) und `AUTOCOMMIT=0` eingestellt ist, und die MySQL-Ebene oberhalb von `InnoDB` weiß ebenso von den Zeilensperren. Sonst kann die automatische Deadlock-Erkennung von `InnoDB` keine Deadlocks erkennen, an denen solche Tabellensperren beteiligt sind. Außerdem wäre es dann möglich, eine Tabellensperre auf einer Tabelle zu errichten, in der ein anderer Benutzer gleichzeitig Zeilensperren hält, weil die höhere MySQL-Ebene diese Zeilensperren nicht kennt. Dies bedroht allerdings nicht die Transaktionsintegrität, wie in Abschnitt 14.2.10.10, „Blockierungserkennung und Rollback“ erläutert. Siehe auch Abschnitt 14.2.16, „Beschränkungen von `InnoDB`-Tabellen“.

#### 14.2.10.9. Implizites Commit und Rollback von Transaktionen

Nach Voreinstellung beginnt MySQL jede Clientverbindung mit eingeschaltetem Autocommit. Dabei wird nach jeder fehlerfrei gelaufenen SQL-Anweisung ein Commit durchgeführt. Gibt die Anweisung einen Fehler zurück, so entscheidet die Art des Fehlers darüber, ob ein Commit oder ein Rollback ausgeführt wird. Siehe Abschnitt 14.2.15, „`InnoDB`-Fehlerbehandlung“.

Wenn Sie Autocommit ausgeschaltet haben und eine Verbindung schließen, ohne explizit die letzte Transaktion zu committen, rollt MySQL diese Transaktion zurück.

Jede der folgenden Anweisungen (und ihre Synonyme) beendet implizit eine Transaktion, als hätten Sie `COMMIT` gesagt:

- `ALTER FUNCTION`, `ALTER PROCEDURE`, `ALTER TABLE`, `BEGIN`, `CREATE DATABASE`, `CREATE FUNCTION`, `CREATE INDEX`, `CREATE PROCEDURE`, `CREATE TABLE`, `DROP DATABASE`, `DROP FUNCTION`, `DROP INDEX`, `DROP PROCEDURE`, `DROP TABLE`, `LOAD MASTER DATA`, `LOCK TABLES`, `RENAME TABLE`, `SET AUTOCOMMIT=1`, `START TRANSACTION`, `TRUNCATE`, `UNLOCK TABLES`.

- `UNLOCK TABLES` committet eine Transaktion nur dann, wenn Tabellen gesperrt sind.

- Die `CREATE TABLE`-Anweisung in `InnoDB` wird als eine einzelne Transaktion ausgeführt. Dies bedeutet, dass ein `ROLLBACK` vom Benutzer nicht die im Lauf der Transaktion erteilten `CREATE TABLE`-Anweisungen rückgängig macht.

Transaktionen können nicht geschachtelt werden. Dies ist eine Konsequenz des impliziten `COMMIT`, das jede laufende Transaktion ausführt, wenn Sie eine `START TRANSACTION`-Anweisung oder ein Synonym erteilen.

#### 14.2.10.10. Blockierungserkennung und Rollback

`InnoDB` erkennt automatisch einen Deadlock von Transaktionen und rollt eine oder mehrere Transaktion(en) zurück, um den Deadlock aufzulösen. `InnoDB` versucht, für den Rollback kleine Transaktionen herauszusuchen, wobei die Größe einer Transaktion anhand der Anzahl der Zeilen bestimmt wird, die sie einfügt, ändert oder löscht.

`InnoDB` weiß von Tabellensperren, wenn `innodb_table_locks=1` (die Standardeinstellung) und `AUTOCOMMIT=0` eingestellt ist, und die MySQL-Ebene oberhalb von `InnoDB` weiß ebenso von den Zeilensperren. Sonst kann die automatische Deadlock-Erkennung von `InnoDB` keine Deadlocks erkennen, wenn eine von der MySQL-Anweisung `LOCK TABLES` gesetzte Tabellensperre oder eine von einer anderen Speicher-Engine als `InnoDB` gesetzte Sperre beteiligt ist. Solche Situationen müssen Sie durch Einstellen der Systemvariablen `innodb_lock_wait_timeout` beherrschen.

Wenn `InnoDB` eine Transaktion vollständig zurückrollt, werden alle von ihr gehaltenen Sperren freigegeben. Wird jedoch nur eine einzige SQL-Anweisung infolge eines Fehlers zurückgerollt, bleiben manche von ihr errichteten Sperren eventuell erhalten, da `InnoDB` Zeilensperren in einem Format speichert, an dem es später nicht mehr erkennen kann, welche Anweisung welche Sperren gesetzt hat.

#### 14.2.10.11. Vom Umgang mit Deadlocks

Deadlocks in Transaktionsdatenbanken sind ein klassisches Problem. Gefährlich werden sie aber erst dann, wenn sie so häufig auftreten, dass bestimmte Transaktionen nicht mehr möglich sind. Normalerweise müssen Sie Ihre Anwendungen so erstellen, dass sie immer bereit sind, eine Transaktion neu zu starten, wenn sie wegen eines Deadlocks zurückgerollt wurde.

`InnoDB` benutzt automatische Zeilensperren. Deadlocks können sogar bei Transaktionen auftreten, die nur eine einzige Zeile einfügen oder löschen. Da diese Operationen nicht wirklich „atomar“ sind, errichten sie automatisch Sperren auf den (vielleicht mehreren) Indexeinträgen der eingefügten oder gelöschten Zeile.

Folgende Techniken helfen Ihnen, mit Deadlocks fertigzuwerden und ihre Zahl zu reduzieren:

- `SHOW ENGINE INNODB STATUS` verrät Ihnen den Grund des letzten Deadlocks. Dies kann Ihnen helfen, Ihre Anwendung so zu tunen, dass Deadlocks vermieden werden.

- Seien Sie immer bereit, eine Transaktion, die wegen eines Deadlocks scheiterte, neu zu starten. Deadlocks sind nichts Schlimmes. Versuchen Sie es einfach noch einmal.

- Committen Sie Ihre Transaktionen oft. Kleine Transaktionen sind nicht so konfliktanfällig.

- Wenn Sie Lesesperren (`SELECT … FOR UPDATE` oder `… LOCK IN SHARE MODE`) setzen, versuchen Sie, eine niedrigere Isolationsebene einzustellen, beispielsweise `READ COMMITTED`.

- Greifen Sie in einer festgelegten Reihenfolge auf Ihre Tabellen und Zeilen zu. Dann bilden die Transaktionen ordentliche Schlangen und können sich nicht gegenseitig blockieren.

- Versehen Sie Ihre Tabellen mit guten Indizes. Dann müssen Ihre Anfragen weniger Indexeinträge scannen und folglich auch weniger Sperren setzen. Mit `EXPLAIN SELECT` können Sie feststellen, welche Indizes nach Ansicht Ihres MySQL-Servers für Ihre Anfragen die besten sind.

- Sperren Sie nicht so viel. Wenn Sie es sich leisten können, dass ein `SELECT` Daten aus einem älteren Snapshot zurückgibt, verzichten Sie auf die Klausel `FOR UPDATE` oder `LOCK IN SHARE MODE`. Die Isolationsebene `READ COMMITTED` ist gut geeignet, da jede konsistente Leseoperation innerhalb derselben Transaktion Daten aus einem eigenen, frischen Snapshot liest.

- Wenn sonst nichts hilft, serialisieren Sie Ihre Transaktionen mit Tabellensperren. Um `LOCK TABLES` mit Transaktionstabellen wie etwa `InnoDB`-Tabellen zu verwenden, setzen Sie `AUTOCOMMIT = 0` und rufen `UNLOCK TABLES` erst auf, nachdem Sie die Transaktion explizit committet haben. Wenn Sie beispielsweise in die Tabelle `t1` schreiben und aus der Tabelle `t2` lesen möchten, tun Sie dies wie folgt:

      SET AUTOCOMMIT=0;
      LOCK TABLES t1 WRITE, t2 READ, ...;
      ... do something with tables t1 and t2 here ...
      COMMIT;
      UNLOCK TABLES;

  Tabellensperren sorgen dafür, dass Ihre Transaktions brav Schlange stehen und Deadlocks vermieden werden.

- Sie können Transaktionen auch serialisieren, indem Sie eine „Semaphoren“-Hilfstabelle mit nur einer einzigen Zeile anlegen. Sorgen Sie dafür, dass jede Transaktion diese Zeile aktualsieren muss, ehe sie auf andere Tabellen zugreifen darf. Auf diese Weise laufen alle Transaktionen nacheinander ab. Beachten Sie, dass der Deadlock-Erkennungsalgorithmus von `InnoDB` auch in diesem Falle funktioniert, da die serialisierende Sperre auf Zeilenebene arbeitet. Bei MySQL-Tabellensperren muss die Timeout-Methode eingesetzt werden, um Deadlocks aufzulösen.

- In Anwendungen, die den `LOCK TABLES`-Befehl verwenden errichtet MySQL keine `InnoDB`-Tabellensperren, wenn `AUTOCOMMIT=1`.

### 14.2.11. Tipps zur Leistungssteigerung

- Wenn das Unix-Tool `top` oder der Windows Task Manager zeigt, dass die CPU-Last unter 70% liegt, läuft Ihre Arbeit wahrscheinlich festplattengebunden ab. Vielleicht committen Sie zu oft Transaktionen oder Ihr Bufferpool ist zu klein. Diesen zu vergrößern könnte helfen, aber stellen sie ihn nicht auf 80% des physikalischen Speichers oder gar mehr ein.

- Verpacken Sie mehrere Modifikationen in eine einzige Transaktion. `InnoDB` muss bei jedem Committ einer Transaktion, die etwas an der Datenbank änderte, die Logs auf die Festplatte zurückschreiben. Da diese meist mit einer Geschwindigkeit von höchstens 167 Umdrehungen/Sekunde rotiert, ist diese 167tel Sekunde auch für Committs die Obergrenze, wenn die Festplatte es nicht schafft, das Betriebssystem zu „überlisten“.

- Wenn Sie es sich leisten können, im Falle eines Absturzes einige Ihrer zuletzt committeten Transaktionen zu verlieren, können Sie den `innodb_flush_log_at_trx_commit`-Parameter auf 0 setzen. `InnoDB` versucht ohnehin, das Log einmal pro Sekunde auf die Platte zu schreiben, auch wenn dies nicht immer klappt.

- Machen Sie Ihre Logdateien groß, vielleicht genauso groß wie den Bufferpool. Wenn `InnoDB` die Logdateien vollgeschrieben hat, muss es den neuen Inhalt des Bufferpools in einem Checkpoint auf die Platte schreiben. Kleine Logdateien verursachen viele überflüssige Schreibvorgänge auf der Festplatte. Der Nachteil großer Logdateien ist die längere Recovery-Zeit.

- Machen Sie auch den Logpuffer recht groß (etwa 8MB).

- Verwenden Sie zur Speicherung von Strings variabler Länge oder wenn die Spalte `NULL`-Werte enthalten kann, `VARCHAR` statt `CHAR` als Datentyp. Eine `CHAR(`N`)`-Spalte speichert immer `N` Zeichen, selbst wenn der String kürzer oder sein Wert `NULL` ist. Kleinere Tabellen passen besser in den Bufferpool und reduzieren die Schreibvorgänge auf der Festplatte.

  Wenn Sie die Standardeinstellung `row_format=compact` für das Datensatzformat von MySQL 5.1 Zeichensätze variabler Länge wie etwa `utf8` oder `sjis` verwenden, belegt `CHAR(`N`)` eine variable Menge Speicherplatz, allerdings mindestens `N` Bytes.

- In manchen Versionen von GNU/Linux und Unix geht es erstaunlich langsam, Daten mit dem Unix-Aufruf `fsync()` (den `InnoDB` standardmäßig verwendet) und anderen, ähnlichen Methoden auf die Platte zu schreiben. Wenn Sie mit der Schreibleistung Ihrer Datenbank unzufrieden sind, setzen Sie den `innodb_flush_method`-Parameter auf `O_DSYNC`. Zwar scheint `O_DSYNC` auf den meisten Systemen die langsamere Variante zu sein, aber vielleicht ist es gerade auf Ihrem schneller.

- Wenn Sie `InnoDB` auf Solaris 10 for x86_64 (AMD Opteron) einsetzen, ist es wichtig, alle zum Speichern von `InnoDB`-Dateien verwendeten Dateisysteme mit der `forcedirectio`-Option zu mounten (die standardmäßig auf Solaris 10/x86_64 nicht benutzt wird). Wenn Sie dies nicht tun, läuft `InnoDB` auf dieser Plattform sehr viel langsamer und hat eine geringere Performance.

  Wenn Sie `InnoDB` mit einem großen `innodb_buffer_pool_size`-Wert auf Solaris 2.6 und höher auf einer beliebigen Plattform (sparc/x86/x64/amd64) einsetzen, können Sie die Performance massiv steigern, indem Sie die `InnoDB`-Daten- und -Logdateien auf Raw Devices oder einem separaten UFS-Dateisystem mit Direkt-E/A speichern (und zwar mit der Mount-Option `forcedirectio`, siehe `mount_ufs(1M)`). Wer das Veritas-Dateisystem VxFS hat, sollte die Mount-Option `convosync=direct` setzen.

  Andere MySQL-Datendateien, wie etwa die für `MyISAM`-Tabellen, sollten nicht in einem Dateisystem mit Direkt-E/A abgelegt werden. Executables oder Bibliotheken dürfen niemals in einem Dateisystem mit Direkt-E/A abgelegt werden

- Beim Datenimport in `InnoDB` müssen Sie darauf achten, dass MySQL nicht im Autocommit-Modus läuft, da dieser bei jedem Insert die Logs auf die Festplatte zurückschreibt. Um während der Import-Operation Autocommit auszuschalten, schließen Sie die Operation in `SET AUTOCOMMIT` und `COMMIT`-Anweisungen ein:

      SET AUTOCOMMIT=0;
      ... SQL import statements ...
      COMMIT;

  Mit der mysqldump-Option `--opt` erhalten Sie Dump-Dateien, die sich in eine `InnoDB`-Tabelle ganz schnell importieren lassen, selbst ohne den Import in die Anweisungen `SET AUTOCOMMIT` und `COMMIT` zu verpacken.

- Hüten Sie sich vor umfangreichen Rollbacks von Masseneinfügeoperationen: `InnoDB` benutzt den Insert-Puffer zwar bei den Einfügungen, um Schreibvorgänge zu minimieren, aber nicht bei den zugehörigen Rollbacks. Ein festplattengebundener Rollback kann 30-mal so lange wie der zugehörige Insert brauchen. Dabei hilft es auch nichts, den Datenbankprozess anzuhalten, da der Rollback beim Hochfahren des Servers wieder von vorne beginnt. Die einzige Möglichkeit, einen außer Kontrolle geratenen Rollback aufzuhalten, besteht darin, den Bufferpool so groß anzusetzen, dass der Rollback CPU-gebunden und somit schneller läuft, oder eine spezielle Prozedur einzusetzen. Siehe Abschnitt 14.2.8.1, „Erzwingen einer `InnoDB`-Wiederherstellung (Recovery)“.

- Hüten Sie sich auch vor anderen umfangreichen festplattengebundenen Operationen. Verwenden Sie zum Leeren einer Tabelle `DROP TABLE` und `CREATE TABLE`, aber nicht `DELETE FROM `tbl_name``.

- Mit dem mehrzeiligen `INSERT` können Sie den Kommunikationsaufwand zwischen Client und Server minimieren, wenn Sie viele Zeilen einfügen müssen:

      INSERT INTO yourtable VALUES (1,2), (5,5), ...;

  Dieser Tipp gilt übrigens für Einfügungen in alle möglichen Tabellen, nicht nur in `InnoDB`-Tabellen.

- Wenn Sie `UNIQUE`-Constraints auf Sekundärschlüsseln haben, können Sie Tabellenimporte beschleunigen, indem Sie die Eindeutigkeitsprüfung während der Import-Session vorübergehend abschalten:

      SET UNIQUE_CHECKS=0;
      ... import operation ...
      SET UNIQUE_CHECKS=1;

  Bei großen Tabellen spart dies eine Menge Plattenzugriffe, da `InnoDB` seine Insert-Puffer dazu benutzen kann, Sekundärindexeinträge als Batch zu verarbeiten.

- Wenn Ihre Tabellen `FOREIGN KEY`-Constraints haben, können Sie Tabellenimporte beschleunigen, indem Sie für die Dauer der Import-Session die Fremschlüsselprüfungen abschalten:

      SET FOREIGN_KEY_CHECKS=0;
      ... import operation ...
      SET FOREIGN_KEY_CHECKS=1;

  Bei großen Tabellen spart dies eine Menge Plattenzugriffe.

- Wenn Sie oft wiederkehrende Anfragen auf Tabellen haben, die sich nur selten ändern, nutzen Sie den Query-Cache:

      [mysqld]
      query_cache_type = ON
      query_cache_size = 10M

#### 14.2.11.1. Der InnoDB-Monitor

`InnoDB` hat Monitore, die Informationen über den internen Zustand von `InnoDB` ausgeben. Sie können jederzeit eine `SHOW ENGINE INNODB STATUS`-Anweisung erteilen, um die Ausgabe des `InnoDB`-Standardmonitors in Ihren SQL-Client zu holen. Diese Informationen sind nützlich für das Performance-Tuning. (Wenn Sie den interaktiven SQL-Client mysql benutzen, ist die Ausgabe einfacher zu lesen, wenn Sie das übliche Semikolon am Ende von Anweisungen durch `\G` ersetzen.) Die Sperrmodi von `InnoDB` werden in Abschnitt 14.2.10.1, „`InnoDB`-Sperrmodi“ erklärt.

    mysql> SHOW ENGINE INNODB STATUS\G

Eine andere Möglichkeit, `InnoDB`-Monitore zu nutzen, besteht darin, sie in regelmäßigen Abständen Daten in die Standardausgabe des mysqld-Servers schreiben zu lassen. In diesem Fall wird keine Ausgabe an die Clients gesandt. Wenn sie eingeschaltet sind, geben `InnoDB`-Monitore etwa alle 15 Sekunden Daten aus. Die Server-Ausgabe wird normalerweise an das `.err`-Log im MySQL Data Directory geschickt. Diese Daten sind nützlich für das Performance-Tuning. Auf Windows müssen Sie den Server von einer Eingabeaufforderung in einem Konsolenfenster mit der `--console`-Option starten, wenn Sie die Ausgabe an das Fenster statt in das Fehlerlog schicken wollen.

Die Monitorausgabe enthält folgende Informationen:

- Tabellen- und Zeilensperren, die von den aktiven Transaktionen gehalten werden

- Wartende Sperranforderungen von Transaktionen (Lock Waits)

- Auf Semaphoren wartende Threads

- Noch schwebende E/A-Requests

- Statistikdaten über den Bufferpool

- Aktivitäten des `InnoDB`-Haupt-Threads zur Verschmelzung von Purge- und Insert-Puffern

Damit der `InnoDB`-Standardmonitor seine Daten an die Standardausgabe von mysqld sendet, geben Sie folgende SQL-Anweisung:

    CREATE TABLE innodb_monitor (a INT) ENGINE=INNODB;

Folgende Anweisung hält den Monitor an:

    DROP TABLE innodb_monitor;

Die `CREATE TABLE`-Syntax ist nur eine Möglichkeit, über den SQL-Parser von MySQL einen Befehl an `InnoDB` zu übergeben. Es ist nur wichtig, dass der Tabellenname `innodb_monitor` und der Tabellentyp `InnoDB` ist. Die Tabellenstruktur ist für den `InnoDB`-Monitor nicht von Belang. (Diese Syntax kann beim Herunterfahren des Servers wichtig sein, der Monitor fährt bei einem Neustart des Servers nicht automatisch wieder hoch. Sie müssen die Monitortabelle löschen und eine neue `CREATE TABLE`-Anweisung geben, um den Monitor zu starten. Das kann sich in einem künftigen Release noch ändern.)

`innodb_lock_monitor` kann ähnlich eingesetzt werden. Dieser gleicht dem `innodb_monitor`, nur dass er außerdem viele Sperrinformationen gibt. Ein separater `innodb_tablespace_monitor` gibt eine Liste der angelegten Dateisegmente im Tablespace aus und validiert die Zuweisung der Datenstrukturen im Tablespace. Zusätzlich gibt es den `innodb_table_monitor`, mit dem Sie den Inhalt des internen Data Dictionary von `InnoDB` ausgeben können.

Hier sehen Sie eine Beispielausgabe des `InnoDB`-Monitors:

    mysql> SHOW ENGINE INNODB STATUS\G
    *************************** 1. row ***************************
    Status:
    =====================================
    030709 13:00:59 INNODB MONITOR OUTPUT
    =====================================
    Per second averages calculated from the last 18 seconds
    ----------
    SEMAPHORES
    ----------
    OS WAIT ARRAY INFO: reservation count 413452, signal count 378357
    --Thread 32782 has waited at btr0sea.c line 1477 for 0.00 seconds the
    semaphore: X-lock on RW-latch at 41a28668 created in file btr0sea.c line 135
    a writer (thread id 32782) has reserved it in mode wait exclusive
    number of readers 1, waiters flag 1
    Last time read locked in file btr0sea.c line 731
    Last time write locked in file btr0sea.c line 1347
    Mutex spin waits 0, rounds 0, OS waits 0
    RW-shared spins 108462, OS waits 37964; RW-excl spins 681824, OS waits
    375485
    ------------------------
    LATEST FOREIGN KEY ERROR
    ------------------------
    030709 13:00:59 Transaction:
    TRANSACTION 0 290328284, ACTIVE 0 sec, process no 3195, OS thread id 34831
    inserting
    15 lock struct(s), heap size 2496, undo log entries 9
    MySQL thread id 25, query id 4668733 localhost heikki update
    insert into ibtest11a (D, B, C) values (5, 'khDk' ,'khDk')
    Foreign key constraint fails for table test/ibtest11a:
    ,
      CONSTRAINT `0_219242` FOREIGN KEY (`A`, `D`) REFERENCES `ibtest11b` (`A`,
      `D`) ON DELETE CASCADE ON UPDATE CASCADE
    Trying to add in child table, in index PRIMARY tuple:
     0: len 4; hex 80000101; asc ....;; 1: len 4; hex 80000005; asc ....;; 2:
     len 4; hex 6b68446b; asc khDk;; 3: len 6; hex 0000114e0edc; asc ...N..;; 4:
     len 7; hex 00000000c3e0a7; asc .......;; 5: len 4; hex 6b68446b; asc khDk;;
    But in parent table test/ibtest11b, in index PRIMARY,
    the closest match we can find is record:
    RECORD: info bits 0 0: len 4; hex 8000015b; asc ...[;; 1: len 4; hex
    80000005; asc ....;; 2: len 3; hex 6b6864; asc khd;; 3: len 6; hex
    0000111ef3eb; asc ......;; 4: len 7; hex 800001001e0084; asc .......;; 5:
    len 3; hex 6b6864; asc khd;;
    ------------------------
    LATEST DETECTED DEADLOCK
    ------------------------
    030709 12:59:58
    *** (1) TRANSACTION:
    TRANSACTION 0 290252780, ACTIVE 1 sec, process no 3185, OS thread id 30733
    inserting
    LOCK WAIT 3 lock struct(s), heap size 320, undo log entries 146
    MySQL thread id 21, query id 4553379 localhost heikki update
    INSERT INTO alex1 VALUES(86, 86, 794,'aA35818','bb','c79166','d4766t',
    'e187358f','g84586','h794',date_format('2001-04-03 12:54:22','%Y-%m-%d
    %H:%i'),7
    *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 0 page no 48310 n bits 568 table test/alex1 index
    symbole trx id 0 290252780 lock mode S waiting
    Record lock, heap no 324 RECORD: info bits 0 0: len 7; hex 61613335383138;
    asc aa35818;; 1:
    *** (2) TRANSACTION:
    TRANSACTION 0 290251546, ACTIVE 2 sec, process no 3190, OS thread id 32782
    inserting
    130 lock struct(s), heap size 11584, undo log entries 437
    MySQL thread id 23, query id 4554396 localhost heikki update
    REPLACE INTO alex1 VALUES(NULL, 32, NULL,'aa3572','','c3572','d6012t','',
    NULL,'h396', NULL, NULL, 7.31,7.31,7.31,200)
    *** (2) HOLDS THE LOCK(S):
    RECORD LOCKS space id 0 page no 48310 n bits 568 table test/alex1 index
    symbole trx id 0 290251546 lock_mode X locks rec but not gap
    Record lock, heap no 324 RECORD: info bits 0 0: len 7; hex 61613335383138;
    asc aa35818;; 1:
    *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 0 page no 48310 n bits 568 table test/alex1 index
    symbole trx id 0 290251546 lock_mode X locks gap before rec insert intention
    waiting
    Record lock, heap no 82 RECORD: info bits 0 0: len 7; hex 61613335373230;
    asc aa35720;; 1:
    *** WE ROLL BACK TRANSACTION (1)
    ------------
    TRANSACTIONS
    ------------
    Trx id counter 0 290328385
    Purge done for trx's n:o < 0 290315608 undo n:o < 0 17
    Total number of lock structs in row lock hash table 70
    LIST OF TRANSACTIONS FOR EACH SESSION:
    ---TRANSACTION 0 0, not started, process no 3491, OS thread id 42002
    MySQL thread id 32, query id 4668737 localhost heikki
    show innodb status
    ---TRANSACTION 0 290328384, ACTIVE 0 sec, process no 3205, OS thread id
    38929 inserting
    1 lock struct(s), heap size 320
    MySQL thread id 29, query id 4668736 localhost heikki update
    insert into speedc values (1519229,1, 'hgjhjgghggjgjgjgjgjggjgjgjgjgjgggjgjg
    jlhhgghggggghhjhghgggggghjhghghghghghhhhghghghjhhjghjghjkghjghjghjghjfhjfh
    ---TRANSACTION 0 290328383, ACTIVE 0 sec, process no 3180, OS thread id
    28684 committing
    1 lock struct(s), heap size 320, undo log entries 1
    MySQL thread id 19, query id 4668734 localhost heikki update
    insert into speedcm values (1603393,1, 'hgjhjgghggjgjgjgjgjggjgjgjgjgjgggjgj
    gjlhhgghggggghhjhghgggggghjhghghghghghhhhghghghjhhjghjghjkghjghjghjghjfhjf
    ---TRANSACTION 0 290328327, ACTIVE 0 sec, process no 3200, OS thread id
    36880 starting index read
    LOCK WAIT 2 lock struct(s), heap size 320
    MySQL thread id 27, query id 4668644 localhost heikki Searching rows for
    update
    update ibtest11a set B = 'kHdkkkk' where A = 89572
    ------- TRX HAS BEEN WAITING 0 SEC FOR THIS LOCK TO BE GRANTED:
    RECORD LOCKS space id 0 page no 65556 n bits 232 table test/ibtest11a index
    PRIMARY trx id 0 290328327 lock_mode X waiting
    Record lock, heap no 1 RECORD: info bits 0 0: len 9; hex 73757072656d756d00;
    asc supremum.;;
    ------------------
    ---TRANSACTION 0 290328284, ACTIVE 0 sec, process no 3195, OS thread id
    34831 rollback of SQL statement
    ROLLING BACK 14 lock struct(s), heap size 2496, undo log entries 9
    MySQL thread id 25, query id 4668733 localhost heikki update
    insert into ibtest11a (D, B, C) values (5, 'khDk' ,'khDk')
    ---TRANSACTION 0 290327208, ACTIVE 1 sec, process no 3190, OS thread id
    32782
    58 lock struct(s), heap size 5504, undo log entries 159
    MySQL thread id 23, query id 4668732 localhost heikki update
    REPLACE INTO alex1 VALUES(86, 46, 538,'aa95666','bb','c95666','d9486t',
    'e200498f','g86814','h538',date_format('2001-04-03 12:54:22','%Y-%m-%d
    %H:%i'),
    ---TRANSACTION 0 290323325, ACTIVE 3 sec, process no 3185, OS thread id
    30733 inserting
    4 lock struct(s), heap size 1024, undo log entries 165
    MySQL thread id 21, query id 4668735 localhost heikki update
    INSERT INTO alex1 VALUES(NULL, 49, NULL,'aa42837','','c56319','d1719t','',
    NULL,'h321', NULL, NULL, 7.31,7.31,7.31,200)
    --------
    FILE I/O
    --------
    I/O thread 0 state: waiting for i/o request (insert buffer thread)
    I/O thread 1 state: waiting for i/o request (log thread)
    I/O thread 2 state: waiting for i/o request (read thread)
    I/O thread 3 state: waiting for i/o request (write thread)
    Pending normal aio reads: 0, aio writes: 0,
     ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
    Pending flushes (fsync) log: 0; buffer pool: 0
    151671 OS file reads, 94747 OS file writes, 8750 OS fsyncs
    25.44 reads/s, 18494 avg bytes/read, 17.55 writes/s, 2.33 fsyncs/s
    -------------------------------------
    INSERT BUFFER AND ADAPTIVE HASH INDEX
    -------------------------------------
    Ibuf for space 0: size 1, free list len 19, seg size 21,
    85004 inserts, 85004 merged recs, 26669 merges
    Hash table size 207619, used cells 14461, node heap has 16 buffer(s)
    1877.67 hash searches/s, 5121.10 non-hash searches/s
    ---
    LOG
    ---
    Log sequence number 18 1212842764
    Log flushed up to   18 1212665295
    Last checkpoint at  18 1135877290
    0 pending log writes, 0 pending chkp writes
    4341 log i/o's done, 1.22 log i/o's/second
    ----------------------
    BUFFER POOL AND MEMORY
    ----------------------
    Total memory allocated 84966343; in additional pool allocated 1402624
    Buffer pool size   3200
    Free buffers       110
    Database pages     3074
    Modified db pages  2674
    Pending reads 0
    Pending writes: LRU 0, flush list 0, single page 0
    Pages read 171380, created 51968, written 194688
    28.72 reads/s, 20.72 creates/s, 47.55 writes/s
    Buffer pool hit rate 999 / 1000
    --------------
    ROW OPERATIONS
    --------------
    0 queries inside InnoDB, 0 queries in queue
    Main thread process no. 3004, id 7176, state: purging
    Number of rows inserted 3738558, updated 127415, deleted 33707, read 755779
    1586.13 inserts/s, 50.89 updates/s, 28.44 deletes/s, 107.88 reads/s
    ----------------------------
    END OF INNODB MONITOR OUTPUT
    ============================

Einige Hinweise zu dieser Ausgabe:

- Wenn der `TRANSACTIONS`-Teil Lock Waits meldet, ist Ihre Anwendung vielleicht durch Sperren blockiert. Die Ausgabe kann auch helfen, die Gründe für Transaktions-Deadlocks festzustellen.

- Der `SEMAPHORES`-Teil meldet Threads, die auf Semaphoren warten, und Statistikdaten darüber, wie oft Threads einen Spin oder Wait auf einen Mutex oder eine Semaphpre einer Lese/Schreib-Sperre benötigt haben. Wenn viele Threads auf Semaphoren warten, kann dies am Festplatten-E/A liegen, oder auch an Verstopfungsproblemen in `InnoDB`. Verstopfungen können durch viele parallele Anfragen oder Probleme im Thread-Management des Betriebssystems bedingt sein. In solchen Situationen kann es helfen, `innodb_thread_concurrency` auf einen kleineren als den Standardwert zu setzen.

- Der Abschnitt `BUFFER POOL AND MEMORY` verrät Ihnen, wie viele Seiten gelesen und geschrieben werden. Aus diesen Zahlen können Sie berechnen, wie viele Datendateizugriffe Ihre Anfragen gerade ausführen.

- Der Abschnitt `ROW OPERATIONS` zeigt, was der Haupt-Thread gerade tut.

`InnoDB` sendet Diagnose-Ausgabe an `stderr` oder an Dateien anstatt an die `stdout`-Ausgabe oder Arbeitsspeicherpuffer fester Größe, um Puffer-Überläufe zu verhindern. Als Nebeneffekt wird die Ausgabe von `SHOW ENGINE INNODB STATUS` alle 15 Sekunden in eine Statusdatei im MySQL Data Directory geschrieben. Diese Datei heißt `innodb_status.`pid``, wobei `pid` die Server-Prozess-ID ist. `InnoDB` entfernt diese Datei bei einem normalen Shutdown. Bei einem unnormalen Shutdown können Instanzen dieser Statusdateien überleben und müssen manuell gelöscht werden. Bevor Sie dies tun, sollten Sie allerdings hineinschauen, um zu sehen ob sie irgendwelche Hinweise auf die Ursache der unnormalen Shutdowns enthalten. Die Datei `innodb_status.`pid`` wird nur angelegt, wenn die Konfigurationsoption `innodb_status_file=1` gesetzt ist.

### 14.2.12. Implementierung der Multiversionierung

Da `InnoDB` eine Speicher-Engine mit Multiversionierung ist, muss sie Informationen über ältere Versionen der Zeilen im Tablespace bewahren. Diese Informationen werden in einer Datenstruktur gespeichert, die wie in Oracle Rollback-Segment heißt.

Internally fügt `InnoDB` jeder Zeile, die in der Datenbank gespeichert wird, zwei Felder hinzu: ein 6 Byte großes Feld mit dem Transaktions-Identifier der letzten Transaktion, mit der die Zeile eingefügt oder geändert worden ist (eine Löschung wird intern wie eine Änderung behandelt, bei der ein bestimmtes Bit in der Zeile gesetzt wird, um sie als gelöscht zu kennzeichnen), und ein 7-Byte-Feld namens Rollpointer. Dieser zeigt auf einen Undo-Logeintrag, der in das Rollback-Segment geschrieben wurde. Wenn die Zeile geändert wurde, enthält dieser Undo-Logeintrag die Daten, die erforderlich sind, um ihren Inhalt von vor der Änderung wiederherzustellen.

`InnoDB` benutzt die Informationen aus dem Rollback-Segment, um die für ein Transaktions-Rollback erforderlichen Wiederherstellungsoperationen auszuführen. Außerdem dienen die Informationen der Erstellung älterer Versionen einer Zeile für eine konsistente Leseoperation.

Undo-Logs im Rollback-Segment sind in Insert- und Update-Undo-Logs getrennt. Insert-Undo-Logs werden nur für das Transaktions-Rollback gebraucht und können gelöscht werden, sobald die Transaktion committet wird. Update-Undo-Logs werden auch für konsistente Leseoperationen benutzt, können aber verworfen werden, wenn keine Transaktion mehr läuft, für die `InnoDB` einen Snapshot zugewiesen hat, der für eine konsistente Leseoperation die Daten des Update-Undo-Logs benötigen könnte, um eine ältere Version der Datenbankzeile wiederherzustellen.

Bitte denken Sie daran, Ihre Transaktionen regelmäßig zu committen, einschließlich derjenigen Transaktionen, die nur konsistente Leseoperationen ausgeben. Andernfalls kann `InnoDB` die Daten aus dem Update-Undo-Log nicht verwerfen und das Rollback-Segment kann so stark anwachsen, dass es Ihren Tablespace ganz ausfüllt.

Die physikalische Größe eines Undo-Logeintrags im Rollback-Segment ist normalerweise kleiner als die zugehörige eingefügte oder geänderte Zeile. Mit diesen Informationen können Sie berechnen, wie viel Platz für Ihr Rollback-Segment erforderlich ist.

In der Multiversionierung von `InnoDB` wird eine Zeile nicht sofort physikalisch aus der Datenbank entfernt, wenn Sie sie mit einer SQL-Anweisung löschen. Erst wenn `InnoDB` den für die Löschung erstellten Update-Undo-Logeintrag entfernen kann, kann es auch die Zeile und ihre Indexeinträge physikalisch aus der Datenbank entfernen. Diese Löschoperation bezeichnet man als Purge. Sie läuft sehr schnell, ungefähr genauso schnell wie die SQL-Löschanweisung.

In einem Szenario, in dem der Benutzer Zeilen in kleinen, ungefähr gleichen Batches aus der Tabelle löscht, kann es passieren, dass der Purge-Thread beginnt, hinterherzuhinken, und die Tabelle immer größer wird, wodurch sich alle Festplattenoperationen stark verlangsamen. Selbst Tabellen, die nur 10MB an brauchbaren Daten aufweisen, können mit allen „toten“ Zeilen auf 10GB anwachsen. In solchen Fällen wäre es gut, neue Zeilenoperationen zurückzufahren und dem Purge-Thread mehr Ressourcen zuzuweisen. Genau zu diesem Zweck gibt es die Systemvariable `innodb_max_purge_lag`. Weitere Informationen finden Sie unter Abschnitt 14.2.4, „`InnoDB`: Startoptionen und Systemvariablen“.

### 14.2.13. Tabellen- und Indexstrukturen

MySQL speichert seine Data Dictionary-Informationen über Tabellen in `.frm`-Dateien in Datenbankverzeichnissen. Das gilt für alle Speicher-Engines von MySQL. Doch jede `InnoDB`-Tabelle hat auch einen eigenen Eintrag im internen `InnoDB`-Data Dictionary innerhalb des Tablespace. Wenn MySQL eine Tabelle oder Datenbank löscht, muss es sowohl die `.frm`-Datei(en) als auch die zugehörigen Einträge im `InnoDB`-Data Dictionary löschen. Daher dürfen Sie auch `InnoDB`-Tabellen nicht einfach durch Verschieben der `.frm`-Dateien von einer Datenbank in die andere übertragen.

Jede `InnoDB`-Tabelle besitzt einen speziellen, so genannten geclusterten Index, der die Daten über die Zeilen speichert. Wenn Sie einen `PRIMARY KEY` auf Ihrer Tabelle definieren, ist der Index dieses Primärschlüssels der geclusterte Index.

Wenn Sie keinen `PRIMARY KEY` für Ihre Tabelle definieren, wählt MySQL den ersten `UNIQUE`-Index, der nur `NOT NULL`-Spalten hat, als Primärschlüssel aus, und `InnoDB` verwendet diesen als geclusterten Index. Wenn die Tabelle keinen solchen Index besitzt, wird intern von `InnoDB` ein geclusterter Index generiert, in dem die Zeilen nach der Zeilen-ID geordnet sind, die `InnoDB` den Zeilen derartiger Tabellen zuweist. Die Zeilen-ID ist ein 6-Byte-Feld, das monoton wächst, wenn neue Zeilen eingefügt werden. Somit stehen die nach Zeilen-ID geordneten Zeilen physikalisch in der Reihenfolge ihrer Einfügung.

Über einen geclusterten Index ist eine Zeile schnell zu erreichen, da die Zeilendaten auf derselben Seite liegen, zu der die Indexsuche hinführt. Wenn eine Tabelle groß ist, speichert die Clustered-Index-Architektur im Vergleich zu der traditionellen Lösung oft Plattenzugriffe. (In vielen Datenbanksystemen sind die Daten auf einer anderen Seite gespeichert als der Indexeintrag.)

In `InnoDB` enthalten die Datensätze in nicht-geclusterten Indizes (auch Sekundärindizes genannt) den Primärschlüsselwert für die Zeile. `InnoDB` benutzt diesen Primärschlüsselwert, um die Zeile in dem geclusterten Index zu suchen. Beachten Sie: Wenn der Primärschlüssel lang ist, brauchen die Sekundärindizes mehr Platz.

`InnoDB` vergleicht unterschiedlich lange `CHAR`- und `VARCHAR`-Strings, indem es die Längendifferenz im kürzeren String als mit Leerzeichen ausgefüllt betrachtet.

#### 14.2.13.1. Physikalische Struktur eines Index

Alle `InnoDB`-Indizes sind B-Trees, wobei die Indexeinträge in den Blattseiten des Baums gespeichert werden. Die Standardgröße einer Indexseite beträgt 16KB. Wenn neue Einträge eingefügt werden, versucht `InnoDB`, 1/16 der Seite für zukünftige Einfügungen und Änderungen der Indexeinträge frei zu halten.

Wenn Indexeinträge in (auf- oder absteigender) sequenzieller Reihenfolge eingefügt werden, werden die Indexseiten zu 15/16 voll. Werden die Einträge in zufälliger Reihenfolge eingefügt, werden die Seiten nur zu 1/2 bis 15/16 voll. Wenn der Füllstand einer Indexseite unter 1/2 fällt, versucht `InnoDB` mit dem Indexbaum zu vereinbaren, dass die Seite freigegeben wird.

#### 14.2.13.2. Einfügepufferung

In Datenbankanwendungen kommt es häufig vor, dass ein Primärschlüssel ein eindeutiger Identifier ist und neue Zeilen in der aufsteigenden Reihenfolge des Primärschlüssels eingefügt werden. So sind für Einfügungen in den geclusterten Index keine willkürlichen Lesezugriffe auf eine Festplatte erforderlich.

Dagegen sind Sekundärindizes normalerweise nicht-eindeutig und Einfügungen in sie finden in relativ willkürlicher Reihenfolge statt. Dies würde eine Menge willkürlicher E/A-Operationen auf der Festplatte erfordern, wenn es in `InnoDB` nicht einen speziellen Mechanismus gäbe.

Wenn ein Indexeintrag in einen nicht-eindeutigen Sekundärindex eingefügt werden soll, prüft `InnoDB`, ob dieser Sekundärindex im Bufferpool liegt. Wenn ja, führt `InnoDB` die Einfügung direkt auf der Indexseite durch. Wenn nicht, fügt `InnoDB` den Eintrag in eine spezielle Insert-Puffer-Struktur ein. Der Insert-Puffer wird so klein gehalten, dass er komplett in den Bufferpool passt und Einfügungen sehr schnell erledigt werden können.

Dieser Insert-Puffer wird regelmäßig mit den Sekundärindexbäumen in der Datenbank zusammengeführt. Oft können mehrere Einfügeoperationen auf derselben Seite des Indexbaums gleichzeitig zusammengeführt werden, was Festplattenzugriffe spart. Messungen haben ergeben, dass der Insert-Puffer Einfügungen in eine Tabelle bis zu 15-mal schneller laufen lässt.

Das Zusammenführen von Insert-Puffer-Daten kann auch dann weitergehen, nachdem die einfügende Transaktion committet wurde, ja sogar nach dem Herunterfahren und Neustart des Servers (siehe Abschnitt 14.2.8.1, „Erzwingen einer `InnoDB`-Wiederherstellung (Recovery)“).

Das Zusammenführen von Insert-Puffer-Daten kann viele Stunden dauern, wenn viele Sekundärindizes aktualisiert werden müssen und viele Zeilen eingefügt wurden. In dieser Zeit erhöht sich die E/A-Aktivität, sodass festplattengebundene Anfragen eventuell viel langsamer laufen. Eine andere, wichtige E/A-Hintergrundoperation ist der Purge-Thread (siehe Abschnitt 14.2.12, „Implementierung der Multiversionierung“).

#### 14.2.13.3. Anpassungsfähige Hash-Indizes

Wenn eine Tabelle fast komplett in den Hauptspeicher passt, sind Hash-Indizes das schnellste Mittel, um Anfragen auszuführen. `InnoDB` hat einen Mechanismus, der Index-Suchen auf den Indizes einer Tabelle beobachtet. Wenn `InnoDB` bemerkt, dass Anfragen von einem Hash-Index profitieren könnten, baut es automatisch einen auf.

Beachten Sie, dass der Hash-Index immer auf einem vorhandenen B-Baum-Index der Tabelle aufbaut. `InnoDB` kann einen Hash-Index auf einem beliebig langen Präfix des für den B-Baum definierten Schlüssels aufbauen, je nachdem, welches Suchmuster `InnoDB` für den B-Baum-Index beachtet. Ein Hash-Index kann auch partiell sein: Es ist nicht notwendig, den gesamten B-Baum-Index im Bufferpool zu cachen. `InnoDB` baut Hash-Indizes nach Bedarf für diejenigen Indexseiten auf, die oft angesprochen werden.

In gewissem Sinne legt sich `InnoDB` durch den anpassungsfähigen Hash-Index-Mechanismus selbst auf einen großen Arbeitsspeicher hin aus und kommt dadurch der Architektur von Hauptspeicher-Datenbanken näher.

#### 14.2.13.4. Physikalische Datensatzstruktur

Datensätze von `InnoDB`-Tabellen haben folgende Merkmale:

- Jeder Indexeintrag hat einen sechs Bytes großen Header, der benutzt wird, um aufeinanderfolgende Datensätze zu verknüpfen, und auch in Zeilensperren zum Einsatz kommt.

- Einträge im geclusterten Index enthalten Felder für alle benutzerdefinierten Spalten. Zusätzlich gibt es ein sechs Bytes großes Feld für die Transaktions-ID und ein sieben Bytes großes für den Rollpointer.

- Wenn für eine Tabelle kein Primärschlüssel definiert wurde, enhält jeder geclusterte Indexeintrag auch ein sechs Bytes großes Feld mit der Zeilen-ID.

- Jeder Sekundärindexeintrag enthält auch alle Felder, die für den geclusterten Indexschlüssel definiert worden sind.

- Ein Datensatz enthält auch einen Zeiger auf die einzelnen Felder des Datensatzes. Wenn deren Gesamtlänge 128 Bytes nicht übersteigt, ist der Zeiger ein Byte, ansonsten zwei Bytes groß. Das Array dieser Zeiger bezeichnet man als Record Directory. Der Bereich, auf den sie zeigen, ist der Datenteil des Datensatzes.

- Intern speichert `InnoDB` Zeichenspalten fester Breite, wie beispielsweise `CHAR(10)`, in einem Format mit fester Länge. `InnoDB` kappt Leerzeichen am Ende von `VARCHAR`-Spalten.

- Ein SQL-`NULL`-Wert reserviert 1 oder 2 Bytes im Record Directory und null Bytes im Datenteil des Eintrags, wenn er in einer Spalte mit variabler Länge gespeichert ist. In einer Spalte fester Länge reserviert er diese festgelegte Länge auch für `NULL`-Werte, damit eine Aktualisierung, die die Spalte von `NULL` auf einen anderen Wert setzt, vor Ort ohne Fragmentierung der Indexseite ausgeführt werden kann.

### 14.2.14. Verwaltung von Speicherplatz für Dateien und von Festplattenein- und -ausgaben

#### 14.2.14.1. Festplattenein- und -ausgaben

`InnoDB` verwendet simulierte, asynchrone Festplattenein- und -ausgaben (E/A) und erzeugt mehrere Threads für E/A-Operationen wie beispielsweise Read-ahead.

Es gibt zwei Read-ahead-Heuristiken in `InnoDB`:

- Beim sequenziellen Read-ahead schickt `InnoDB`, wenn es bemerkt, dass ein Segment im Tablespace ein sequenzielles Zugriffsmuster aufweist, im Voraus einen Batch Leseoperationen auf Datenbankseiten an das E/A-System.

- Beim willkürlichen Read-ahead schickt `InnoDB`, wenn es bemerkt, dass ein Bereich im Tablespace im Begriff ist, vollständig in den Bufferpool eingelesen zu werden, die restlichen Leseoperationen an das E/A-System.

`InnoDB` verwendet eine neue Technik namens Doublewrite, um Dateien auf die Festplatte zurückzuschreiben. Dadurch wird die Recovery nach einem Betriebssystemabsturz oder einem Stromausfall sicherer und die Leistung der meisten Unix-Varianten besser, da nicht mehr so viele `fsync()`-Operationen nötig sind.

Doublewrite sorgt dafür, dass `InnoDB` Seiten nicht gleich in die Datendatei, sondern zuerst in einen zusammenhängenden Tablespace-Bereich namens Doublewrite-Puffer schreibt. Erst wenn dieser Vorgang abgeschlossen ist, werden die Seiten in ihre angestammten Plätze in der Datendatei geschrieben. Stürzt das Betriebssystem mitten im Schreiben einer Seite ab, kann `InnoDB` später bei der Recovery eine gute Kopie der Seite im Doublewrite-Puffer finden.

#### 14.2.14.2. Speicherplatzverwaltung

Die in der Konfigurationsdatei definierten Datendateien bilden den Tablespace von `InnoDB`. Die Dateien werden einfach aneinander gehängt, um diesen Tablespace zu bilden. Es wird kein Striping verwendet. Zurzeit können Sie noch nicht bestimmen, an welche Stelle des Tablespaces Ihre Tabellen zugewiesen werden. Doch in einem neuen Tablespace wird der Platz beginnend mit der ersten Datendatei zugewiesen.

Der Tablespace besteht aus Datenbankseiten mit einer Standardgröße von 16KB. Die Seiten werden zu Extents von 64 aufeinander folgenden Seiten zusammengefasst. Die „Dateien“ eines Tablespace nennt man in `InnoDB` Segmente. Der Begriff „Rollback-Segment“ ist etwas irreführend, da er in Wirklichkeit viele Tablespace-Segmente umfasst.

Für jeden Index in `InnoDB` werden zwei Segmente zugewiesen. eines für Nicht-Blattknoten und das andere für Blattknoten des B-Baums. Dadurch wird eine bessere Abfolge der Blattknoten, die die eigentlichen Daten enthalten, erreicht.

Wenn ein Segment im Tablespace anwächst, weist ihm `InnoDB` die ersten 32 Seiten einzeln zu, und danach nur noch vollständige Extents. `InnoDB` kann einem großen Segment bis zu 4 Extents zugleich zuweisen, um eine gute Datensequenzialität zu gewährleisten.

Da manche Seiten im Tablespace Bitmaps anderer Seiten enthalten, können einige Extents eines `InnoDB`-Tablespace den Segmenten nicht als Ganzes, sondern nur in Form einzelner Seiten zugewiesen werden.

Wenn Sie mit einer `SHOW TABLE STATUS`-Anweisung fragen, wie viel Platz in einem Tablespace noch frei ist, meldet `InnoDB` nur die Extents, die darin definitiv noch nicht belegt sind. `InnoDB` reserviert immer einige Extents für die Reinigungsaufgaben und andere interne Zwecke. Diese reservierten Extents sind in dem freien Platz nicht inbegriffen.

Wenn Sie Daten aus einer Tabelle löschen, kontrahiert `InnoDB` die entsprechenden B-Baum-Indizes. Ob der frei gewordene Platz für ander Benutzer verfügbar wird, hängt davon ab, ob das Löschungsmuster einzelne Seiten oder ganze Extents des Tablespace freigibt. Wenn Sie eine Tabelle oder alle in ihr befindlichen Zeilen löschen, dann wird garantiert der Platz für andere Benutzer freigegeben, aber vergessen Sie nicht, dass gelöschte Zeilen physikalisch erst in einer (automatischen) Purge-Operation entfernt werden, wenn sie auch für Transaktions-Rollbacks und konsistente Leseoperationen nicht mehr gebraucht werden. (Siehe Abschnitt 14.2.12, „Implementierung der Multiversionierung“.)

#### 14.2.14.3. Eine Tabelle defragmentieren

Wenn willkürliche Einfügungen oder Löschungen in den Indizes einer Tabelle vorgenommen werden, können diese Indizes fragmentiert werden. Fragmentierung bedeutet, dass die physikalische Reihenfolge der Indexseiten auf der Platte nicht der Reihenfolge des Index für die Datensätze der Seiten entspricht, oder dass viele ungenutzte Seiten in den 64-Seiten-Blöcken vorhanden sind, die dem Index zugewiesen wurden.

Ein Fragmentierungssymptom liegt vor, wenn eine Tabelle mehr Platz belegt, als sie „sollte“. Wieviel mehr, das ist in der Praxis schwer zu sagen. Alle `InnoDB`-Daten und -Indizes werden in B-Bäumen gespeichert und ihr Füllfaktor kann zwischen 50% und 100% variieren. Ein anderes Symptom für Fragmentierung wäre es, wenn ein Tabellenscan wie dieser mehr Zeit braucht, als er „sollte“:

    SELECT COUNT(*) FROM t WHERE a_non_indexed_column <> 12345;

(In der obigen Anfrage „überlisten“ wir die SQL-Optimierung, damit sie statt des Sekundärindex den geclusterten Index durchsucht.) Die meisten Festplatten können 10 bis 50MB/s lesen. Anhand dieses Werts können sie einschätzen, wie schnell ein Tabellenscan eigentlich laufen sollte.

Index-Scans können schneller laufen, wenn Sie regelmäßig eine „Null“-`ALTER TABLE`-Operation durchführen:

    ALTER TABLE tbl_name ENGINE=INNODB

Dies veranlasst MySQL, die Tabelle neu zu erstellen. Eine andere Möglichkeit für eine Defragmentierungsoperation wäre es, die Tabelle mit mysqldump in eine Textdatei zu dumpen, zu löschen und aus der Dump-Datei neu zu laden.

Wenn die Einfügungen in einen Index immer in aufsteigender Reihenfolge und Löschungen nur an seinem Ende stattfinden, garantiert der Dateiraumverwaltungs-Algorithmus von `InnoDB`, dass keine Fragmentierung in diesem Index auftreten kann.

### 14.2.15. `InnoDB`-Fehlerbehandlung

Die Fehlerbehandlung in `InnoDB` entspricht nicht immer dem SQL-Standard. Nach dem Standard müsste jeder Fehler, der während einer SQL-Anweisung auftritt, den Rollback dieser Anweisung zur Folge haben. `InnoDB` rollt jedoch manchmal nur einen Teil der Anweisung zurück, oder aber auch die ganze Transaktion. Die folgenden Einträge beschreiben die Fehlerbehandlung von `InnoDB`:

- Wenn Ihr Tablespace vollläuft, tritt ein MySQL `Table is full`-Fehler auf und `InnoDB` rollt die SQL-Anweisung zurück.

- Ein Transaktions-Deadlock lässt `InnoDB` die gesamte Transaktion zurückrollen. Im Fall eines Lock Wait Timeout rollt `InnoDB` nur die letzte SQL-Anweisung zurück.

  Wenn wegen eines Deadlocks oder Lock Wait Timeouts eine Transaktion zurückgerollt wird, so werden die in dieser Transaktion ausgeführten Anweisungen wirkungslos. Wenn jedoch die Startanweisung der Transaktion `START TRANSACTION` oder `BEGIN` war, so wird die Anweisung durch den Rollback nicht betroffen. Weitere SQL-Anweisungen werden dann zu einem Teil der Transaktion, bis ein `COMMIT`, `ROLLBACK` oder eine andere SQL-Anweisung eintritt, die einen impliziten Commit verursacht.

- Ein Schlüsselduplikat-Fehler rollt die SQL-Anweisung zurück, wenn Sie nicht die `IGNORE`-Option in der Anweisung angegeben haben.

- Ein `row too long error` rollt die SQL-Anweisung zurück.

- Andere Fehler werden zumeist auf der MySQL-Ebene erkannt (also oberhalb der Ebene von `InnoDB`), und rollen die zugehörige SQL-Anweisung zurück. Sperren werden bei einem Rollback einer einzelnen SQL-Anweisung nicht freigegeben.

Während eines impliziten Rollback und während der Ausführung eines expliziten `ROLLBACK`-Befehls von SQL zeigt `SHOW PROCESSLIST` den Wert `Rolling back` in der `State`-Spalte der betreffenden Verbindung an.

#### 14.2.15.1. `InnoDB`-Fehlercodes

Es folgt eine (nicht vollständige) Liste häufiger `InnoDB`-spezifischer Fehler, einschließlich ihrer Ursachen und Lösungsmöglichkeiten.

- `1005 (ER_CANT_CREATE_TABLE)`

  Tabelle kann nicht angelegt werden. Wenn die Fehlermeldung auf `errno` 150 verweist, schlug die Tabellenerzeugung fehl, weil ein Fremdschlüssel-Constraint nicht richtig gebildet wurde.

- `1016 (ER_CANT_OPEN_FILE)`

  Die `InnoDB`-Tabelle kann in den `InnoDB`-Datendateien nicht gefunden werden, obwohl die `.frm`-Datei für die Tabelle existiert. Siehe Abschnitt 14.2.17.1, „Troubleshooting von `InnoDB` bei Data Dictionary-Operationen“.

- `1114 (ER_RECORD_FILE_FULL)`

  `InnoDB` hat keine Platz im Tablespace mehr frei. Rekonfigurieren Sie den Tablespace, indem Sie eine neue Datendatei hinzufügen.

- `1205 (ER_LOCK_WAIT_TIMEOUT)`

  Lock Wait Timeout ist abgelaufen. Transaktion wurde zurückgerollt.

- `1213 (ER_LOCK_DEADLOCK)`

  Transaktions-Deadlock. Führen Sie die Transaktion erneut aus.

- `1216 (ER_NO_REFERENCED_ROW)`

  Sie versuchen, eine neue Zeile einzufügen, aber da es keine Elternzeile gibt, wird ein Fremdschlüssel-Constraint verletzt. Fügen Sie als Erstes die Elternzeile ein.

- `1217 (ER_ROW_IS_REFERENCED)`

  Sie versuchen, eine Elternzeile zu löschen, die Kinder hat, wodurch ein Fremdschlüssel-Constraint verletzt wird. Löschen Sie zuerst die Kinder.

#### 14.2.15.2. Betriebssystembedingte Fehlercodes

Die Bedeutung einer Betriebssystem-Fehlernummer können Sie mit dem Programm perror ermitteln, das zur MySQL-Distribution gehört.

Die folgende Tabelle enthält eine Liste der häufigsten Systemfehlercodes von Linux. Eine vollständigere Aufstellung finden Sie unter Linux-Quellcode.

- `1 (EPERM)`

  Operation nicht gestattet

- `2 (ENOENT)`

  Datei oder Verzeichnis nicht vorhanden

- `3 (ESRCH)`

  Prozess nicht vorhanden

- `4 (EINTR)`

  Unterbrochener Systemaufruf

- `5 (EIO)`

  E/A-Fehler

- `6 (ENXIO)`

  Device oder Adresse nicht vorhanden

- `7 (E2BIG)`

  Argumenteliste zu lang

- `8 (ENOEXEC)`

  Exec-Format-Fehler

- `9 (EBADF)`

  Dateinummer stimmt nicht

- `10 (ECHILD)`

  Kein Kindprozess

- `11 (EAGAIN)`

  Versuchen Sie es erneut

- `12 (ENOMEM)`

  Kein Arbeitsspeicher mehr frei

- `13 (EACCES)`

  Berechtigung verweigert

- `14 (EFAULT)`

  Adresse stimmt nicht

- `15 (ENOTBLK)`

  Block-Device erforderlich

- `16 (EBUSY)`

  Device oder Ressource ist belegt

- `17 (EEXIST)`

  Datei vorhanden

- `18 (EXDEV)`

  Cross-Device-Link

- `19 (ENODEV)`

  Device nicht vorhanden

- `20 (ENOTDIR)`

  Ist kein Verzeichnis

- `21 (EISDIR)`

  Ist ein Verzeichnis

- `22 (EINVAL)`

  Ungültiges Argument

- `23 (ENFILE)`

  Dateitabellenüberlauf

- `24 (EMFILE)`

  Zu viele geöffnete Dateien

- `25 (ENOTTY)`

  Unpassender ioctl für Device

- `26 (ETXTBSY)`

  Textdatei belegt

- `27 (EFBIG)`

  Datei zu groß

- `28 (ENOSPC)`

  Device hat keinen Platz mehr frei

- `29 (ESPIPE)`

  Unzulässige Suche

- `30 (EROFS)`

  Schreibgeschütztes Dateisystem

- `31 (EMLINK)`

  Zu viele Links

Die folgende Tabelle enthält eine Liste häufiger Windows-Systemfehlercodes. Eine vollständigere Aufstellung finden Sie auf der Website von Microsoft.

- `1 (ERROR_INVALID_FUNCTION)`

  Funktion nicht korrekt

- `2 (ERROR_FILE_NOT_FOUND)`

  System kann die Datei nicht finden

- `3 (ERROR_PATH_NOT_FOUND)`

  System kann den Pfad nicht finden

- `4 (ERROR_TOO_MANY_OPEN_FILES)`

  System kann die Datei nicht öffnen

- `5 (ERROR_ACCESS_DENIED)`

  Zugriff verweigert

- `6 (ERROR_INVALID_HANDLE)`

  Ungültiger Handle

- `7 (ERROR_ARENA_TRASHED)`

  Speichersteuerungsblöcke wurden zerstört

- `8 (ERROR_NOT_ENOUGH_MEMORY)`

  Zu wenig Speicher, um diesen Befehl zu verarbeiten

- `9 (ERROR_INVALID_BLOCK)`

  Speichersteuerungsblock-Adresse ist ungültig

- `10 (ERROR_BAD_ENVIRONMENT)`

  Die Umgebung ist nicht korrekt

- `11 (ERROR_BAD_FORMAT)`

  Versuch, ein Programm mit einem unzulässigen Format zu laden

- `12 (ERROR_INVALID_ACCESS)`

  Zugriffscode ist ungültig

- `13 (ERROR_INVALID_DATA)`

  Daten sind ungültig

- `14 (ERROR_OUTOFMEMORY)`

  Nicht genug Speicher vorhanden, um die Operation auszuführen

- `15 (ERROR_INVALID_DRIVE)`

  System kann das angegebene Laufwerk nicht finden

- `16 (ERROR_CURRENT_DIRECTORY)`

  Verzeichnis kann nicht entfernt werden

- `17 (ERROR_NOT_SAME_DEVICE)`

  System kann die Datei nicht auf ein anderes Festplattenlaufwerk verlagern

- `18 (ERROR_NO_MORE_FILES)`

  Keine weiteren Dateien vorhanden

- `19 (ERROR_WRITE_PROTECT)`

  Schreibgeschütztes Speichermedium

- `20 (ERROR_BAD_UNIT)`

  System kann das angegebene Gerät nicht finden

- `21 (ERROR_NOT_READY)`

  Gerät ist nicht bereit

- `22 (ERROR_BAD_COMMAND)`

  Gerät erkennt den Befehl nicht

- `23 (ERROR_CRC)`

  Datenfehler (zyklische Redundanzprüfung)

- `24 (ERROR_BAD_LENGTH)`

  Das Programm hat einen Befehl gegeben, dessen Länge nicht stimmt

- `25 (ERROR_SEEK)`

  Das Laufwerk konnte einen bestimmten Bereich oder Track auf der Festplatte nicht finden

- `26 (ERROR_NOT_DOS_DISK)`

  Kein Zugriff auf die Platte oder Diskette

- `27 (ERROR_SECTOR_NOT_FOUND)`

  Das Laufwerk kann den angeforderten Sektor nicht finden

- `28 (ERROR_OUT_OF_PAPER)`

  Kein Papier im Drucker

- `29 (ERROR_WRITE_FAULT)`

  System kann auf das angegebene Gerät nicht schreiben

- `30 (ERROR_READ_FAULT)`

  System kann von dem angegebenen Gerät nicht lesen

- `31 (ERROR_GEN_FAILURE)`

  Ein mit dem System verbundenes Gerät funktioniert nicht

- `32 (ERROR_SHARING_VIOLATION)`

  Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess benutzt wird

- `33 (ERROR_LOCK_VIOLATION)`

  Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess teilweise gesperrt wurde

- `34 (ERROR_WRONG_DISK)`

  Verkehrte Diskette im Laufwerk. Legen Sie %2 (Volume-Seriennummer: %3) in Laufwerk %1 ein

- `36 (ERROR_SHARING_BUFFER_EXCEEDED)`

  Zu viele Dateien für gemeinsame Nutzung geöffnet

- `38 (ERROR_HANDLE_EOF)`

  Dateiende erreicht

- `39 (ERROR_HANDLE_DISK_FULL)`

  Festplatte ist voll

- `87 (ERROR_INVALID_PARAMETER)`

  Der Parameter ist verkehrt. (Wenn auf Windows dieser Fehler auftritt und `innodb_file_per_table` in einer Serveroptionsdatei eingeschaltet wurde, fügen Sie der Datei zusätzlich die Zeile `innodb_flush_method=unbuffered` hinzu.)

- `112 (ERROR_DISK_FULL)`

  Festplatte ist voll

- `123 (ERROR_INVALID_NAME)`

  Dateiname oder Verzeichnisname oder Volume-Label-Syntax ist verkehrt

- `1450 (ERROR_NO_SYSTEM_RESOURCES)`

  Systemressourcen reichen nicht, um den angefragten Service abzuschließen

### 14.2.16. Beschränkungen von `InnoDB`-Tabellen

- Warnung: Konvertieren Sie niemals MySQL-Systemtabellen in der `mysql`-Datenbank aus dem `MyISAM`- in das `InnoDB`-Format! Diese Operation wird nicht unterstützt. Wenn Sie dies tun, kann MySQL erst dann wieder gestartet werden, wenn Sie die alten Systemtabellen aus einem Backup wiederhergestellt oder mit dem Skript mysql_install_db neu generiert haben.

- Eine Tabelle darf nicht mehr als 1000 Spalten haben.

- Die interne maximale Schlüssellänge beträgt 3500 Bytes, aber MySQL selbst schränkt dies auf 1024 Bytes ein.

- Die maximale Zeilenlänge beträgt (außer bei `VARCHAR`-, `BLOB`- und `TEXT`-Spalten) etwas weniger als die Hälfte einer Datenbankseite, also rund 8000 Bytes. `LONGBLOB`- und `LONGTEXT`-Spalten müssen kleiner als 4GB und die maximale Länge einer Zeile (auch bei `BLOB`- und `TEXT`-Spalten) muss kleiner als 4GB sein. `InnoDB` speichert die ersten 768 Bytes einer `VARCHAR`-, `BLOB`- oder `TEXT`-Spalte in der Zeile und den Rest in separaten Seiten.

- Obwohl `InnoDB` intern auch Zeilen von mehr als 65535 unterstützt, können Sie keine Zeile definieren, die `VARCHAR`-Spalten mit einer kombinierten Größe von mehr als 65535 enthält:

      mysql> CREATE TABLE t (a VARCHAR(8000), b VARCHAR(10000),
          -> c VARCHAR(10000), d VARCHAR(10000), e VARCHAR(10000),
          -> f VARCHAR(10000), g VARCHAR(10000)) ENGINE=InnoDB;
      ERROR 1118 (42000): Row size too large. The maximum row size for the
      used table type, not counting BLOBs, is 65535. You have to change some
      columns to TEXT or BLOBs

- Auf manchen älteren Betriebssystemen müssen Dateien kleiner als 2GB sein. Das ist zwar keine `InnoDB`-spezifische Beschränkung, aber wenn Sie einen großen Tablespace benötigen, müssen Sie diesen so konfigurieren, dass er mehrere kleine statt einer großen Datendatei enthält.

- Die kombinierte Größe aller `InnoDB`-Logdateien muss unter 4GB liegen.

- Die Mindestgröße eines Tablespace beträgt 10MB und seine Höchstgröße vier Milliarden Datenbankseiten (64TB). Dies ist auch die Maximalgröße für eine Tabelle.

- `InnoDB`-Tabellen unterstützen keine `FULLTEXT`-Indizes.

- `ANALYZE TABLE` ermittelt die Indexkardinalität (anhand der `Cardinality`-Spalte der Ausgabe von `SHOW INDEX`), indem er acht Zufallssprünge in jeden der Indexbäume unternimmt und die Schätzungen der Indexkardinalität entsprechend aktualisiert. Da dies nur Schätzungen sind, können unterschiedliche Ausführungen von `ANALYZE TABLE` unterschiedliche Zahlen ergeben. Dadurch läuft `ANALYZE TABLE` auf `InnoDB`-Tabellen zwar schnell, aber nicht zu 100% präzise, da es nicht alle Zeilen berücksichtigt.

  MySQL verwendet die Indexkardinalitäts-Schätzungen nur in der Optimierung von Joins. Ist ein Join nicht richtig optimiert, können Sie es mit `ANALYZE TABLE` versuchen. In den seltenen Fällen, da `ANALYZE TABLE` für Ihre Tabellen keine ausreichend guten Werte produziert, können Sie Ihre Anfragen mit `FORCE INDEX` ausführen und ihnen damit einen bestimmten Index aufzwingen, oder die Systemvariable `max_seeks_for_key` setzen, damit MySQL eher im Index nachschaut, als Tabellen zu scannen. Siehe Abschnitt 5.2.2, „Server-Systemvariablen“ und Abschnitt A.6, „Probleme im Zusammenhang mit dem Optimierer“.

- `SHOW TABLE STATUS` zeigt keine präzisen Statistikdaten über `InnoDB`-Tabellen an, wenn man von der physikalischen Größe absieht, die für sie reserviert ist. Die Zeilenzahl ist nur eine grobe Schätzung, die für die SQL-Optimierung genutzt wird.

- `InnoDB` pflegt keinen internen Zähler für die Anzahl der Zeilen in einer Tabelle. (In der Praxis wäre das wegen der Multiversionierung auch etwas kompliziert.) Um eine `SELECT COUNT(*) FROM t`-Anweisung zu verarbeiten, muss `InnoDB` einen Index der Tabelle scannen. Das braucht Zeit, wenn der Index nicht komplett im Bufferpool liegt. Um schneller eine Zahl zu erhalten, müssen Sie selbst eine Zählertabelle erstellen und dafür sorgen, dass Ihre Anwendung sie bei Einfügungen und Löschungen aktualisiert. Wenn sich Ihre Tabelle nicht oft ändert, ist der MySQL-Anfragecache eine gute Lösung. `SHOW TABLE STATUS` kann ebenfalls eingesetzt werden, wenn Ihnen ein Näherungswert genügt. Siehe Abschnitt 14.2.11, „Tipps zur Leistungssteigerung“.

- Auf Windows speichert `InnoDB` Datenbank- und Tabellennamen intern immer in Kleinbuchstaben. Um Datenbanken im Binärformat von Unix auf Windows oder umgekehrt zu übertragen, sollten Sie immer explizit klein geschriebene Namen für Datenbanken und Tabellen verwenden.

- Für eine `AUTO_INCREMENT`-Spalte müssen Sie immer einen Index für die Tabelle definieren, der nur diese `AUTO_INCREMENT`-Spalte enthält. In `MyISAM`-Tabellen kann die `AUTO_INCREMENT`-Spalte auch Teil eines Mehrspaltenindex sein.

- Wenn Sie den MySQL-Server neu starten, kann `InnoDB` einen alten Wert wiederverwenden, der für eine `AUTO_INCREMENT`-Spalte zwar angelegt, aber nie gespeichert wurde (also einen Wert, der während einer älteren Transaktion generiert wurde, die zurückgerollt worden ist).

- Wenn einer `AUTO_INCREMENT`-Spalte die Werte ausgehen, bricht `InnoDB` einen `BIGINT` auf `-9223372036854775808` und `BIGINT UNSIGNED` auf `1` um. Da jedoch `BIGINT`-Werte 64 Bits haben, würde es, selbst wenn Sie eine Million Zeilen pro Sekunde einfügten, immer noch fast dreihunderttausend Jahre dauern, bis `BIGINT` an seine Grenze stößt. Bei allen anderen Integerspalten würde ein Fehler wegen Schlüsselduplikat die Folge sein. `MyISAM` funktioniert ähnlich, da dies im Wesentlichen dem normalen MySQL-Verhalten und nicht dem einer bestimmten Speicher-Engine entspricht.

- `DELETE FROM `tbl_name`` generiert die Tabelle nicht neu, sondern löscht eine nach der anderen alle Zeilen.

- Unter bestimmten Gegebenheiten wird `TRUNCATE `tbl_name`` für eine `InnoDB`-Tabelle wie `DELETE FROM `tbl_name`` behandelt und setzt nicht den `AUTO_INCREMENT`-Zähler zurück. Siehe Abschnitt 13.2.9, „`TRUNCATE`“.

- In MySQL 5.1, erwirbt die MySQL-Operation `LOCK TABLES` zwei Sperren auf jeder Tabelle, wenn `innodb_table_locks=1` (die Standardeinstellung). Zusätzlich zu einer Tabellensperre auf der MySQL-Ebene, errichtet es auch eine `InnoDB`-Tabellensperre. Ältere MySQL-Versionen errichteten keine `InnoDB`-Tabellensperren. Dieses alte Verhalten kann mit `innodb_table_locks=0` eingestellt werden. Wenn keine `InnoDB`-Tabellensperre erworben wird, läuft `LOCK TABLES` auch dann, wenn einige Datensätze der Tabellen von anderen Transaktionen gesperrt sind.

- Alle `InnoDB`-Sperren, die eine Transaktion hält, werden freigegeben, wenn die Transaktion committet oder abgebrochen wird. Also hat es wenig Sinn `LOCK TABLES` auf `InnoDB`-Tabellen im `AUTOCOMMIT=1`-Modus aufzurufen, da die `InnoDB`-Tabellensperren sofort freigegeben würden.

- Manchmal wäre es nützlich, im Laufe einer Transaktion noch weitere Tabellen zu sperren. Doch leider führt `LOCK TABLES` in MySQL ein implizites `COMMIT` und `UNLOCK TABLES` aus. Es ist eine `InnoDB`-Variante von `LOCK TABLES` geplant, die auch inmitten einer Transaktion ausgeführt werden kann.

- Die `LOAD TABLE FROM MASTER`-Anweisung zum Einrichten eines Slaveservers für die Replikation funktioniert noch nicht mit `InnoDB`-Tabellen. Als Workaround können Sie die Tabelle auf dem Master in `MyISAM` konvertieren, dann laden und hinterher die Mastertabelle wieder auf `InnoDB` umstellen. Dies geht jedoch nicht mit Tabellen, die `InnoDB`-spezifische Features wie etwa Fremdschlüssel verwenden.

- Die Standardgröße für Datenbankseiten in `InnoDB` beträgt 16KB. Indem Sie den Code rekompilieren, können Sie Werte zwischen 8KB und 64KB einstellen. Sie müssen dazu die Werte von `UNIV_PAGE_SIZE` und `UNIV_PAGE_SIZE_SHIFT` in der Quelldatei `univ.i` ändern.

- Trigger werden zurzeit noch nicht durch kaskadierende Fremdschlüsselaktionen aktiviert.

### 14.2.17. `InnoDB`-Troubleshooting

Die folgenden allgemeinen Richtlinien gelten für die Behebung von `InnoDB`-Problemen:

- Wenn eine Operation scheitert oder Sie einen Bug vermuten, schauen Sie in das Fehlerlog des MySQL-Servers. es ist die Datei im Data Directory, die das Suffix `.err` trägt.

- Für die Problembehebung führen Sie MySQL am besten an der Eingabeaufforderung aus, anstatt durch den mysqld_safe-Wrapper oder als Windows-Dienst. Dann können Sie erkennen, was mysqld auf die Konsole ausgibt, und verstehen besser, was vor sich geht. Auf Windows müssen Sie den Server mit der `--console`-Option starten, um die Ausgabe ans Konsolenfenster zu schicken.

- Nutzen Sie die `InnoDB`-Monitore, um Informationen über ein Problem einzuholen (siehe Abschnitt 14.2.11.1, „Der InnoDB-Monitor“). Wenn es sich um ein Leistungsproblem handelt oder Ihr Server sich anscheinend aufgehängt hat, geben Sie mithilfe von `innodb_monitor` Informationen über den internen Zustand von `InnoDB` aus. Handelt es sich um ein Sperrenproblem, verwenden Sie `innodb_lock_monitor`. Hat das Problem mit der Erzeugung von Tabellen oder mit anderen Data Dictionary-Operationen zu tun, geben Sie mithilfe von `innodb_table_monitor` den Inhalt des `InnoDB`-internen Data Dictionary aus.

- Wenn Sie Tabellenschäden vermuten, führen Sie auf der betreffenden Tabelle `CHECK TABLE` aus.

#### 14.2.17.1. Troubleshooting von `InnoDB` bei Data Dictionary-Operationen

Ein besonderes Problem mit Tabellen besteht darin, dass der MySQL-Server Data Dictionary-Informationen in `.frm`-Dateien in Datenbankverzeichnissen ablegt, während `InnoDB` die Informationen auch in sein eigenes Data Dictionary innerhalb der Tablespace-Dateien speichert. Wenn Sie `.frm`-Dateien verschieben oder der Server inmitten einer Data Dictionary-Operation abstürzt, kann es geschehen, dass die Speicherorte der `.frm`-Dateien hinterher nicht mehr zu den Angaben im `InnoDB`-internen Data Dictionary passen.

Eine gescheiterte `CREATE TABLE`-Anweisung ist ein Symptom für ein nicht mehr synchrones Data Dictionary. Wenn dies passiert, schauen Sie im Fehlerlog des Servers nach. Wird dort behauptet, dass die Tabelle im `InnoDB`-internen Data Dictionary bereits existiert, so haben Sie in den `InnoDB`-Tablespace-Dateien, eine verwaiste Tabelle, zu der keine `.frm`-Datei gehört. Die Fehlermeldung sieht wie folgt aus:

    InnoDB: Error: table test/parent already exists in InnoDB internal
    InnoDB: data dictionary. Have you deleted the .frm file
    InnoDB: and not used DROP TABLE? Have you used DROP DATABASE
    InnoDB: for InnoDB tables in MySQL version <= 3.23.43?
    InnoDB: See the Restrictions section of the InnoDB manual.
    InnoDB: You can drop the orphaned table inside InnoDB by
    InnoDB: creating an InnoDB table with the same name in another
    InnoDB: database and moving the .frm file to the current database.
    InnoDB: Then MySQL thinks the table exists, and DROP TABLE will
    InnoDB: succeed.

Nach den Instruktionen der Fehlermeldung können Sie die verwaiste Tabelle löschen. Wenn Sie `DROP TABLE` immer noch nicht ausführen können, kann das Problem auch an der AutoVervollständigung von Namen durch den mysql-Client liegen. Um dieses zu verhindern, starten Sie den mysql-Client mit der `--disable-auto-rehash`-Option und versuchen noch einmal Ihr `DROP TABLE`. (Bei eingeschalteter Namensvervollständigung versucht mysql eine Liste von Tabellennamen zu erstellen. Das scheitert, wenn ein Problem wie das oben beschriebene auftritt.)

Ein anderes Symptom für ein nicht mehr synchrones Data Dictionary liegt vor, wenn MySQL meldet, dass eine `.InnoDB`-Datei nicht geöffnet werden kann:

    ERROR 1016: Can't open file: 'child2.InnoDB'. (errno: 1)

Im Fehlerlog können Sie dann eine Nachricht wie diese vorfinden:

    InnoDB: Cannot find table test/child2 from the internal data dictionary
    InnoDB: of InnoDB though the .frm file for the table exists. Maybe you
    InnoDB: have deleted and recreated InnoDB data files but have forgotten
    InnoDB: to delete the corresponding .frm files of InnoDB tables?

Dies bedeutet, dass Sie eine verwaiste `.frm`-Datei haben, zu der es in `InnoDB` keine Tabelle gibt. Die verwaiste `.frm`-Datei können Sie dann manuell löschen.

Wenn MySQL mitten in einer `ALTER TABLE`-Operation abstürzt, haben Sie hinterher vielleicht eine verwaiste temporäre Tabelle im `InnoDB`-Tablespace. Mit `innodb_table_monitor` sehen Sie dann eine Tabelle namens `#sql-...` aufgeführt. Sie können SQL-Anweisungen auf Tabellen ausführen, deren Name das Zeichen ‘`#`’ enthält, wenn Sie den Namen in Backticks setzen. So können Sie die verwaiste Tabelle wie jede andere auch mit den zuvor beschriebenen Methoden löschen. Achtung: Um eine Datei in der Unix-Shell zu kopieren oder umzubenennen, müssen Sie den Dateinamen, wenn er ‘`#`’ enthält, in doppelte Anführungszeichen setzen.

## 14.3. Die `MERGE`-Speicher-Engine

Eine `MERGE`-Tabelle, auch bekannt als `MRG_MyISAM`, ist eine Sammlung identischer `MyISAM`-Tabellen, die als eine einzige Tabelle verwendet werden können. „Identisch“ bedeutet, dass alle Tabellen dieselben Spalten- und Indexdaten haben. Sie können keine `MyISAM`-Tabellen zusammenführen („mergen“) , in denen die Spalten oder Indizes in einer unterschiedlichen Reihenfolge stehen, oder die nicht genau gleich viele Spalten haben. Allerdings können alle `MyISAM`-Tabellen mit myisampack komprimiert werden. Siehe Abschnitt 8.4, „myisampack — Erzeugung komprimierter, schreibgeschützter MyISAM Tabellen“. Unterschiede in den Tabellenoptionen, wie beispielsweise `AVG_ROW_LENGTH`, `MAX_ROWS` oder `PACK_KEYS` spielen keine Rolle.

Wenn Sie eine `MERGE`-Tabelle anlegen, erzeugt MySQL auf der Festplatte zwei Dateien, deren Namen jeweils mit dem Tabellennamen beginnen und Erweiterungen haben, die den Dateityp angeben. Eine `.frm`-Datei speichert das Tabellenformat und eine `.MRG`-Datei enthält die Namen der Tabellen, die wie eine einzige benutzt werden sollen. Die Tabellen müssen nicht in derselben Datenbank vorliegen wie die `MERGE`-Tabelle selbst.

Mit `MERGE`-Tabellen können Sie die Anweisungen `SELECT`, `DELETE`, `UPDATE` und `INSERT` verwenden. Sie benötigen `SELECT`-, `UPDATE`- und `DELETE`-Rechte für die `MyISAM`-Tabellen, die Sie einer `MERGE`-Tabelle zuordnen möchten.

Wenn Sie die `MERGE`-Tabelle mit `DROP` löschen, löschen Sie damit nur die `MERGE`-Spezifikation. Die zugrunde liegenden Tabellen sind davon nicht betroffen.

Um eine `MERGE`-Tabelle anzulegen, müssen Sie in einer `UNION=(`list-of-tables`)` -Klausel angeben, welche `MyISAM`-Tabellen Sie als eine einzige benutzen möchten. Optional können Sie mit der Option `INSERT_METHOD` erreichen, dass Einfügeoperationen in der `MERGE`-Tabelle auf der ersten oder der letzten Tabelle der `UNION`-Liste stattfinden. Wenn Sie den Wert `FIRST` einsetzen, geschieht die Einfügung in der ersten, wenn Sie `LAST` einsetzen, in der letzten Tabelle. Wenn Sie die `INSERT_METHOD`-Option nicht oder nur mit dem Wert `NO` angeben, zieht jeder Versuch, Zeilen in die `MERGE`-Tabelle einzufügen, eine Fehlermeldung nach sich.

Das folgende Beispiel zeigt, wie eine `MERGE`-Tabelle angelegt wird:

    mysql> CREATE TABLE t1 (
        ->    a INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
        ->    message CHAR(20)) ENGINE=MyISAM;
    mysql> CREATE TABLE t2 (
        ->    a INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
        ->    message CHAR(20)) ENGINE=MyISAM;
    mysql> INSERT INTO t1 (message) VALUES ('Testing'),('table'),('t1');
    mysql> INSERT INTO t2 (message) VALUES ('Testing'),('table'),('t2');
    mysql> CREATE TABLE total (
        ->    a INT NOT NULL AUTO_INCREMENT,
        ->    message CHAR(20), INDEX(a))
        ->    ENGINE=MERGE UNION=(t1,t2) INSERT_METHOD=LAST;

Der ältere Begriff `TYPE` wird aus Gründen der Abwärtskompatibilität noch als Synonym für `ENGINE` akzeptiert, doch `ENGINE` ist der aktuelle Begriff, während `TYPE` mittlerweile veraltet ist.

Beachten Sie, dass die Spalte `a` in den zugrunde liegenden `MyISAM`-Tabellen ein `PRIMARY KEY` ist, aber nicht in der `MERGE`-Tabelle. Dort ist diese Spalte zwar auch indiziert, aber nicht als `PRIMARY KEY`, da eine `MERGE`-Tabelle für die ihr zugrunde liegenden Tabellen keine Eindeutigkeit erzwingen kann.

Nachdem Sie die `MERGE`-Tabelle angelegt haben, können Sie Anfragen schreiben, die auf der Tabellengruppe als Ganzes operieren:

    mysql> SELECT * FROM total;
    +---+---------+
    | a | message |
    +---+---------+
    | 1 | Testing |
    | 2 | table   |
    | 3 | t1      |
    | 1 | Testing |
    | 2 | table   |
    | 3 | t2      |
    +---+---------+

Beachten Sie, dass sich die `.MRG`-Datei direkt von außerhalb des MySQL-Servers bearbeiten lässt:

    shell> cd /mysql-data-directory/current-database
    shell> ls -1 t1 t2 > total.MRG
    shell> mysqladmin flush-tables

Um eine Neuzuordnung einer `MERGE`-Tabelle zu einer anderen Gruppe von `MyISAM`-Tabellen zu erzielen, gibt es folgende Methoden:

- Sie löschen die `MERGE`-Tabelle mit `DROP` und erstellen sie neu.

- Sie ändern die Liste der zugrunde liegenden Tabellen mit `ALTER TABLE `tbl_name` UNION=(...)`.

- Sie ändern die `.MRG`-Datei und geben eine `FLUSH TABLE`-Anweisung für die `MERGE`-Tabelle und alle zugrunde liegenden Tabellen aus, damit die Speicher-Engine die neue Definitionsdatei lesen muss.

`MERGE`-Tabellen können bei der Lösung folgender Probleme behilflich sein:

- Einfache Verwaltung einer Menge von Logtabellen. So können Sie zum Beispiel Daten verschiedener Monate in getrennte Tabellen laden, einige davon mit myisampack komprimieren und dann eine `MERGE`-Tabelle anlegen, um das Ganze als ein einziges Log zu verwenden.

- Mehr Schnelligkeit. Sie können eine große, schreibgeschützte Tabelle nach bestimmten Kriterien aufspalten und die entstehenden Tabellen auf verschiedene Festplatten speichern. Mit einer `MERGE`-Tabelle aus diesen Einzeltabellen haben Sie schnelleren Zugriff als mit der großen Ursprungstabelle.

- Effizienteres Suchen. Wenn Sie genau wissen, was Sie wollen, können Sie für manche Anfragen eine einzelne der aufgespaltenen Tabellen und für andere die `MERGE`- Tabelle benutzen. Sie können sogar eine Vielzahl verschiedener `MERGE`-Tabellen bilden, die zum Teil dieselben Einzeltabellen benutzen.

- Reparaturen werden effizienter. Es ist einfacher, kleinere Tabellen zu reparieren, die einer `MERGE`-Tabelle zugeordnet sind, als eine einzige große Tabelle.

- Viele Tabellen können wie eine einzige sofort zugeordnet werden. Eine `MERGE`-Tabelle benötigt keinen eigenen Index, da sie die Indizes der Einzeltabellen verwendet. Infolgedessen lassen sich Sammlungen von `MERGE`-Tabellen sehr schnell erstellen oder neu zuordnen. (Beachten Sie, dass Sie trotzdem bei der Erstellung einer `MERGE`-Tabelle die Indexdefinitionen angeben müssen auch wenn keine Indizes angelegt werden.)

- Wenn Sie aus mehreren Tabellen nach Bedarf eine einzige, große Tabelle erstellen müssen, ist es günstiger, stattdessen eine `MERGE`-Tabelle anzulegen. Diese ist viel schneller und spart eine Menge Speicherplatz.

- Mit `MERGE`-Tabellen können Sie die Dateigrößenbeschränkung Ihres Betriebssystems umgehen. Eine einzelne, große `MyISAM`-Tabelle würde durch dieses Limit begrenzt, aber eine Sammlung von kleineren `MyISAM`-Tabellen nicht.

- Sie können einen Alias oder ein Synonym für eine `MyISAM`-Tabelle anlegen, indem Sie eine `MERGE`-Tabelle definieren, der nur diese eine Tabelle zugeordnet ist. Der Einfluss auf die Leistung dürfte kaum spürbar sein (nur ein paar indirekte Aufrufe und `memcpy()`-Aufrufe für jede Leseoperation).

Die Nachteile von `MERGE`-Tabellen sind:

- Es dürfen nur identische `MyISAM`-Tabellen für eine `MERGE`-Tabelle benutzt werden.

- Eine Reihe von `MyISAM`-Features steht für `MERGE`-Tabellen nicht zur Verfügung. Sie können zum Beispiel keine `FULLTEXT`-Indizes auf ihnen anlegen. (Es bleibt Ihnen natürlich unbenommen, `FULLTEXT`-Indizes auf den zugrunde liegenden `MyISAM`-Tabellen anzulegen, doch aber auf der `MERGE`-Tabelle können Sie keine Volltextsuche ausführen.)

- Wenn die `MERGE`-Tabelle nicht-temporär ist, müssen auch die zugrunde liegenden `MyISAM`-Tabellen nicht-temporär sein. Ist die `MERGE`-Tabelle hingegen temporär, können die `MyISAM`-Tabellen eine beliebige Mixtur aus temporären und nicht-temporären Tabellen sein.

- `MERGE`-Tabellen verwenden mehr Dateideskriptoren. Wenn 10 Clients eine `MERGE`-Tabelle ansprechen, die ihrerseits 10 Tabellen abbildet, benutzt der Server (10 × 10) + 10 Dateideskriptoren. (10 Datendateideskriptoren für jeden der 10 Clients und 10 Indexdateideskriptoren, die von den Clients gemeinsam genutzt werden.)

- Lesevorgänge von Schlüsseln sind langsamer. Wenn Sie einen Schlüssel lesen, muss die `MERGE`-Speicher-Engine eine Leseoperation auf allen zugrunde liegenden Tabellen ausführen, um festzustellen, welche dem gegebenen Schlüssel am besten entspricht. Um den nächsten Schlüssel zu lesen, muss die `MERGE`-Speicher-Engine die Lesepuffer nach ihm durchsuchen. Erst wenn ein Schlüsselpuffer aufgebraucht ist, muss die Speicher-Engine den nächsten Schlüsselblock lesen. Das macht `MERGE`-Schlüssel in `eq_ref`-Suchen viel langsamer, aber nicht in `ref`-Suchen. Unter Abschnitt 7.2.1, „`EXPLAIN`-Syntax (Informationen über ein `SELECT` erhalten)“ finden Sie weitere Informationen über `eq_ref` und `ref`.

Mehr zum Thema

- Ein spezielles Forum zur Speicher-Engine `MERGE` finden Sie unter http://forums.mysql.com/list.php?93.

### 14.3.1. MERGE-Tabellenprobleme

Folgende Probleme mit `MERGE`-Tabellen sind bekannt:

- Wenn Sie mit `ALTER TABLE` versuchen, eine `MERGE`-Tabelle in eine andere Speicher-Engine umzuwandeln, geht die Zuordnung der zugrunde liegenden Tabellen verloren. Stattdessen werden die Zeilen der zugrunde liegenden `MyISAM`-Tabellen in die geänderte Tabelle kopiert, die dann die neue Speicher-Engine verwendet.

- `REPLACE` funktioniert nicht.

- `DROP TABLE`, `ALTER TABLE`, `DELETE` ohne `WHERE`-Klausel, `REPAIR TABLE`, `TRUNCATE TABLE`, `OPTIMIZE TABLE` oder `ANALYZE TABLE` dürfen auf keine Tabelle angewendet werden, die einer offenen `MERGE`-Tabelle zugeordnet ist. Wenn Sie dies tun, verweist die `MERGE`-Tabelle später weiterhin auf die Originaltabelle, was zu unerwarteten Ergebnissen führen kann. Am einfachsten können Sie dieses Problem umgehen, indem Sie mit einer `FLUSH TABLES`-Anweisung vor diesen Operationen dafür sorgen, dass keine `MERGE`-Tabellen offen bleiben.

- Unter Windows funktioniert kein `DROP TABLE` auf einer Tabelle, die gerade von einer `MERGE`-Tabelle benutzt wird, da die Tabellenzuordnung der Speicher-Engine `MERGE` vor der oberen Schicht von MySQL verborgen wird. Da Windows das Löschen geöffneter Dateien nicht gestattet, müssen Sie zuerst alle `MERGE`-Tabellen auf die Festplatte zurückschreiben (mit `FLUSH TABLES`) oder die `MERGE`-Tabelle vor der anderen Tabelle löschen.

- Eine `MERGE`-Tabelle kann keine Uniqueness Constraints über die gesamte Tabelle hinweg aufrecht erhalten. Wenn Sie ein `INSERT` ausführen, werden die Daten in die erste oder letzte der `MyISAM`-Tabellen geladen (je nach dem Wert der Option `INSERT_METHOD`). MySQL gewährleistet die Eindeutigkeit von Unique-Keys innerhalb dieser einen `MyISAM`-Tabelle, aber nicht für die gesamte Tabellengruppe.

- Beim Anlegen einer `MERGE`-Tabelle wird nicht geprüft, ob die zugrunde liegenden Tabellen existieren und gleich strukturiert sind. Wenn die `MERGE`-Tabelle verwendet wird, prüft MySQL, ob alle zugeordneten Tabellen gleich lange Zeilen haben, aber narrensicher ist diese Überprüfung nicht. Legen Sie eine `MERGE`-Tabelle aus ungleichen `MyISAM`-Tabellen an, so müssen Sie sich auf ein paar seltsame Probleme gefasst machen.

- Die Reihenfolge der Indizes in der `MERGE`-Tabelle und den ihr zugrunde liegenden Tabellen sollte gleich sein. Wenn Sie mit `ALTER TABLE` einer Tabelle, die in einer `MERGE`-Tabelle benutzt wird, einen `UNIQUE`-Index hinzufügen, und dann mit einem weiteren `ALTER TABLE` der `MERGE`-Tabelle einen nicht-eindeutigen Index geben, ist die Reihenfolge der Indizes unterschiedlich, wenn auch die zugrunde liegende Tabelle zuvor bereits einen nicht-eindeutigen Index hatte. (Dazu kommt es, weil `ALTER TABLE` `UNIQUE`-Indizes vor nicht-eindeutige Indizes setzt, um das schnelle Auffinden doppelter Schlüsselwerte zu erleichtern.) Infolgedessen können Anfragen von Tabellen mit solchen Indizes unerwartete Ergebnisse liefern.

## 14.4. Die `MEMORY`-Speicher-Engine

Die Speicher-Engine `MEMORY` legt Tabellen mit Inhalten an, die im Arbeitsspeicher gespeichert sind. Früher wurden sie als `HEAP`-Tabellen bezeichnet. Heute ist `MEMORY` der bevorzugte Ausdruck, auch wenn `HEAP` aus Gründen der Abwärtskompatibilität weiter unterstützt wird.

Zu jeder `MEMORY`-Tabelle gehört eine Festplattendatei. Der Dateiname beginnt mit dem Tabellennamen und hat die Erweiterung `.frm`, um anzuzeigen, dass hier die Tabellendefinition (frm = form) gespeichert ist.

Um explizit eine `MEMORY`-Tabelle anzulegen, geben Sie dies in der Tabellenoption `ENGINE` an:

    CREATE TABLE t (i INT) ENGINE = MEMORY;

Der ältere Begriff `TYPE` wird aus Gründen der Abwärtskompatibilität noch als Synonym für `ENGINE` akzeptiert, doch `ENGINE` ist der aktuelle Begriff, während `TYPE` mittlerweile veraltet ist.

Wie der Name schon sagt, werden `MEMORY`-Tabellen im Arbeitsspeicher gespeichert und sie benutzen nach Voreinstellung einen gehashten Index. Das macht sie sehr schnell und nützlich für temporäre Tabellen. Wenn allerdings der Server abstürzt, gehen alle in `MEMORY`-Tabellen gespeicherten Daten verloren. Die Tabellen selbst bestehen weiter, da ihre Definitionen in `.frm`-Dateien auf der Festplatte gespeichert sind, doch ihre Daten sind fort, wenn der Server wieder hochfährt.

Das folgende Beispiel zeigt, wie eine `MEMORY`-Tabelle erzeugt, benutzt und gelöscht wird:

    mysql> CREATE TABLE test ENGINE=MEMORY
        ->     SELECT ip,SUM(downloads) AS down
        ->     FROM log_table GROUP BY ip;
    mysql> SELECT COUNT(ip),AVG(down) FROM test;
    mysql> DROP TABLE test;

Kennzeichen von `MEMORY`-Tabellen:

- Speicherplatz für `MEMORY`-Tabellen wird in kleinen Blöcken zugewiesen. Die Tabellen verwenden 100% dynamisches Hashing für Einfügeoperationen. Es werden keine Overflow- Bereiche und kein zusätzlicher Platz für Schlüssel oder für Freelists benötigt. Gelöschte Zeilen werden in eine verkettete Liste geschrieben und wiederverwendet, wenn neue Daten in die Tabelle eingefügt werden. `MEMORY`-Tabellen haben auch keine Probleme mit Löschen plus Einfügen, was normalerweise bei gehashten Tabellen häufig vorkommt.

- `MEMORY`-Tabellen können bis zu 32 Indizes mit jeweils bis zu 16 Spalten und einer maximalen Schlüssellänge von 500 Bytes haben.

- Die Speicher-Engine `MEMORY` implementiert sowohl `HASH`- als auch `BTREE`-Indizes. Mit einer `USING`-Klausel können Sie angeben, welchen von beiden Sie wünschen:

      CREATE TABLE lookup
          (id INT, INDEX USING HASH (id))
          ENGINE = MEMORY;
      CREATE TABLE lookup
          (id INT, INDEX USING BTREE (id))
          ENGINE = MEMORY;

  Die allgemeinen Merkmale von B-Baum- und Hash-Indizes werden in Abschnitt 7.4.5, „Wie MySQL Indizes benutzt“ beschrieben.

- Es darf keine nicht-eindeutigen Schlüssel auf eine `MEMORY`-Tabelle geben. (Dies ist ungebräuchlich für Implementierungen von Hash-Indizes.)

- Wenn Sie einen Hash-Index auf einer `MEMORY`-Tabelle mit sehr vielen doppelten Schlüsseln haben (viele Indexeinträge enthalten denselben Wert), dann laufen Updates, die Schlüsselwerte betreffen, sowie sämtliche Löschoperationen deutlich langsamer. Wie viel langsamer, hängt von dem Ausmaß der Schlüsselduplikation ab (umgekehrt proportional zur Indexkardinalität). Um dieses Problem zu vermeiden, verwenden Sie einen `BTREE`-Index.

- Indizierte Spalten können `NULL`-Werte enthalten.

- `MEMORY`-Tabellen verwenden ein Speicherformat mit fester Zeilenlänge.

- `MEMORY`-Tabellen dürfen keine `BLOB`- oder `TEXT`-Spalten enthalten.

- `MEMORY` unterstützt `AUTO_INCREMENT`-Spalten.

- `INSERT DELAYED` kann mit `MEMORY`-Tabellen verwendet werden. Siehe Abschnitt 13.2.4.2, „`INSERT DELAYED`“.

- `MEMORY`-Tabellen werden von allen Clients gemeinsam genutzt (wie jede andere nicht-`TEMPORARY`-Tabelle).

- `MEMORY`-Tabellen speichern ihren Inhalt im Arbeitsspeicher, eine Eigenschaft, die sie mit internen Tabellen gemeinsam haben, die der Server bei der Verarbeitung von Anfragen nebenbei anlegt. Die beiden Tabellentypen unterscheiden sich jedoch darin, dass `MEMORY`-Tabellen im Gegensatz zu den internen Tabellen nicht von Speicherkonvertierung betroffen sind:

  - Wenn eine interne Tabelle zu groß wird, konvertiert der Server sie automatisch in eine Festplattentabelle. Deren maximale Größe wird durch die Systemvariable `tmp_table_size` festgelegt.

  - `MEMORY`-Tabellen werden nie in Festplattentabellen konvertiert. Um sicherzustellen, dass Sie nicht versehentlich den gesamten Arbeitsspeicher benutzen, können Sie die Systemvariable `max_heap_table_size` so einstellen, dass auch `MEMORY`-Tabellen einem Größenlimit unterliegen. Für einzelne Tabellen können Sie auch in der `CREATE TABLE`-Anweisung die Tabellenoption `MAX_ROWS`-Tabelle setzen.

- Der Server benötigt genug Arbeitsspeicher, um alle `MEMORY`-Tabellen zu pflegen, die zur selben Zeit gebraucht werden.

- Um den von einer `MEMORY`-Tabelle belegten Speicher wieder freizugeben, wenn sie nicht länger benötigt wird, führen Sie `DELETE` oder `TRUNCATE TABLE` aus oder löschen die Tabelle mit `DROP TABLE`.

- Möchten Sie beim Starten des MySQL-Servers Daten in eine `MEMORY`-Tabelle laden, so können Sie die Option `--init-file` nutzen. In diese Datei können Sie Anweisungen wie `INSERT INTO ... SELECT` oder `LOAD DATA INFILE` setzen, um die Tabelle aus einer persistenten Datenquelle zu laden. Siehe Abschnitt 5.2.1, „Befehlsoptionen für mysqld“, and Abschnitt 13.2.5, „`LOAD DATA INFILE`“.

- Wenn Sie Replikation benutzen, werden die `MEMORY`-Tabellen auf dem Masterserver auf Platte geschrieben, wenn dieser heruntergefahren und neu gestartet wird. Ein Slave merkt allerdings nicht, dass die Tabellen inzwischen leer sind, und gibt ihren alten Inhalt zurück, wenn Sie Daten von ihm abfragen. Wird eine `MEMORY`-Tabelle auf einem Master nach einem Neustart erstmals wieder genutzt, so wird automatisch eine `DELETE`-Anweisung in sein Binärlog geschrieben, um den Slave wieder mit ihm zu synchronisieren. Doch Vorsicht: Auch bei dieser Strategie hat der Slave in dem Zeitraum zwischen dem Neustart des Masters und seinem ersten Zugriff auf die Tabelle veraltete Daten im Speicher. Wenn Sie jedoch die `MEMORY`-Tabelle gleich beim Hochfahren des Masters mithilfe der Option `--init-file` wieder mit Inhalt füllen, ist gewährleistet, dass dieser Zeitraum auf Null schrumpft.

- Der für eine Zeile in einer `MEMORY`-Tabelle benötigte Speicher lässt sich mit folgendem Ausdruck berechnen:

      SUM_OVER_ALL_BTREE_KEYS(max_length_of_key + sizeof(char*) × 4)
      + SUM_OVER_ALL_HASH_KEYS(sizeof(char*) × 2)
      + ALIGN(length_of_row+1, sizeof(char*))

  `ALIGN()` ist ein Rundungsfaktor, der gewährleisten soll, dass die Zeilenlänge ein Vielfaches der `char`-Pointergröße ist. `sizeof(char*)` ist auf 32-Bit-Rechnern gleich 4 und auf 64-Bit-Rechnern gleich 8.

Mehr zum Thema

- Ein spezielles Forum zur Speicher-Engine `MEMORY` finden Sie unter http://forums.mysql.com/list.php?92.

## 14.5. Die `BDB`-Speicher-Engine

Sleepycat Software hat für MySQL die transaktionssichere Speicher-Engine Berkeley DB, kurz `BDB`, bereitgestellt. `BDB`-Tabellen haben bessere Chancen, einen Absturz zu überstehen, und können darüber hinaus `COMMIT`- und `ROLLBACK`-Operationen auf Transaktionen durchführen.

`BDB`-Support ist in MySQL Quelldistributionen enthalten und in MySQL-Max-Binärdistributionen aktiviert. Die MySQL-Quelldistribution wird mit einer `BDB`-Distribution ausgeliefert, die durch einen Patch auf MySQL zugeschnitten ist. Für MySQL kann keine nicht-gepatchte Version von `BDB` verwendet werden.

MySQL AB arbeitet eng mit Sleepycat zusammen, um eine hochwertige MySQL/BDB-Schnittstelle zu gewährleisten. (Obgleich Berkeley DB selbst gründlich getestet und sehr zuverlässig ist, hat die MySQL-Schnittstelle immer noch Gamma-Qualität. Wir arbeiten weiter an ihrer Verbesserung und Optimierung.)

Wenn Probleme mit `BDB`-Tabellen auftreten, sind wir bestrebt, unseren Benutzern beim Isolieren des Problems zu helfen und reproduzierbare Testfälle zu erstellen. Alle derartigen Testfälle werden an Sleepycat weitergeleitet, wo man uns wiederum hilft, die Probleme zu finden und zu beheben. Da dieser Vorgang in zwei Phasen abläuft, kann die Behebung von Problemen mit `BDB`-Tabellen etwas länger dauern als bei anderen Speicher-Engines. Wir denken jedoch nicht, dass es mit diesem Vorgehen besondere Schwierigkeiten gibt, da der Berkeley DB-Code selbst noch in vielen anderen Anwendungen als MySQL läuft.

Allgemeine Informationen über Berkeley DB finden Sie auf der Sleepycat-Website http://www.sleepycat.com/.

### 14.5.1. Betriebssysteme, die von BDB unterstützt werden

Nach unserem derzeitigen Kenntnisstand funktioniert `BDB` mit folgenden Betriebssystemen:

- Linux 2.x Intel

- Sun Solaris (SPARC und x86)

- FreeBSD 4.x/5.x (x86, sparc64)

- IBM AIX 4.3.x

- SCO OpenServer

- SCO UnixWare 7.1.x

- Windows NT/2000/XP

Die Speicher-Engine `BDB` funktioniert nicht auf folgenden Betriebssystemen:

- Linux 2.x Alpha

- Linux 2.x AMD64

- Linux 2.x IA-64

- Linux 2.x s390

- Mac OS X

Hinweis: Diese Listen sind nicht vollständig. Sie werden bei Eintreffen neuer Informationen aktualisiert.

Wenn Sie MySQL aus der Quelle mit Unterstützung für `BDB`-Tabellen erstellen, aber beim Starten mit mysqld folgender Fehler auftritt, so bedeutet dies, dass Ihre Rechnerarchitektur die `BDB`-Speicher-Engine nicht unterstützt:

    bdb: architecture lacks fast mutexes: applications cannot be threaded
    Can't init databases

In diesem Fall müssen Sie MySQL ohne `BDB`-Unterstützung bauen oder den Server mit der Option `--skip-bdb` starten.

### 14.5.2. BDB installieren

Wenn Sie eine Binärversion von MySQL heruntergeladen haben, die Unterstützung für Berkeley DB bietet, befolgen Sie einfach die üblichen Installationsanweisungen für Binärdistributionen. (MySQL-Max-Distributionen umfassen `BDB`-Support.)

Erstellen Sie MySQL aus einer Quelldistribution, so können Sie `BDB`-Unterstützung aktivieren, indem Sie beim Aufruf von configure zusätzlich zu den anderen Optionen, die Sie normalerweise verwenden, die Option `--with-berkeley-db` einschalten. Laden Sie eine MySQL-Distribution der Version 5.1 herunter, gehen Sie in das oberste Verzeichnis und führen Sie folgenden Befehl aus:

    shell> ./configure --with-berkeley-db [other-options]

Weitere Informationen, Abschnitt 5.3, „mysqld-max, ein erweiterter mysqld-Server“, siehe Abschnitt 2.7, „Installation von MySQL auf anderen Unix-ähnlichen Systemen“ und Abschnitt 2.8, „Installation der Quelldistribution“.

### 14.5.3. BDB-Startoptionen

Mit den folgenden mysqld-Optionen lässt sich das Verhalten der Speicher-Engine `BDB` ändern. Weitere Informationen siehe Abschnitt 5.2.1, „Befehlsoptionen für mysqld“.

- `--bdb-data-direct`

  Schaltet für `BDB`-Datenbankdateien den Systempuffer aus, um doppeltes Cachen zu verhindern. Diese Option wurde in MySQL 5.1.4 hinzugefügt.

- `--bdb-home=`path``

  Das Basisverzeichnis für `BDB`-Tabellen. Es sollte dasselbe Verzeichnis sein, das auch für die `--datadir`-Option verwendet wird.

- `--bdb-lock-detect=`method``

  Die `BDB`-Methode zur Erkennung von Sperren. Der Wert der Option ist `DEFAULT`, `OLDEST`, `RANDOM`, `YOUNGEST`, `MAXLOCKS`, `MINLOCKS`, `MAXWRITE` oder `MINWRITE`.

- `--bdb-log-direct`

  Schaltet für `BDB`-Datenbankdateien den Systempuffer aus, um doppeltes Cachen zu verhindern. Diese Option wurde in MySQL 5.1.4 hinzugefügt.

- `--bdb-logdir=`file_name``

  Das Verzeichnis für `BDB`-Logdateien.

- `--bdb-no-recover`

  Berkeley DB nicht im Wiederherstellungsmodus starten.

- `--bdb-no-sync`

  `BDB`-Logs nicht synchronisieren. Diese Option ist veraltet; bitte verwenden Sie stattdessen `--skip-sync-bdb-logs` (siehe Beschreibung von `--sync-bdb-logs`).

- `--bdb-shared-data`

  Berkeley DB im Multi-Prozess-Modus starten. (Nicht beim Initialisieren von Berkeley DB `DB_PRIVATE` benutzen.)

- `--bdb-tmpdir=`path``

  Name des temporären Dateiverzeichnisses von `BDB`.

- `--skip-bdb`

  `BDB`-Speicher-Engine nicht benutzen.

- `--sync-bdb-logs`

  `BDB`-Logs synchronisieren. Diese Option ist standardmäßig aktiviert und kann mit `--skip-sync-bdb-logs` deaktiviert werden.

Mit `--skip-bdb` initialisiert MySQL nicht die BerkeleyDB-Bibliothek und spart deshalb viel Speicher. Natürlich können Sie `BDB`-Tabellen nicht benutzen, wenn Sie diese Option verwenden. Versuchen Sie dennoch, eine `BDB`-Tabelle anzulegen, so verwendet MySQL stattdessen die Standard-Speicher-Engine.

Normalerweise sollten Sie mysqld ohne die Option `--bdb-no-recover` starten, wenn Sie `BDB`-Tabellen benutzen möchten. Das kann jedoch Probleme verursachen, wenn Sie mysqld starten und die `BDB`-Logdateien beschädigt sind. Siehe Abschnitt 2.9.2.3, „Probleme mit dem Start des MySQL Servers“.

Mit der Variablen `bdb_max_lock` können Sie angeben, wie viele Sperren auf einer `BDB`-Tabelle höchstens aktiv sein dürfen. Der Standardwert beträgt 10.000. Sie können ihn heraufsetzen, wenn Fehler wie der folgende bei langen Transaktionen auftreten, oder wenn mysqld viele Zeilen betrachten muss, um eine Anfrage auszuführen:

    bdb: Lock table is out of available locks
    Got error 12 from ...

Außerdem sollten Sie die Variablen `binlog_cache_size` und `max_binlog_cache_size` ändern, wenn Sie große, aus mehreren Anweisungen bestehende Transaktionen ausführen. Siehe Abschnitt 5.12.3, „Die binäre Update-Logdatei“.

See also Abschnitt 5.2.2, „Server-Systemvariablen“.

### 14.5.4. Kennzeichen von `BDB`-Tabellen

Jede `BDB`-Tabelle wird in zwei Dateien auf der Festplatte gespeichert. Die Namen dieser Dateien setzen sich aus dem Namen der Tabelle und einer Dateityperweiterung zusammen. Eine `.frm`-Datei speichert das Format und eine `.db`-Datei den Inhalt und die Indizes der Tabelle.

Um explizit deutlich zu machen, dass Sie eine `BDB`-Tabelle benötigen, verwenden Sie die Tabellenoption `ENGINE`:

    CREATE TABLE t (i INT) ENGINE = BDB;

Der ältere Begriff `TYPE` wird aus Gründen der Abwärtskompatibilität noch als Synonym für `ENGINE` akzeptiert, doch `ENGINE` ist der aktuelle Begriff, während `TYPE` mittlerweile veraltet ist.

`BerkeleyDB` ist ein Synonym für `BDB` in der Tabellenoption `ENGINE`.

Die Speicher-Engine `BDB` ermöglicht transaktionssichere Tabellen. Wie diese benutzt werden, hängt vom Autocommit-Modus ab:

- Arbeiten Sie mit eingeschaltetem Autocommit (der Standard), werden Änderungen an `BDB`-Tabellen sofort festgeschrieben (commit) und können nicht zurückgerollt werden.

- Arbeiten Sie mit ausgeschaltetem Autocommit, werden die Änderungen erst permanent, nachdem Sie eine `COMMIT`-Anweisung ausgeführt haben. Stattdessen können Sie jedoch auch ein `ROLLBACK` ausführen, um die Änderungen zu widerrufen.

  Eine Transaktion wird mit `START TRANSACTION` oder `BEGIN` gestartet, um den Autocommit-Modus implizit auszusetzen, oder mit `SET AUTOCOMMIT=0`, um den Autocommit-Modus explizit auszuschalten.

Weitere Informationen über Transaktionen finden Sie unter Abschnitt 13.4.1, „`BEGIN/COMMIT/ROLLBACK`“.

Die `BDB`-Speicher-Engine hat folgende Merkmale:

- `BDB`-Tabellen können bis zu 31 Indizes pro Tabelle, 16 Spalten pro Index und 1024 Bytes pro Schlüssel haben.

- MySQL erfordert einen Primärschlüssel in jeder `BDB`-Tabelle, um auf jede Zeile eindeutig verweisen zu können. Wenn Sie nicht explizit einen`PRIMARY KEY` deklarieren, erzeugt und wartet MySQL einen verborgenen Primärschlüssel. Dieser hat eine Länge von 5 Bytes und wird bei jedem Einfügeversuch um eins inkrementiert. Der Schlüssel erscheint nicht in der Ausgabe von `SHOW CREATE TABLE` oder `DESCRIBE`.

- Der Primärschlüssel ist schneller als jeder andere Index, da er zusammen mit den Zeilendaten gespeichert wird. Da die anderen Indizes als Schlüsseldaten plus Primärschlüssel gespeichert werden, ist es wichtig, den Primärschlüssel möglichst kurz zu halten, um Plattenplatz zu sparen und eine höhere Geschwindigkeit zu erzielen.

  Dieses Verhalten ist ähnlich wie das von `InnoDB`, wo kürzere Primärschlüssel nicht nur im Primärindex, sondern auch in den Sekundärindizes Platz sparen.

- Wenn alle Spalten, auf die Sie in einer `BDB`-Tabelle zugreifen, zu demselben Index oder zum Primärschlüssel gehören, kann MySQL die Anfrage ausführen, ohne auf die eigentliche Zeile zugreifen zu müssen. In einer `MyISAM`-Tabelle ist dies nur möglich, wenn die Spalten zu demselben Index gehören.

- Sequenzielles Scannen ist bei `BDB`-Tabellen langsamer als bei `MyISAM`-Tabellen, da die Daten in `BDB`-Tabellen in B-Bäumen und nicht in einer separaten Datendatei gespeichert werden.

- Schlüsselwerte werden nicht Präfix- oder Suffix-komprimiert wie Schlüsselwerte in `MyISAM`-Tabellen. Mit anderen Worten: Die Schlüsselinformationen benötigen in `BDB`-Tabellen etwas mehr Platz als in `MyISAM`-Tabellen.

- Oft gibt es Löcher in der `BDB`-Tabelle, damit Sie neue Zeilen in der Mitte des Schlüsselbaums einfügen können. Das macht `BDB`-Tabellen etwas größer als `MyISAM`-Tabellen.

- `SELECT COUNT(*) FROM `tbl_name`` ist bei `BDB`-Tabellen langsam, da in der Tabelle kein Zeilenzähler gepflegt wird.

- Der Optimierer muss näherungsweise die Anzahl von Zeilen in der Tabelle kennen. MySQL löst dieses Problem, indem Einfügeoperationen gezählt werden, und unterhält diese in einem separaten Segment in jeder `BDB`-Tabelle. Wenn Sie nicht viele `DELETE`- oder `ROLLBACK`-Anweisungen ausführen, sollte diese Zahl ausreichend genau für den MySQL-Optimierer sein. Da MySQL die Zahl nur beim Schließen speichert, kann sie falsch sein, wenn MySQL unerwartet beendet wird. Das sollte kein schwerer Fehler sein, selbst wenn die Zahl nicht zu 100% korrekt ist. Man kann die Anzahl von Zeilen aktualisieren, indem man `ANALYZE TABLE` oder `OPTIMIZE TABLE` ausführt. Siehe Abschnitt 13.5.2.1, „`ANALYZE TABLE`“ und Abschnitt 13.5.2.5, „`OPTIMIZE TABLE`“

- Internes Sperren in `BDB`-Tabellen wird auf Seitenebene durchgeführt (page locking).

- `LOCK TABLES` funktioniert bei `BDB`-Tabellen wie bei anderen Tabellen. Wenn Sie `LOCK TABLES` nicht benutzen, errichtet MySQL eine interne Sperre für Mehrfach-Schreibvorgänge auf der Tabelle (eine Sperre, die andere Schreibvorgänge nicht blockiert), um sicherzustellen, dass die Tabelle korrekt gesperrt ist, wenn ein anderer Thread eine Tabellensperre ausführt.

- Um Transaktionen zurückrollen zu können, unterhält die `BDB`-Speicher-Engine Logdateien. Um maximale Performance zu erzielen, sollten Sie diese auf andere Festplatten platzieren als Ihre Datenbanken, indem Sie die Option `--bdb-logdir` verwenden.

- MySQL macht jedes Mal, wenn eine neue `BDB`-Logdatei gestartet wird, einen Checkpoint und entfernt alle `BDB`-Logdateien, die nicht für aktuelle Transaktionen benötigt werden. Sie können auch jederzeit `FLUSH LOGS` laufen lassen, um einen Checkpoint für die Berkeley DB-Tabellen anzulegen.

  Für die Wiederherstellung nach Abstürzen sollten Sie Datensicherungen der Tabellen plus das Binärlog von MySQL benutzen. Siehe Abschnitt 5.10.1, „Datenbank-Datensicherungen“.

  Achtung: Wenn Sie alte Logdateien löschen, die noch in Gebrauch sind, ist `BDB` nicht in der Lage, Wiederherstellungen durchzuführen, und Sie könnten Daten verlieren, wenn etwas schief geht.

- Die Anwendung muss stets darauf vorbereitet sein, Fälle zu handhaben, bei denen jegliche Änderung einer `BDB`-Tabelle zu einem automatischen Rollback führen kann und jegliches Lesen fehlschlagen kann, weil ein Deadlock auftritt.

- Wenn die Platte bei einer `BDB`-Tabelle voll wird, wird ein Fehler gemeldet (wahrscheinlich Fehler 28) und die Transaktion sollte zurückgerollt werden. Im Gegensatz dazu wartet bei `MyISAM`-Tabellen der Server darauf, dass genügend Plattenplatz frei ist, ehe er fortfährt.

### 14.5.5. Einschränkungen bei Verwendung von `BDB`-Tabellen

Im Folgenden werden Einschränkungen aufgeführt, die Sie bei der Verwendung von `BDB`-Tabellen beachten müssen:

- Jede `BDB`-Tabelle speichert in ihrer `.db`-Datei den Pfad, mit dem sie angelegt wurde. Dies wird getan, um die Erkennung von Sperren in Mehrbenutzerumgebungen zu ermöglichen, in denen Symlinks unterstützt werden. Infolgedessen ist es nicht möglich, `BDB`-Tabellendateien von einem Datenbankverzeichnis in ein anderes zu verlagern.

- Wenn Sie `BDB`-Tabellen sichern, müssen Sie entweder mysqldump verwenden oder eine Sicherung anlegen, die Dateien für jede `BDB`-Tabelle (also die `.frm`- und die `.db`-Dateien) sowie die `BDB`-Logdateien speichert. Die Speicher-Engine `BDB` speichert unvollendete Transaktionen in ihren Logdateien und erfordert, dass diese beim Starten von mysqld präsent sind. Die `BDB`-Logs sind die Dateien im Data Directory, deren Namen die Form `log.`NNNNNNNNNN`` haben (zehn Ziffern).

- Wenn eine Spalte, die `NULL`-Werte zulässt, einen eindeutigen Index hat, darf nur ein einziger `NULL`-Wert vorhanden sein. Im Gegensatz dazu erlauben andere Speicher-Engines auch mehrere `NULL`-Werte in Unique-Indizes.

### 14.5.6. Fehler, die bei der Benutzung von BDB-Tabellen auftreten können

- Wenn Sie mysqld nach einem Upgrade starten und folgenden Fehler erhalten, so bedeutet dies, dass die neue Version von `BDB` das alte Logdateiformat nicht mehr unterstützt:

      bdb:  Ignoring log file: .../log.NNNNNNNNNN:
      unsupported log version #

  In diesem Fall müssen Sie alle `BDB`-Logs aus Ihrem Datenverzeichnis löschen (also die Dateien, deren Namen die Form `log.`NNNNNNNNNN`` haben) und mysqld neu starten. Außerdem sollten Sie mit mysqldump --opt Ihre `BDB`-Tabellen dumpen, löschen und dann aus der Dump-Datei rekonstruieren.

- Wenn Sie bei ausgeschaltetem Autocommit eine `BDB`-Tabelle löschen, die in einer anderen Transaktion verwendet wird, wird vielleicht eine Fehlermeldung wie die folgende in Ihr MySQL-Fehlerlog geschrieben:

      001119 23:43:56  bdb:  Missing log fileid entry
      001119 23:43:56  bdb:  txn_abort: Log undo failed for LSN:
                             1 3644744: Invalid

  Das ist zwar nicht fatal, lässt sich aber auch nicht ganz einfach beheben. So lange dieses Problem noch nicht behoben ist, raten wir Ihnen, `BDB`-Tabellen nur bei eingeschaltetem Autocommit zu löschen.

## 14.6. Die `EXAMPLE`-Speicher-Engine

Die Speicher-Engine `EXAMPLE` ist eine Sockel-Engine, die eigentlich gar nichts tut, sondern einzig als Beispiel im MySQL-Quellcode dienen soll, um zu veranschaulichen, wie man neue Speicher-Engines erstellt. Sie ist vor allem für Entwickler von Interesse.

Die Speicher-Engine `EXAMPLE` ist in den MySQL-Max-Binärdistributionen enthalten. Wenn Sie MySQL von der Quelldistribution bauen, können Sie diese Speicher-Engine aktivieren, indem Sie configure mit der Option `--with-example-storage-engine` aufrufen.

Die Quelle für die `EXAMPLE`-Engine finden Sie im Verzeichnis `storage/example` der MySQL-Quelldistribution.

Wenn Sie eine `EXAMPLE`-Tabelle anlegen, erstellt der Server eine Tabellen-Formatdatei im Datenbankverzeichnis. Die Datei beginnt mit dem Tabellennamen und hat die Erweiterung `.frm`. Andere Dateien werden nicht erzeugt und Daten können in der Tabelle auch nicht gespeichert werden. Anfragen geben eine leere Ergebnismenge zurück.

    mysql> CREATE TABLE test (i INT) ENGINE = EXAMPLE;
    Query OK, 0 rows affected (0.78 sec)

    mysql> INSERT INTO test VALUES(1),(2),(3);
    ERROR 1031 (HY000): Table Speicher-Engine for 'test' doesn't have this option

    mysql> SELECT * FROM test;
    Empty set (0.31 sec)

Die Speicher-Engine `EXAMPLE` unterstützt keine Indizierung.

## 14.7. Die `FEDERATED`-Speicher-Engine

Die Speicher-Engine `FEDERATED` greift auf Daten in entfernten Datenbanktabellen (auf anderen Hosts) zu, nicht in lokalen Tabellen.

Die Speicher-Engine `FEDERATED` ist in MySQL-Max-Binärdistributionen enthalten. Wenn Sie MySQL von der Quelldistribution bauen, können Sie diese Speicher-Engine aktivieren, indem Sie configure mit der Option `--with-federated-storage-engine` aufrufen.

Die Quelle für die `FEDERATED`-Engine finden Sie im Verzeichnis `sql` einer MySQL-Quelldistribution.

Mehr zum Thema

- Ein spezielles Forum zur Speicher-Engine `FEDERATED` finden Sie unter http://forums.mysql.com/list.php?105.

### 14.7.1. Beschreibung der `FEDERATED`-Speicher-Engine

Wenn Sie eine `FEDERATED`-Tabelle anlegen, erzeugt der Server eine Tabellen-Formatdatei im Datenbankverzeichnis. Die Datei beginnt mit dem Tabellennamen und hat die Erweiterung `.frm`. Andere Dateien werden nicht angelegt, da die eigentlichen Daten in einer Remote-Tabelle vorliegen. Das steht im Gegensatz zu den Speicher-Engines für lokale Tabellen.

Für lokale Datenbanktabellen liegen auch die Datendateien lokal vor. Wenn Sie beispielsweise eine `MyISAM`-Tabelle namens `users` anlegen, erzeugt der `MyISAM`-Handler eine Datendatei namens `users.MYD`. Ein Handler für lokale Tabellen liest, ergänzt, löscht und aktualisiert Daten in lokalen Datendateien und die Zeilen werden in einem für den Handler spezifischen Format gespeichert. Um Datenzeilen zu lesen, muss der Handler die Daten in Spalten parsen, und um Zeilen zu schreiben, müssen die Spaltenwerte in das vom Handler benutzte Zeilenformat umgewandelt und in die lokale Datendatei geschrieben werden.

Doch bei der MySQL-Speicher-Engine `FEDERATED` existieren lokal keine Datendateien für eine Tabelle (es gibt beispielsweise keine `.MYD`-Datei). Stattdessen speichert eine entfernte Datenbank die Daten, die normalerweise in der Tabelle vorliegen würden. Der lokale Server verbindet sich mit einem entfernten Server und liest, löscht, aktualisiert und ergänzt die Daten in der entfernten Tabelle über eine MySQL-API. Abgefragt werden die Daten mit einer `SELECT * FROM `tbl_name``-SQL-Anweisung. Um das Ergebnis zu lesen, werden die Zeilen eine nach der anderen mit der C-API-Funktion `mysql_fetch_row()` abgeholt. Danach werden die Spalten der `SELECT`-Ergebnismenge in das vom `FEDERATED`-Handler erwartete Format umgewandelt.

Der Informationsfluss ist wie folgt:

1. Lokaler SQL-Aufruf

2. MySQL-Handler-API (Daten im Format des Handlers)

3. MySQL-Client-API (Daten werden in SQL-Aufrufe konvertiert)

4. Remote-Datenbank -> MySQL-Client-API

5. Ergebnismengen (sofern vorhanden) werden in Handler-Format konvertiert

6. Handler-API -> Ergebniszeilen oder Zahl der betroffenen Zeilen werden lokal angegeben

### 14.7.2. Benutzung von `FEDERATED`-Tabellen

Das Verfahren zur Benutzung der `FEDERATED`-Tabellen ist sehr einfach. Normalerweise betreiben Sie zwei Server, entweder auf demselben oder auf verschiedenen Hosts. (Es ist möglich, allerdings nicht sehr sinnvoll, dass eine `FEDERATED`-Tabelle eine andere Tabelle verwendet, die von demselben Server verwaltet wird.)

Zuerst muss auf dem Remote-Server eine Tabelle liegen, auf die Sie mit einer `FEDERATED`-Tabelle zugreifen möchten. Angenommen, die entfernte Tabelle liegt in der Datenbank `federated` und ist folgendermaßen definiert:

    CREATE TABLE test_table (
        id     INT(20) NOT NULL AUTO_INCREMENT,
        name   VARCHAR(32) NOT NULL DEFAULT '',
        other  INT(20) NOT NULL DEFAULT '0',
        PRIMARY KEY  (id),
        INDEX name (name),
        INDEX other_key (other)
    )
    ENGINE=MyISAM
    DEFAULT CHARSET=latin1;

Im Beispiel wird eine `MyISAM`-Tabelle verwendet, aber es könnte auch eine andere Speicher-Engine benutzt werden.

Nun erstellen Sie eine `FEDERATED`-Tabelle auf dem lokalen Server, um auf die entfernte Tabelle zuzugreifen:

    CREATE TABLE federated_table (
        id     INT(20) NOT NULL AUTO_INCREMENT,
        name   VARCHAR(32) NOT NULL DEFAULT '',
        other  INT(20) NOT NULL DEFAULT '0',
        PRIMARY KEY  (id),
        INDEX name (name),
        INDEX other_key (other)
    )
    ENGINE=FEDERATED
    DEFAULT CHARSET=latin1
    CONNECTION='mysql://root@remote_host:9306/federated/test_table';

(Hinweis: `CONNECTION` ersetzt `COMMENT`, was in früheren MySQL-Versionen verwendet wurde.)

Die Struktur dieser Tabelle muss der Struktur der entfernten Tabelle genau entsprechen, nur die Tabellenoption `ENGINE` ist `FEDERATED` und die Tabellenoption `CONNECTION` ist ein Verbindungsstring, welcher der `FEDERATED`-Engine sagt, wie sie sich mit dem entfernten Server verbinden kann.

Die `FEDERATED`-Engine erzeugt in der `federated`-Datenbank nur die `test_table.frm`-Datei.

Die Remote-Host-Daten geben an, mit welchem entfernten Server sich Ihr lokaler Server verbindet, und die Datenbank- und Tabelleninformationen geben an, welche entfernte Tabelle als Datenquelle verwendet werden soll. Da im vorliegenden Beispiel der entfernte Server als `remote_host` auf Port 9306 definiert ist, muss ein MySQL-Server auf dem Remote-Host laufen und auf Port 9306 lauschen.

Die allgemeine Form eines Verbindungsstrings in der Option `CONNECTION` ist:

    scheme://user_name[:password]@host_name[:port_num]/db_name/tbl_name

Vorläufig wird nur `mysql` als Wert für `scheme` akzeptiert. Das Passwort und die Port-Nummer sind optional.

Im Folgenden sehen Sie einige Beispiele für Verbindungsstrings:

    CONNECTION='mysql://username:password@hostname:port/database/tablename'
    CONNECTION='mysql://username@hostname/database/tablename'
    CONNECTION='mysql://username:password@hostname/database/tablename'

`CONNECTION` ist für die Angabe des Verbindungsstrings nicht optimal geeignet und wird wahrscheinlich irgendwann ersetzt. Für Anwendungen mit `FEDERATED`-Tabellen müssen Sie sich daher merken, dass diese Anwendungen modifiziert werden müssen, wenn sich das Format für die Verbindungsinformationen eines Tages ändert.

Da jedes Passwort, das Sie im Verbindungsstring angeben, als einfacher Text gespeichert wird, ist es für jeden Benutzer ersichtlich, der `SHOW CREATE TABLE` oder `SHOW TABLE STATUS` für die `FEDERATED`-Tabelle ausführen oder die `TABLES`-Tabelle in der `INFORMATION_SCHEMA`-Datenbank abfragen darf.

### 14.7.3. Beschränkungen der `FEDERATED`-Speicher-Engine

Die folgenden Features werden von der Speicher-Engine `FEDERATED` unterstützt bzw. nicht unterstützt:

- In der vorliegenden Version muss der entfernte Server ein MySQL-Server sein. In Zukunft wird `FEDERATED` möglicherweise auch andere Datenbank-Engines unterstützen.

- Die entfernte Tabelle, die von einer `FEDERATED`-Tabelle benutzt wird, muss vorhanden sein, bevor Sie mit `FEDERATED` versuchen, auf sie zuzugreifen.

- Es ist möglich, mit einer `FEDERATED`-Tabelle auf eine andere zu verweisen, aber bitte achten Sie darauf, keine Endlosschleife zu erzeugen.

- `FEDERATED` unterstützt keine Transaktionen.

- Die `FEDERATED`-Engine kann nicht wissen, ob die entfernte Tabelle sich geändert hat. Der Grund dafür: Diese Tabelle muss wie eine Datendatei funktionieren, in die niemand anders als die Datenbank schreiben kann. Die Datenintegrität in der lokalen Tabelle könnte beschädigt werden, wenn sich in der entfernten Datenbank etwas ändert.

- Die `FEDERATED`-Speicher-Engine unterstützt `SELECT`, `INSERT`, `UPDATE`, `DELETE` und Indizes. Nicht unterstützt werden `ALTER TABLE`, `DROP TABLE` oder andere Data Definition Language-Anweisungen. Die aktuelle Implementierung verwendet keine vorbereiteten Anweisungen (Prepared-Statements).

- Die Implementierung verwendet `SELECT`, `INSERT`, `UPDATE` und `DELETE`, aber nicht `HANDLER`.

- `FEDERATED`-Tabellen arbeiten nicht mit dem Anfragen-Cache.

Manche dieser Beschränkungen werden vielleicht in künftigen Versionen des `FEDERATED`-Handlers entfallen.

## 14.8. Die `ARCHIVE`-Speicher-Engine

Die Speicher-Engine `ARCHIVE` dient der Speicherung großer Datenmengen ohne Indizes mit einem sehr kleinen Speicherbedarf.

Die Speicher-Engine `ARCHIVE` ist in den Binärdistributionen von MySQL enthalten. Wenn Sie MySQL aus der Quelldistribution bauen, aktivieren Sie diese Speicher-Engine, indem Sie configure mit der Option `--with-archive-storage-engine` aufrufen.

Die Quelle für die `ARCHIVE`-Engine finden Sie im Verzeichnis `storage/archive` der MySQL-Quelldistribution.

Ob die `ARCHIVE`-Engine zur Verfügung steht, prüfen Sie mit folgender Anweisung:

    mysql> SHOW VARIABLES LIKE 'have_archive';

Wenn Sie eine `ARCHIVE`-Tabelle anlegen, erzeugt der Server eine Tabellen-Formatdatei im Datenbankverzeichnis. Die Datei beginnt mit dem Tabellennamen und hat die Erweiterung`.frm`. Die Speicher-Engine legt noch weitere Dateien an, deren Namen alle mit dem Tabellennamen anfangen. Die Datendateien haben die Erweiterung `.ARZ` und die Metadatendateien die Erweiterung `.ARM`. Eine `.ARN`-Datei kann bei Optimierungsoperationen erscheinen.

Die `ARCHIVE`-Engine unterstützt `INSERT` und `SELECT`, aber nicht `DELETE`, `REPLACE` oder `UPDATE`. Sie unterstützt `ORDER BY`-Operationen, `BLOB`-Spalten und im Grunde alle Datentypen außer den raumbezogenen (Spatial-Daten) (siehe Abschnitt 18.4.1, „Raumbezogene Datentypen in MySQL“). Außerdem nutzt `ARCHIVE` Zeilensperren.

Seit MySQL 5.1.6 unterstützt `ARCHIVE` das `AUTO_INCREMENT`-Spaltenattribut. Die `AUTO_INCREMENT`-Spalten können einen eindeutigen oder einen nicht-eindeutigen Index haben. Der Versuch, einen Index auf einer anderen Spalte anzulegen, führt zu einem Fehler. Außerdem unterstützt `ARCHIVE` die Tabellenoption `AUTO_INCREMENT` in `CREATE TABLE`- und `ALTER TABLE`-Anweisungen. So kann der erste Wert der Folge für eine neue Tabelle angegeben oder für eine vorhandene Tabelle zurückgesetzt werden.

Seit MySQL 5.1.6 ignoriert die `ARCHIVE`-Engine `BLOB`-Spalten, wenn diese nicht angefordert werden, und übergeht sie beim Lesen. Früher bedeuteten die folgenden beiden Anweisungen denselben Aufwand, doch seit der Version 5.1.6 ist die zweite viel effizienter als die erste:

    SELECT a, b, blob_col FROM archive_table;
    SELECT a, b FROM archive_table;

Speicherung: Zeilen werden beim Einfügen komprimiert. `ARCHIVE` verwendet verlustfreie `zlib`-Datenkompression (siehe http://www.zlib.net/). Mit `OPTIMIZE TABLE` können Sie die Tabelle analysieren und in ein kleineres Format packen (einen Grund zur Verwendung von `OPTIMIZE TABLE` finden Sie weiter unten in diesem Abschnitt). Außerdem unterstützt diese Engine `CHECK TABLE`. Mehrere verschiedene Arten von Einfügungen sind möglich:

- Eine `INSERT`-Anweisung schiebt die Zeilen einfach in einen Kompressionspuffer, der nach Bedarf auf die Platte zurückgeschrieben wird. Die Einfügung von Daten in den Puffer ist durch eine Sperre geschützt. Mit `SELECT` wird das Schreiben auf die Festplatte erzwungen, sofern nicht nur `INSERT DELAYED`-Einfügungen vorgekommen waren (diese werden nur nach Bedarf auf die Platte geschrieben). Siehe Abschnitt 13.2.4.2, „`INSERT DELAYED`“.

- Eine Massen-Einfügeoperation (bulk insert) wird erst nach ihrem Abschluss sichtbar, wenn nicht gleichzeitig andere Einfügungen auftreten: In diesem Fall wird sie teilweise sichtbar. Ein `SELECT` hat normalerweise nicht zur Folge, dass eine Massen-Einfügeoperation auf die Festplatte geschrieben wird, es sei denn, eine normale Einfügeoperation tritt auf, während die andere gerade geladen wird..

Anfragen: Bei Anfragen werden die Zeilen nach Bedarf dekomprimiert; es gibt keinen Zeilen-Cache. Eine `SELECT`-Operation führt einen kompletten Tabellen-Scan durch: Wenn ein `SELECT` auftritt, stellt es fest, wie viele Zeilen gerade zur Verfügung stehen und liest diese Anzahl Zeilen. `SELECT` wird als konsistente Leseoperation durchgeführt. Beachten Sie, dass viele `SELECT`-Anweisungen während einer Einfügeoperation die Datenkompression schwächt, es sei denn, Sie verwenden nur Massen- oder verzögerte Einfügungen. Eine bessere Kompression können Sie mit `OPTIMIZE TABLE` oder `REPAIR TABLE` erzielen. Die Anzahl der Zeilen, die `SHOW TABLE STATUS` für `ARCHIVE`-Tabellen meldet, ist immer korrekt. Siehe Abschnitt 13.5.2.5, „`OPTIMIZE TABLE`“, Abschnitt 13.5.2.6, „`REPAIR TABLE`“ und Abschnitt 13.5.4.21, „`SHOW TABLE STATUS`“.

Mehr zum Thema

- Ein spezielles Forum zur Speicher-Engine `ARCHIVE` finden Sie unter http://forums.mysql.com/list.php?112.

## 14.9. Die `CSV`-Speicher-Engine

Die `CSV`-Speicher-Engine speichert Daten in Textdateien im Format von kommagetrennten Werten (Comma Separated Values, CSV).

Um diese Speicher-Engine zu aktivieren, verwenden Sie configure mit der Option `--with-csv-storage-engine`, wenn Sie MySQL bauen.

Die Speicher-Engine `CSV` ist in MySQL-Max- Binärdistributionen enthalten. Wenn Sie MySQL von einer Quelldistribution bauen, können Sie sie aktivieren, indem Sie configure mit der Option `--with-csv-storage-engine` aufrufen.

Die Quelle für die `CSV`-Engine finden Sie im Verzeichnis `storage/csv` einer MySQL- Quelldistribution.

Wenn Sie eine `CSV`-Tabelle anlegen, erstellt der Server eine Tabellen-Formatdatei im Datenbankverzeichnis. Die Datei beginnt mit dem Tabellennamen und hat die Erweiterung `.frm`. Außerdem legt die Speicher-Engine eine Datendatei an, deren Name mit dem Tabellennamen anfängt und die Erweiterung `.CSV` hat. Die Datendatei ist eine einfache Textdatei. Wenn Sie Daten in der Tabelle speichern, schreibt die Engine sie im CVS-Format in die Datei.

    mysql> CREATE TABLE test(i INT, c CHAR(10)) ENGINE = CSV;
    Query OK, 0 rows affected (0.12 sec)

    mysql> INSERT INTO test VALUES(1,'record one'),(2,'record two');
    Query OK, 2 rows affected (0.00 sec)
    Records: 2  Duplicates: 0  Warnings: 0

    mysql> SELECT * FROM test;
    +------+------------+
    | i    | c          |
    +------+------------+
    |    1 | record one |
    |    2 | record two |
    +------+------------+
    2 rows in set (0.00 sec)

Wenn Sie die mit der obigen Anweisung im Datenbankverzeichnis erzeugte Datei `test.CSV` anschauen, müsste sie folgenden Inhalt haben:

    "1","record one"
    "2","record two"

Die Speicher-Engine `CSV` unterstützt keine Indizierung.

## 14.10. Die `BLACKHOLE`-Speicher-Engine

Die Speicher-Engine `BLACKHOLE` ist wie ein „Schwarzes Loch“, das Daten zwar entgegennimmt, aber nicht speichert. Anfragen geben immer eine leere Ergebnismenge zurück:

    mysql> CREATE TABLE test(i INT, c CHAR(10)) ENGINE = BLACKHOLE;
    Query OK, 0 rows affected (0.03 sec)

    mysql> INSERT INTO test VALUES(1,'record one'),(2,'record two');
    Query OK, 2 rows affected (0.00 sec)
    Records: 2  Duplicates: 0  Warnings: 0

    mysql> SELECT * FROM test;
    Empty set (0.00 sec)

Die Speicher-Engine `BLACKHOLE` ist in MySQL-Max- Binärdistributionen enthalten. Wenn Sie MySQL von einer Quelldistribution bauen, können Sie sie aktivieren, indem Sie configure mit der Option `--with-blackhole-storage-engine` aufrufen.

Die Quelle für die `BLACKHOLE`-Engine finden Sie im Verzeichnis `sql` einer MySQL- Quelldistribution.

Wenn Sie eine `BLACKHOLE`-Tabelle anlegen, erstellt der Server eine Tabellen-Formatdatei im Datenbankverzeichnis. Die Datei beginnt mit dem Tabellennamen und hat die Erweiterung `.frm`. Andere Dateien werden mit der Tabelle nicht verknüpft.

Die `BLACKHOLE`-Speicher-Engine unterstützt alle Arten von Indizes. Das bedeutet, dass Sie Indexdeklarationen in die Tabellendefinition aufnehmen können.

Ob `BLACKHOLE` zur Verfügung steht, prüfen Sie mit folgender Anweisung:

    mysql> SHOW VARIABLES LIKE 'have_blackhole_engine';

Bei Einfügungen in eine `BLACKHOLE`-Tabelle werden keine Daten gespeichert, doch wenn das Binärlog aktiviert ist, werden die SQL-Anweisungen protokolliert (und auf die Slaveserver repliziert). Das kann als Wiederholungs- oder Filtermechanismus ganz nützlich sein. Nehmen wir zum Beispiel an, Ihre Anwendung benötigt Filterregeln auf der Slave-Seite, aber eine Übertragung sämtlicher Logdaten auf den Slave würde zu viel Traffic verursachen. In solchen Fällen kann auf dem Master-Host ein „Dummy“-Slave-Prozess mit `BLACKHOLE` als Speicher-Engine eingerichtet werden:

Der Master schreibt in sein Binärlog und der als Slave fungierende „Dummy“-mysqld-Prozess wendet die gewünschte Kombination von `replicate-do-*`- und `replicate-ignore-*`-Regeln an und schreibt ein eigenes gefiltertes Binärlog. (Siehe Abschnitt 6.9, „Replikationsoptionen in my.cnf“.) Dieses gefilterte Log wird dann dem Slave zur Verfügung gestellt.

Da der Dummy-Prozess selbst gar keine Daten speichert, entsteht durch den zusätzlichen mysqld-Prozess auf dem Replikations-Master-Host kaum Verarbeitungs-Overhead. Dieser Mechanismus kann mit weiteren Replikations-Slaves wiederholt werden.

Andere mögliche Einsatzgebiete für `BLACKHOLE` sind:

- Syntaxprüfung für Dump-Dateien.

- Sie können den Overhead für Binärlogs messen, indem Sie die Performance eines `BLACKHOLE`s mit und ohne Binärlog vergleichen.

- Da `BLACKHOLE` im Grunde eine „leere“ Speicher-Engine ist, könnte sie eingesetzt werden, um Leistungsengpässe ausfindig zu machen, die nicht mit der Speicher-Engine selbst zusammenhängen.

Seit MySQL 5.1.4 kann die `BLACKHOLE`-Engine mit Transaktionen umgehen, und zwar in dem Sinne, dass sie bestätigte Transaktionen in das Binärlog schreibt und zurückgerollte nicht.

Dies ist eine Übersetzung des MySQL-Referenzhandbuchs, das sich auf dev.mysql.com befindet. Das ursprüngliche Referenzhandbuch ist auf Englisch, und diese Übersetzung ist nicht notwendigerweise so aktuell wie die englische Ausgabe. Das vorliegende deutschsprachige Handbuch behandelt MySQL bis zur Version 5.1.

Zurück |  | Weiter
Kapitel 13. SQL-Anweisungssyntax | Zum Anfang | Kapitel 15. Erstellung einer eigenen Speicher-Engine