SQL Server 2005'te Kilitlenmeleri Teşhis Etme


82

Stack Overflow SQL Server 2005 veritabanında bazı tehlikeli ancak nadir görülen kilitlenme durumları görüyoruz.

Profil oluşturucuyu ekledim , kilitlenme sorunlarının giderilmesiyle ilgili bu mükemmel makaleyi kullanarak bir izleme profili oluşturdum ve bir dizi örnek yakaladım. İşin garibi , çıkmaza giren yazının her zaman aynı olmasıdır :

UPDATE [dbo].[Posts]
SET [AnswerCount] = @p1, [LastActivityDate] = @p2, [LastActivityUserId] = @p3
WHERE [Id] = @p0

Diğer kilitlenme ifadesi değişiklik gösterir, ancak genellikle postalar tablosunun bir tür önemsiz, basit okunmasıdır . Bu her zaman çıkmazda ölür. İşte bir örnek

SELECT
[t0].[Id], [t0].[PostTypeId], [t0].[Score], [t0].[Views], [t0].[AnswerCount], 
[t0].[AcceptedAnswerId], [t0].[IsLocked], [t0].[IsLockedEdit], [t0].[ParentId], 
[t0].[CurrentRevisionId], [t0].[FirstRevisionId], [t0].[LockedReason],
[t0].[LastActivityDate], [t0].[LastActivityUserId]
FROM [dbo].[Posts] AS [t0]
WHERE [t0].[ParentId] = @p0

Tamamen açık olmak gerekirse, yazma / yazma kilitlenmeleri değil, okuma / yazma görüyoruz.

Şu anda LINQ ve parametreli SQL sorgularının bir karışımına sahibiz. with (nolock)Tüm SQL sorgularına ekledik . Bu bazılarına yardımcı olmuş olabilir. Ayrıca dün düzelttiğim, her seferinde 20 saniyeden fazla süren ve bunun üzerine her dakika çalışan tek (çok) kötü yazılmış bir rozet sorgumuz vardı. Bazı kilitleme sorunlarının kaynağının bu olduğunu umuyordum!

Ne yazık ki, yaklaşık 2 saat önce başka bir kilitlenme hatası aldım. Aynı kesin belirtiler, aynı suçlu yazısı.

Gerçekten tuhaf olan şey, yukarıda gördüğünüz kilitleme yazma SQL deyiminin çok özel bir kod yolunun parçası olmasıdır. O var sadece yeni cevap bir soruya eklendiğinde infaz - yeni cevap sayısı ve en son tarih / kullanıcı ile ebeveyn soru günceller. Bu, elbette, yaptığımız muazzam okuma sayısına göre o kadar yaygın değil! Anladığım kadarıyla, uygulamanın hiçbir yerinde çok sayıda yazma yapmıyoruz.

NOLOCK'un devasa bir çekiç olduğunun farkındayım, ancak burada yaptığımız sorguların çoğunun o kadar doğru olması gerekmiyor. Kullanıcı profilinizin birkaç saniye eski olması umrunda mı?

NOLOCK'u Linq ile kullanmak, Scott Hanselman'ın burada tartıştığı gibi biraz daha zor .

Kullanma fikri ile flört ediyoruz

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

temel veritabanı bağlamında, böylece tüm LINQ sorgularımız bu kümeye sahip olur. Bu olmadan, yaptığımız her LINQ çağrısını (bunların büyük çoğunluğu olan basit okuma çağrılarını) çirkin olan 3-4 satırlık bir işlem kodu bloğunda sarmamız gerekir.

Sanırım SQL 2005'teki önemsiz okumaların yazma işlemlerini çıkmaza sokması biraz hayal kırıklığına uğradım. Yazma / yazma kilitlenmelerinin büyük bir sorun olduğunu görebiliyordum, ancak okuyor? Burada bir bankacılık sitesi işletmiyoruz, her seferinde mükemmel doğruluğa ihtiyacımız yok.

