Optimistische NebenläufigkeitOptimistic Concurrency

In einer Umgebung mit mehreren Benutzern gibt es zwei Modelle für das Update von Daten in einer Datenbank: das Modell der vollständigen Parallelität und das Modell der eingeschränkten Parallelität.In a multiuser environment, there are two models for updating data in a database: optimistic concurrency and pessimistic concurrency. Das DataSet-Objekt unterstützt die Verwendung der vollständigen Parallelität für lange Aktivitäten, wie bei der Datenfernverarbeitung und der Interaktion mit Daten.The DataSet object is designed to encourage the use of optimistic concurrency for long-running activities, such as remoting data and interacting with data.

Die eingeschränkte Parallelität umfasst das Sperren von Zeilen an der Datenquelle, damit Benutzer Daten, die andere Benutzer betreffen, nicht ändern können.Pessimistic concurrency involves locking rows at the data source to prevent other users from modifying data in a way that affects the current user. Wenn ein Benutzer bei einem eingeschränkten Modell eine Aktion ausführt, durch die eine Sperre angewendet wird, können andere Benutzer so lange keine mit der Sperre in Konflikt stehenden Aktionen ausführen, bis der Eigentümer der Sperre diese aufgehoben hat.In a pessimistic model, when a user performs an action that causes a lock to be applied, other users cannot perform actions that would conflict with the lock until the lock owner releases it. Dieses Modell wird hauptsächlich in Umgebungen verwendet, in denen häufig Datenkonflikte auftreten, oder in denen der Aufwand von Datensperren geringer ist als der Aufwand für Rollbacks von Transaktionen, die aufgrund von Konflikten durch eine Parallelbearbeitung ausgeführt werden müssen.This model is primarily used in environments where there is heavy contention for data, so that the cost of protecting data with locks is less than the cost of rolling back transactions if concurrency conflicts occur.

Daher erstellt ein Benutzer, der eine Zeile aktualisiert, bei einem Modell für pessimistische Parallelität eine Sperre.Therefore, in a pessimistic concurrency model, a user who updates a row establishes a lock. Die Zeile kann erst wieder von einem anderen Benutzer geändert werden, wenn der Benutzer den Updatevorgang für die Zeile abgeschlossen und die Sperre aufgehoben hat.Until the user has finished the update and released the lock, no one else can change that row. Aus diesem Grund eignet sich die eingeschränkte Parallelität am besten bei kurzen Sperrfristen wie bei der programmgesteuerten Verarbeitung von Datensätzen.For this reason, pessimistic concurrency is best implemented when lock times will be short, as in programmatic processing of records. Die eingeschränkte Parallelität stellt keine flexible Option dar, wenn Benutzer mit Daten interagieren und Datensätze dadurch für einen relativ langen Zeitraum sperren.Pessimistic concurrency is not a scalable option when users are interacting with data and causing records to be locked for relatively large periods of time.

Hinweis

Wenn Sie mehrere Zeilen auf einmal aktualisieren müssen, ermöglicht das Erstellen einer Transaktion ein höheres Maß an Skalierung als das Sperren bei eingeschränkter Parallelität.If you need to update multiple rows in the same operation, then creating a transaction is a more scalable option than using pessimistic locking.

Im Gegensatz dazu sperren Benutzer bei der vollständigen Parallelität keine Zeile, während sie sie lesen.By contrast, users who use optimistic concurrency do not lock a row when reading it. Wenn ein Benutzer eine Zeile aktualisieren möchte, muss durch die Anwendung festgestellt werden, ob ein anderer Benutzer die Zeile seit dem Abruf geändert hat.When a user wants to update a row, the application must determine whether another user has changed the row since it was read. Die vollständige Parallelität wird im Allgemeinen in Umgebungen mit wenigen Datenkonflikten verwendet.Optimistic concurrency is generally used in environments with a low contention for data. Vollständige Parallelität führt zu einer Leistungssteigerung, weil keine Datensätze gesperrt werden müssen, denn für das Sperren von Datensätzen sind zusätzliche Serverressourcen erforderlich.Optimistic concurrency improves performance because no locking of records is required, and locking of records requires additional server resources. Außerdem ist für die Verwaltung von Datensatzsperren eine ständige Verbindung zum Datenbankserver notwendig.Also, in order to maintain record locks, a persistent connection to the database server is required. Da dies beim Modell der vollständigen Parallelität nicht der Fall ist, können Verbindungen zum Server innerhalb eines kürzeren Zeitraums eine Vielzahl von Clients bedienen.Because this is not the case in an optimistic concurrency model, connections to the server are free to serve a larger number of clients in less time.

