Diagnóstico de interbloqueos en SQL Server 2005

82

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?

Jeff Atwood
fuente
2
¿Qué <a href=" en.wikipedia.org/wiki/… modo</a> estás usando, "pesimista" (basado en bloqueo) u "optimista" (<a href = " en.wikipedia.org/wiki/ … )?
John Siracusa
Estoy de acuerdo con la respuesta de Guy anterior : en lugar de tratar de solucionar el síntoma, ¿por qué no abordar las causas subyacentes? Al agregar el total acumulado de AnswerCount a la tabla Publicaciones, ha creado un recurso de bloqueo potencial. ¿Le gustaría a Jeff publicar su ERD para StackOverflow para que la gente pueda criticar?
andyp
2
Vaya, escuché sobre esto en tu podcast con Scott. No puedo creer que tampoco se envíe de fábrica con una mejor configuración. Mostraré esto a nuestros DBA (porque también usan 'nolock' ampliamente)
Dan Esparza
3
Consulte: samsaffron.com/archive/2008/08/27/Deadlocked+ por la razón por la que se bloqueaba . Activar el aislamiento de instantáneas es una buena solución a este problema.
Sam Saffron

Respuestas:

44

Según MSDN:

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

Cuando las opciones de base de datos READ COMMITTED SNAPSHOT o ALLOW SNAPSHOT ISOLATION están activadas, se mantienen copias lógicas (versiones) para todas las modificaciones de datos realizadas en la base de datos. Cada vez que una fila es modificada por una transacción específica, la instancia de Motor de base de datos almacena una versión de la imagen previamente confirmada de la fila en tempdb. Cada versión está marcada con el número de secuencia de la transacción que realizó el cambio. Las versiones de las filas modificadas se encadenan mediante una lista de enlaces. El valor de fila más reciente siempre se almacena en la base de datos actual y se encadena a las filas versionadas almacenadas en tempdb.

Para transacciones de ejecución corta, una versión de una fila modificada puede almacenarse en caché en el grupo de búfer sin que se escriba en los archivos de disco de la base de datos tempdb. Si la necesidad de la fila versionada es de corta duración, simplemente se eliminará del grupo de búfer y no necesariamente incurrirá en gastos generales de E / S.

Parece haber una leve penalización en el rendimiento por los gastos generales adicionales, pero puede ser insignificante. Deberíamos probar para asegurarnos.

Intente configurar esta opción y QUITAR todos los NOLOCK de las consultas de código a menos que sea realmente necesario. Los NOLOCK o el uso de métodos globales en el controlador de contexto de la base de datos para combatir los niveles de aislamiento de transacciones de la base de datos son una ayuda para resolver el problema. NOLOCKS enmascarará problemas fundamentales con nuestra capa de datos y posiblemente conducirá a la selección de datos no confiables, donde la selección / actualización automática de versiones de filas parece ser la solución.

ALTER Database [StackOverflow.Beta] SET READ_COMMITTED_SNAPSHOT ON
Geoff Dalgas
fuente
3
"NOLOCKS enmascarará problemas fundamentales con nuestra capa de datos" ... ¿Qué tipo de problemas enmascara NOLOCK? Si creo que necesito NOLOCK, ¿qué problemas debo buscar?
Matt Hamilton
3
¿Qué pasa con esta respuesta que la convierte en "la respuesta"? Todavía no entiendo por qué la lectura de milisegundos interrumpiría la escritura de inmediato. Supongo que la respuesta por "user13484" es una lástima que no haya ninguna referencia a ella si ese es el caso.
RichardTheKiwi
37

NOLOCK y READ UNCOMMITTED son una pendiente resbaladiza. Nunca debe usarlos a menos que comprenda primero por qué ocurre el interbloqueo. Me preocuparía que dijera: "Hemos agregado con (nolock) a todas las consultas SQL". Necesitar agregar WITH NOLOCK en todas partes es una señal segura de que tiene problemas en su capa de datos.