Fikirler? Düşünceler?


Her işlem için yeni bir LINQ to SQL DataContext nesnesinin örneğini mi oluşturuyorsunuz yoksa tüm çağrılarınız için aynı statik bağlamı mı paylaşıyorsunuz?

Jeremy, temel Denetleyicide çoğunlukla bir statik veri içeriğini paylaşıyoruz:

private DBContext _db;
/// <summary>
/// Gets the DataContext to be used by a Request's controllers.
/// </summary>
public DBContext DB
{
    get
    {
        if (_db == null)
        {
            _db = new DBContext() { SessionName = GetType().Name };
            //_db.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
        }
        return _db;
    }
}

Her Denetleyici için veya Sayfa başına veya daha sık yeni bir bağlam oluşturmamızı tavsiye ediyor musunuz?


2
Hangi <a href=" en.wikipedia.org/wiki/… modunu</a> kullanıyorsunuz, "karamsar" (kilit tabanlı) veya "iyimser" (<a href = " en.wikipedia.org/wiki/ … )?
John Siracusa

Yukarıdaki Guy'ın yanıtına katılıyorum - belirti üzerinde çalışmaya çalışmak yerine neden altta yatan nedenleri ele almıyoruz? Devam eden toplam AnswerCount'u Gönderiler tablosuna ekleyerek potansiyel bir engelleme kaynağı oluşturdunuz. Jeff, StackOverflow için ERD'sini yayınlamak ister, böylece insanlar eleştirebilir?
andyp

2
Vay canına - bunu Scott ile olan podcast'inizde duydum. Kutudan daha iyi bir yapılandırma ile gönderilmediğine de inanamıyorum. Bunu DBA'larımıza göstereceğim (çünkü onlar da yaygın olarak 'nolock' kullanıyorlar)
Dan Esparza

3
Bunun kilitlenme nedeni için samsaffron.com/archive/2008/08/27/Deadlocked+ adresine bakın . Anlık görüntü izolasyonunu açmak, bu soruna iyi bir çözümdür.
Sam Saffron

Yanıtlar:


44

MSDN'ye göre:

http://msdn.microsoft.com/en-us/library/ms191242.aspx

READ COMMITTED SNAPSHOT veya ALLOW SNAPSHOT İZOLASYON veritabanı seçenekleri AÇIK olduğunda, veritabanında gerçekleştirilen tüm veri değişiklikleri için mantıksal kopyalar (sürümler) saklanır. Bir satır belirli bir işlemle değiştirildiğinde, Veritabanı Motoru örneği, satırın önceden kaydedilmiş görüntüsünün bir sürümünü tempdb'de depolar. Her sürüm, değişikliği yapan işlemin işlem sıra numarasıyla işaretlenir. Değiştirilen satırların sürümleri bir bağlantı listesi kullanılarak zincirlenir. En yeni satır değeri her zaman geçerli veritabanında depolanır ve tempdb'de depolanan sürümü belirlenmiş satırlara zincirlenir.

Kısa süreli işlemler için, değiştirilmiş bir satırın bir sürümü, tempdb veritabanının disk dosyalarına yazılmadan arabellek havuzunda önbelleğe alınabilir. Sürümü belirlenmiş satıra duyulan ihtiyaç kısa sürerse, sadece arabellek havuzundan çıkar ve G / Ç ek yüküne neden olmayabilir.

Ekstra ek yük için hafif bir performans cezası var gibi görünüyor, ancak bu önemsiz olabilir. Emin olmak için test etmeliyiz.

Bu seçeneği ayarlamayı deneyin ve gerçekten gerekli olmadıkça kod sorgularındaki tüm NOLOCK'ları KALDIRIN. NOLOCK'lar veya veritabanı işlem izolasyon düzeyleriyle mücadele etmek için veritabanı bağlam işleyicisinde küresel yöntemler kullanmak, soruna Bant Yardımlarıdır. NOLOCKS, veri katmanımızla ilgili temel sorunları maskeleyecek ve muhtemelen, otomatik seçme / güncelleme satırı sürümlemesinin çözüm gibi göründüğü güvenilir olmayan verilerin seçilmesine yol açacaktır.