Beim Modell der vollständigen Parallelität wird dann von einer Verletzung ausgegangen, wenn, nachdem ein Benutzer einen Wert aus der Datenbank empfangen hat, ein anderer Benutzer den Wert ändert, bevor der erste Benutzer den Wert zu ändern versucht hat.In an optimistic concurrency model, a violation is considered to have occurred if, after a user receives a value from the database, another user modifies the value before the first user has attempted to modify it. Wie der Server eine Parallelitätsverletzung auflöst, kann am Besten anhand des folgenden Beispiels erläutert werden.How the server resolves a concurrency violation is best shown by first describing the following example.

Die folgenden Tabellen stellen ein Beispiel für eine vollständige Parallelität dar.The following tables follow an example of optimistic concurrency.

Um 13:00 Uhr ruft User1 eine Zeile mit den folgenden Werten aus der Datenbank auf:At 1:00 p.m., User1 reads a row from the database with the following values:

CustID "LastName" FirstNameCustID LastName FirstName

101 Smith Bob101 Smith Bob

SpaltennameColumn name Ursprünglicher WertOriginal value Aktueller WertCurrent value Wert in DatenbankValue in database
CustIDCustID 101101 101101 101101
LastNameLastName SmithSmith SmithSmith SmithSmith
FirstNameFirstName BobBob BobBob BobBob

Um 13:01 Uhr ruft User2 dieselbe Zeile ab.At 1:01 p.m., User2 reads the same row.

Um 13:03 Uhr ändert User2 FirstName von "Bob" in "Robert" und die Datenbank aktualisiert.At 1:03 p.m., User2 changes FirstName from "Bob" to "Robert" and updates the database.

SpaltennameColumn name Ursprünglicher WertOriginal value Aktueller WertCurrent value Wert in DatenbankValue in database
CustIDCustID 101101 101101 101101
LastNameLastName SmithSmith SmithSmith SmithSmith
FirstNameFirstName BobBob RobertRobert BobBob

Das Update ist erfolgreich, weil die Werte in der Datenbank zur Zeit des Updates mit den ursprünglichen Werten übereinstimmen, die User2 vorliegen.The update succeeds because the values in the database at the time of update match the original values that User2 has.

Um 13:05 Uhr ändert User1 FirstName von Bob in James und versucht, die Zeile zu aktualisieren.At 1:05 p.m., User1 changes "Bob"'s first name to "James" and tries to update the row.

SpaltennameColumn name Ursprünglicher WertOriginal value Aktueller WertCurrent value Wert in DatenbankValue in database
CustIDCustID 101101 101101 101101
LastNameLastName SmithSmith SmithSmith SmithSmith
FirstNameFirstName BobBob JamesJames RobertRobert

Zu diesem Zeitpunkt stellt User1 eine Verletzung der optimistischen Parallelität fest, weil der Wert in der Datenbank ("Robert") nicht mit dem ursprünglichen Wert übereinstimmt, den User1 erwartet hat ("Bob").At this point, User1 encounters an optimistic concurrency violation because the value in the database ("Robert") no longer matches the original value that User1 was expecting ("Bob"). Die Parallelitätsverletzung sagt Ihnen einfach nur, dass das Update fehlgeschlagen ist.The concurrency violation simply lets you know that the update failed. Nun muss entschieden werden, ob die von User2 vorgenommenen Änderungen mit den Änderungen von User1 überschrieben oder die Änderungen von User1 verworfen werden sollen.The decision now needs to be made whether to overwrite the changes supplied by User2 with the changes supplied by User1, or to cancel the changes by User1.

Testen auf Verletzung der vollständigen ParallelitätTesting for Optimistic Concurrency Violations

