Bir okuma işlemini gerçekleştirmeli veya geri almalı mıyım?


96

İzolasyon seviyesini belirtebilmem için bir işlem içinde yürüttüğüm bir okuma sorgum var. Sorgu tamamlandıktan sonra ne yapmalıyım?

  • İşlemi tamamla
  • İşlemi geri alın
  • Hiçbir şey yapmayın (bu, kullanım bloğunun sonunda işlemin geri alınmasına neden olur)

Her birini yapmanın sonuçları nelerdir?

using (IDbConnection connection = ConnectionFactory.CreateConnection())
{
    using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
    {
        using (IDbCommand command = connection.CreateCommand())
        {
            command.Transaction = transaction;
            command.CommandText = "SELECT * FROM SomeTable";
            using (IDataReader reader = command.ExecuteReader())
            {
                // Read the results
            }
        }

        // To commit, or not to commit?
    }
}

DÜZENLEME: Soru, bir işlemin kullanılması gerekip gerekmediği veya işlem seviyesini belirlemenin başka yolları olup olmadığı değildir. Buradaki soru, herhangi bir değişiklik yapmayan bir işlemin taahhüt edilmesi veya geri alınmasıdır. Performans farkı var mı? Diğer bağlantıları etkiliyor mu? Başka fark var mı?


1
Muhtemelen bunu zaten biliyorsunuzdur, ancak sağladığınız örneğe göre, sorguyu basitçe çalıştırarak eşdeğer sonuçlara sahip olabilirsiniz: SELECT * FROM SomeTable with NOLOCK
JasonTrue

@Stefan, öyle görünüyor ki çoğumuz neden salt okunur bir işlemle işlem yapmakla uğraştığınızı merak ediyoruz. NOLOCK hakkında bilginiz olup olmadığını ve varsa neden o yola gitmediğinizi bize bildirir misiniz?
StingyJack

NOLOCK'u biliyorum, ancak bu sistem farklı veritabanlarına ve SQL Server'a karşı çalışıyor, bu yüzden SQL Server'a özgü kilitleme ipuçlarından kaçınmaya çalışıyorum. Uygulama yukarıdaki kodla iyi çalıştığından, bu her şeyden çok merak uyandıran bir sorudur.
Stefan Moser

Ah, bu durumda sqlserver etiketini kaldırıyorum çünkü bu MSSqlServer'ı hedef ürün olarak gösteriyor.
StingyJack

@StingyJack - Haklısın, sqlserver etiketini kullanmamalıydım.
Stefan Moser

Yanıtlar:


52

Sen taahhüt et. Dönem. Başka mantıklı bir alternatif yok. Bir işlem başlattıysanız, kapatmalısınız. Kaydetmek, sahip olabileceğiniz tüm kilitleri serbest bırakır ve ReadUncommitted veya Serializable yalıtım düzeylerinde de aynı derecede mantıklıdır. Örtük geri dönüşe güvenmek - belki teknik olarak eşdeğer olsa da - sadece kötü bir biçimdir.

Bu sizi ikna etmediyse, kodunuzun ortasına bir güncelleme ifadesi ekleyen ve meydana gelen ve verilerini kaldıran örtük geri dönüşü izlemesi gereken bir sonraki kişiyi hayal edin.


45
Mantıklı bir alternatif var - geri alma. Açıkça geri alma, yani. Hiçbir şeyi değiştirmek istemediyseniz, geri alma her şeyin geri alınmasını sağlar. Elbette herhangi bir değişiklik olmamalıydı; geri alma bunu garanti eder.
Jonathan Leffler