ALTER Database [StackOverflow.Beta] SET READ_COMMITTED_SNAPSHOT ON

3
"NOLOCKS, veri katmanımızla temel sorunları maskeleyecek" ... NOLOCK ne tür sorunları maskeliyor? NOLOCK'a ihtiyacım olduğunu düşünürsem, hangi sorunları aramalıyım?
Matt Hamilton

3
Peki ya bu cevap onu "cevap" yapıyor? Hala milisaniye okumanın neden yazıyı çıkmaza sokup hemen sonlandıracağını anlamıyorum? "User13484" tarafından verilen cevabın çok kötü olduğunu tahmin ediyorum, eğer durum buysa, buna referans yok.
RichardTheKiwi

37

NOLOCK ve READ UNCOMMITTED kaygan bir eğimdir. İlk önce neden kilitlendiğini anlamadıkça bunları asla kullanmamalısınız. "Tüm SQL sorgularına (nolock) ekledik" demeniz beni endişelendiriyor. Her yerde WITH NOLOCK eklemeniz , veri katmanınızda sorun yaşadığınızın kesin bir işaretidir.

Güncelleme ifadesinin kendisi biraz sorunlu görünüyor. Sayıyı işlemde daha önce mi belirliyorsunuz yoksa bir nesneden mi alıyorsunuz? AnswerCount = AnswerCount+1Bir soru eklendiğinde muhtemelen bunu halletmek için daha iyi bir yoldur. O zaman doğru sayımı almak için bir işleme ihtiyacınız yok ve potansiyel olarak kendinizi maruz bıraktığınız eşzamanlılık sorunu hakkında endişelenmenize gerek yok.