Es gibt mehrere Testverfahren, mit denen eine Verletzung der vollständigen Parallelität festgestellt werden kann.There are several techniques for testing for an optimistic concurrency violation. Eine Methode besteht im Einfügen einer Timestamp-Spalte in die Tabelle.One involves including a timestamp column in the table. Datenbanken stellen im Allgemeinen Timestamp-Funktionen zur Verfügung, mit denen der Updatezeitpunkt (Datum und Uhrzeit) des Datensatzes gekennzeichnet werden kann.Databases commonly provide timestamp functionality that can be used to identify the date and time when the record was last updated. Bei dieser Methode wird eine Timestamp-Spalte in die Tabellendefinition eingefügt.Using this technique, a timestamp column is included in the table definition. Bei jedem Update des Datensatzes wird der Timestamp aktualisiert, sodass er den aktuellen Zeitpunkt (Datum und Uhrzeit) angibt.Whenever the record is updated, the timestamp is updated to reflect the current date and time. Bei einem Test auf eine Verletzung der vollständigen Parallelität wird die Timestamp-Spalte bei jeder Abfrage des Tabelleninhalts zurückgegeben.In a test for optimistic concurrency violations, the timestamp column is returned with any query of the contents of the table. Bei einem Updateversuch wird der Timestamp-Wert in der Datenbank mit dem ursprünglichen Timestamp-Wert verglichen, der in der geänderten Zeile enthalten ist.When an update is attempted, the timestamp value in the database is compared to the original timestamp value contained in the modified row. Wenn die Werte identisch sind, wird die Timestamp-Spalte mit der aktuellen Uhrzeit aktualisiert, um das Update zu kennzeichnen.If they match, the update is performed and the timestamp column is updated with the current time to reflect the update. Wenn die Werte nicht identisch sind, wurde die vollständige Parallelität verletzt.If they do not match, an optimistic concurrency violation has occurred.

Beim zweiten Testverfahren, mit dem eine Verletzung der vollständigen Parallelität festgestellt werden kann, wird überprüft, ob alle ursprünglichen Spaltenwerte einer Zeile mit denen in der Datenbank übereinstimmen.Another technique for testing for an optimistic concurrency violation is to verify that all the original column values in a row still match those found in the database. Betrachten Sie z. B. folgende Abfrage:For example, consider the following query:

SELECT Col1, Col2, Col3 FROM Table1  

Zum Prüfen auf eine Verletzung der vollständigen Parallelität beim Aktualisieren einer Zeile in Table1, geben Sie die folgende UPDATE-Anweisung:To test for an optimistic concurrency violation when updating a row in Table1, you would issue the following UPDATE statement:

UPDATE Table1 Set Col1 = @NewCol1Value,  
              Set Col2 = @NewCol2Value,  
              Set Col3 = @NewCol3Value  
WHERE Col1 = @OldCol1Value AND  
      Col2 = @OldCol2Value AND  
      Col3 = @OldCol3Value  

Wenn die ursprünglichen Werte mit den Werten in der Datenbank übereinstimmen, wird das Update durchgeführt.As long as the original values match the values in the database, the update is performed. Wenn ein Wert geändert wurde, wird die Zeile von der UPDATE-Anweisung nicht aktualisiert, weil die WHERE-Klausel keine Übereinstimmung findet.If a value has been modified, the update will not modify the row because the WHERE clause will not find a match.

Es empfiehlt sich, in einer Abfrage stets einen eindeutigen Primärschlüsselwert zurückzugeben.Note that it is recommended to always return a unique primary key value in your query. Andernfalls könnte die vorhergehende UPDATE-Anweisung mehr als eine Zeile aktualisieren, was möglicherweise nicht von Ihnen beabsichtigt ist.Otherwise, the preceding UPDATE statement may update more than one row, which might not be your intent.

Wenn in einer Spalte Ihrer Datenquelle NULL-Werte zulässig sind, müssen Sie Ihre WHERE-Klausel erweitern, um nach einem entsprechenden NULL-Verweis in Ihrer lokalen Tabelle und in der Datenquelle zu suchen.If a column at your data source allows nulls, you may need to extend your WHERE clause to check for a matching null reference in your local table and at the data source. Die folgende UPDATE-Anweisung prüft z. B., ob ein NULL-Verweis in der lokalen Zeile noch mit einem NULL-Verweis in der Datenquelle übereinstimmt oder ob der Wert in der lokalen Zeile noch mit dem Wert in der Datenquelle übereinstimmt.For example, the following UPDATE statement verifies that a null reference in the local row still matches a null reference at the data source, or that the value in the local row still matches the value at the data source.

UPDATE Table1 Set Col1 = @NewVal1  
  WHERE (@OldVal1 IS NULL AND Col1 IS NULL) OR Col1 = @OldVal1  

Sie können bei einem Modell der vollständigen Parallelität auch weniger strenge Kriterien anwenden.You may also choose to apply less restrictive criteria when using an optimistic concurrency model. Wenn Sie z. B. nur die Primärschlüsselspalten in der WHERE-Klausel verwenden, werden die Daten unabhängig davon überschrieben, ob die anderen Spalten seit der letzten Abfrage aktualisiert wurden oder nicht.For example, using only the primary key columns in the WHERE clause causes the data to be overwritten regardless of whether the other columns have been updated since the last query. Zudem besteht die Möglichkeit, eine WHERE-Klausel auf spezifische Spalten anzuwenden, sodass die Daten überschrieben werden, sofern nicht bestimmte Felder seit deren letzten Abfrage aktualisiert wurden.You can also apply a WHERE clause only to specific columns, resulting in data being overwritten unless particular fields have been updated since they were last queried.