La declaración de actualización en sí parece un poco problemática. ¿Determina el recuento antes en la transacción o simplemente lo extrae de un objeto? AnswerCount = AnswerCount+1cuando se agrega una pregunta es probablemente una mejor manera de manejar esto. Entonces no necesita una transacción para obtener el recuento correcto y no tiene que preocuparse por el problema de concurrencia al que se está exponiendo potencialmente.

Una manera fácil de solucionar este tipo de problema de interbloqueo sin mucho trabajo y sin habilitar lecturas sucias es usar "Snapshot Isolation Mode"(nuevo en SQL 2005) que siempre le dará una lectura limpia de los últimos datos no modificados. También puede detectar y reintentar declaraciones interbloqueadas con bastante facilidad si desea manejarlas con elegancia.

JEzell
fuente
4
Estoy con JEzell: lo primero que me concentré es 'SET AnswerCount = <valor fijo>'. ¿De dónde viene ese valor? Eso me hace preguntarme si en otra parte de la transacción lo ha recuperado de una manera que está agarrando un montón de bloqueos. Empezaría con eso. Y sí, global NOLOCK es una curita.
Cowan
25

La pregunta de OP era preguntar por qué ocurrió este problema. Esta publicación espera responder a eso mientras deja posibles soluciones para que otros las resuelvan.

Probablemente se trate de un problema relacionado con el índice. Por ejemplo, digamos que la tabla Posts tiene un índice X no agrupado que contiene el ParentID y uno (o más) de los campos que se actualizan (AnswerCount, LastActivityDate, LastActivityUserId).

Se produciría un interbloqueo si el cmd SELECT realiza un bloqueo de lectura compartida en el índice X para buscar por el ParentId y luego necesita hacer un bloqueo de lectura compartida en el índice agrupado para obtener las columnas restantes mientras que el cmd UPDATE realiza una escritura exclusiva bloquear el índice agrupado y es necesario obtener un bloqueo exclusivo de escritura en el índice X para actualizarlo.

Ahora tiene una situación en la que A bloqueó X y está tratando de obtener Y mientras que B bloqueó Y y está tratando de obtener X.

Por supuesto, necesitaremos que el OP actualice su publicación con más información sobre qué índices están en juego para confirmar si esta es realmente la causa.

MrB
fuente
Estoy de acuerdo con este análisis: SELECT y UPDATE están procesando las filas en un orden diferente, por lo que cada uno está tratando de obtener un bloqueo de fila que el otro tiene.
Mike Dimmick
Esta es la mejor respuesta a todo este hilo y es la única explicación de por qué ocurre realmente el interbloqueo. Lástima que no sea la respuesta número uno porque es la mejor aquí.
Jonathan Kehayias
Algunas respuestas pierden el punto sobre el bloqueo entre 2 declaraciones simples atómicas. Esta es la única publicación que intenta explicarlo. Si bien la declaración es simple, una actualización de tabla puede involucrar múltiples actualizaciones de CIX y NCIX, que se expanden a muchas operaciones. Lo mismo para READ que involucra el recorrido de NCIX, la búsqueda de marcadores CIX. Esto no tiene nada que ver con unir tablas en el mismo orden, etc. (¿la gente lee las preguntas?)
RichardTheKiwi
18

Estoy bastante incómodo con esta pregunta y las respuestas que la acompañan. Hay mucho "¡prueba este polvo mágico! ¡No ese polvo mágico!"

No veo en ninguna parte que haya analizado los bloqueos que se toman y determinado qué tipo exacto de bloqueos están bloqueados.

Todo lo que ha indicado es que se producen algunos bloqueos, no lo que es interbloqueo.

En SQL 2005, puede obtener más información sobre qué bloqueos se están quitando usando:

DBCC TRACEON (1222, -1)

de modo que cuando se produzca el interbloqueo tendrá mejores diagnósticos.

Leon Bambrick
fuente
13
Un interbloqueo es manejado inmediatamente por el monitor de interbloqueo en SQL Server. Los DMV son inútiles para solucionar problemas de un punto muerto porque la víctima será seleccionada y asesinada antes de que usted pueda detectar que ocurre.
Jonathan Kehayias
14