Çok fazla çalışma yapmadan ve kirli okumaları etkinleştirmeden bu tür bir kilitlenme sorununu aşmanın kolay bir yolu "Snapshot Isolation Mode", son değiştirilmemiş verileri size her zaman temiz bir şekilde okuyacak olan (SQL 2005'te yeni) kullanmaktır . Ayrıca, sorunsuz bir şekilde işlemek istiyorsanız, kilitlenmiş ifadeleri oldukça kolay bir şekilde yakalayabilir ve yeniden deneyebilirsiniz.


4
JEzell ile birlikteyim - sıfırladığım ilk şey 'SET AnswerCount = <sabit değer>'. Bu değer nereden geliyor? Bu, işlemin başka bir yerinde bir grup kilidi kapacak şekilde alıp almadığınızı merak etmeme neden oluyor. Bununla başlayacağım. Ve evet, küresel NOLOCK bir yara bandıdır.
Cowan

25

OP sorusu, bu sorunun neden ortaya çıktığını sormaktı. Bu gönderi, başkaları tarafından çözülecek olası çözümleri bırakırken buna cevap vermeyi umuyor.

Bu muhtemelen indeksle ilgili bir sorundur. Örneğin, Yazılar tablosunun, ParentID'yi ve güncellenmekte olan alan (lar) dan birini (veya daha fazlasını) (AnswerCount, LastActivityDate, LastActivityUserId) içeren kümelenmemiş bir X dizini olduğunu varsayalım.

SELECT cmd, ParentId ile arama yapmak için X dizininde paylaşılan-okuma kilidi yaparsa ve UPDATE cmd özel bir yazma işlemi yaparken kalan sütunları almak için kümelenmiş dizinde paylaşılan-okuma kilidi yapması gerekirse bir kilitlenme meydana gelir. kümelenmiş dizini kilitleyin ve güncellemek için X dizininde yazmaya özel bir kilit almanız gerekir.

Şimdi, A'nın X'i kilitlediği ve Y'yi almaya çalışırken B'nin Y'yi kilitlediği ve X'i almaya çalıştığı bir durum var.

Tabii ki, OP'nin, gerçekte nedenin bu olup olmadığını doğrulamak için hangi indekslerin oyunda olduğuna dair daha fazla bilgi ile gönderisini güncellemesine ihtiyacımız var.


Bu analize katılıyorum - SELECT ve UPDATE satırları farklı bir sırayla işliyor, bu nedenle her biri diğerinin sahip olduğu bir satır kilidi elde etmeye çalışıyor.
Mike Dimmick

Bu, tüm iş parçacığına verilen en iyi yanıttır ve kilitlenmenin gerçekte neden oluştuğuna dair tek açıklamayı yapar. Kötü olan 1 numaralı cevap değil çünkü buradaki en iyisi.
Jonathan Kehayias

Bazı cevaplar, kilidin 2 atomik basit ifade arasında olduğu noktasını kaçırıyor. Bunu açıklamaya çalışan tek gönderi bu. İfade basit olsa da, bir tablo güncellemesi, birçok işleme genişleyen birden çok CIX ve NCIX güncellemesini içerebilir. NCIX çapraz geçiş, CIX yer imi araması içeren READ için de aynı. Bunun, masaların aynı sırayla birleştirilmesiyle hiçbir ilgisi yok (insanlar soru okuyor mu?)
RichardTheKiwi

18

Bu sorudan ve görevlinin cevaplarından oldukça rahatsızım. Bir sürü "bu sihirli tozu dene! Hayır o sihirli toz!"

Alınan kilitleri analiz ettiğinizi ve tam olarak hangi kilitlerin kilitlendiğini belirlediğinizi hiçbir yerde göremiyorum.

Tüm belirttiğiniz bazı kilitlerin oluştuğudur - kilitlenme olan şey değil.

SQL 2005'te aşağıdakileri kullanarak hangi kilitlerin çıkarıldığı hakkında daha fazla bilgi edinebilirsiniz:

DBCC TRACEON (1222, -1)

böylece kilitlenme meydana geldiğinde daha iyi teşhislere sahip olursunuz.


13
Bir kilitlenme, SQL Server'daki kilitlenme izleyicisi tarafından hemen ele alınır. DMV'ler bir kilitlenmeyi gidermek için işe yaramaz çünkü kurban siz daha ortaya çıkmadan önce seçilecek ve öldürülecektir.
Jonathan Kehayias

14

Her işlem için yeni bir LINQ to SQL DataContext nesnesinin örneğini mi oluşturuyorsunuz yoksa tüm çağrılarınız için aynı statik bağlamı mı paylaşıyorsunuz? Başlangıçta ikinci yaklaşımı denedim ve hatırladığım kadarıyla DB'de istenmeyen kilitlenmeye neden oldu. Şimdi her atomik işlem için yeni bir bağlam oluşturuyorum.


10

Her yerde NOLOCK ile bir sinek yakalamak için evi yakmadan önce, Profiler ile yakalamanız gereken o kilitlenme grafiğine bir göz atmak isteyebilirsiniz.

Bir kilitlenmenin (en az) 2 kilit gerektirdiğini unutmayın. Bağlantı 1'de Kilit A var, Kilit B istiyor - ve Bağlantı 2 için tam tersi. Bu çözülemeyen bir durum ve birinin vermesi gerekiyor.

Şimdiye kadar gösterdikleriniz, Sql Server'ın gün boyu yapmaktan mutluluk duyacağı basit kilitlemeyle çözüldü.

Sizin (veya LINQ), içindeki bu UPDATE ifadesiyle bir işleme başladığından ve elden önce başka bir bilgi parçasını SEÇTİĞİNİZDEN şüpheleniyorum. Ancak, her bir iş parçacığı tarafından tutulan kilitleri bulmak için gerçekten kilitlenme grafiğinden geri dönmeniz ve ardından bu kilitlerin verilmesine neden olan ifadeleri bulmak için Profiler aracılığıyla geri dönmeniz gerekir.

Bu bulmacayı tamamlamak için en az 4 ifade olmasını bekliyorum (veya birden fazla kilit alan bir ifade - belki de Gönderiler tablosunda bir tetikleyici olabilir?).


7

Kullanıcı profilinizin birkaç saniye eski olması umrunda mı?

Hayır - bu tamamen kabul edilebilir. Temel işlem yalıtım düzeyini ayarlamak, muhtemelen en iyi / en temiz yoldur.


5

Tipik okuma / yazma kilitlenmesi, dizin sipariş erişiminden gelir. Okuma (T1), A dizinindeki satırı bulur ve ardından B dizinindeki (genellikle kümelenmiş) öngörülen sütunu arar. Yazma (T2), dizin B'yi (küme) değiştirir ve ardından dizin A'yı güncellemesi gerekir. T1, A'da S-Lck'e sahip, B'de S-Lck istiyor, T2'de X-Lck var, A'da U-Lck istiyor. , puf. T1 öldürüldü. Bu, yoğun OLTP trafiği ve biraz fazla indeks bulunan ortamlarda yaygındır :). Çözüm, ya okumanın A'dan B'ye atlaması gerekmemesi (yani, sütun A'ya dahil edilmesi ya da sütunu öngörülen listeden kaldırılması) ya da T2'nin B'den A'ya atlaması gerekmemesidir (indeksli sütunu güncelleme). Maalesef linq burada senin arkadaşın değil ...


