De meeste databases worden in de loop van de tijd groter. De groei is niet altijd snel genoeg om de prestaties van de database te beïnvloeden, maar er zijn zeker gevallen waarin dat gebeurt. Als dat het geval is, vragen we ons vaak af wat er kan worden gedaan om die impact te verminderen en hoe we kunnen zorgen voor soepele databasebewerkingen wanneer we op grote schaal met gegevens omgaan.
Laten we eerst proberen te definiëren wat een "groot datavolume" betekent? Voor MySQL of MariaDB is het ongecomprimeerde InnoDB. InnoDB werkt zo dat het sterk profiteert van het beschikbare geheugen - voornamelijk de InnoDB-bufferpool. Zolang de gegevens daar passen, wordt schijftoegang geminimaliseerd tot het verwerken van alleen schrijfbewerkingen - leesbewerkingen worden uit het geheugen uitgevoerd. Wat gebeurt er als de gegevens het geheugen ontgroeien? Er moeten steeds meer gegevens van schijf worden gelezen als er toegang nodig is tot rijen die momenteel niet in de cache staan. Wanneer de hoeveelheid data toeneemt, schakelt de workload over van CPU-gebonden naar I/O-gebonden. Het betekent dat het knelpunt niet langer de CPU is (wat het geval was toen de gegevens in het geheugen pasten - de gegevenstoegang in het geheugen is snel, de gegevenstransformatie en -aggregatie is langzamer) maar eerder het I/O-subsysteem (CPU-bewerkingen op gegevens zijn veel sneller dan toegang tot gegevens van schijf.) Met de toegenomen acceptatie van flash zijn I/O-gebonden workloads niet zo verschrikkelijk als vroeger in de tijd van draaiende schijven (willekeurige toegang is veel sneller met SSD), maar de prestatiehit is er nog steeds .
Een ander ding dat we in gedachten moeten houden, is dat we meestal alleen om de actieve dataset geven. Natuurlijk heb je misschien terabytes aan gegevens in je schema, maar als je alleen toegang moet hebben tot de laatste 5 GB, is dit eigenlijk best een goede situatie. Natuurlijk, het brengt nog steeds operationele uitdagingen met zich mee, maar qua prestaties zou het nog steeds goed moeten zijn.
Laten we voor het doel van deze blog, en dit is geen wetenschappelijke definitie, aannemen dat we met het grote gegevensvolume het geval bedoelen waarin de actieve gegevensgrootte aanzienlijk groter is dan de grootte van het geheugen. Het kan 100 GB zijn als je 2 GB geheugen hebt, het kan 20 TB zijn als je 200 GB geheugen hebt. Het omslagpunt is dat je workload strikt I/O-gebonden is. Heb geduld met ons terwijl we enkele van de opties bespreken die beschikbaar zijn voor MySQL en MariaDB.
Partitioneren
De historische (maar perfect geldige) benadering voor het verwerken van grote hoeveelheden gegevens is het implementeren van partitionering. Het idee erachter is om de tabel op te splitsen in partities, een soort subtabellen. De splitsing gebeurt volgens de regels die door de gebruiker zijn gedefinieerd. Laten we enkele voorbeelden bekijken (de SQL-voorbeelden zijn afkomstig uit de MySQL 8.0-documentatie)
MySQL 8.0 wordt geleverd met de volgende soorten partitionering:
- BEREIK
- LIJST
- KOLOMMEN
- HASH
- SLEUTEL
Het kan ook subpartities maken. We gaan hier geen documentatie herschrijven, maar we willen je toch enig inzicht geven in hoe partities werken. Om partities te maken, moet u de partitiesleutel definiëren. Het kan een kolom zijn of in het geval van RANGE of LIST meerdere kolommen die worden gebruikt om te definiëren hoe de gegevens in partities moeten worden opgesplitst.
HASH-partitionering vereist dat de gebruiker een kolom definieert, die wordt gehasht. Vervolgens worden de gegevens opgesplitst in een door de gebruiker gedefinieerd aantal partities op basis van die hash-waarde:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
)
PARTITION BY HASH( YEAR(hired) )
PARTITIONS 4;
In dit geval wordt hash gemaakt op basis van de uitkomst die is gegenereerd door de functie YEAR() in de kolom 'ingehuurd'.
KEY-partitionering is vergelijkbaar met de uitzondering dat de gebruiker bepaalt welke kolom moet worden gehasht en de rest is aan de MySQL om te verwerken.
Terwijl HASH- en KEY-partities willekeurig gegevens over het aantal partities verdelen, laten RANGE en LIST de gebruiker beslissen wat hij moet doen. BEREIK wordt vaak gebruikt met tijd of datum:
CREATE TABLE quarterly_report_status (
report_id INT NOT NULL,
report_status VARCHAR(20) NOT NULL,
report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
PARTITION p9 VALUES LESS THAN (MAXVALUE)
);
Het kan ook worden gebruikt met ander type kolommen:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT NOT NULL,
store_id INT NOT NULL
)
PARTITION BY RANGE (store_id) (
PARTITION p0 VALUES LESS THAN (6),
PARTITION p1 VALUES LESS THAN (11),
PARTITION p2 VALUES LESS THAN (16),
PARTITION p3 VALUES LESS THAN MAXVALUE
);
De LIST-partities werken op basis van een lijst met waarden die de rijen over meerdere partities sorteert:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
)
PARTITION BY LIST(store_id) (
PARTITION pNorth VALUES IN (3,5,6,9,17),
PARTITION pEast VALUES IN (1,2,10,11,19,20),
PARTITION pWest VALUES IN (4,12,13,14,18),
PARTITION pCentral VALUES IN (7,8,15,16)
);
Wat heeft het voor zin om partities te gebruiken, vraagt u zich misschien af? Het belangrijkste punt is dat de lookups aanzienlijk sneller zijn dan bij een niet-gepartitioneerde tabel. Stel dat u wilt zoeken naar de rijen die in een bepaalde maand zijn gemaakt. Als u meerdere jaren aan gegevens in de tabel hebt opgeslagen, zal dit een uitdaging zijn - er zal een index moeten worden gebruikt en, zoals we weten, indexen helpen om rijen te vinden, maar toegang tot die rijen zal resulteren in een aantal willekeurige reads van de hele tafel. Als je partities hebt gemaakt op jaar-maandbasis, kan MySQL gewoon alle rijen van die specifieke partitie lezen - geen toegang tot de index nodig, geen willekeurige reads nodig:lees gewoon alle gegevens van de partitie, sequentieel, en we zijn helemaal klaar.
Partities zijn ook erg handig bij het omgaan met gegevensrotatie. Als MySQL gemakkelijk rijen kan identificeren om te verwijderen en ze toe te wijzen aan een enkele partitie, in plaats van DELETE FROM table WHERE ... uit te voeren, die index zal gebruiken om rijen te lokaliseren, kunt u de partitie afkappen. Dit is uitermate handig met RANGE-partitionering - als we ons aan het bovenstaande voorbeeld houden, kunnen we, als we gegevens slechts 2 jaar willen bewaren, eenvoudig een cron-taak maken, die de oude partitie verwijdert en een nieuwe, lege partitie maakt voor de volgende maand.
InnoDB-compressie
Als we een grote hoeveelheid gegevens hebben (niet noodzakelijkerwijs denkend aan databases), is het eerste dat in ons opkomt het comprimeren ervan. Er zijn talloze tools die een optie bieden om uw bestanden te comprimeren, waardoor ze aanzienlijk kleiner worden. InnoDB heeft daar ook een optie voor - zowel MySQL als MariaDB ondersteunen InnoDB-compressie. Het belangrijkste voordeel van het gebruik van compressie is de vermindering van de I/O-activiteit. Gegevens zijn, wanneer ze zijn gecomprimeerd, kleiner en dus sneller te lezen en te schrijven. Een typische InnoDB-pagina is 16 KB groot, voor SSD zijn dit 4 I/O-bewerkingen om te lezen of te schrijven (SSD gebruikt meestal 4 KB-pagina's). Als het ons lukt om 16 KB te comprimeren tot 4 KB, hebben we I/O-bewerkingen met vier verminderd. Het helpt niet echt veel met betrekking tot de verhouding tussen dataset en geheugen. Het kan het zelfs erger maken - MySQL moet de pagina decomprimeren om met de gegevens te kunnen werken. Toch leest het gecomprimeerde pagina's van schijf. Dit resulteert in een InnoDB-bufferpool die 4 KB aan gecomprimeerde gegevens en 16 KB niet-gecomprimeerde gegevens opslaat. Natuurlijk zijn er algoritmen om onnodige gegevens te verwijderen (ongecomprimeerde pagina's worden indien mogelijk verwijderd, waarbij slechts een gecomprimeerde pagina in het geheugen blijft), maar je kunt op dit gebied niet al te veel verbetering verwachten.
Het is ook belangrijk om in gedachten te houden hoe compressie werkt met betrekking tot de opslag. Solid State-schijven zijn tegenwoordig de norm voor databaseservers en ze hebben een aantal specifieke kenmerken. Ze zijn snel, het maakt ze niet zoveel uit of het verkeer sequentieel of willekeurig is (hoewel ze nog steeds de voorkeur geven aan sequentiële toegang boven willekeurig). Ze zijn duur voor grote volumes. Ze hebben last van "versleten" omdat ze een beperkt aantal schrijfcycli aankunnen. Compressie helpt hier aanzienlijk - door de grootte van de gegevens op de schijf te verkleinen, verlagen we de kosten van de opslaglaag voor de database. Door de grootte van de gegevens die we naar schijf schrijven te verkleinen, verlengen we de levensduur van de SSD.
Helaas, zelfs als compressie helpt, is het voor grotere hoeveelheden gegevens misschien nog steeds niet genoeg. Een andere stap zou zijn om naar iets anders te zoeken dan InnoDB.
MyRocks
MyRocks is een storage engine die beschikbaar is voor MySQL en MariaDB en die gebaseerd is op een ander concept dan InnoDB. Mijn collega, Sebastian Insausti, heeft een mooie blog over het gebruik van MyRocks met MariaDB. De essentie is dat MyRocks vanwege het ontwerp (het gebruikt Log Structured Merge, LSM) aanzienlijk beter is in termen van compressie dan InnoDB (dat is gebaseerd op de B+Tree-structuur). MyRocks is ontworpen voor het verwerken van grote hoeveelheden gegevens en om het aantal schrijfbewerkingen te verminderen. Het is afkomstig van Facebook, waar de gegevensvolumes groot zijn en de vereisten om toegang te krijgen tot de gegevens hoog zijn. Dus SSD-opslag - toch, op zo'n grote schaal is elke winst in compressie enorm. MyRocks kan zelfs tot 2x betere compressie leveren dan InnoDB (wat betekent dat je het aantal servers met twee vermindert). Het is ook ontworpen om de schrijfversterking te verminderen (het aantal schrijfbewerkingen dat nodig is om een wijziging van de rij-inhoud af te handelen) - het vereist 10x minder schrijfbewerkingen dan InnoDB. Dit vermindert uiteraard de I/O-belasting, maar, nog belangrijker, het zal de levensduur van een SSD tien keer verlengen in vergelijking met dezelfde belasting met InnoDB). Vanuit het oogpunt van prestaties geldt:hoe kleiner het gegevensvolume, hoe sneller de toegang, dus dergelijke opslagengines kunnen ook helpen om de gegevens sneller uit de database te krijgen (hoewel dit niet de hoogste prioriteit had bij het ontwerpen van MyRocks).
Kolomgegevensopslag
Gerelateerde bronnen ClusterControl-prestatiebeheer Inzicht in de effecten van hoge latentie in MySQL- en MariaDB-oplossingen met hoge beschikbaarheid Cheatsheet voor MySQL-prestatiesOp een gegeven moment kunnen we alleen maar toegeven dat we een dergelijke hoeveelheid gegevens niet aankunnen met MySQL. Natuurlijk kun je het verscheuren, je kunt verschillende dingen doen, maar uiteindelijk heeft het gewoon geen zin meer. Het wordt tijd om naar aanvullende oplossingen te zoeken. Een daarvan zou zijn om kolomvormige datastores te gebruiken - databases die zijn ontworpen met big data-analyse in het achterhoofd. Natuurlijk zullen ze niet helpen met het OLTP-type van het verkeer, maar analyses zijn tegenwoordig vrijwel standaard, omdat bedrijven proberen gegevensgestuurd te zijn en beslissingen nemen op basis van exacte cijfers, niet op willekeurige gegevens. Er zijn talloze zuilvormige datastores, maar we willen er hier graag twee noemen. MariaDB AX en ClickHouse. We hebben een aantal blogs waarin wordt uitgelegd wat MariaDB AX is en hoe MariaDB AX kan worden gebruikt. Wat belangrijk is, MariaDB AX kan worden opgeschaald in de vorm van een cluster, waardoor de prestaties worden verbeterd. ClickHouse is een andere optie voor het uitvoeren van analyses - ClickHouse kan eenvoudig worden geconfigureerd om gegevens uit MySQL te repliceren, zoals we in een van onze blogposts hebben besproken. Het is snel, het is gratis en het kan ook worden gebruikt om een cluster te vormen en gegevens te sharden voor nog betere prestaties.
Conclusie
We hopen dat deze blogpost je inzicht heeft gegeven in hoe grote hoeveelheden gegevens kunnen worden verwerkt in MySQL of MariaDB. Gelukkig hebben we een aantal opties tot onze beschikking en uiteindelijk, als we het niet echt kunnen laten werken, zijn er goede alternatieven.