2
Farklı DBMS, farklı 'örtük işlem tamamlama' anlamlarına sahip olabilir. IBM Informix (ve ben DB2'nin) örtük geri dönüş yaptığına inanıyorum; söylentiye göre, Oracle örtük bir taahhütte bulunur. Örtük geri dönüşü tercih ederim.
Jonathan Leffler

8
Bir geçici tablo oluşturduğumu, onu kimliklerle doldurduğumu, kimliklerle birlikte gelen verileri seçmek için bir veri tablosuyla birleştirdiğimi ve ardından geçici tabloyu sildiğimi varsayalım. Gerçekten sadece veri okuyorum ve geçici olduğu için geçici tabloya ne olacağı umrumda değil ... ama performans açısından, işlemi geri almak veya gerçekleştirmek daha mı pahalı olur? Geçici tablolar ve okuma işlemlerinden başka bir şey söz konusu olmadığında, kesinleştirme / geri alma işleminin etkisi nedir?
Triynko

4
@Triynko - Sezgisel olarak, ROLLBACK'in daha pahalı olduğunu tahmin ediyorum. COMMIT normal kullanım durumudur ve ROLLBACK istisnai durumdur. Ama akademik dışında kimin umurunda? Eminim uygulamanız için 1000 daha iyi optimizasyon noktası vardır. Gerçekten merak ediyorsanız, mySQL işlem işleme kodunu bazaar.launchpad.net/~mysql/mysql-server/mysql-6.0/annotate/…
Mark Brackett

3
@Triynko - Optimize etmenin tek yolu profil yapmaktır . Bu çok basit bir kod değişikliği, gerçekten optimize etmek istiyorsanız her iki yöntemi de profillememek için hiçbir neden yok. Bizi sonuçlarla güncellediğinizden emin olun!
Mark Brackett

28

Hiçbir şeyi değiştirmediyseniz, COMMIT veya ROLLBACK kullanabilirsiniz. Her ikisi de edindiğiniz okuma kilitlerini serbest bırakacak ve başka herhangi bir değişiklik yapmadığınız için eşdeğer olacaktır.


2
Eşdeğer olduklarını bana bildirdiğiniz için teşekkürler. Bence bu, asıl soruyu en iyi yanıtlıyor.
chowey

Gerçek güncelleme olmadan commit kullanırsak, işlemin etkin olmadığını verir. yeni sitemde karşılaştım
Muhammad Omer Aslam

6

Bir işleme başlarsanız, en iyi uygulama her zaman onu yapmaktır. Kullanım (işlem) bloğunuzun içine bir istisna atılırsa, işlem otomatik olarak geri alınacaktır.


3

IMHO, işlemlerde salt okunur sorguları sarmalamak mantıklı olabilir çünkü (özellikle Java'da) işlemin "salt okunur" olmasını söyleyebilirsiniz, bu durumda JDBC sürücüsü sorguyu optimize etmeyi düşünebilir (ancak buna gerek yoktur, yani kimse INSERTyine de bir düzenleme yapmanıza engel olacaktır ). Örneğin, Oracle sürücüsü, salt okunur olarak işaretlenmiş bir işlemdeki sorgularda tablo kilitlerini tamamen önleyecektir, bu da yoğun şekilde okuma güdümlü uygulamalarda çok fazla performans elde eder.


3

İç içe geçmiş işlemleri düşünün .

Çoğu RDBMS, iç içe geçmiş işlemleri desteklemez veya bunları çok sınırlı bir şekilde taklit etmeye çalışır.

Örneğin, MS SQL Server'da, bir iç işlemdeki geri dönüş (gerçek bir işlem değildir, MS SQL Server yalnızca işlem seviyelerini sayar!), En dıştaki işlemde (gerçek işlem olan) gerçekleşen her şeyi geri alır .

Bazı veritabanı sarmalayıcıları, bir iç işlemdeki geri dönüşü, en dıştaki işlemin tamamlanmış veya geri alınmış olmasına bakılmaksızın, bir hatanın oluştuğunun bir işareti olarak kabul edebilir ve en dıştaki işlemdeki her şeyi geri alabilir.

Dolayısıyla COMMIT, bileşeninizin bazı yazılım modülleri tarafından kullanıldığını göz ardı edemediğinizde güvenli yoldur.

Lütfen bunun sorunun genel bir cevabı olduğunu unutmayın. Kod örneği, yeni bir veritabanı bağlantısı açarak bir dış işlemle ilgili sorunu akıllıca çözer.

Performansla ilgili olarak: izolasyon seviyesine bağlı olarak, SEÇİM'ler değişen derecelerde KİLİTLER ve geçici veriler (anlık görüntüler) gerektirebilir. İşlem kapatıldığında bu temizlenir. Bunun COMMIT veya ROLLBACK ile yapılıp yapılmadığı önemli değildir. Harcanan CPU süresinde önemsiz bir fark olabilir - bir COMMIT'in ayrıştırılması muhtemelen bir ROLLBACK'ten (iki karakter daha az) ve diğer küçük farklardan daha hızlıdır. Açıkçası, bu yalnızca salt okunur işlemler için geçerlidir!

Kesinlikle istenmedi: Kodu okuyabilecek başka bir programcı, bir ROLLBACK'in bir hata durumu anlamına geldiğini varsayabilir.


2

Sadece bir ek not, ancak bu kodu şu şekilde de yazabilirsiniz:

using (IDbConnection connection = ConnectionFactory.CreateConnection())
using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
using (IDbCommand command = connection.CreateCommand())
{
    command.Transaction = transaction;
    command.CommandText = "SELECT * FROM SomeTable";
    using (IDataReader reader = command.ExecuteReader())
    {
        // Do something useful
    }
    // To commit, or not to commit?
}

Ve işleri birazcık yeniden yapılandırırsanız, IDataReader için kullanım bloğunu da en üste taşıyabilirsiniz.


1

SQL'i bir saklı yordama koyarsanız ve bunu sorgunun üzerine eklerseniz:

set transaction isolation level read uncommitted

o zaman C # kodunda herhangi bir çemberden geçmek zorunda kalmazsınız. Bir saklı yordamda işlem yalıtım düzeyini ayarlamak, ayarın bu bağlantının gelecekteki tüm kullanımları için geçerli olmasına neden olmaz (bu, bağlantılar havuza alındığından diğer ayarlarla ilgili endişelenmeniz gereken bir şeydir). Depolanan yordamın sonunda, bağlantı ile başlatılan her şeye geri döner.


1

ROLLBACK çoğunlukla bir hata veya istisnai durumlarda kullanılır ve başarılı bir tamamlama durumunda COMMIT kullanılır.

İşlemleri COMMIT (başarı için) ve ROLLBACK (başarısızlık için) ile kapatmalıyız, önemsiz göründüğü salt okunur işlemlerde bile. Aslında tutarlılık ve geleceğe hazır olma açısından önemlidir.

Salt okunur bir işlem, mantıksal olarak pek çok şekilde "başarısız olabilir", örneğin:

  • bir sorgu beklendiği gibi tam olarak bir satır döndürmüyor
  • saklı yordam bir istisna yaratır
  • alınan veriler tutarsız bulundu
  • kullanıcı işlemi çok uzun sürdüğü için iptal ediyor
  • kilitlenme veya zaman aşımı

COMMIT ve ROLLBACK salt okunur bir işlem için doğru şekilde kullanılırsa, örneğin önbelleğe alma, denetim veya istatistik gibi bir noktada DB yazma kodu eklenirse beklendiği gibi çalışmaya devam edecektir.

Örtülü ROLLBACK, yalnızca uygulama çöktüğünde veya kurtarılamaz bir hata, ağ arızası, elektrik kesintisi vb. İle çıktığında "ölümcül hata" durumları için kullanılmalıdır.


0

OKUMA'nın durumu değiştirmediği göz önüne alındığında, hiçbir şey yapmam. Bir commit yapmak, isteği veritabanına göndermek için bir döngü harcamak dışında hiçbir şey yapmaz. Durumu değiştiren bir işlem gerçekleştirmediniz. Aynı şekilde geri alma için.

Bununla birlikte, nesnelerinizi temizlediğinizden ve veritabanına olan bağlantılarınızı kapattığınızdan emin olmalısınız. Bu kod tekrar tekrar aranırsa bağlantılarınızı kapatmamak sorunlara yol açabilir.


3
İzolasyon düzeyine bağlı olarak, diğer işlemleri engelleyecek kilitler elde edebilir.
Graeme Perrow

Kullanım bloğunun sonunda bağlantı kapatılacaktır - bunun için var. Ancak, ağ trafiğinin muhtemelen denklemin en yavaş kısmı olduğu iyi bir nokta.
Joel Coehoorn

1
İşlem bir şekilde gerçekleştirilecek veya geri alınacaktır, bu nedenle en iyi uygulama, başarılı olursa her zaman bir taahhüt yayınlamak olacaktır.
Neil Barnwell

0

AutoCommit'i false olarak ayarlarsanız, ardından YES.

JDBC (Postgresql sürücüsü) ile yapılan bir deneyde, sorgu sonlarını seçerseniz (zaman aşımı nedeniyle), geri almadıkça yeni seçme sorgusunu başlatamayacağınızı buldum.


-2

Kod örneğinizde, sahip olduğunuz

  1. // Kullanışlı bir şeyler yap

    Verileri değiştiren bir SQL İfadesi mi yürütüyorsunuz?

Değilse, "Oku" İşlemi diye bir şey yoktur ... Yalnızca Ekleme, Güncelleme ve Silme İfadelerinden (verileri değiştirebilen ifadeler) yapılan değişiklikler bir İşlemdedir ... Bahsettiğiniz şey, SQL'in kilitleridir. Sunucu, okuduğunuz verileri, bu verileri etkileyen DİĞER işlemler nedeniyle koyar. Bu kilitlerin seviyesi SQL Server İzolasyon Seviyesine bağlıdır.

Ancak SQL deyiminiz hiçbir şeyi değiştirmediyse, Commit veya ROll Back yapamazsınız.

Verileri değiştiriyorsanız, açıkça bir işleme başlatmadan izolasyon düzeyini değiştirebilirsiniz ... Her bir SQL İfadesi örtük olarak bir işlemdedir. Açıkça bir İşlem başlatmak, yalnızca 2 veya daha fazla ifadenin aynı işlem içinde olmasını sağlamak için gereklidir.

Tek yapmanız gereken işlem yalıtım düzeyini ayarlamaksa, bir komutun CommandText'ini "İşlem Yalıtım düzeyini Tekrarlanabilir Okuma" (veya istediğiniz herhangi bir düzeye) olarak ayarlayın, CommandType'ı CommandType.Text olarak ayarlayın ve komutu çalıştırın. (Command.ExecuteNonQuery () kullanabilirsiniz)

NOT: MULTIPLE okuma deyimi yapıyorsanız ve hepsinin veritabanının ilkiyle aynı durumunu "görmesini" istiyorsanız, yalıtım Düzeyini en üst Tekrarlanabilir Okuma veya Serileştirilebilir olarak ayarlamanız gerekir ...


// Yararlı bir şey yapın, hiçbir veriyi değiştirmez, sadece okuyun. Tek yapmak istediğim, sorgunun izolasyon düzeyini belirtmek.
Stefan Moser

Sonra bunu istemciden açıkça bir işlem başlatmadan yapabilirsiniz ... Sadece "İşlem İzolasyon Düzeyini Ayarla ReadUncommitted", "... Read Committed", "... RepeatableRead", "... Snapshot" sql dizesini çalıştırın. , veya "... Serileştirilebilir" "İzolasyon Seviyesini Ayarla Okundu"
Charles Bretana

3
Sadece okuyor olsanız bile işlemler yine de önemlidir. Birkaç okuma işlemi yapmak istiyorsanız, bunları bir işlem içinde yapmak tutarlılığı sağlayacaktır. Bunları kimse olmadan yapmak olmaz.
MarkR

evet üzgünüm, haklısınız, en azından İzolasyon seviyesi Tekrarlanabilir Okuma veya daha yüksek bir değere ayarlanmışsa bu doğrudur.
Charles Bretana

-3

Başkalarının aynı verileri okumasını engellemeniz mi gerekiyor? Neden bir işlem kullanmalı?

@Joel - Sorum şu şekilde daha iyi ifade edilebilir: "Okuma sorgusunda neden işlem kullanmalı?"

@Stefan - Depolanmış bir proc değil de AdHoc SQL kullanacaksanız, sorgudaki tablolardan sonra WITH (NOLOCK) ekleyin. Bu şekilde, bir işlem için uygulamada ve veritabanında ek yüke (minimum olsa da) maruz kalmazsınız.

SELECT * FROM SomeTable WITH (NOLOCK)

EDIT @ Yorum 3: Soru etiketlerinde "sqlserver" bulunduğundan, MSSQLServer'ın hedef ürün olduğunu varsaymıştım. Şimdi bu nokta açıklığa kavuştuğuna göre, belirli ürün referansını kaldırmak için etiketleri düzenledim.

İlk başta neden bir okuma işleminde işlem yapmak istediğinizden hala emin değilim.


1
Tek seferde ayarlanan izolasyon seviyesine. İşlemi , sorgu için kilitleme miktarını gerçekten azaltmak için kullanabilirsiniz .
Joel Coehoorn

1
Daha düşük bir izolasyon seviyesi kullanabilmek ve kilitlemeyi azaltabilmek için işlemi kullanıyorum.
Stefan Moser

@StingyJack - Bu kod bir dizi farklı veritabanında çalıştırılabilir, bu nedenle NOLOCK bir seçenek değildir.
Stefan Moser
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.