Medium severityschema
MySQL Error:
1071
What does this error mean?
MySQL weigert een index aan te maken omdat de sleutellengte de maximale limiet voor het opgegeven opslagformaat overschrijdt. Bij InnoDB met het COMPACT- of REDUNDANT-rijformaat geldt een limiet van 767 bytes per indexsleutel. Met utf8mb4-codering verbruikt elk teken 4 bytes, waardoor een gewone VARCHAR(255)-kolom al 1020 bytes aan indexruimte inneemt. In een data-pipeline ziet de engineer dit tijdens schema-initialisatie, dbt-migraties of wanneer een ETL-tool probeert een tabel aan te maken met een index op een tekstkolom. Het symptoom is een harde CREATE TABLE- of ALTER TABLE-fout die de gehele pipeline-run afbreekt.
Common causes
- 1VARCHAR(255) met utf8mb4-codering gebruikt 4 bytes per teken, waardoor de indexsleutel uitkomt op 1020 bytes — ruim boven de 767-byte limiet van InnoDB COMPACT-rijformaat. Dit is veruit de meest voorkomende oorzaak bij recente MySQL-installaties.
- 2Samengestelde index op meerdere grote VARCHAR-kolommen: als je een composite index maakt op bijv. (col_a VARCHAR(200), col_b VARCHAR(100)) met utf8mb4, is de gecombineerde sleutellengte (200+100)*4 = 1200 bytes, wat ook het COMPACT-formaat-limiet overschrijdt.
- 3MySQL 5.6 of 5.7 zonder innodb_large_prefix=ON: standaard staat deze optie uitgeschakeld, waardoor ook DYNAMIC-rijformaat de 767-byte limiet aanhoudt. dbt-modellen die gedraaid worden op oudere MySQL-configuraties lopen hier tegenaan zonder expliciete server-configuratie.
- 4Gebruik van het COMPACT of REDUNDANT rijformaat op een tabel, terwijl de rest van de database al op DYNAMIC staat. Dit gebeurt wanneer tabellen zijn gemigreerd van een oudere MySQL-versie of wanneer CREATE TABLE-statements geen expliciete ROW_FORMAT-clausule bevatten.
- 5Index op een ENUM- of SET-kolom die intern als VARCHAR wordt behandeld bij complexe character sets, of op een VARBINARY-kolom met een lengte boven 767 bytes — minder frequent maar wordt gezien in data-warehouse staginglagen met binaire identifiers.
- 6dbt seed-bestanden of externe tools (Airbyte, Fivetran) die automatisch een unieke index aanmaken op een tekstkolom als surrogaatsleutel, zonder rekening te houden met de doeldatabaseconfiguratie. De tool stuurt een standaard-DDL die op PostgreSQL werkt maar op oudere MySQL-versies faalt.
- 7Migratie-tooling (Flyway, Liquibase) die een index aanmaakt via een changelog die oorspronkelijk geschreven was voor een andere MySQL-versie of character set. Het charset-verschil tussen omgevingen (dev utf8, prod utf8mb4) zorgt ervoor dat het schema werkt in ontwikkel maar faalt in productie.
How to fix it
- 1Stap 1 — Snel vaststellen welke kolom de limiet overschrijdt: voer `SHOW CREATE TABLE your_table;` uit en bekijk welke kolommen in de index zitten. Bereken: kolomlengte × bytes_per_char (utf8mb4 = 4, utf8 = 3, latin1 = 1). Als het totaal boven 767 bytes komt voor COMPACT of boven 3072 voor DYNAMIC, is dat de oorzaak.
- 2Stap 2 — MySQL 5.6/5.7: schakel innodb_large_prefix in voor een 3072-byte limiet met DYNAMIC-formaat: `SET GLOBAL innodb_large_prefix=ON; SET GLOBAL innodb_file_format=Barracuda;`. Maak vervolgens de tabel aan met `ROW_FORMAT=DYNAMIC` of voer `ALTER TABLE your_table ROW_FORMAT=DYNAMIC;` uit op bestaande tabellen.
- 3Stap 3 — MySQL 8.0+: controleer eerst het rijformaat van de tabel: `SELECT TABLE_NAME, ROW_FORMAT FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'jouw_db' AND TABLE_NAME = 'your_table';`. Staat het op COMPACT of REDUNDANT, converteer dan: `ALTER TABLE your_table ROW_FORMAT=DYNAMIC;`. In MySQL 8.0 is DYNAMIC de standaard voor nieuwe tabellen.
- 4Stap 4 — Prefix-index als snelle tijdelijke fix: beperk de geïndexeerde tekenlengtes tot 191 tekens voor utf8mb4 (191 × 4 = 764 bytes, net onder 767): `CREATE INDEX idx_col ON your_table (col(191));`. Voor een composite index: `CREATE INDEX idx_ab ON your_table (col_a(100), col_b(91));`. Let op: een prefix-index werkt niet voor UNIQUE-constraints waarbij de volledige kolomwaarde uniek moet zijn.
- 5Stap 5 — Bij dbt: voeg een `index_length` configuratie toe aan het model of pas de index-macro aan. Als de index puur voor lookupsnelheid is, overweeg een `not_null`+`unique`-test te vervangen door een prefix-index, of schakel de automatische unique-index uit via `unique_key` in dbt_project.yml en beheer de index handmatig via een post-hook: `{{ config(post_hook='CREATE INDEX ...') }}`.
- 6Stap 6 — Structurele oplossing voor tekst-identifiers: als de kolom een UUID, hash of URL bevat die volledig uniek geïndexeerd moet worden, overweeg dan de waarden op te slaan in een afzonderlijke INT/BIGINT surrogate-key kolom met AUTO_INCREMENT en de tekstkolom zonder index te laten of met een full-text index: `ALTER TABLE your_table ADD COLUMN id BIGINT AUTO_INCREMENT PRIMARY KEY;`.
- 7Stap 7 — Valideer na de fix: `SHOW INDEXES FROM your_table;` om te bevestigen dat de index aangemaakt is, gevolgd door een EXPLAIN op een representatieve query om te controleren dat de index ook daadwerkelijk gebruikt wordt. Voer bij dbt daarna `dbt run --select your_model` opnieuw uit en controleer dat de run succesvol is.
Example log output
ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes
[dbt] Compilation Error in model staging_customers (models/staging/staging_customers.sql)
Database Error: 1071 (42000): Specified key was too long; max key length is 767 bytes