Das "DataAdapter.RowUpdated"-EreignisThe DataAdapter.RowUpdated Event

Die RowUpdated Ereignis die DataAdapter Objekt kann in Verbindung mit den Methoden, die weiter oben beschriebene, um Benachrichtigungen an Ihre Anwendung der Verletzung der vollständigen Parallelität verwendet werden.The RowUpdated event of the DataAdapter object can be used in conjunction with the techniques described earlier, to provide notification to your application of optimistic concurrency violations. RowUpdated tritt nach jedem Versuch zum Aktualisieren einer "geändert" Zeile aus einer DataSet.RowUpdated occurs after each attempt to update a Modified row from a DataSet. Damit können Sie spezifischen Behandlungscode hinzufügen, einschließlich Verarbeitung bei Ausnahmen, Einfügen von benutzerdefinierten Fehlerinformationen, Hinzufügen einer Wiederholungslogik usw.This enables you to add special handling code, including processing when an exception occurs, adding custom error information, adding retry logic, and so on. Die RowUpdatedEventArgs Objekt gibt ein RecordsAffected Eigenschaft, die die Anzahl der von einem bestimmten Updatebefehl für eine geänderte Zeile in einer Tabelle betroffenen Zeilen enthält.The RowUpdatedEventArgs object returns a RecordsAffected property containing the number of rows affected by a particular update command for a modified row in a table. Durch Festlegen des Befehls "Update" zum Prüfen auf vollständige Parallelität verwendet, die RecordsAffected Eigenschaft, daher gibt der Wert 0, wenn eine vollständige Parallelität verletzt wurde, weil keine Datensätze aktualisiert wurden.By setting the update command to test for optimistic concurrency, the RecordsAffected property will, as a result, return a value of 0 when an optimistic concurrency violation has occurred, because no records were updated. Wenn dies der Fall ist, wird eine Ausnahme ausgelöst.If this is the case, an exception is thrown. Die RowUpdated Ereignis können Sie dieses Ereignis behandeln und die Ausnahme zu vermeiden, indem Sie eine geeignete Einstellung RowUpdatedEventArgs.Status Wert, z. B. UpdateStatus.SkipCurrentRow.The RowUpdated event enables you to handle this occurrence and avoid the exception by setting an appropriate RowUpdatedEventArgs.Status value, such as UpdateStatus.SkipCurrentRow. Weitere Informationen zu den RowUpdated Ereignis finden Sie unter Behandeln von DataAdapter-Ereignissen.For more information about the RowUpdated event, see Handling DataAdapter Events.

Sie können optional festlegen DataAdapter.ContinueUpdateOnError zu "true", vor dem Aufruf Update, und reagieren auf die Fehlerinformationen in die gespeichertRowError -Eigenschaft einer bestimmten Zeile, wenn die Update abgeschlossen ist.Optionally, you can set DataAdapter.ContinueUpdateOnError to true, before calling Update, and respond to the error information stored in the RowError property of a particular row when the Update is completed. Weitere Informationen finden Sie unter Zeilenfehlerinformationen.For more information, see Row Error Information.

Beispiel für eine vollständige ParallelitätOptimistic Concurrency Example

Im folgenden ist ein einfaches Beispiel, die festlegt der UpdateCommand von einer DataAdapter zum Prüfen auf vollständige Parallelität verwendet, und verwendet dann die RowUpdated Ereignis zum Prüfen auf Verletzung der vollständigen Parallelität.The following is a simple example that sets the UpdateCommand of a DataAdapter to test for optimistic concurrency, and then uses the RowUpdated event to test for optimistic concurrency violations. Wenn eine Verletzung der vollständigen Parallelität festgestellt wird, legt die Anwendung die RowError der Zeile, die das Update für eine Verletzung der vollständigen Parallelität entsprechend ausgestellt wurde.When an optimistic concurrency violation is encountered, the application sets the RowError of the row that the update was issued for to reflect an optimistic concurrency violation.

Beachten Sie, die die Parameterwerte, die an die WHERE-Klausel der UPDATE-Befehl zugeordnet sind, die ursprünglichen Werte der entsprechenden Spalten.Note that the parameter values passed to the WHERE clause of the UPDATE command are mapped to the Original values of their respective columns.

' Assumes connection is a valid SqlConnection.  
Dim adapter As SqlDataAdapter = New SqlDataAdapter( _  
  "SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID", _  
  connection)  
  
