SQLite is een populaire, relationele database die u in uw toepassing insluit. Met een toenemende hoeveelheid gegevens in uw database, moet u SQLite prestatieafstemming toepassen. Dit artikel bespreekt indices en hun valkuilen, het gebruik van de queryplanner, de Write-Ahead-Logging (WAL) journaalmodus en het vergroten van de cachegrootte. Het gaat ook dieper in op het belang van het meten van de impact van je tweaks, met behulp van geautomatiseerde tests.
Inleiding
SQLite is een populair, relationeel databasesysteem (DB) . In tegenstelling tot zijn grotere, client-server-gebaseerde broers, zoals MySQL, kan SQLite als bibliotheek in uw applicatie worden ingesloten . SQLite heeft een zeer vergelijkbare functieset en kan ook miljoenen rijen aan, aangezien u een paar tips en trucs kent over het afstemmen van prestaties. Zoals de volgende secties zullen laten zien, er valt meer te weten over het afstemmen van SQLite-prestaties dan alleen het maken van indices.
Maak indices, maar wees voorzichtig
Het basisidee van een index is om lezen te versnellen van specifieke gegevens , dat wil zeggen, SELECT
statements met een WHERE
clausule. Indices versnellen ook sorteren gegevens (ORDER BY
), of JOIN
tafels in. Helaas zijn indices een tweesnijdend zwaard, omdat ze extra schijfruimte in beslag nemen en gegevensmanipulatie vertragen (INSERT
, UPDATE
, DELETE
).
Het algemene advies is om zo min mogelijk indexen te maken, maar zoveel als nodig . Indices zijn ook alleen zinvol voor groter databases, met duizenden of miljoenen rijen.
Gebruik de queryplanner om uw query's te analyseren
De manier waarop indices intern door SQLite worden gebruikt, is gedocumenteerd, maar niet erg gemakkelijk te begrijpen. Zoals verder uitgewerkt in dit artikel, is het een goed idee om een query te analyseren door er EXPLAIN QUERY PLAN
voor te voegen. . Bekijk elke uitgangslijn, waarvan er drie basisvarianten zijn:
SEARCH table ...
regels zijn een goed teken – SQLite gebruikt een van je indices!SCAN table ... USING INDEX
is een slecht teken,SCAN table ...
is nog erger!
Probeer SCAN table [using index]
te vermijden vermeldingen in de uitvoer van EXPLAIN QUERY PLAN
waar mogelijk, omdat u prestatieproblemen zult tegenkomen op grotere databases. Gebruik EXPLAIN QUERY PLAN
om iteratief uw indices toe te voegen of te wijzigen totdat er geen SCAN table
meer is vermeldingen verschijnen.
Optimaliseer queries waarbij IS NOT
betrokken is
Controleren op IS NOT ...
is duur omdat SQLite moet scannen alle rijen van de tabel, zelfs als de betreffende kolom een index heeft . Indices zijn alleen nuttig als u op zoek bent naar specifieke waarden, d.w.z. vergelijkingen met < (kleiner), > (groter), of = (gelijk), maar ze zijn niet van toepassing op !=(ongelijk).
Een leuk trucje is dat je WHERE column != value
. kunt vervangen met WHERE column > value OR column < value
. Dit zal de index van de kolom gebruiken en effectief alle rijen beïnvloeden waarvan de waarde niet gelijk is aan value
. Evenzo, een WHERE stringColumn != ''
kan worden vervangen door WHERE stringColumn > ''
, omdat strings sorteerbaar zijn. Wanneer je deze truc toepast, zorg er dan voor dat je weet hoe SQLite omgaat met NULL
vergelijkingen. SQLite evalueert bijvoorbeeld NULL > ''
als FALSE
.
Als je zo'n vergelijkingstruc gebruikt, is er nog een waarschuwing voor het geval je zoekopdracht WHERE
. bevat en ORDER BY
, elk met een andere kolom:hierdoor wordt de query weer inefficiënt. Gebruik indien mogelijk de dezelfde kolom in WHERE
en ORDER BY
, of bouw een dekkende index waarbij zowel de WHERE
en ORDER BY
kolom.
Verbeter de schrijfsnelheid met de Write-Ahead-Log
De Write-Ahead-Logging (WAL) journaalmodus verbetert de schrijf-/updateprestaties aanzienlijk , vergeleken met de standaard terugdraaien journaal modus. Echter, zoals hier gedocumenteerd, er zijn een paar kanttekeningen . De WAL-modus is bijvoorbeeld niet beschikbaar op bepaalde besturingssystemen. Ook zijn er verminderde garanties voor gegevensconsistentie in geval van hardwarestoring . Zorg ervoor dat u de eerste paar pagina's leest om te begrijpen wat u doet.
Ik ontdekte dat het commando PRAGMA synchronous = NORMAL
biedt een 3-4x versnelling. journal_mode
instellen naar WAL
verbetert vervolgens de prestaties opnieuw aanzienlijk (ongeveer 10x of meer, afhankelijk van het besturingssysteem).
Afgezien van de waarschuwingen die ik al noemde, moet u zich ook bewust zijn van het volgende:
- Als u de WAL-journaalmodus gebruikt, zijn er twee extra bestanden naast het databasebestand op uw bestandssysteem, die dezelfde naam hebben als de database, maar met het achtervoegsel "-shm" en "-wal". Normaal gesproken hoeft u zich daar geen zorgen over te maken, maar als u de database naar een andere machine zou sturen terwijl uw toepassing draait, vergeet dan niet om die twee bestanden op te nemen. SQLite comprimeert deze twee bestanden in het hoofdbestand wanneer u gewoonlijk alle open databaseverbindingen sluit.
- De prestaties van het invoegen of bijwerken zullen af en toe afnemen, wanneer de query het samenvoegen van de inhoud van het WAL-logbestand in het hoofddatabasebestand activeert. Dit heet checkpointing , zie hier.
- Ik heb gevonden dat
PRAGMA
s diejournal_mode
veranderen ensynchronous
lijken niet permanent in de database te worden opgeslagen. Dus ik altijd voer ze opnieuw uit wanneer ik een nieuwe databaseverbinding open, in plaats van ze alleen uit te voeren wanneer ik de tabellen voor de eerste keer maak.
Meet alles
Als je prestatietweaks toevoegt, moet je de impact meten. Geautomatiseerde (eenheids)tests zijn hiervoor een goede aanpak. Ze helpen bij het documenteren uw bevindingen voor uw team, en zij zullen na verloop van tijd afwijkend gedrag aan het licht brengen , bijv. wanneer u bijwerkt naar een nieuwere SQLite-versie. Voorbeelden van wat u kunt meten:
- Wat is het effect van het gebruik van de WAL journaalmodus via terugdraaien modus? Wat is het effect van andere (vermoedelijk) prestatieverhogende
PRAGMA
s? - Als u een index heeft toegevoegd/wijzigd/verwijderd, hoeveel sneller werkt dan
SELECT
uitspraken worden? Hoeveel langzamer werkenINSERT/DELETE/UPDATE
uitspraken worden? - Hoeveel extra schijfruimte nemen de indices in beslag?
Overweeg voor elk van deze tests ze te herhalen met verschillende databasegroottes. bijv. voer ze uit op een lege database, en ook op een database die al duizenden (of miljoenen) vermeldingen bevat. U moet de tests ook op verschillende apparaten en besturingssystemen uitvoeren, vooral als uw ontwikkel- en productieomgeving wezenlijk anders is.
De cachegrootte aanpassen
SQLite slaat tijdelijke informatie op in een cache (in het RAM), b.v. tijdens het bouwen van de resultaten van een SELECT
query, of bij het manipuleren van gegevens die nog niet zijn vastgelegd. Standaard is deze grootte een magere 2 MB . Moderne desktopmachines kunnen veel meer missen. Voer PRAGMA cache_size = -kibibytes
uit om deze waarde te verhogen (let op de min teken voor de waarde!). Zie hier voor meer informatie. Nogmaals, meet welke impact deze instelling heeft op de prestaties!
Gebruik REPLACE INTO om een rij te maken of bij te werken
Dit is misschien niet zozeer een prestatie-tweak, maar het is een leuk trucje. Stel dat u moet bijwerken een rij in tabel t
, of maken een rij als deze nog niet bestaat. In plaats van twee zoekopdrachten te gebruiken (SELECT
gevolgd door INSERT
of UPDATE
), gebruik de REPLACE INTO
(officiële documenten).
Om dit te laten werken, voegt u een extra dummy-kolom toe (bijv. replacer
) naar tabel t
, die een UNIQUE
. heeft beperken. De declaratie van de kolom kan b.v. be ... replacer INTEGER UNIQUE ...
dat deel uitmaakt van uw CREATE TABLE
uitspraak. Gebruik dan een zoekopdracht zoals
REPLACE INTO t (col1, col2, ..., replacer) VALUES (?,?,...,1)
Code language: SQL (Structured Query Language) (sql)
Wanneer deze query voor de eerste keer wordt uitgevoerd, wordt er gewoon een INSERT
. uitgevoerd . Wanneer het de tweede keer wordt uitgevoerd, wordt de UNIQUE
beperking van de replacer
kolom wordt geactiveerd en het gedrag bij het oplossen van conflicten zorgt ervoor dat de oude rij wordt verwijderd, waardoor automatisch een nieuwe wordt gemaakt. Mogelijk vindt u het gerelateerde UPSERT-commando ook nuttig.
Conclusie
Zodra het aantal rijen in uw database groeit, worden prestatieaanpassingen een noodzaak. Indexen zijn de meest voorkomende oplossing. Ze ruilen verbeterde tijdcomplexiteit in voor verminderde ruimtecomplexiteit, verbeteren de leessnelheden en hebben een negatief effect op de prestaties van gegevensmodificatie. A Ik heb aangetoond dat je extra voorzichtig moet zijn bij het vergelijken voor ongelijkheid in SELECT
statements, omdat SQLite geen indices kan gebruiken voor dit soort vergelijkingen. Ik raad over het algemeen aan om de queryplanner te gebruiken dat verklaart wat er intern gebeurt voor elke SQL-query. Meet de impact wanneer je iets aanpast!