Frage

Wir haben eine Anwendung, die ursprünglich als Desktop-Anwendung geschrieben wurde, lo diese vor vielen Jahren. Es beginnt eine Transaktion, wenn Sie einen Bearbeitungsbildschirm zu öffnen, und Commits, wenn Sie auf OK klicken, oder Rollen zurück, wenn Sie auf Abbrechen klicken. Das funktionierte in Ordnung für eine Desktop-Anwendung, aber jetzt sind wir versuchen, ADO.NET und SQL Server, und die lang andauernden Transaktionen zu bewegen sind problematisch.

fand ich, dass wir ein Problem haben, wenn mehrere Benutzer versuchen, alle zu bearbeiten (verschiedene Untergruppen von) den gleichen Tisch zur gleichen Zeit. In unserer alten Datenbank, jeder Transaktion des Benutzers würde rekordSperren zu jedem Datensatz erwerben sie während ihrer Transaktion geändert; da verschiedene Benutzer verschiedene Datensätze bearbeitet haben, bekommt jeder seine eigene Schlösser und alles funktioniert. Aber in SQL Server, sobald ein Benutzer bearbeitet eine Aufzeichnung innerhalb einer Transaktion, wird SQL Server eine Sperre auf der gesamte Tabelle zu erhalten. Wenn ein zweiter Benutzer versucht, bearbeiten ein anderer Datensatz in derselben Tabelle , der App zweite Benutzer einfach sperrt, weil die SqlConnection blockiert, bis die ersten Benutzer entweder Commits oder Rollen zurück.

Ich bin mir bewusst, dass lang laufende Transaktionen sind schlecht, und ich weiß, dass die am besten Lösung wäre, diese Bildschirme zu ändern, so dass sie nicht mehr für eine lange Zeit öffnen Transaktionen halten. Da aber, dass einige invasiven und riskante Veränderungen bedeuten würde, ich will auch erforschen, ob es ein Weg gibt, diesen Code zum Laufen, wie sie ist, nur damit ich weiß, was meine Optionen sind.

Wie kann ich zwei verschiedene Benutzer-Transaktionen in SQL Server einzelne Datensätze zu sperren, anstatt die gesamte Tabelle?

Hier ist eine schnelle und unsaubere Konsolenanwendung, die das Problem veranschaulicht. Ich habe eine Datenbank mit dem Namen „Test1“ erstellt, mit einer Tabelle „Wert“ genannt, die gerade ID (int) hat und Wert (nvarchar) Spalten. Wenn Sie die App ausgeführt wird, fragt es für eine ID zu ändern, startet eine Transaktion, ändert diese Aufzeichnung, und verlässt dann die Transaktion geöffnet, bis Sie die Eingabetaste drücken. Ich möchte in der Lage sein

  1. , um das Programm zu starten und melden Sie es den Update-ID 1;
  2. lassen Sie es seine Transaktion erhalten und den Datensatz ändern;
  3. eine zweite Kopie des Programms starten und melden Sie es der Update-ID 2;
  4. haben, sie zu aktualisieren können (und begehen), während die Transaktion des ersten App noch offen ist.

Zur Zeit friert es in Schritt 4, bis ich auf die erste Kopie der App gehen Sie zurück und schließen Sie es oder drücken Sie die Eingabetaste, um es verpflichtet. Der Aufruf von command.ExecuteNonQuery blockiert, bis die erste Verbindung geschlossen ist.

public static void Main()
{
    Console.Write("ID to update: ");
    var id = int.Parse(Console.ReadLine());
    Console.WriteLine("Starting transaction");
    using (var scope = new TransactionScope())
    using (var connection = new SqlConnection(@"Data Source=localhost\sqlexpress;Initial Catalog=test1;Integrated Security=True"))
    {
        connection.Open();
        var command = connection.CreateCommand();
        command.CommandText = "UPDATE [Values] SET Value = 'Value' WHERE ID = " + id;
        Console.WriteLine("Updating record");
        command.ExecuteNonQuery();
        Console.Write("Press ENTER to end transaction: ");
        Console.ReadLine();
        scope.Complete();
    }
}

Hier sind einige Dinge, die ich schon versucht habe, ohne eine Änderung im Verhalten:

  • Ändern der Transaktionsisolationsstufe zu „lesen ungebundenen“
  • Die Angabe eines "WITH (ROWLOCK)" auf der Update-Anweisung
War es hilfreich?

Lösung

Just checking, aber haben Sie einen Primärschlüssel oder eindeutigen Index für die ID-Spalte?

Andere Tipps

Schauen Sie in optimistisch im Vergleich zu pessimistischen Sperren.

Edit: Vorherige Artikel klassische ado verknüpft ... sorry.

http://msdn.microsoft.com/ en-us / library / cs6hb8k4 (VS.71) aspx

Wahrscheinlich wurde der Index mit Zeilensperren auf „off“ erstellt.
„MIT (ROWLOCK)“ in einer Abfrage würde keine Wirkung in diesem Fall hat.

Sie können sie wieder aktivieren mit ALTER INDEX , zB :

ALTER INDEX [PK_Values] ON [Values] SET (ALLOW_ROW_LOCKS = ON)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top