' The Update command checks for optimistic concurrency violations  
' in the WHERE clause.  
adapter.UpdateCommand = New SqlCommand("UPDATE Customers " &  
  "(CustomerID, CompanyName) VALUES(@CustomerID, @CompanyName) " & _  
  "WHERE CustomerID = @oldCustomerID AND CompanyName = " &  
  "@oldCompanyName", connection)  
adapter.UpdateCommand.Parameters.Add( _  
  "@CustomerID", SqlDbType.NChar, 5, "CustomerID")  
adapter.UpdateCommand.Parameters.Add( _  
  "@CompanyName", SqlDbType.NVarChar, 30, "CompanyName")  
  
' Pass the original values to the WHERE clause parameters.  
Dim parameter As SqlParameter = dataSet.UpdateCommand.Parameters.Add( _  
  "@oldCustomerID", SqlDbType.NChar, 5, "CustomerID")  
parameter.SourceVersion = DataRowVersion.Original  
parameter = adapter.UpdateCommand.Parameters.Add( _  
  "@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName")  
parameter.SourceVersion = DataRowVersion.Original  
  
' Add the RowUpdated event handler.  
AddHandler adapter.RowUpdated, New SqlRowUpdatedEventHandler( _  
  AddressOf OnRowUpdated)  
  
Dim dataSet As DataSet = New DataSet()  
adapter.Fill(dataSet, "Customers")  
  
' Modify the DataSet contents.  
adapter.Update(dataSet, "Customers")  
  
Dim dataRow As DataRow  
  
For Each dataRow In dataSet.Tables("Customers").Rows  
    If dataRow.HasErrors Then   
       Console.WriteLine(dataRow (0) & vbCrLf & dataRow.RowError)  
    End If  
Next  
  
Private Shared Sub OnRowUpdated( _  
  sender As object, args As SqlRowUpdatedEventArgs)  
   If args.RecordsAffected = 0  
      args.Row.RowError = "Optimistic Concurrency Violation!"  
      args.Status = UpdateStatus.SkipCurrentRow  
   End If  
End Sub  
// Assumes connection is a valid SqlConnection.  
SqlDataAdapter adapter = new SqlDataAdapter(  
  "SELECT CustomerID, CompanyName FROM Customers ORDER BY CustomerID",  
  connection);  
  
// The Update command checks for optimistic concurrency violations  
// in the WHERE clause.  
adapter.UpdateCommand = new SqlCommand("UPDATE Customers Set CustomerID = @CustomerID, CompanyName = @CompanyName " +  
   "WHERE CustomerID = @oldCustomerID AND CompanyName = @oldCompanyName", connection);  
adapter.UpdateCommand.Parameters.Add(  
  "@CustomerID", SqlDbType.NChar, 5, "CustomerID");  
adapter.UpdateCommand.Parameters.Add(  
  "@CompanyName", SqlDbType.NVarChar, 30, "CompanyName");  
  
// Pass the original values to the WHERE clause parameters.  
SqlParameter parameter = adapter.UpdateCommand.Parameters.Add(  
  "@oldCustomerID", SqlDbType.NChar, 5, "CustomerID");  
parameter.SourceVersion = DataRowVersion.Original;  
parameter = adapter.UpdateCommand.Parameters.Add(  
  "@oldCompanyName", SqlDbType.NVarChar, 30, "CompanyName");  
parameter.SourceVersion = DataRowVersion.Original;  
  
// Add the RowUpdated event handler.  
adapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);  
  
DataSet dataSet = new DataSet();  
adapter.Fill(dataSet, "Customers");  
  
// Modify the DataSet contents.  
  
adapter.Update(dataSet, "Customers");  
  
foreach (DataRow dataRow in dataSet.Tables["Customers"].Rows)  
{  
    if (dataRow.HasErrors)  
       Console.WriteLine(dataRow [0] + "\n" + dataRow.RowError);  
}  
  
protected static void OnRowUpdated(object sender, SqlRowUpdatedEventArgs args)  
{  
  if (args.RecordsAffected == 0)   
  {  
    args.Row.RowError = "Optimistic Concurrency Violation Encountered";  
    args.Status = UpdateStatus.SkipCurrentRow;  
  }  
}  

Siehe auchSee Also

Abrufen und Ändern von Daten in ADO.NETRetrieving and Modifying Data in ADO.NET
Updating Data Sources with DataAdapters (Aktualisieren von Datenquellen mit DataAdapters)Updating Data Sources with DataAdapters
ZeilenfehlerinformationenRow Error Information
Transaktionen und ParallelitätTransactions and Concurrency
ADO.NET Managed Provider und DataSet Developer CenterADO.NET Managed Providers and DataSet Developer Center