BTW A ve B aynı tablonun indeksleridir
Remus Rusanu

3

@Jeff - Bu konuda kesinlikle bir uzman değilim, ancak neredeyse her aramada yeni bir bağlam oluşturarak iyi sonuçlar aldım. ADO ile her aramada yeni bir Bağlantı nesnesi oluşturmaya benzer olduğunu düşünüyorum. Bağlantı havuzlaması yine de kullanılacağından, ek yük düşündüğünüz kadar kötü değil.

Ben sadece bunun gibi global bir statik yardımcı kullanıyorum:

public static class AppData
{
    /// <summary>
    /// Gets a new database context
    /// </summary>
    public static CoreDataContext DB
    {
        get
        {
            var dataContext = new CoreDataContext
            {
                DeferredLoadingEnabled = true
            };
            return dataContext;
        }
    }
}

ve sonra şöyle bir şey yapıyorum:

var db = AppData.DB;

var results = from p in db.Posts where p.ID = id select p;

Güncellemeler için de aynısını yapardım. Her neyse, neredeyse senin kadar fazla trafiğim yok, ama önceden bir avuç kullanıcıyla paylaşılan bir DataContext kullandığımda kesinlikle biraz kilitleniyordum. Garanti yok, ama denemeye değer olabilir.

Güncelleme : Sonra tekrar, kodunuza baktığınızda, yalnızca belirli bir denetleyici örneğinin ömrü boyunca veri bağlamını paylaşıyorsunuz; bu, denetleyicideki çoklu çağrılarla aynı anda bir şekilde kullanılmadığı sürece temelde iyi görünüyor. ScottGu konuyla ilgili bir başlıkta şunları söyledi:

