Aracılığıyla paylaş


İyimser Eşzamanlılık

Çok kullanıcılı bir ortamda, veritabanındaki verileri güncelleştirmek için iki model vardır: iyimser eşzamanlılık ve kötümser eşzamanlılık. DataSet nesnesi, verileri uzaktan izleme ve verilerle etkileşim kurma gibi uzun süre çalışan etkinlikler için iyimser eşzamanlılık kullanımını teşvik etmek için tasarlanmıştır.

Kötümser eşzamanlılık, diğer kullanıcıların verileri geçerli kullanıcıyı etkileyecek şekilde değiştirmesini önlemek için veri kaynağındaki satırları kilitlemeyi içerir. Kötümser bir modelde, bir kullanıcı bir kilidin uygulanmasına neden olan bir eylem gerçekleştirdiğinde, kilit sahibi onu serbest bırakana kadar diğer kullanıcılar kilitle çakışacak eylemleri gerçekleştiremez. Bu model öncelikli olarak veriler için yoğun çekişme olan ortamlarda kullanılır, böylece verileri kilitlerle koruma maliyeti eşzamanlılık çakışmaları oluşursa işlemleri geri alma maliyetinden daha düşük olur.

Bu nedenle, kötümser bir eşzamanlılık modelinde, bir satırı güncelleştiren bir kullanıcı bir kilit oluşturur. Kullanıcı güncelleştirmeyi tamamlayıp kilidi serbest bırakıncaya kadar, başka kimse bu satırı değiştiremez. Bu nedenle, kötümser eşzamanlılık, kayıtların programlı işlenmesinde olduğu gibi kilit süreleri kısa olduğunda en iyi şekilde uygulanır. Kötümser eşzamanlılık, kullanıcılar verilerle etkileşime geçtiğinde ve kayıtların nispeten büyük süreler için kilitlenmesine neden olduğunda ölçeklenebilir bir seçenek değildir.

Not

Aynı işlemde birden çok satırı güncelleştirmeniz gerekiyorsa, işlem oluşturmak kötümser kilitleme kullanmaktan daha ölçeklenebilir bir seçenektir.

Buna karşılık, iyimser eşzamanlılık kullanan kullanıcılar bir satırı okurken kilitlemez. Kullanıcı bir satırı güncelleştirmek istediğinde, uygulamanın okunduktan sonra başka bir kullanıcının satırı değiştirip değiştirmediğini belirlemesi gerekir. İyimser eşzamanlılık genellikle veri çekişmesi düşük ortamlarda kullanılır. İyimser eşzamanlılık, kayıtların kilitlenmesine gerek olmadığından ve kayıtların kilitlenmesi ek sunucu kaynakları gerektirdiğinden performansı artırır. Ayrıca, kayıt kilitlerini korumak için veritabanı sunucusuna kalıcı bir bağlantı gerekir. İyimser eşzamanlılık modelinde bu durum söz konusu olmadığından, sunucu bağlantıları daha kısa sürede daha fazla sayıda istemciye hizmet vermekte serbesttir.

İyimser eşzamanlılık modelinde, bir kullanıcı veritabanından bir değer aldıktan sonra, ilk kullanıcı bunu değiştirmeye çalışmadan önce başka bir kullanıcı değeri değiştirirse bir ihlal oluştuğu kabul edilir. Sunucunun eşzamanlılık ihlalini nasıl çözümlediği en iyi aşağıdaki örneği açıklayarak gösterilir.

Aşağıdaki tablolarda iyimser eşzamanlılık örneği verilmiştir.

Saat 13:00'da User1 veritabanından aşağıdaki değerleri içeren bir satır okur:

CustID Soyadı Adı

101 Smith Bob

Sütun adı Özgün değer Geçerli değer Veritabanındaki değer
CustID 101 101 101
LastName Smith Smith Smith
FirstName Bob Bob Bob

Saat 13:01'de User2 aynı satırı okur.

Saat 13:03'te User2, FirstName değerini "Bob" yerine "Robert" olarak değiştirir ve veritabanını güncelleştirir.

Sütun adı Özgün değer Geçerli değer Veritabanındaki değer
CustID 101 101 101
LastName Smith Smith Smith
FirstName Bob Robert Bob

Güncelleştirme sırasında veritabanındaki değerler User2'nin sahip olduğu özgün değerlerle eşleştiğinden güncelleştirme başarılı olur.

Saat 13:05'te, User1 "Bob"ın adını "James" olarak değiştirir ve satırı güncelleştirmeye çalışır.

Sütun adı Özgün değer Geçerli değer Veritabanındaki değer
CustID 101 101 101
LastName Smith Smith Smith
FirstName Bob James Robert

Bu noktada, veritabanındaki değer ("Robert") artık User1'in beklediği özgün değerle ("Bob") eşleşmediğinden, User1 iyimser bir eşzamanlılık ihlaliyle karşılaşır. Eşzamanlılık ihlali yalnızca güncelleştirmenin başarısız olduğunu size bildirir. Bu kararın artık User1 tarafından sağlanan değişikliklerle User2 tarafından sağlanan değişikliklerin üzerine yazılması veya User1 tarafından yapılan değişikliklerin iptal edilmesi gerekir.

İyimser Eşzamanlılık İhlallerini Test Etme

