Widzimy pewne zgubne, ale rzadkie warunki zakleszczenia w bazie danych Stack Overflow SQL Server 2005.
Podłączyłem profiler, skonfigurowałem profil śledzenia, korzystając z tego doskonałego artykułu na temat rozwiązywania problemów z zakleszczeniami i uchwyciłem kilka przykładów. Dziwne jest to, że zapis zakleszczenia jest zawsze taki sam :
UPDATE [dbo].[Posts]
SET [AnswerCount] = @p1, [LastActivityDate] = @p2, [LastActivityUserId] = @p3
WHERE [Id] = @p0
Druga instrukcja zakleszczenia jest różna, ale zwykle jest to jakiś trywialny, prosty odczyt tabeli wpisów. Ten zawsze ginie w impasie. Oto przykład
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
Aby było jasne, nie widzimy zakleszczeń zapisu / zapisu, ale odczytu / zapisu.
W tej chwili mamy mieszankę zapytań LINQ i sparametryzowanych zapytań SQL. Dodaliśmy with (nolock)
do wszystkich zapytań SQL. To mogło niektórym pomóc. Mieliśmy również jedno (bardzo) słabo napisane zapytanie dotyczące znaczka, które wczoraj naprawiłem, które za każdym razem zajmowało 20 sekund, a do tego było uruchamiane co minutę. Miałem nadzieję, że to było przyczyną niektórych problemów z blokowaniem!
Niestety około 2 godziny temu wyskoczył mi kolejny błąd zakleszczenia. Te same dokładne objawy, napisz dokładnie ten sam winowajca.
Naprawdę dziwne jest to, że instrukcja blokująca write SQL, którą widzisz powyżej, jest częścią bardzo specyficznej ścieżki kodu. Jest wykonywany tylko po dodaniu nowej odpowiedzi do pytania - aktualizuje pytanie nadrzędne o nową liczbę odpowiedzi i ostatnią datę / użytkownika. Nie jest to oczywiście aż tak powszechne w porównaniu z ogromną liczbą odczytów, które wykonujemy! O ile wiem, nigdzie w aplikacji nie wykonujemy ogromnej liczby zapisów.
Zdaję sobie sprawę, że NOLOCK to coś w rodzaju gigantycznego młotka, ale większość zapytań, które tu wykonujemy, nie musi być tak dokładna. Czy będzie Cię obchodzić, jeśli Twój profil użytkownika jest nieaktualny o kilka sekund?
Używanie NOLOCK z Linq jest nieco trudniejsze, o czym mówi tutaj Scott Hanselman .
Flirtujemy z pomysłem użycia
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
w podstawowym kontekście bazy danych, tak aby wszystkie nasze zapytania LINQ miały ten zestaw. Bez tego musielibyśmy zawijać każde wywołanie LINQ, które wykonujemy (cóż, proste odczyty, które stanowią zdecydowaną większość) w 3-4-wierszowym bloku kodu transakcji, co jest brzydkie.
Myślę, że jestem trochę sfrustrowany, że trywialne odczyty w SQL 2005 mogą blokować zapisy. Widziałem, że zakleszczenia zapisu / zapisu są dużym problemem, ale czyta? Nie prowadzimy tutaj witryny bankowej, nie potrzebujemy za każdym razem doskonałej dokładności.
Pomysły? Myśli?
Czy tworzysz wystąpienie nowego obiektu LINQ to SQL DataContext dla każdej operacji, czy może udostępniasz ten sam kontekst statyczny dla wszystkich wywołań?
Jeremy, w większości udostępniamy jeden statyczny kontekst danych w podstawowym kontrolerze:
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;
}
}
Czy zalecamy, abyśmy tworzyli nowy kontekst dla każdego kontrolera, dla każdej strony, czy ... częściej?