Denetleyiciler yalnızca tek bir istek için yaşarlar - bu nedenle, bir isteği işledikten sonra çöp toplanırlar (bu, DataContext'in toplandığı anlamına gelir) ...

Her neyse, bu olmayabilir, ancak yine de denemeye değer, belki de bazı yük testleri ile bağlantılı olarak.


3

S. Neden depoladığınız AnswerCountiçindePosts ilk etapta masaya?

Alternatif bir yaklaşım, Poststabloya "geri yazmayı" ortadan kaldırmak, bunu tabloda saklamak yerine AnswerCount, gerektiğinde postaya verilen cevapların sayısını dinamik olarak hesaplamaktır.

Evet, bu ek bir sorgu çalıştırdığınız anlamına gelir:

SELECT COUNT(*) FROM Answers WHERE post_id = @id

veya daha tipik olarak (bunu ana sayfa için görüntülüyorsanız):

SELECT p.post_id, 
     p.<additional post fields>,
     a.AnswerCount
FROM Posts p
    INNER JOIN AnswersCount_view a
    ON <join criteria>
WHERE <home page criteria>

ancak bu tipik INDEX SCANolarak kaynakların kullanımında kullanmaktan daha verimli olabilir ve sonuçlanır READ ISOLATION.

Bir kedinin derisini yüzmenin birden fazla yolu vardır. Bir veritabanı şemasının normalden erken kaldırılması, ölçeklenebilirlik sorunları ortaya çıkarabilir.


3

READ_COMMITTED_SNAPSHOT ayarının kesinlikle açık olmasını istiyorsunuz, bu varsayılan olarak değildir. Bu size MVCC semantiğini verir. Oracle'ın varsayılan olarak kullandığı şeyle aynı. Bir MVCC veritabanına sahip olmak o kadar inanılmaz derecede faydalıdır ki, birini KULLANMAK deliliktir. Bu, bir işlem içinde aşağıdakileri çalıştırmanıza olanak tanır:

KULLANICILARI Güncelle Set FirstName = 'foobar'; // bir yıl uyumaya karar ver.

bu arada yukarıdakileri taahhüt etmeden herkes bu tablodan gayet iyi bir şekilde seçim yapmaya devam edebilir. MVCC'ye aşina değilseniz, onsuz yaşayabildiğiniz için şok olacaksınız. Ciddi anlamda.


3

Varsayılanınızı taahhüt edilmemiş olarak okumak iyi bir fikir değildir. Hiç şüphesiz tutarsızlıklar ortaya çıkaracak ve şu anda sahip olduğunuzdan daha kötü bir problemle sonuçlanacaksınız. Anlık görüntü yalıtımı iyi çalışabilir, ancak Sql Server'ın çalışma biçiminde önemli bir değişiklik ve çok büyük yük bindiriyor.

Yapmanız gereken şudur: kilitlenme durumunu tespit etmek için try-catch (T-SQL'de) kullanın. Bu olduğunda, sorguyu yeniden çalıştırın. Bu, standart veritabanı programlama uygulamasıdır.

Paul Nielson'ın Sql Server 2005 İncilinde bu tekniğin iyi örnekleri vardır .

İşte kullandığım hızlı bir şablon:

-- Deadlock retry template

declare @lastError int;
declare @numErrors int;

set @numErrors = 0;

LockTimeoutRetry:

begin try;

-- The query goes here

return; -- this is the normal end of the procedure

end try begin catch
    set @lastError=@@error
    if @lastError = 1222 or @lastError = 1205 -- Lock timeout or deadlock
    begin;
        if @numErrors >= 3 -- We hit the retry limit
        begin;
            raiserror('Could not get a lock after 3 attempts', 16, 1);
            return -100;
        end;

        -- Wait and then try the transaction again
        waitfor delay '00:00:00.25';
        set @numErrors = @numErrors + 1;
        goto LockTimeoutRetry;

    end;

    -- Some other error occurred
    declare @errorMessage nvarchar(4000), @errorSeverity int
    select    @errorMessage = error_message(),
            @errorSeverity = error_severity()

    raiserror(@errorMessage, @errorSeverity, 1)

    return -100
end catch;    

2
bu çözüm neden beni utandırıyor? !! NEDEN bir çıkmaza bakacaktım .. sorun üzerinde gerçekten fakir bir adamın yara bandı değil.
Pure.Krome

2

Geçmişte benim için çalışan bir şey, tüm sorgularımın ve güncellemelerimin kaynaklara (tablolara) aynı sırayla erişmesini sağlamaktır.

Yani, Tablo1, Tablo2 sırasına göre bir sorgu güncellenir ve farklı bir sorgu onu Tablo2, Tablo1 sırasına göre güncellerse, kilitlenmeler görebilirsiniz.

LINQ kullandığınız için güncellemelerin sırasını değiştirmenin mümkün olup olmadığından emin değilim. Ama bakılması gereken bir şey.


1

Kullanıcı profilinizin birkaç saniye eski olması umrunda mı?

Birkaç saniye kesinlikle kabul edilebilir. Her halükarda, çok sayıda insan aynı anda cevaplar göndermediği sürece, o kadar uzun gibi görünmüyor.


1

Bu konuda Jeremy ile aynı fikirdeyim. Her denetleyici için veya sayfa başına yeni bir veri bağlamı oluşturmanız gerekip gerekmediğini sorarsınız - Her bağımsız sorgu için yeni bir tane oluşturma eğilimindeyim.

Şu anda sizin yaptığınız gibi statik bağlamı uygulamak için kullanılan bir çözüm geliştiriyorum ve stres testleri sırasında bir sunucunun canavarına (milyondan fazla) tonlarca istek attığımda, rastgele okuma / yazma kilitleri alıyordum.

Sorgu başına LINQ düzeyinde farklı bir veri bağlamı kullanmak için stratejimi değiştirdiğimde ve SQL sunucusunun bağlantı havuzu oluşturma sihrini çalıştırabileceğine güvendiğim anda, kilitler kayboldu.

Elbette biraz zaman baskısı altındaydım, bu yüzden aynı anda birçok şeyi deniyorum, bu yüzden% 100 emin olamıyorum, bunun düzelttiğinden emin değilim, ancak yüksek bir güven düzeyim var - hadi öyle koyalım .


1

Kirli okumalar yapmalısınız.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

Sorgularınız için kesinlikle mükemmel bir işlem bütünlüğüne ihtiyacınız yoksa, yüksek eşzamanlılık içeren tablolara erişirken kirli okumalar kullanmalısınız. Gönderiler tablonuzun bunlardan biri olacağını varsayıyorum.

Bu size sözde "hayali okumalar" verebilir, ki bu, sorgunuzun taahhüt edilmemiş bir işlemden gelen verilere göre hareket ettiği zamandır.

Burada bir bankacılık sitesi işletmiyoruz, her seferinde mükemmel doğruluğa ihtiyacımız yok

Kirli okumalar kullanın. Size mükemmel bir doğruluk sağlamayacakları konusunda haklısınız, ancak kilitlenme sorunlarını gidermeleri gerekiyor.

Bu olmadan, yaptığımız her LINQ çağrısını (bunların büyük çoğunluğu olan basit okuma çağrılarını) 3-4 satırlık bir işlem kodu bloğunda sarmamız gerekir ki bu çirkin

"Temel veritabanı bağlamında" kirli okumalar uygularsanız, işlem bütünlüğüne ihtiyacınız varsa, bireysel aramalarınızı her zaman daha yüksek bir yalıtım düzeyi kullanarak sarmalayabilirsiniz.


1

Peki, bir yeniden deneme mekanizmasının uygulanmasındaki sorun nedir? Her zaman bir kilitlenmenin meydana gelme olasılığı olacaktır, öyleyse neden onu tanımlayıp tekrar deneyecek bir mantığınız olmasın?

En azından diğer seçeneklerden bazıları, bir yeniden deneme sisteminin nadiren devreye gireceği durumlarda her zaman alınan performans cezalarını getirmeyecek mi?

Ayrıca, bir yeniden deneme gerçekleştiğinde, bu nadir görülen duruma girmemek için bir tür günlüğe kaydetmeyi de unutmayın.


1

Artık Jeremy'nin cevabını gördüğüme göre, en iyi uygulamanın her veri işlemi için yeni bir DataContext kullanmak olduğunu duyduğumu hatırlıyorum. Rob Conery, DataContext hakkında birkaç gönderi yazdı ve bir tekil kullanmak yerine bunları her zaman haber veriyor.

İşte Video.Show için kullandığımız model ( CodePlex'te kaynak görünümüne bağlantı ):

using System.Configuration;
namespace VideoShow.Data
{
  public class DataContextFactory
  {
    public static VideoShowDataContext DataContext()
    {
        return new VideoShowDataContext(ConfigurationManager.ConnectionStrings["VideoShowConnectionString"].ConnectionString);
    }
    public static VideoShowDataContext DataContext(string connectionString)
    {
        return new VideoShowDataContext(connectionString);
    }
  }
}

Ardından hizmet düzeyinde (veya güncellemeler için daha ayrıntılı olarak):

private VideoShowDataContext dataContext = DataContextFactory.DataContext();

public VideoSearchResult GetVideos(int pageSize, int pageNumber, string sortType)
{
  var videos =
  from video in DataContext.Videos
  where video.StatusId == (int)VideoServices.VideoStatus.Complete
  orderby video.DatePublished descending
  select video;
  return GetSearchResult(videos, pageSize, pageNumber);
}

0

İzolasyon seviyesini taahhüt edilmeyen okumaya ayarlamanın diğer sorgular üzerinde herhangi bir kötü etkisi olmadığı sürece Greg ile aynı fikirde olmalıyım.

Bunu veritabanı düzeyinde ayarlamanın aşağıdaki gibi bir sorguyu nasıl etkileyeceğini bilmek isterim Jeff:

Begin Tran
Insert into Table (Columns) Values (Values)
Select Max(ID) From Table
Commit Tran

0

Profilimin birkaç dakika geçmesine rağmen benim için sorun değil.

Başarısız olduktan sonra okumayı yeniden mi deniyorsunuz? Bir ton rastgele okuma ateşlerken, birkaçının okuyamadığı zaman vuracağı kesinlikle mümkündür. Çalıştığım uygulamaların çoğu, okuma sayısına kıyasla çok az yazıyor ve eminim okumalar, aldığınız sayıya yakın değildir.

"HABER VERMEYEN OKUYUN" uygulaması sorununuzu çözmüyorsa, işlem hakkında daha fazla bilgi sahibi olmadan yardım etmek zordur. Bu davranışa yardımcı olacak başka bir ayarlama seçeneği olabilir. Bazı MSSQL uzmanları kurtarmaya gelmedikçe, sorunu satıcıya göndermenizi öneririm.


0

Her şeyi akort etmeye devam edecektim; disk alt sistemi nasıl çalışıyor? Ortalama disk sırası uzunluğu nedir? G / Ç'ler yedekleniyorsa, asıl sorun kilitlenen bu iki sorgu olmayabilir, sistemi tıkayan başka bir sorgu olabilir; Ayarlanmış 20 saniye süren bir sorgudan bahsettiniz, başkaları var mı?

Uzun süren sorguları kısaltmaya odaklanın, bahse girerim kilitlenme sorunları ortadan kalkacaktır.


0

Aynı sorunu yaşadım ve TransactionScope'ta "IsolationLevel = IsolationLevel.ReadUncommitted" kullanılamıyor çünkü sunucuda DTS etkin değil (!).

Bir uzatma yöntemiyle yaptığım buydu:

public static void SetNoLock(this MyDataContext myDS)
{
    myDS.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
}

Bu nedenle, kritik eşzamanlılık tablolarını kullanan seçimler için "nolock" özelliğini şu şekilde etkinleştiririz:

using (MyDataContext myDS = new MyDataContext())
{
   myDS.SetNoLock();

   //  var query = from ...my dirty querys here...
}

Önerilere açığız!

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.