Estamos viendo algunas condiciones de interbloqueo perniciosas, pero raras, en la base de datos Stack Overflow SQL Server 2005.
Adjunté el generador de perfiles, configuré un perfil de seguimiento utilizando este excelente artículo sobre solución de problemas de interbloqueos y capturé un montón de ejemplos. Lo extraño es que la escritura de interbloqueo es siempre la misma :
UPDATE [dbo].[Posts]
SET [AnswerCount] = @p1, [LastActivityDate] = @p2, [LastActivityUserId] = @p3
WHERE [Id] = @p0
La otra declaración de interbloqueo varía, pero generalmente es una especie de lectura simple y trivial de la tabla de publicaciones. Este siempre muere en el punto muerto. Aquí hay un ejemplo
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
Para ser perfectamente claros, no estamos viendo puntos muertos de escritura / escritura, sino de lectura / escritura.
Tenemos una mezcla de consultas LINQ y SQL parametrizadas en este momento. Hemos agregado with (nolock)
a todas las consultas SQL. Esto puede haber ayudado a algunos. También tuvimos una sola consulta de insignia (muy) mal escrita que arreglé ayer, que tardaba más de 20 segundos en ejecutarse cada vez, y además se ejecutaba cada minuto. ¡Esperaba que esta fuera la fuente de algunos de los problemas de bloqueo!
Desafortunadamente, recibí otro error de interbloqueo hace aproximadamente 2 horas. Los mismos síntomas exactos, el mismo culpable exacto escribe.
Lo realmente extraño es que la declaración SQL de escritura de bloqueo que ve arriba es parte de una ruta de código muy específica. Es solamente ejecuta cuando se añade una nueva respuesta a una pregunta - se actualiza la pregunta madre con el nuevo recuento de respuesta y última fecha / usuario. Esto, obviamente, no es tan común en relación con la gran cantidad de lecturas que estamos haciendo. Por lo que puedo decir, no estamos haciendo una gran cantidad de escrituras en ninguna parte de la aplicación.
Me doy cuenta de que NOLOCK es una especie de martillo gigante, pero la mayoría de las consultas que ejecutamos aquí no necesitan ser tan precisas. ¿Le importará si su perfil de usuario está desactualizado unos segundos?
Usar NOLOCK con Linq es un poco más difícil, como comenta Scott Hanselman aquí .
Estamos coqueteando con la idea de usar
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
en el contexto de la base de datos base para que todas nuestras consultas LINQ tengan este conjunto. Sin eso, tendríamos que envolver cada llamada LINQ que hacemos (bueno, las de lectura simple, que es la gran mayoría de ellas) en un bloque de código de transacción de 3-4 líneas, lo cual es feo.
Supongo que estoy un poco frustrado de que las lecturas triviales en SQL 2005 puedan bloquearse en las escrituras. Pude ver que los puntos muertos de escritura / escritura son un gran problema, pero ¿lee? No estamos ejecutando un sitio bancario aquí, no necesitamos una precisión perfecta siempre.
Ideas? Pensamientos?
¿Está creando una instancia de un nuevo objeto LINQ to SQL DataContext para cada operación o quizás está compartiendo el mismo contexto estático para todas sus llamadas?
Jeremy, estamos compartiendo un contexto de datos estáticos en el controlador base en su mayor parte:
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;
}
}
¿Recomiendas que creemos un nuevo contexto para cada controlador, o por página, o ... más a menudo?