wie Entity-IDs kollisionsfrei vergeben werden
Wer Bestellnummern, Rechnungsnummern oder eigene Entity-IDs in Magento 2 verstehen will, kommt an Sequence-Tables nicht vorbei: dedizierte Ein-Spalten-Tabellen, die AUTO_INCREMENT-Werte atomar reservieren und dabei komplett vom internen Primärschlüssel der Fachtabelle entkoppelt sind. Dieser Artikel erklärt die Architektur von sequence_meta und sequence_profile, zeigt eine eigene Sequence-Table für ein Custom-Entity per db_schema.xml und Data Patch, und liefert konkrete SQL-Befehle zur Diagnose und Reparatur im Live-Betrieb.
Inhaltsverzeichnis
- 1. Was Sequence-Tables sind und welches Problem sie lösen
- 2. Warum Magento von AUTO_INCREMENT auf Sequence-Tables umgestiegen ist
- 3. Architektur: SequenceBuilder, Sequence und Magento_SalesSequence
- 4. Sequence-Tables pro Store View für Orders, Invoices, Shipments, Creditmemos
- 5. sequence_meta und sequence_profile: die Registry dahinter
- 6. Eigene Sequence-Tables anlegen: db_schema.xml und Setup Patch
- 7. increment_prefix und increment_pad_length: das Formatierungsmuster
- 8. Lücken, Kollisionen und Performance unter hoher Last
- 9. Eine defekte Sequence-Table im Live-Shop sicher reparieren
- 10. Zusammenfassung
- 11. FAQ
1. Was Sequence-Tables sind und welches Problem sie lösen
Eine Sequence-Table ist in Magento 2 eine extrem schlanke Hilfstabelle mit genau einer Spalte: sequence_value, definiert als INT UNSIGNED AUTO_INCREMENT PRIMARY KEY. Der Tabellenname folgt dem festen Muster sequence_<entity>_<store>, zum Beispiel sequence_order_1 für Bestellungen der Store View mit der ID 1, oder sequence_invoice_2 für Rechnungen der zweiten Store View. Jede Store View erhält für jeden relevanten Entity-Typ ihre eigene physische Sequence-Table, komplett getrennt von der eigentlichen Datentabelle wie sales_order oder sales_invoice.
Der Zweck von Sequence-Tables ist simpel und zugleich fundamental: Sie liefern eine garantiert kollisionsfreie, monoton steigende Ganzzahl, die vom internen entity_id der eigentlichen Entität vollständig unabhängig ist. Legt Magento eine neue Bestellung an, wird nicht der AUTO_INCREMENT-Wert von sales_order als sichtbare Bestellnummer verwendet, sondern ein Wert, der über eine dedizierte Sequence-Table aus dem Modul Magento_SalesSequence reserviert wird. Diese Trennung betrifft in Magento 2.4.8 alle zentralen Sales-Entitäten: Orders, Invoices, Shipments und Creditmemos beziehen ihre sichtbare Nummer ausschließlich über Sequence-Tables, nicht über die AUTO_INCREMENT-Spalte der Fachtabelle selbst.
Für Entwickler, die aus klassischen relationalen Datenmodellen kommen, wirkt dieses Muster zunächst wie unnötige Komplexität. Tatsächlich löst es aber ein sehr konkretes Betriebsproblem: Sequence-Tables entkoppeln die Vergabe von Anzeige-IDs von der physischen Speicherung der Entität selbst, was in horizontal skalierten, replizierten oder auf mehrere Datenquellen aufgeteilten Magento-Installationen entscheidend ist.
2. Warum Magento von AUTO_INCREMENT auf Sequence-Tables umgestiegen ist
Vor der heutigen Architektur nutzte Magento für Bestellnummern ein Muster, das aus dem EAV-System stammt: die Tabelle eav_entity_store mit den Spalten increment_prefix, increment_last_id, increment_pad_length und increment_pad_char. Der nächste Wert wurde ermittelt, indem increment_last_id gelesen, in PHP hochgezählt und anschließend zurückgeschrieben wurde, ein klassisches Read-Modify-Write-Muster ohne atomare Garantie auf Datenbankebene. Unter paralleler Bestellabwicklung mit mehreren PHP-Prozessen führte das zu Race Conditions: Zwei Checkout-Prozesse konnten denselben increment_last_id-Wert lesen, bevor einer der beiden zurückschreibt, und erzeugten dadurch doppelte Bestellnummern.
Mit Multi-Source-Inventory und der Notwendigkeit, Magento-Installationen über mehrere Datenbank-Master oder replizierte Knoten zu betreiben, wurde dieses Problem gravierender. AUTO_INCREMENT-Werte einer Fachtabelle wie sales_order sind an die physische Tabelle und ihre Replikationskonfiguration gebunden. Bei Multi-Master-Replikation mit auto_increment_increment und auto_increment_offset springen vergebene IDs je nach Master in unterschiedlichen Intervallen, was für interne Primärschlüssel unkritisch ist, für eine dem Kunden sichtbare, fortlaufende Bestellnummer aber inakzeptabel wäre. Sequence-Tables lösen dieses Problem, indem sie die ID-Vergabe in eine eigene, extrem kleine Tabelle auslagern, die unabhängig von der Sharding-Strategie der Hauptdaten auf einem einzigen kanonischen Schreibknoten geführt werden kann.
Der zweite Vorteil betrifft die Entkopplung von internem Primärschlüssel und sichtbarer Nummer. Der entity_id einer Bestellung kann ein simpler, datenbankweiter AUTO_INCREMENT-Wert bleiben, wie es für Fremdschlüsselbeziehungen und Indizierung optimal ist, während die kundenseitig sichtbare Bestellnummer über eine separate Sequence-Table pro Store View fortlaufend und store-spezifisch formatiert bleibt. Diese Trennung ist ein Kernprinzip der heutigen Magento-Architektur und war mit dem alten EAV-Increment-Muster praktisch nicht sauber abbildbar.
3. Architektur: SequenceBuilder, Sequence und Magento_SalesSequence
Das Framework-Fundament liegt im Namespace Magento\Framework\DB\Sequence. Das Interface SequenceInterface definiert genau eine Methode: getNextValue(). Die konkrete Implementierung Magento\Framework\DB\Sequence\Sequence führt bei jedem Aufruf ein leeres INSERT INTO sequence_<tabelle> () VALUES () aus und liest anschließend LAST_INSERT_ID() über die Connection aus. Diese zwei Datenbankoperationen sind durch InnoDBs internen AUTO_INCREMENT-Mechanismus atomar, ganz ohne eigene Anwendungslogik für Locking, Race-Condition-Vermeidung oder Retry-Schleifen.
Für das physische Anlegen der Tabelle existiert Magento\Framework\DB\Sequence\SequenceBuilder, eine Fluent-Builder-Klasse mit Methoden wie setPrefix(), setSuffix(), setStartValue() und create(). Der Builder generiert daraus das CREATE TABLE-Statement für die passende Sequence-Table, inklusive korrektem Startwert über AUTO_INCREMENT = <startValue>. Dieser Baustein wird nicht im Checkout selbst verwendet, sondern ausschließlich zur Setup-Zeit, wenn eine neue Store View oder ein neuer Entity-Typ registriert wird.
Das Modul Magento_SalesSequence baut eine komfortable Schicht darüber: Magento\SalesSequence\Model\Builder kombiniert den SequenceBuilder mit der Registrierung in den Verwaltungstabellen sequence_meta und sequence_profile. Magento\SalesSequence\Model\EntityPool definiert per di.xml, welche Entity-Typen überhaupt eigene Sequence-Tables benötigen: Standardmäßig sind das order, invoice, shipment und creditmemo. Jeder Eintrag im Entity-Pool erhält bei der Store-View-Anlage automatisch seine eigene Sequence-Table, ohne dass ein Entwickler manuell eingreifen muss.
4. Sequence-Tables pro Store View für Orders, Invoices, Shipments, Creditmemos
Sobald im Admin eine neue Store View angelegt wird, iteriert Magento über den EntityPool und erzeugt für jeden registrierten Entity-Typ eine neue Sequence-Table für genau diese Store View. Für die Store View mit der ID 3 entstehen so sequence_order_3, sequence_invoice_3, sequence_shipment_3 und sequence_creditmemo_3, jede mit eigenem, bei 1 startendem Zähler. Das ist der Grund, warum zwei Shops in derselben Magento-Installation unabhängig voneinander bei Bestellnummer 100000001 beginnen können, selbst wenn sie dieselbe sales_order-Tabelle teilen.
Diese Store-View-Granularität ist bewusst gewählt: Viele Magento-Betreiber konfigurieren unterschiedliche increment_prefix-Werte je Store View, etwa DE- für den deutschen und AT- für den österreichischen Shop, während beide Store Views dieselbe physische Bestelltabelle nutzen. Ohne separate Sequence-Tables pro Store View wäre eine solche pro-Markt-Nummerierung mit garantierter Kollisionsfreiheit nicht ohne komplexe Zusatzlogik möglich.
Wichtig für den Praxisbetrieb: Wird eine Store View gelöscht, bleibt ihre Sequence-Table in der Datenbank in der Regel bestehen, sofern kein expliziter Cleanup-Schritt läuft. Das ist unkritisch, verdient aber Beachtung bei Datenbank-Audits, weil verwaiste Sequence-Tables mit eingefrorenem Zählerstand über Jahre hinweg als scheinbar tote Tabellen im Schema auftauchen können. Ein Blick in sequence_meta zeigt zuverlässig, welche Store-ID zu welcher Tabelle gehört, auch wenn die Store View selbst längst gelöscht wurde.
5. sequence_meta und sequence_profile: die Registry dahinter
Neben den eigentlichen sequence_<entity>_<store>-Tabellen legt Magento_SalesSequence zwei Verwaltungstabellen an. sequence_meta enthält pro Zeile eine Kombination aus entity_type und store_id und referenziert damit eindeutig, welche physische Sequence-Table für welchen Store und welchen Entity-Typ zuständig ist. Diese Tabelle ist die zentrale Nachschlagestelle, über die Magento zur Laufzeit die passende Sequence-Instanz auflöst, ohne Tabellennamen im Code hart zu verdrahten.
Die Tabelle sequence_profile referenziert einen Eintrag in sequence_meta über meta_id und speichert die eigentliche Konfiguration: prefix, suffix, start_value, step, max_value, warning_value und ein is_active-Flag. Genau dieses Profil-Konzept macht Sequence-Tables flexibler als eine reine AUTO_INCREMENT-Spalte: Ein Betreiber kann ein neues Profil mit neuem Prefix aktivieren, etwa zum Jahreswechsel, ohne die physische Sequence-Table selbst zu verändern oder ihren Zählerstand zu verlieren. Es entsteht lediglich ein neuer Profil-Datensatz mit is_active = 1, während das alte Profil auf 0 gesetzt wird.
In der Praxis bleibt sequence_profile bei den meisten Standard-Installationen unverändert bei einem einzigen aktiven Profil pro Entity-Typ und Store. Für Individualentwicklungen, etwa wenn ein Kunde jährlich neu beginnende Rechnungsnummern mit Jahres-Prefix benötigt, ist die Profil-Tabelle der korrekte Ansatzpunkt, nicht die direkte Manipulation der physischen Sequence-Table.
6. Eigene Sequence-Tables anlegen: db_schema.xml und Setup Patch
Für ein eigenes Modul, das kollisionsfreie, store-spezifische Nummern benötigt, etwa eine Service-Auftragsnummer in einem individuellen Reparatur- oder Abo-Modul, folgt man demselben Muster wie die Magento-Kernmodule. Die Fachtabelle selbst wird ganz normal über db_schema.xml deklariert, mit einem klassischen AUTO_INCREMENT-entity_id als Primärschlüssel und einer separaten increment_id-Spalte, die später aus der Sequence-Table befüllt wird, nicht aus der eigenen AUTO_INCREMENT-Spalte.
Die physische Sequence-Table selbst wird nicht über db_schema.xml deklariert, sondern zur Setup-Zeit programmatisch über eine Data-Patch-Klasse erzeugt, die Magento\SalesSequence\Model\Builder nutzt. Diese Klasse iteriert über alle vorhandenen Store Views, ruft für jede Store View setEntityType(), setStoreId(), setPrefix() und setStartValue() auf und schließt mit create() ab. Der Builder legt daraufhin sowohl die physische Tabelle als auch die zugehörigen Zeilen in sequence_meta und sequence_profile an, exakt wie beim Anlegen einer neuen Store View für die Kernmodule.
Der entscheidende Vorteil dieses Ansatzes gegenüber einer eigenen AUTO_INCREMENT-Spalte in der Fachtabelle: Sollte das Custom-Modul später auf eine geteilte oder replizierte Datenbank umziehen, bleibt die Nummernvergabe unverändert korrekt, weil sie bereits von Tag eins an über dieselbe entkoppelte Sequence-Table-Architektur läuft, die auch die Magento-Kernmodule verwenden.
<?xml version="1.0"?>
<!-- File: app/code/Mironsoft/ServiceOrder/etc/db_schema.xml -->
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
<table name="mironsoft_service_order" resource="default" engine="innodb"
comment="Mironsoft Service Order Entity">
<column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false"
identity="true" comment="Entity ID, internal primary key, plain AUTO_INCREMENT"/>
<column xsi:type="varchar" name="increment_id" nullable="false" length="32"
comment="Display ID, filled from a dedicated sequence table, never AUTO_INCREMENT"/>
<column xsi:type="smallint" name="store_id" padding="5" unsigned="true" nullable="false"
comment="Store View ID, one sequence table exists per store view"/>
<column xsi:type="timestamp" name="created_at" on_update="false" nullable="false"
default="CURRENT_TIMESTAMP" comment="Created At"/>
<constraint xsi:type="primary" referenceId="PRIMARY">
<column name="entity_id"/>
</constraint>
<constraint xsi:type="unique" referenceId="MIRONSOFT_SERVICE_ORDER_INCREMENT_ID_STORE_ID">
<column name="increment_id"/>
<column name="store_id"/>
</constraint>
<index referenceId="MIRONSOFT_SERVICE_ORDER_STORE_ID" indexType="btree">
<column name="store_id"/>
</index>
</table>
</schema>
<?php
// File: app/code/Mironsoft/ServiceOrder/Setup/Patch/Data/AddServiceOrderSequence.php
declare(strict_types=1);
namespace Mironsoft\ServiceOrder\Setup\Patch\Data;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\SalesSequence\Model\Builder as SequenceBuilder;
use Magento\Store\Model\StoreManagerInterface;
/**
* Registers a dedicated sequence table for the mironsoft_service_order entity,
* one physical sequence_serviceorder_<store_id> table per store view.
*/
class AddServiceOrderSequence implements DataPatchInterface
{
/**
* @param ModuleDataSetupInterface $moduleDataSetup Setup connection wrapper
* @param SequenceBuilder $sequenceBuilder Builds and registers physical sequence tables
* @param StoreManagerInterface $storeManager Provides the list of existing store views
*/
public function __construct(
private readonly ModuleDataSetupInterface $moduleDataSetup,
private readonly SequenceBuilder $sequenceBuilder,
private readonly StoreManagerInterface $storeManager,
) {
}
/**
* Creates one sequence table per store view for the custom entity type.
*
* @return static
*/
public function apply(): static
{
$this->moduleDataSetup->getConnection()->startSetup();
foreach ($this->storeManager->getStores(true) as $store) {
$this->sequenceBuilder
->setPrefix('serviceorder')
->setSuffix((string) $store->getId())
->setStartValue(1)
->setStoreId((int) $store->getId())
->setEntityType('serviceorder')
->create();
}
$this->moduleDataSetup->getConnection()->endSetup();
return $this;
}
/**
* Declares patches that must run before this one.
*
* @return string[]
*/
public static function getDependencies(): array
{
return [];
}
/**
* Declares aliases for this patch, used when a patch class was renamed.
*
* @return string[]
*/
public function getAliases(): array
{
return [];
}
}
7. increment_prefix und increment_pad_length: das Formatierungsmuster
Auch wenn die eigentliche Zählung heute über Sequence-Tables läuft, lebt das alte Formatierungsmuster aus eav_entity_store konzeptionell weiter. Damals bestimmten die Spalten increment_prefix, increment_pad_length und increment_pad_char, wie aus einem rohen Zähler eine lesbare Bestellnummer wie 100000001 wird: ein optionales Präfix, gefolgt vom Zähler, aufgefüllt auf eine feste Stellenanzahl mit einem Füllzeichen, meist der Ziffer 0. Dieses Muster wurde nicht verworfen, sondern in die Profil-Konfiguration von sequence_profile überführt, ergänzt um die eigentliche Formatierungslogik in der jeweiligen Model-Schicht.
Konkret liefert eine Sequence-Table nur einen rohen, ungepolsterten Integer-Wert über getNextValue(), zum Beispiel 247. Die Formatierung zur finalen Anzeige-ID mit Präfix und Padding erfolgt in einer eigenen Schicht, typischerweise mit str_pad() auf eine feste Länge, standardmäßig 8 oder 9 Stellen bei Sales-Entitäten. Wer für ein Custom-Entity dasselbe Verhalten will, kombiniert einen eigenen Service, der die Sequence-Instanz kapselt, mit einer Formatierungsmethode nach demselben Prinzip.
Das folgende Beispiel zeigt einen eigenen ID-Generator im Service-Contract-Stil mit PHP 8.4 und Constructor Property Promotion, der eine Sequence-Instanz für ein Custom-Entity kapselt und die Anzeige-ID mit Präfix und Padding formatiert, exakt nach dem increment_prefix/increment_pad_length-Muster.
<?php
// File: app/code/Mironsoft/ServiceOrder/Api/ServiceOrderNumberGeneratorInterface.php
declare(strict_types=1);
namespace Mironsoft\ServiceOrder\Api;
/**
* Service Contract for generating collision free, store specific
* service order display numbers backed by a Sequence-Table.
*/
interface ServiceOrderNumberGeneratorInterface
{
/**
* Generates the next display ID for the given store, formatted with
* prefix and zero padding, backed by a dedicated sequence table.
*
* @param int $storeId Store View ID the number is generated for
* @return string Formatted display ID, e.g. "SO-000000248"
*/
public function generate(int $storeId): string;
}
<?php
// File: app/code/Mironsoft/ServiceOrder/Model/ServiceOrderNumberGenerator.php
declare(strict_types=1);
namespace Mironsoft\ServiceOrder\Model;
use Magento\Framework\App\ResourceConnection;
use Magento\Framework\DB\Sequence\Sequence;
use Mironsoft\ServiceOrder\Api\ServiceOrderNumberGeneratorInterface;
/**
* Resolves the physical sequence table by naming convention and formats
* the raw counter value into a padded, prefixed display ID, following
* the classic increment_prefix / increment_pad_length pattern.
*/
class ServiceOrderNumberGenerator implements ServiceOrderNumberGeneratorInterface
{
private const string PREFIX = 'SO-';
private const int PAD_LENGTH = 9;
private const string PAD_CHAR = '0';
/**
* @param ResourceConnection $resourceConnection Provides the DB adapter used by the sequence
*/
public function __construct(
private readonly ResourceConnection $resourceConnection,
) {
}
/**
* @inheritDoc
*/
public function generate(int $storeId): string
{
$sequenceTable = sprintf('sequence_serviceorder_%d', $storeId);
$sequence = new Sequence($this->resourceConnection, $sequenceTable);
/** @var int $rawValue */
$rawValue = $sequence->getNextValue();
return self::PREFIX . str_pad((string) $rawValue, self::PAD_LENGTH, self::PAD_CHAR, STR_PAD_LEFT);
}
}
8. Lücken, Kollisionen und Performance unter hoher Last
Lücken in der Nummernfolge sind bei Sequence-Tables keine Störung, sondern erwartetes Verhalten. Jeder Aufruf von getNextValue() führt ein echtes INSERT aus und reserviert damit unwiderruflich einen AUTO_INCREMENT-Wert, selbst wenn die eigentliche Bestellung anschließend fehlschlägt, der Checkout-Prozess abbricht oder die übergeordnete Transaktion zurückgerollt wird. InnoDB gibt einen einmal vergebenen AUTO_INCREMENT-Wert nie wieder frei, auch nicht nach einem Rollback. Ein Support-Ticket mit der Meldung "Bestellnummer 100000042 fehlt in der Liste" ist in den allermeisten Fällen kein Fehler in der Sequence-Table, sondern ein abgebrochener Checkout-Versuch, der bereits vor dem eigentlichen Speichern der Bestellung eine Nummer reserviert hat.
Bei Kollisionen liegt die Ursache fast immer außerhalb der Sequence-Logik selbst: manuelle Dateneingriffe, das Zurückspielen eines älteren Datenbank-Backups ohne Anpassung des Zählerstands, oder das Zusammenführen zweier vormals getrennter Shop-Datenbanken mit überlappenden Bestellnummern. Die eigentliche Sequence-Table kann durch das atomare AUTO_INCREMENT-Verhalten von InnoDB aus sich selbst heraus keine doppelten Werte liefern, solange niemand von außen manuell in sie hineinschreibt oder ihren Zählerstand manipuliert.
Unter hoher Parallellast, etwa bei Flash-Sale-Szenarien mit hunderten gleichzeitigen Checkouts, bleibt die Performance-Charakteristik von Sequence-Tables vorteilhaft: Die Tabelle hat nur eine Spalte und keinen sekundären Index, wodurch der interne AUTO_INCREMENT-Lock von InnoDB nur extrem kurz gehalten wird, unabhängig von der Größe der eigentlichen sales_order-Tabelle. Mit innodb_autoinc_lock_mode = 2, dem in Magento 2.4 empfohlenen interleaved Modus, entfällt zusätzlich der table-level Lock zwischen mehreren gleichzeitigen INSERT-Statements, was den Durchsatz bei parallelen Checkouts spürbar verbessert gegenüber dem klassischen, table-level-gesperrten auto_increment_lock_mode = 0.
-- Physical structure of a Sequence-Table, exactly as created by SequenceBuilder
CREATE TABLE `sequence_order_1` (
`sequence_value` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`sequence_value`)
) ENGINE=InnoDB AUTO_INCREMENT=1248 DEFAULT CHARSET=utf8mb4;
-- Check the current AUTO_INCREMENT counter of a sequence table
SELECT AUTO_INCREMENT
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'sequence_order_1';
-- Compare the sequence counter with the highest increment_id actually
-- used in sales_order for the same store, to detect a desynced sequence
SELECT MAX(CAST(SUBSTRING_INDEX(increment_id, '-', -1) AS UNSIGNED)) AS highest_used
FROM sales_order
WHERE store_id = 1;
-- List all registered entity types and stores for a quick sanity check
SELECT m.entity_type, m.store_id, p.prefix, p.start_value, p.is_active
FROM sequence_meta m
INNER JOIN sequence_profile p ON p.meta_id = m.meta_id
ORDER BY m.entity_type, m.store_id;
-- Detect gaps between consecutive sequence values (expected after
-- rolled back orders, not a bug, but useful for support audits)
SELECT sequence_value,
sequence_value - LAG(sequence_value) OVER (ORDER BY sequence_value) AS gap
FROM sequence_order_1
HAVING gap > 1;
9. Eine defekte Sequence-Table im Live-Shop sicher reparieren
Bevor an einer Sequence-Table irgendetwas verändert wird, steht die Bestandsaufnahme: Welcher Entity-Typ und welche Store-ID sind betroffen, welche physische Tabelle ist das konkret, und was ist der höchste bereits im produktiven Datenbestand verwendete Wert. Ein Blick in sequence_meta und sequence_profile liefert die Zuordnung, ein Abgleich mit der höchsten tatsächlich verwendeten increment_id in der Fachtabelle liefert den Referenzwert, unter den der Zähler niemals fallen darf.
Der sichere Reparaturweg ist ausschließlich ALTER TABLE sequence_order_1 AUTO_INCREMENT = <neuer_wert>, wobei <neuer_wert> mindestens eins über dem höchsten bereits verwendeten Wert liegen muss. Diese Operation ist in InnoDB eine reine Metadaten-Operation, läuft in der Regel unter einer Sekunde und benötigt keinen langen exklusiven Lock auf der Tabelle. Vor der Änderung empfiehlt sich ein gezielter mysqldump ausschließlich der betroffenen Sequence-Table, nicht des gesamten Schemas, um die Downtime des Wartungsfensters kurz zu halten.
Absolut zu vermeiden ist TRUNCATE TABLE auf einer produktiven Sequence-Table: Ein TRUNCATE setzt den AUTO_INCREMENT-Zähler unwiderruflich auf den Startwert zurück, wodurch der allernächste Checkout eine bereits vergebene, im Shop längst sichtbare Bestellnummer erneut erzeugen würde, mit Folgeproblemen für Buchhaltung, Rechnungsnummern und ERP-Abgleich. Ebenso tabu ist das direkte Löschen oder Verändern von Zeilen in sequence_meta oder sequence_profile ohne exakte Kenntnis der Beziehung zur physischen Tabelle, da eine inkonsistente Registry dazu führt, dass Magento zur Laufzeit die falsche oder gar keine Sequence-Instanz für einen Entity-Typ und Store auflösen kann. Nach jeder Reparatur gehört eine Testbestellung in einer Staging-Umgebung mit anschließender Kontrolle der erzeugten Nummer zum Pflichtprogramm, bevor die Änderung als abgeschlossen gilt.
Viele der genannten Entscheidungen lassen sich am besten im direkten Vergleich einordnen: Welcher Ansatz war vorher üblich, welcher gilt heute als Standard, und welchen konkreten Vorteil bringt die Sequence-Table gegenüber der klassischen AUTO_INCREMENT-Spalte auf der Fachtabelle selbst.
| Aufgabe | Unsicher / Veraltet | Empfohlenes Muster | Vorteil |
|---|---|---|---|
| Bestellnummer erzeugen | eav_entity_store Read-Modify-Write |
Sequence-Table mit AUTO_INCREMENT | Atomar, keine Race Conditions im Checkout |
| Multi-Master-Betrieb | auto_increment_offset auf Fachtabelle |
Sequence-Table auf einem Schreibknoten | Fortlaufende Nummern trotz verteilter Master |
| Store-spezifische Nummernkreise | Ein globaler Zähler für alle Stores | Eine Sequence-Table je Store View | Unabhängige Nummernkreise pro Markt |
| Nummernkreis zurücksetzen | TRUNCATE TABLE sequence_order_1 |
ALTER TABLE ... AUTO_INCREMENT = n |
Kein Kollisionsrisiko, keine Doppelvergabe |
| Lock-Verhalten unter Last | AUTO_INCREMENT-Lock auf breiter Fachtabelle | Kurzer Lock auf Ein-Spalten-Sequence-Table | Höherer Durchsatz bei parallelen Checkouts |
10. Zusammenfassung
Sequence-Tables sind das Fundament, mit dem Magento 2 kollisionsfreie, store-spezifische Anzeige-IDs für Orders, Invoices, Shipments und Creditmemos vergibt, komplett entkoppelt vom internen AUTO_INCREMENT-Primärschlüssel der jeweiligen Fachtabelle. Statt eines fehleranfälligen Read-Modify-Write-Musters wie im alten eav_entity_store-Ansatz nutzt jede Sequence-Table InnoDBs eigenen, atomaren AUTO_INCREMENT-Mechanismus auf einer minimalen Ein-Spalten-Tabelle, was Race Conditions unter parallelen Checkouts strukturell ausschließt und gleichzeitig die Grundlage für Multi-Master- und Multi-Source-fähige Installationen legt.
Die Registry aus sequence_meta und sequence_profile macht das System darüber hinaus konfigurierbar, ohne die physische Zähltabelle anzufassen: Prefix, Padding und Startwert lassen sich über neue Profile ändern, während der zugrunde liegende Zählerstand unangetastet bleibt. Für eigene Entitäten steht mit Magento\SalesSequence\Model\Builder, db_schema.xml und einer Data-Patch-Klasse dieselbe robuste Infrastruktur zur Verfügung, die auch die Magento-Kernmodule verwenden, statt eine eigene, potenziell fehleranfällige ID-Vergabe zu implementieren.
Sequence-Tables in Magento 2: das Wichtigste auf einen Blick
Kollisionsfreiheit
Sequence-Tables nutzen InnoDBs atomaren AUTO_INCREMENT auf einer Ein-Spalten-Tabelle, keine Race Conditions wie im alten Read-Modify-Write-Muster.
Store-Granularität
Jede Store View bekommt über den EntityPool automatisch eigene physische Sequence-Tables für Order, Invoice, Shipment und Creditmemo.
Meta- und Profil-Registry
sequence_meta und sequence_profile entkoppeln Konfiguration wie Prefix und Padding vom eigentlichen Zählerstand.
Sichere Reparatur
Nur ALTER TABLE ... AUTO_INCREMENT = n verwenden, niemals TRUNCATE, immer gegen die höchste verwendete increment_id prüfen.
11. FAQ: Sequence-Tables in Magento 2
1Was ist eine Sequence-Table in Magento 2?
2Warum keine normale AUTO_INCREMENT-Spalte?
3Welche Entitäten nutzen Sequence-Tables standardmäßig?
4sequence_meta vs. sequence_profile?
5Eigene Sequence-Table je Store View?
6Eigene Sequence-Table für Custom-Entity anlegen?
7Warum entstehen Lücken in der Nummernfolge?
8Defekte Sequence-Table sicher reparieren?
9Wirken sich Sequence-Tables auf Performance aus?
10increment_prefix / increment_pad_length wiederverwenden?
Mironsoft
Magento 2 Backend-Entwicklung, Datenbank-Architektur und Sales-Prozesse
Sequence-Tables, die auch unter Last kollisionsfrei bleiben?
Wir analysieren bestehende Nummernkreise, reparieren desynchronisierte Sequence-Tables im Live-Betrieb und implementieren eigene, sequence-basierte ID-Generatoren für Custom-Entities nach Magento-Standard.
Sequence-Audit
Prüfung von sequence_meta, sequence_profile und Zählerständen auf Konsistenz
Custom-Entity-IDs
db_schema.xml, Data Patch und Service-Contract-Generator nach Magento-Kernmuster
Live-Reparatur
Sichere ALTER TABLE Reparaturen ohne Downtime und ohne Nummernkollisionen