Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Met transacties kunt u meerdere SQL-instructies groeperen in één werkeenheid die als één atomische eenheid aan de database wordt toegewezen. Als een verklaring in de transactie mislukt, kunnen wijzigingen die door de vorige verklaringen zijn aangebracht, worden teruggedraaid. De initiële status van de database toen de transactie werd gestart, blijft behouden. Het gebruik van een transactie kan ook de prestaties van SQLite verbeteren wanneer u meerdere wijzigingen in de database tegelijk aanbrengt.
Concurreniteit
In SQLite mag slechts één transactie tegelijkertijd wijzigingen hebben die in de database in behandeling zijn. Hierdoor kunnen aanroepen naar BeginTransaction en de Execute methoden van SqliteCommand een time-out krijgen als een andere transactie te lang duurt om te voltooien.
Zie Databasefouten voor meer informatie over vergrendeling, nieuwe pogingen en time-outs.
Isolatieniveaus
Transacties zijn standaard serialiseerbaar in SQLite. Dit isolatieniveau garandeert dat alle wijzigingen die in een transactie zijn aangebracht, volledig geïsoleerd zijn. Andere instructies die buiten de transactie worden uitgevoerd, worden niet beïnvloed door de wijzigingen van de transactie.
SQLite biedt ook ondersteuning voor niet-verzonden leesbewerkingen bij het gebruik van een gedeelde cache. Dit niveau maakt vuile leesbewerkingen, niet-herhaalbare leesbewerkingen en schijnafbeeldingen mogelijk.
Een dirty read treedt op wanneer wijzigingen die binnen een transactie in behandeling zijn, worden geretourneerd door een query buiten de transactie, maar de wijzigingen in de transactie uiteindelijk worden teruggedraaid. De resultaten bevatten gegevens die nooit daadwerkelijk zijn doorgevoerd in de database.
Een niet-opnieuw leesbare leesbewerking vindt plaats wanneer een transactie twee keer dezelfde rij opvraagt, maar de resultaten verschillen omdat deze is gewijzigd tussen de twee query's door een andere transactie.
Phantoms zijn rijen die worden gewijzigd of toegevoegd om te voldoen aan de where-component van een query tijdens een transactie. Als dit is toegestaan, kan dezelfde query verschillende rijen retourneren wanneer deze twee keer in dezelfde transactie worden uitgevoerd.
Microsoft.Data.Sqlite behandelt het aan BeginTransaction doorgegeven isolatieniveau als een minimumniveau. Het daadwerkelijke isolatieniveau wordt verhoogd naar read uncommitted of serializable.
Met de volgende code wordt een vuile leesbewerking gesimuleerd. Opmerking: de verbindingsreeks moet bevatten Cache=Shared.
using (var firstTransaction = firstConnection.BeginTransaction())
{
var updateCommand = firstConnection.CreateCommand();
updateCommand.CommandText =
@"
UPDATE data
SET value = 'dirty'
";
updateCommand.ExecuteNonQuery();
// Without ReadUncommitted, the command will time out since the table is locked
// while the transaction on the first connection is active
using (secondConnection.BeginTransaction(IsolationLevel.ReadUncommitted))
{
var queryCommand = secondConnection.CreateCommand();
queryCommand.CommandText =
@"
SELECT *
FROM data
";
var value = (string)queryCommand.ExecuteScalar();
Console.WriteLine($"Value: {value}");
}
firstTransaction.Rollback();
}
Uitgestelde transacties
Vanaf Microsoft.Data.Sqlite versie 5.0 kunnen transacties worden uitgesteld. Hierdoor wordt het maken van de werkelijke transactie in de database uitgesteld totdat de eerste opdracht wordt uitgevoerd. Het zorgt er ook voor dat de transactie geleidelijk van een leestransactie naar een schrijftransactie wordt geüpgraded, zoals vereist door de opdrachten. Dit kan handig zijn voor het inschakelen van gelijktijdige toegang tot de database tijdens de transactie.
using (var transaction = connection.BeginTransaction(deferred: true))
{
// Before the first statement of the transaction is executed, both concurrent
// reads and writes are allowed
var readCommand = connection.CreateCommand();
readCommand.CommandText =
@"
SELECT *
FROM data
";
var value = (long)readCommand.ExecuteScalar();
// After a the first read statement, concurrent writes are blocked until the
// transaction completes. Concurrent reads are still allowed
var writeCommand = connection.CreateCommand();
writeCommand.CommandText =
@"
UPDATE data
SET value = $newValue
";
writeCommand.Parameters.AddWithValue("$newValue", value + 1L);
writeCommand.ExecuteNonQuery();
// After the first write statement, both concurrent reads and writes are blocked
// until the transaction completes
transaction.Commit();
}
Waarschuwing
Opdrachten in een uitgestelde transactie kunnen mislukken als ze ertoe leiden dat de transactie wordt bijgewerkt van een leestransactie naar een schrijftransactie terwijl de database is vergrendeld. Wanneer dit gebeurt, moet de toepassing de hele transactie opnieuw proberen.
Opslagpunten
Versie 6.0 van Microsoft.Data.Sqlite ondersteunt savepoints. Savepoints kunnen worden gebruikt om genestelde transacties te maken. Savepoints kunnen worden teruggedraaid zonder dat dit van invloed is op andere onderdelen van de transactie, en hoewel een savepoint kan worden doorgevoerd (vrijgegeven), kunnen de wijzigingen later worden teruggedraaid als onderdeel van de bovenliggende transactie.
De volgende code illustreert het gebruik van het Optimistisch Offline Lock-patroon om concurrente updates te detecteren en conflicten binnen een bewaarpunt op te lossen als onderdeel van een grotere transactie.
using (var transaction = connection.BeginTransaction())
{
// Transaction may include additional statements before the savepoint
var updated = false;
do
{
// Begin savepoint
transaction.Save("optimistic-update");
var insertCommand = connection.CreateCommand();
insertCommand.CommandText =
@"
INSERT INTO audit
VALUES (datetime('now'), 'User updates data with id 1')
";
insertCommand.ExecuteScalar();
var updateCommand = connection.CreateCommand();
updateCommand.CommandText =
@"
UPDATE data
SET value = 2,
version = $expectedVersion + 1
WHERE id = 1
AND version = $expectedVersion
";
updateCommand.Parameters.AddWithValue("$expectedVersion", expectedVersion);
var recordsAffected = updateCommand.ExecuteNonQuery();
if (recordsAffected == 0)
{
// Concurrent update detected! Rollback savepoint and retry
transaction.Rollback("optimistic-update");
// TODO: Resolve update conflicts
}
else
{
// Update succeeded. Commit savepoint and continue with the transaction
transaction.Release("optimistic-update");
updated = true;
}
}
while (!updated);
// Additional statements may be included after the savepoint
transaction.Commit();
}