¿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? Originalmente probé el último enfoque y, por lo que recuerdo, causó un bloqueo no deseado en la base de datos. Ahora creo un nuevo contexto para cada operación atómica.

jeremcc
fuente
10

Antes de quemar la casa para atrapar una mosca con NOLOCK por todas partes, es posible que desee echar un vistazo a ese gráfico de punto muerto que debería haber capturado con Profiler.

Recuerde que un interbloqueo requiere (al menos) 2 bloqueos. La conexión 1 tiene la cerradura A, quiere la cerradura B, y viceversa para la conexión 2. Esta es una situación que no se puede resolver y alguien tiene que ceder.

Lo que ha mostrado hasta ahora se resuelve con un bloqueo simple, que Sql Server está feliz de hacer durante todo el día.

Sospecho que usted (o LINQ) está iniciando una transacción con esa declaración UPDATE en ella y SELECCIONANDO alguna otra información de antemano. Pero, realmente necesita retroceder a través del gráfico de interbloqueo para encontrar los bloqueos retenidos por cada hilo, y luego retroceder a través de Profiler para encontrar las declaraciones que causaron que se concedieran esos bloqueos.

Espero que haya al menos 4 declaraciones para completar este acertijo (o una declaración que tenga múltiples bloqueos, ¿quizás haya un disparador en la tabla de Publicaciones?).

Mark Brackett
fuente
7

¿Le importará si su perfil de usuario está desactualizado unos segundos?

No, eso es perfectamente aceptable. Establecer el nivel de aislamiento de transacciones base es probablemente la mejor y más limpia forma de hacerlo.

Greg Hurlman
fuente
5

El bloqueo típico de lectura / escritura proviene del acceso a la orden de índice. Leer (T1) ubica la fila en el índice A y luego busca la columna proyectada en el índice B (generalmente agrupada). Escribir (T2) cambia el índice B (el clúster) luego tiene que actualizar el índice A. T1 tiene S-Lck en A, quiere S-Lck en B, T2 tiene X-Lck en B, quiere U-Lck en A. Interbloqueo , soplo. T1 muere. Esto es frecuente en entornos con mucho tráfico OLTP y solo un poco demasiados índices :). La solución es hacer que la lectura no tenga que saltar de A a B (es decir, la columna incluida en A o eliminar la columna de la lista proyectada) o que T2 no tenga que saltar de B a A (no actualice la columna indexada). Desafortunadamente, linq no es tu amigo aquí ...

Remus Rusanu
fuente
Por cierto, A y B son índices de la misma tabla
Remus Rusanu
3

@Jeff - Definitivamente no soy un experto en esto, pero he tenido buenos resultados al crear instancias de un nuevo contexto en casi todas las llamadas. Creo que es similar a crear un nuevo objeto Connection en cada llamada con ADO. La sobrecarga no es tan mala como podría pensar, ya que la agrupación de conexiones se seguirá utilizando de todos modos.

Solo uso un ayudante estático global como este:

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

y luego hago algo como esto:

var db = AppData.DB;

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

Y haría lo mismo con las actualizaciones. De todos modos, no tengo tanto tráfico como tú, pero definitivamente me estaba bloqueando cuando usé un DataContext compartido desde el principio con solo un puñado de usuarios. No hay garantías, pero podría valer la pena intentarlo.

Actualizar : Por otra parte, al mirar su código, solo está compartiendo el contexto de datos durante la vida útil de esa instancia de controlador en particular, lo que básicamente parece estar bien a menos que de alguna manera se esté usando simultáneamente por múltiples llamadas dentro del controlador. En un hilo sobre el tema, ScottGu dijo:

Los controladores solo viven para una sola solicitud, por lo que al final de procesar una solicitud, se recolectan basura (lo que significa que se recopila el DataContext) ...

De todos modos, puede que no sea así, pero de nuevo probablemente valga la pena intentarlo, quizás junto con algunas pruebas de carga.

jeremcc
fuente
3

P. ¿Por qué está almacenando AnswerCountelPosts mesa en primer lugar?

Un enfoque alternativo es eliminar la "escritura" en la Poststabla al no almacenar elAnswerCount en la tabla, sino calculando dinámicamente la cantidad de respuestas a la publicación según sea necesario.

Sí, esto significará que está ejecutando una consulta adicional:

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

o más típicamente (si está mostrando esto para la página de inicio):

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>

pero esto generalmente da como resultado un INDEX SCANy puede ser más eficiente en el uso de recursos que en el uso READ ISOLATION.

Hay más de una forma de despellejar a un gato. La desnormalización prematura de un esquema de base de datos puede presentar problemas de escalabilidad.

Chico
fuente
3

Definitivamente desea que READ_COMMITTED_SNAPSHOT esté activado, lo cual no es predeterminado. Eso le da semántica MVCC. Es lo mismo que usa Oracle de forma predeterminada. Tener una base de datos MVCC es increíblemente útil, NO usar una es una locura. Esto le permite ejecutar lo siguiente dentro de una transacción:

Actualizar USUARIOS Set FirstName = 'foobar'; // decide dormir durante un año.

mientras tanto, sin cometer lo anterior, todos pueden continuar seleccionando de esa tabla sin problemas. Si no está familiarizado con MVCC, se sorprenderá de haber podido vivir sin él. Seriamente.

Aquino
fuente
3

Establecer su valor predeterminado para leer sin confirmar no es una buena idea. Sin duda, introducirás inconsistencias y acabarás con un problema peor que el que tienes ahora. El aislamiento de instantáneas puede funcionar bien, pero es un cambio drástico en la forma en que funciona Sql Server y supone un gran carga para tempdb.

Esto es lo que debe hacer: use try-catch (en T-SQL) para detectar la condición de interbloqueo. Cuando suceda, simplemente vuelva a ejecutar la consulta. Esta es una práctica estándar de programación de bases de datos.

Hay buenos ejemplos de esta técnica en la Biblia Sql Server 2005 de Paul Nielson. .

Aquí hay una plantilla rápida que utilizo:

-- 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;    
Eric Z Barba
fuente
2
¡¿Por qué esta solución me da vergüenza? !! Estaría mirando POR QUÉ hay un punto muerto ... no una curita de un pobre hombre sobre el problema.
Pure.Krome
2

Una cosa que me ha funcionado en el pasado es asegurarme de que todas mis consultas y actualizaciones accedan a los recursos (tablas) en el mismo orden.

Es decir, si una consulta se actualiza en el orden Tabla1, Tabla2 y una consulta diferente la actualiza en el orden de Tabla2, Tabla1, es posible que vea puntos muertos.

No estoy seguro de si es posible cambiar el orden de las actualizaciones ya que está utilizando LINQ. Pero es algo para mirar.

Michael Sharek
fuente
1

¿Le importará si su perfil de usuario está desactualizado unos segundos?

Definitivamente unos segundos serían aceptables. De todos modos, no parece que sea tan largo, a menos que una gran cantidad de personas estén enviando respuestas al mismo tiempo.

un_hardin
fuente
1

Estoy de acuerdo con Jeremy en este. Usted pregunta si debe crear un nuevo contexto de datos para cada controlador o por página; tiendo a crear uno nuevo para cada consulta independiente.

Estoy construyendo una solución en este momento que solía implementar el contexto estático como lo hace usted, y cuando lancé toneladas de solicitudes a la bestia de un servidor (más de un millón) durante las pruebas de estrés, también recibía bloqueos de lectura / escritura al azar.

Tan pronto como cambié mi estrategia para usar un contexto de datos diferente en el nivel LINQ por consulta, y confié en que el servidor SQL podría hacer su magia de agrupación de conexiones, los bloqueos parecieron desaparecer.

Por supuesto, estaba bajo cierta presión de tiempo, así que intenté varias cosas al mismo tiempo, así que no puedo estar 100% seguro de que eso fue lo que lo solucionó, pero tengo un alto nivel de confianza, digámoslo de esa manera. .

