¿Qué son los "tablelocks"?
Tablelocks = bloqueos de tabla...
...no son nada malo para empezar. Todas las bases de datos las utilizan para garantizar la integridad de los datos.
Un ejemplo: El usuario A tiene un registro maestro de cliente en la pantalla y ahora cambia la calle. Este proceso se guarda ahora en la base de datos para que la nueva calle quede guardada de forma permanente. El usuario B está editando el mismo registro de datos y quiere cambiar el número de teléfono. En realidad, ambas acciones pueden realizarse independientemente la una de la otra.
Sin embargo, como el cambio realizado por el usuario A siempre vuelve a escribir todo el registro de datos y el cambio de número de teléfono realizado por el usuario B también vuelve a escribir todo el registro del cliente (incl. nombre, dirección, calle, nuevo número de teléfono, límite de crédito, etc.), en este caso puede ocurrir lo siguiente: El usuario A y el usuario B reciben en pantalla el mismo registro con la antigua dirección y el antiguo número de teléfono.
El usuario A ahora sólo cambia la dirección (el número de teléfono no) y guarda este registro. El usuario B no se ha dado cuenta. Sigue teniendo el registro de datos sin modificación con la antigua dirección y ahora cambia el número de teléfono. Ahora también vuelve a guardar su registro.
¿Cuál es la consecuencia? Su cambio transfiere ahora el nuevo número de teléfono.... junto con la antigua dirección a la base de datos. La corrección de A se pierde.
¿Cómo se puede evitar una situación tan cotidiana? Usted trabaja con bloqueos de tablas: El usuario A recibe el registro de datos en la pantalla. En ese momento, Navision o Business Central bloquea el registro de datos para modificaciones. Está "bloqueado". No debe confundirse con un registro de acceso, pues entonces se "registra" (del término "libro deregistro").
Ahora, el usuario B no puede visualizar en absoluto este registro de datos. Sólo cuando A termina, por ejemplo, de ver o cambiar la dirección de la calle, B recibe el registro en la pantalla, que ahora se ha liberado de nuevo. Ahora el juego vuelve a empezar.
¿Qué causa los bloqueos de tablas?
Afortunadamente, Business Central y Navision son mucho más inteligentes que muchos sistemas de bases de datos, que en realidad hacen esto como se ha descrito anteriormente. Navision utiliza la concurrencia optimista: en la gran mayoría de los casos, un registro de datos sólo se visualiza y no se modifica.
¿Y si se cambia una dirección? Estadísticamente, es muy poco probable que el mismo registro de datos ¿Y si se cambia una dirección? Estadísticamente, es muy poco probable que el mismo registro de datos se modifique al mismo tiempo en otro ordenador. Por este motivo, Navision no borra este registro de datos en ningún caso, sino que utiliza la marca de tiempo para comprobar si el registro de datos de la base de datos sigue siendo la misma versión que el registro de datos que se acaba de guardar para modificarlo.
Esto se hace a través del campo invisible Timestamp en cada registro de una base de datos Business Central & Navision. Por lo tanto, no puede crear usted mismo un campo llamado TimeStamp. "En el pasado" esto todavía era posible, un error "canoso" muy popular al transferir una base de datos nativa a SQL.
Entonces, si Navision no hace bloqueos, ¿cuál es el problema? El ejemplo anterior es uno de los cientos en los que los desarrolladores de Navision han aliviado al usuario de ese trabajo. Pero también hay casos reales en los que la base de datos debe estar segura de que se encuentra en un estado coherente.
Escribir duele...
Un nuevo ejemplo: un usuario reserva un pedido. Esto puede tardar 1-2 segundos. Durante este tiempo, se escriben partidas de mayor, se contabilizan albaranes y facturas, se abren partidas de cliente...
Al mismo tiempo, otro usuario podría querer borrar este pedido o incluso este cliente. Si esto se permitiera, acabarían existiendo albaranes, partidas abiertas y facturas para un cliente que ya no existía en ese momento. Dependiendo del cambio de hora, ¡ni siquiera habría habido artículos en el cliente que hubieran prohibido la eliminación!
Business Central y Navision se esfuerzan mucho más por evitar esto, pero es suficiente para nuestro ejemplo.
Navision bloquea ("lock") el pedido y el cliente al contabilizar para evitar exactamente esta situación. Esto se hace de otra manera realmente, pero eso no es tan importante aquí.
Así que ahora hay algunas tablas que están bloqueadas ("abiertas a escritura") durante un breve periodo de tiempo: Pedidos, líneas de pedido, cabeceras de albarán reservado, líneas de albarán reservado... y algunas más.
Si no hay mucha gente trabajando en el sistema, esto no supone ningún problema: al fin y al cabo, estas tablas vuelven a estar libres uno o dos segundos después. (¿No es su caso? ¿Tardas muchos segundos o incluso minutos en publicar un pedido? ¡Eso sí que es malo! Esto sí que puede acelerarse.)
Entonces, ¿aún no hay problema? Exacto. A menos que... esas transacciones tarden demasiado, o bloqueen tablas en secuencias diferentes. Esto es exactamente por lo que ocurren los bloqueos.
Deadlocks... la vista de las puertas del infierno
Deadlocks: Supongamos que un programa quiere bloquear deudores para introducir después pedidos para ellos. Primero BUSINESS CENTRAL o NAVISION bloquean los clientes, después... hay un momento de retraso. Durante este tiempo, se inicia otro programa, que bloquea los pedidos y, si es necesario, realiza cambios de datos en el cliente.
Ahora, el primer programa también quiere bloquear los pedidos de sus clientes. Esto no es posible porque el segundo programa ya ha bloqueado los pedidos para sí mismo. Así que el primer programa simplemente espera a poder bloquear también los pedidos. Realmente espera. El segundo programa llega al punto en el que quiere cambiar los clientes, es decir, bloquearlos también. Solicita el bloqueo de clientes a la base de datos... ¡pero ya están bloqueados!
Ahora se produce un deadlock: ambos programas esperan la otra tabla bloqueada por el otro programa. El bloqueo es, en principio, infinito, ya que ambos programas esperan a que se libere la otra tabla del primer programa. Esta situación se conoce como "punto muerto": nunca se libera por sí solo.
El servidor SQL lo reconoce y termina una de las dos consultas para que la otra pueda seguir funcionando.
Regla de oro: Cuanto peor es el código del programa, más largo es el procesamiento del programa y más probables son los bloqueos. Simplemente porque los bloqueos de escritura están activos durante demasiado tiempo. Por lo tanto, la optimización del rendimiento también consiste en evitar los bloqueos.
Encontrar tablelocks/deadlocks
En Navision o Business Central, un bloqueo resuelto sólo se muestra con un simple mensaje de error:
"No se ha podido completar el proceso porque un registro de datos de la tabla "xxx" estaba bloqueado por otro usuario". Vuelva a ejecutar la acción. Este mensaje aparece siempre en el ordenador que ha perdido el deadlock.
Ocasionalmente, sin embargo, los bloqueos se distribuyen entre varios programas, de modo que ni siquiera el propio servidor (SQL) puede determinar que se trata de un bloqueo. Aquí es donde entra en juego, por ejemplo, el tiempo máximo de ejecución de SQL.
Hasta Navision 2009R2, bastaba con mirar en la tabla de sesiones para ver qué proceso estaba bloqueando al otro. Con un rápido vistazo a la pantalla derecha, se podía localizar rápidamente el problema... o iniciar una búsqueda exhaustiva.
Sin embargo, desde Navision & Business Central 2013 RTC, la política de Microsoft de hacerlo todo más complicado y más difícil siempre que sea posible ha surtido efecto. Aquí hay que desahogarse directamente en el servidor SQL. He aquí algunos métodos de análisis:
Siempre se inicia en el servidor SQL en la base de datos actual con una consulta
Auflisten von blockierenden Usern
exec sp_who2
SQL enumera todas las sesiones. Una sesión bloqueada muestra en la columna "Blkby" si/por qué sesión está bloqueada.
Por regla general, encontrará el mismo identificador de sesión en varias sesiones que esperan la misma sesión de bloqueo.
Si lo desea, puede ir un nivel más allá con los objetos bloqueados. La lista resultante es mucho más clara, pero quizás no suficiente. Especialmente si ha tenido mucho cuidado de escribir sólo en tablas temporales antes de un Page.runmodal, y un desagradable mensaje de error sigue indicando una tabla bloqueada, le costará averiguar qué tabla(s) está(n) bloqueada(s)... Esto a menudo te ayuda a ponerte en el camino correcto.
Microsoft Dynamics 365 Business Central
Contenido del mensaje de error: Las funciones C/AL enumeradas a continuación están restringidas durante las transacciones de escritura porque al menos una tabla está bloqueada. Form.RunModal no está permitida en las transacciones de escritura. Codeunit.Run sólo se permite en transacciones de escritura si no se utiliza el valor de retorno. OK := Codeunit.Run() no está permitido, por ejemplo. Report.RunModal sólo se permite en transacciones de escritura si RequestForm = FALSE. Report.RunModal(...,FALSE) está permitido, por ejemplo. XmlPort.RunModal sólo se permite en transacciones de escritura si RequestForm = FALSE. XmlPort.RunModal(...,FALSE) está permitido, por ejemplo. Utilice la función COMMIT para guardar los cambios antes de la llamada, o estructure el código de otra manera.
-- List all Locks of the Current Database Source: https://stackoverflow.com/questions/694581/how-to-check-which-locks-are-held-on-a-table
SELECT TL.resource_type AS ResType
,TL.resource_description AS ResDescr
,TL.request_mode AS ReqMode
,TL.request_type AS ReqType
,TL.request_status AS ReqStatus
,TL.request_owner_type AS ReqOwnerType
,TAT.[name] AS TransName
,TAT.transaction_begin_time AS TransBegin
,DATEDIFF(ss, TAT.transaction_begin_time, GETDATE()) AS TransDura
,ES.session_id AS S_Id
,ES.login_name AS LoginName
,COALESCE(OBJ.name, PAROBJ.name) AS ObjectName
,PARIDX.name AS IndexName
,ES.host_name AS HostName
,ES.program_name AS ProgramName
FROM sys.dm_tran_locks AS TL
INNER JOIN sys.dm_exec_sessions AS ES
ON TL.request_session_id = ES.session_id
LEFT JOIN sys.dm_tran_active_transactions AS TAT
ON TL.request_owner_id = TAT.transaction_id
AND TL.request_owner_type = 'TRANSACTION'
LEFT JOIN sys.objects AS OBJ
ON TL.resource_associated_entity_id = OBJ.object_id
AND TL.resource_type = 'OBJECT'
LEFT JOIN sys.partitions AS PAR
ON TL.resource_associated_entity_id = PAR.hobt_id
AND TL.resource_type IN ('PAGE', 'KEY', 'RID', 'HOBT')
LEFT JOIN sys.objects AS PAROBJ
ON PAR.object_id = PAROBJ.object_id
LEFT JOIN sys.indexes AS PARIDX
ON PAR.object_id = PARIDX.object_id
AND PAR.index_id = PARIDX.index_id
WHERE TL.resource_database_id = DB_ID()
AND ES.session_id <> @@Spid -- Exclude "my" session
-- optional filter
AND TL.request_mode <> 'S' -- Exclude simple shared locks
ORDER BY TL.resource_type
,TL.request_mode
,TL.request_type
,TL.request_status
,ObjectName
,ES.login_name;
--TSQL commands
SELECT
db_name(rsc_dbid) AS 'DATABASE_NAME',
case rsc_type when 1 then 'null'
when 2 then 'DATABASE'
WHEN 3 THEN 'FILE'
WHEN 4 THEN 'INDEX'
WHEN 5 THEN 'TABLE'
WHEN 6 THEN 'PAGE'
WHEN 7 THEN 'KEY'
WHEN 8 THEN 'EXTEND'
WHEN 9 THEN 'RID ( ROW ID)'
WHEN 10 THEN 'APPLICATION' end AS 'REQUEST_TYPE',
CASE req_ownertype WHEN 1 THEN 'TRANSACTION'
WHEN 2 THEN 'CURSOR'
WHEN 3 THEN 'SESSION'
WHEN 4 THEN 'ExSESSION' END AS 'REQUEST_OWNERTYPE',
OBJECT_NAME(rsc_objid ,rsc_dbid) AS 'OBJECT_NAME',
PROCESS.HOSTNAME ,
PROCESS.program_name ,
PROCESS.nt_domain ,
PROCESS.nt_username ,
PROCESS.program_name ,
SQLTEXT.text
FROM sys.syslockinfo LOCK JOIN
sys.sysprocesses PROCESS
ON LOCK.req_spid = PROCESS.spid
CROSS APPLY sys.dm_exec_sql_text(PROCESS.SQL_HANDLE) SQLTEXT
where 1=1
and db_name(rsc_dbid) = db_name()
--Lock on a specific object
SELECT *
FROM sys.dm_tran_locks
WHERE resource_database_id = DB_ID()
AND resource_associated_entity_id = object_id('Specific Table');
Carga de la base de datos
Como los bloqueos prácticamente siempre van de la mano de tiempos de ejecución del programa excesivamente largos, y éstos casi siempre están causados por consultas a la base de datos mal planteadas, siempre es útil echar un vistazo a la carga de la base de datos:
select spid,cmd,waittime,lastwaittype,cpu,physical_io,login_time,last_batch,status,hostname,program_name,nt_username, nt_domain
from master.dbo.sysprocesses where dbid = db_id(‚BaseDeDatosDeseada‘)
o
SELECT DISTINCT
name AS database_name,
session_id,
host_name,
login_time,
login_name,
reads,
writes
FROM sys.dm_exec_sessions
LEFT OUTER JOIN sys.dm_tran_locks ON sys.dm_exec_sessions.session_id = sys.dm_tran_locks.request_session_id
INNER JOIN sys.databases ON sys.dm_tran_locks.resource_database_id = sys.databases.database_id
WHERE resource_type <> ‚DATABASE‘
–AND request_mode LIKE ‚%X%‘
–AND name =’BaseDeDatosInteresada‘
ORDER BY name
Mientras que las lecturas y escrituras en el rango de 5 dígitos no suelen ser dignas de atención, deberías echar un vistazo a los accesos de 6 dígitos, y las campanas de alarma deberían sonar en los accesos de 7 dígitos... dependiendo del tiempo de ejecución previo del proceso respectivo, por supuesto.
1,5 millones de accesos de escritura para un Conector EDIfact que ha estado en línea durante 12 días es un número diferente que 500.000 accesos de lectura para un usuario que se ha conectado hace 5 minutos. Performance Troubleshooting necesita experiencia y paciencia.
En Business Central y Navision Debugger desde 2017
Aquí ya se ha incorporado la supervisión del bloqueo en el depurador. Sin embargo, Navision o Business Central requieren cambios en el servidor SQL para que también libere los datos necesarios.
Si Business Central o el propio Navision han instalado el servidor, estos ajustes ya se habrán realizado.
Realice los ajustes en SQL_Server, si no lo ha hecho ya:
En Servidor MSSMS/Propiedades -> Autorizaciones, para el login bajo el que se está ejecutando el servicio Navision:
Cambiar cualquier sesión de eventos: Conceder.
Mostrar el estado del servidor: Conceder.
Asigne este usuario a la base de datos deseada como propietario a través de Servidor/Seguridad/Inicio de sesión.
Active la supervisión del bloqueo en los ajustes de Business Central y Navision:
El monitor de actividad también puede ser útil para solucionar problemas de rendimiento. Se puede acceder a él a través del MSSMS: