Jegyzet
Az oldalhoz való hozzáférés engedélyezést igényel. Próbálhatod be jelentkezni vagy könyvtárat váltani.
Az oldalhoz való hozzáférés engedélyezést igényel. Megpróbálhatod a könyvtár váltását.
A tranzakciók lehetővé teszik több SQL-utasítás csoportosítását egyetlen munkaegységbe, amely egy atomi egységként van lekötve az adatbázishoz. Ha a tranzakció bármely utasítása meghiúsul, az előző utasítások módosításai visszaállíthatók. A tranzakció elindításakor az adatbázis kezdeti állapota megmarad. A tranzakció használata az SQLite teljesítményét is javíthatja, ha egyszerre számos módosítást hajt végre az adatbázisban.
Konkurencia
Az SQLite-ben egyszerre csak egy tranzakció rendelkezhet függőben lévő módosításokkal az adatbázisban. Emiatt előfordulhat, hogy a BeginTransaction és Execute metódusok hívásai SqliteCommand időtúllépést szenvedhetnek, ha egy másik tranzakció túl sokáig tart.
További információ a zárolásról, az újrapróbálkozásokról és az időtúllépésekről: Adatbázishibák.
Elkülönítési szintek
A tranzakciók alapértelmezés szerint szerializálhatók az SQLite-ben. Ez az elkülönítési szint garantálja, hogy a tranzakción belül végrehajtott módosítások teljesen el vannak különítve. A tranzakción kívül végrehajtott egyéb utasításokat a tranzakció módosításai nem befolyásolják.
Az SQLite támogatja a nem véglegesített olvasást is megosztott gyorsítótár használatakor. Ez a szint lehetővé teszi a piszkos olvasásokat, a nem megismételhető olvasásokat és a fantomokat:
A piszkos olvasás akkor fordul elő, ha az egyik tranzakcióban függőben lévő módosításokat egy, a tranzakción kívüli lekérdezés adja vissza, de a tranzakció módosításait a rendszer visszaállítja. Az eredmények olyan adatokat tartalmaznak, amelyeket soha nem véglegesítettek az adatbázishoz.
Nem megismételhető olvasás akkor fordul elő, ha egy tranzakció kétszer lekérdezi ugyanazt a sort, de az eredmények eltérnek, mert a két lekérdezés között egy másik tranzakció módosította.
A fantomok olyan sorok, amelyek módosulnak vagy hozzáadódnak egy tranzakció során, hogy megfeleljenek a lekérdezés WHERE záradékának. Ha engedélyezve van, ugyanaz a lekérdezés eltérő sorokat adhat vissza, ha kétszer hajtják végre ugyanabban a tranzakcióban.
A Microsoft.Data.Sqlite minimális szintként kezeli az átadott BeginTransaction elkülönítési szintet. A rendszer előlépteti a tényleges elkülönítési szintet a nem véglegesített vagy szerializálható olvasási szintre.
Az alábbi kód egy piszkos olvasást szimulál. Vegye figyelembe, hogy a kapcsolati sztringnek tartalmaznia kell a következőt 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();
}
Halasztott tranzakciók
A Microsoft.Data.Sqlite 5.0-s verziójától kezdve a tranzakciók elhalaszthatók. Ez az adatbázis tényleges tranzakciójának létrehozását ellenjavallja az első parancs végrehajtásáig. Azt is eredményezi, hogy a tranzakció fokozatosan frissül az olvasási tranzakcióról egy írási tranzakcióra a parancsai által szükséges módon. Ez hasznos lehet az adatbázishoz való egyidejű hozzáférés engedélyezéséhez a tranzakció során.
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();
}
Figyelmeztetés
A késleltetett tranzakción belüli parancsok meghiúsulhatnak, ha a tranzakciót olvasási tranzakcióról írási tranzakcióra frissítik, miközben az adatbázis zárolva van. Ha ez történik, az alkalmazásnak újra meg kell próbálkoznia a teljes tranzakcióval.
Mentési pontok
A Microsoft.Data.Sqlite 6.0-s verziója támogatja a mentési pontokat. A mentési pontok beágyazott tranzakciók létrehozásához használhatók. A mentési pontok a tranzakció más részeinek befolyásolása nélkül is visszaállíthatók, és bár a mentési pontok véglegesítésre (kiadásra) kerülhetnek, a módosítások később visszahozhatók a szülőtranzakció részeként.
Az alábbi kód bemutatja, hogy az optimista offline zárolási minta használatával észlelheti az egyidejű frissítéseket, és feloldhatja a mentési pontok ütközéseit egy nagyobb tranzakció részeként.
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();
}