İyimser eşzamanlılık ihlali için test için çeşitli teknikler vardır. Bunlardan biri, tabloya bir zaman damgası sütunu dahil etmeyi içerir. Veritabanları genellikle kaydın son güncelleştirildiği tarih ve saati tanımlamak için kullanılabilecek zaman damgası işlevselliği sağlar. Bu teknik kullanılarak, tablo tanımına bir zaman damgası sütunu eklenir. Kayıt her güncelleştirildiğinde, zaman damgası geçerli tarih ve saati yansıtacak şekilde güncelleştirilir. İyimser eşzamanlılık ihlallerine yönelik bir testte, zaman damgası sütunu tablonun içeriğinin herhangi bir sorgusuyla döndürülür. Bir güncelleştirme denendiğinde, veritabanındaki zaman damgası değeri, değiştirilen satırda yer alan özgün zaman damgası değeriyle karşılaştırılır. Eşleşirse, güncelleştirme gerçekleştirilir ve zaman damgası sütunu güncelleştirmeyi yansıtacak şekilde geçerli saatle güncelleştirilir. Eşleşmezlerse iyimser bir eşzamanlılık ihlali oluştu.

İyimser eşzamanlılık ihlalini test etmek için kullanılan bir diğer teknik de, bir satırdaki tüm özgün sütun değerlerinin veritabanında bulunanlarla hala eşleşdiğini doğrulamaktır. Örneğin, aşağıdaki sorguyu göz önüne alın:

SELECT Col1, Col2, Col3 FROM Table1  

Tablo1'de bir satırı güncelleştirirken iyimser eşzamanlılık ihlalini test etmek için aşağıdaki UPDATE deyimini yayınlarsınız:

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

Özgün değerler veritabanındaki değerlerle eşleşdiği sürece güncelleştirme gerçekleştirilir. Bir değer değiştirildiyse, WHERE yan tümcesi eşleşme bulamadığı için güncelleştirme satırı değiştirmez.

Sorgunuzda her zaman benzersiz bir birincil anahtar değeri döndürmenin önerildiğini unutmayın. Aksi takdirde, önceki UPDATE deyimi birden fazla satırı güncelleştirebilir ve amacınız bu olmayabilir.

Veri kaynağınızdaki bir sütun null değere izin veriyorsa, yerel tablonuzda ve veri kaynağında eşleşen null başvuru olup olmadığını denetlemek için WHERE yan tümcenizi genişletmeniz gerekebilir. Örneğin, aşağıdaki UPDATE deyimi yerel satırdaki null başvurunun yine de veri kaynağındaki null başvuruyla eşleşdiğini veya yerel satırdaki değerin yine de veri kaynağındaki değerle eşleşdiğini doğrular.

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

İyimser eşzamanlılık modeli kullanırken daha az kısıtlayıcı ölçütler uygulamayı da seçebilirsiniz. Örneğin, WHERE yan tümcesinde yalnızca birincil anahtar sütunlarının kullanılması, diğer sütunların son sorgudan bu yana güncelleştirilip güncelleştirilmediğine bakılmaksızın verilerin üzerine yazılmasını sağlar. Ayrıca, yalnızca belirli sütunlara WHERE yan tümcesi uygulayarak verilerin üzerine yazılmasını ve son sorgulanmalarının ardından belirli alanların güncelleştirilmediği sürece bunların üzerine yazılmasını sağlayabilirsiniz.

DataAdapter.RowUpdated Olayı

Nesnenin DataAdapter RowUpdated olayı, iyimser eşzamanlılık ihlalleri uygulamanıza bildirim sağlamak için daha önce açıklanan tekniklerle birlikte kullanılabilir. RowUpdated, DataSet'ten değiştirilen bir satırı güncelleştirme girişiminden sonra gerçekleşir. Bu, özel durum oluştuğunda işleme, özel hata bilgileri ekleme, yeniden deneme mantığı ekleme vb. dahil olmak üzere özel işleme kodu eklemenize olanak tanır. nesnesi, RowUpdatedEventArgs tablodaki değiştirilmiş bir satır için belirli bir güncelleştirme komutundan etkilenen satır sayısını içeren bir RecordsAffected özelliği döndürür. İyimser eşzamanlılığı test etmek için update komutunu ayarlayarak, RecordsAffected özelliği sonuç olarak, hiçbir kayıt güncelleştirilemediğinden iyimser eşzamanlılık ihlali oluştuğunda 0 değerini döndürür. Bu durumda, bir özel durum oluşturulur. RowUpdated olayı bu oluşumu işlemenize ve UpdateStatus.SkipCurrentRow gibi uygun bir RowUpdatedEventArgs.Status değeri ayarlayarak özel durumdan kaçınmanıza olanak tanır. RowUpdated olayı hakkında daha fazla bilgi için bkz. DataAdapter Olaylarını İşleme.

İsteğe bağlı olarak, Update'i çağırmadan önce DataAdapter.ContinueUpdateOnError değerini true olarak ayarlayabilir ve Güncelleştirme tamamlandığında belirli bir satırın RowError özelliğinde depolanan hata bilgilerini yanıtlayabilirsiniz. Daha fazla bilgi için bkz . Satır Hatası Bilgileri.

İyimser Eşzamanlılık Örneği

Aşağıda, bir DataAdapter'ın UpdateCommand değerini iyimser eşzamanlılığı test etmek üzere ayarlayan ve ardından iyimser eşzamanlılık ihlallerini test etmek için RowUpdated olayını kullanan basit bir örnek verilmiştir. İyimser bir eşzamanlılık ihlaliyle karşılaşıldığında, uygulama güncelleştirmenin verildiği satırın RowError'ını iyimser bir eşzamanlılık ihlalini yansıtacak şekilde ayarlar.

UPDATE komutunun WHERE yan tümcesine geçirilen parametre değerlerinin ilgili sütunlarının Özgün değerleriyle eşlendiğini unutmayın.

' 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 = 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.  
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;  
  }  
}  

Ayrıca bkz.