RobertTheGrey
fuente
1

Debe implementar lecturas sucias.

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

Si no necesita absolutamente una integridad transaccional perfecta con sus consultas, debe usar lecturas sucias al acceder a tablas con alta concurrencia. Supongo que su tabla de Publicaciones sería una de esas.

Esto puede darle las llamadas "lecturas fantasmas", que es cuando su consulta actúa sobre los datos de una transacción que no se ha confirmado.

No estamos ejecutando un sitio bancario aquí, no necesitamos una precisión perfecta siempre

Usa lecturas sucias. Tiene razón en que no le darán una precisión perfecta, pero deberían aclarar sus problemas de bloqueo muerto.

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

Si implementa lecturas sucias en "el contexto de la base de datos base", siempre puede envolver sus llamadas individuales usando un nivel de aislamiento más alto si necesita la integridad transaccional.

Seibar
fuente
1

Entonces, ¿cuál es el problema con la implementación de un mecanismo de reintento? Siempre existirá la posibilidad de que se produzca un punto muerto, así que ¿por qué no tener algo de lógica para identificarlo e intentarlo de nuevo?

¿No introducirán al menos algunas de las otras opciones penalizaciones de rendimiento que se aplican todo el tiempo cuando un sistema de reintento se activa raramente?

Además, no olvide algún tipo de registro cuando se produzca un reintento para no entrar en esa situación de rareza frecuente.

John Dyer
fuente
1

Ahora que veo la respuesta de Jeremy, creo que recuerdo haber escuchado que la mejor práctica es usar un nuevo DataContext para cada operación de datos. Rob Conery ha escrito varias publicaciones sobre DataContext, y siempre las informa en lugar de usar un singleton.

Este es el patrón que usamos para Video.Show ( enlace a la vista de fuente en CodePlex ):

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);
    }
  }
}

Luego, en el nivel de servicio (o incluso más granular, para actualizaciones):

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);
}
Jon Galloway
fuente
0

Tendría que estar de acuerdo con Greg siempre que establecer el nivel de aislamiento para leer no confirmado no tenga efectos nocivos en otras consultas.

Me interesaría saber, Jeff, cómo la configuración en el nivel de la base de datos afectaría una consulta como la siguiente:

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

Me parece bien si mi perfil está desactualizado incluso varios minutos.

¿Está volviendo a intentar la lectura después de que falla? Ciertamente es posible cuando se disparan una tonelada de lecturas aleatorias que algunas acertarán cuando no puedan leer. La mayoría de las aplicaciones con las que trabajo son muy pocas escrituras en comparación con la cantidad de lecturas y estoy seguro de que las lecturas no se acercan al número que está obteniendo.

Si implementar "READ UNCOMMITTED" no resuelve su problema, entonces es difícil ayudar sin saber mucho más sobre el procesamiento. Puede haber alguna otra opción de ajuste que ayude a este comportamiento. A menos que algún gurú de MSSQL venga al rescate, recomiendo enviar el problema al proveedor.

bruceatk
fuente
0

Seguiría afinando todo; ¿Cómo está funcionando el subsistema de disco? ¿Cuál es la longitud promedio de la cola de disco? Si las E / S están realizando copias de seguridad, es posible que el problema real no sean estas dos consultas que se bloquean, podría ser otra consulta la que está bloqueando el sistema; mencionaste una consulta que tarda 20 segundos que ha sido ajustada, ¿hay otras?

Concéntrese en acortar las consultas de larga duración, apuesto a que los problemas de interbloqueo desaparecerán.

SqlACID
fuente
0

Tuve el mismo problema y no puedo usar "IsolationLevel = IsolationLevel.ReadUncommitted" en TransactionScope porque el servidor no tiene DTS habilitado (!).

Eso es lo que hice con un método de extensión:

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

Entonces, para selecciones que usan tablas de concurrencia críticas, habilitamos el "nolock" de esta manera:

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

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

¡Las sugerencias son bienvenidas!


fuente