Continuando con mi serie de artículos sobre pestillos, esta vez hablaré sobre el pestillo APPEND_ONLY_STORAGE_INSERT_POINT y mostraré cómo puede ser un cuello de botella importante para cargas de trabajo de actualización pesadas donde se utiliza cualquiera de las formas de aislamiento de instantáneas.
Le recomiendo encarecidamente que lea la publicación inicial de la serie antes de esta, para que tenga todos los conocimientos básicos generales sobre pestillos.
¿Qué es el pestillo APPEND_ONLY_STORAGE_INSERT_POINT?
Para explicar este pestillo, necesito explicar un poco cómo funciona el aislamiento de instantáneas.
Cuando habilita una de las dos formas de control de versiones, SQL Server usa un mecanismo llamado control de versiones para conservar las versiones anteriores al cambio de un registro en la tienda de versiones en tempdb. Esto se hace de la siguiente manera:
- Un registro se identifica como a punto de cambiarse.
- El registro actual se copia en el almacén de versiones.
- Se cambia el registro.
- Si el registro aún no tenía una etiqueta de versión de 14 bytes , se agrega uno al final del registro. La etiqueta contiene una marca de tiempo (no una hora real) y un puntero a la versión anterior del registro en el almacén de versiones.
- Si el registro ya tenía una etiqueta de control de versiones, se actualiza con la nueva marca de tiempo y el puntero del almacén de versiones.
La marca de tiempo de control de versiones de toda la instancia se incrementa cada vez que comienza una nueva declaración o un lote, o se crea una nueva versión de un registro, en cualquier base de datos donde esté habilitada cualquiera de las formas de aislamiento de instantáneas. Esta marca de tiempo se utiliza para garantizar que una consulta procese la versión correcta de un registro.
Por ejemplo, imagine una base de datos que tenía habilitada la instantánea confirmada de lectura, por lo que se garantiza que cada declaración vea los registros a partir del momento en que se inició la declaración. La marca de tiempo de control de versiones se establece para cuando se inició la declaración, por lo que cualquier registro que encuentre que tenga una marca de tiempo más alta es la versión "incorrecta", por lo que la versión "correcta", con una marca de tiempo anterior a la marca de tiempo de la declaración, debe recuperarse de la almacén de versiones. La mecánica de este proceso no es relevante para los fines de esta publicación.
Entonces, ¿cómo se almacenan físicamente las versiones en el almacén de versiones? Todo el registro anterior al cambio, incluidas las columnas fuera de la fila, se copia en el almacén de versiones, se divide en fragmentos de 8000 bytes, que pueden abarcar dos páginas si es necesario (por ejemplo, 2000 bytes al final de una página y 6000 bytes al final de una página). el comienzo de la siguiente). Este almacenamiento de propósito especial se compone de unidades de asignación de solo agregar y solo se usa para operaciones de almacenamiento de versiones. Se llama así porque los datos nuevos solo se pueden agregar inmediatamente después del final de la versión ingresada más recientemente. De vez en cuando se crea una nueva unidad de asignación, y esto permite que la limpieza regular del almacén de versiones sea muy eficiente, ya que una unidad de asignación innecesaria simplemente se puede descartar. Nuevamente, la mecánica de eso está más allá del alcance de esta publicación.
Y ahora llegamos a la definición del latch:cualquier subproceso que necesite copiar un registro previo al cambio en el almacén de versiones necesita saber dónde está el punto de inserción en la unidad de asignación actual de solo agregar. Esta información está protegida por el pestillo APPEND_ONLY_STORAGE_INSERT_POINT.
¿Cómo se convierte el pestillo en un cuello de botella?
Aquí está el problema:solo hay un modo aceptable en el que se puede adquirir el pestillo APPEND_ONLY_STORAGE_INSERT_POINT:modo EX (exclusivo). Y como sabrá al leer la publicación de introducción a la serie, solo un hilo a la vez puede sostener el pestillo en modo EX.
Reuniendo toda esta información:cuando una o más bases de datos tienen habilitado el aislamiento de instantáneas, y hay una carga de trabajo simultánea lo suficientemente alta de actualizaciones para esas bases de datos, habrá muchas versiones generadas por las diversas conexiones, y este pestillo se convertirá en un un poco de cuello de botella, con el aumento del tamaño del cuello de botella a medida que aumenta la carga de trabajo de actualización donde se involucra el control de versiones.
Mostrando el cuello de botella
Puede reproducir fácilmente el cuello de botella por sí mismo. Lo hice de la siguiente manera:
- Creó una tabla con un montón de columnas de enteros llamadas cXXX donde XXX es un número y un índice agrupado en una columna de identidad int llamada DocID
- 100.000 registros insertados, con valores aleatorios para todas las columnas
- Creó una secuencia de comandos con un bucle infinito para seleccionar un DocID aleatorio en el rango de 1 a 10 000, seleccionar un nombre de columna aleatorio e incrementar el valor de la columna en 1 (creando así una versión)
- Creó nueve secuencias de comandos idénticas, pero cada una seleccionó de un rango de clave de clúster de 10 000 valores diferente.
- Establezca DELAYED_DURABILITY en FORCED para reducir las esperas de WRITELOG (es cierto que rara vez haría esto, pero ayuda a exacerbar el cuello de botella para fines de demostración)
Luego ejecuté los diez scripts simultáneamente y medí el contador Métodos de acceso:búsquedas de índice/segundo para rastrear cuántas actualizaciones estaban ocurriendo. No podía usar Bases de datos:Solicitudes por lotes/seg., ya que cada secuencia de comandos solo tenía un lote (el ciclo infinito) y no quería usar Transacciones/seg., ya que podría contar las transacciones internas y las que envuelven cada actualización.
Cuando el aislamiento de instantáneas no estaba habilitado, en mi computadora portátil con Windows 10 que ejecutaba SQL Server 2019, recibía alrededor de 80 000 actualizaciones por segundo en las diez conexiones. Luego, cuando activé la configuración READ_COMMMITED_SNAPSHOT para la base de datos y volví a ejecutar la prueba, el rendimiento de la carga de trabajo se redujo a unas 60 000 actualizaciones por segundo (una caída del 25 % en el rendimiento). Según las estadísticas de espera, el 85 % de todas las esperas fueron LATCH_EX y, según las estadísticas de bloqueo, el 100 % fueron para APPEND_ONLY_STORAGE_INSERT_POINT.
Tenga en cuenta que configuré el escenario para mostrar el cuello de botella en su peor momento. En un entorno de la vida real con una carga de trabajo mixta, la guía generalmente aceptada para una caída del rendimiento cuando se usa el aislamiento de instantáneas es del 10 al 15 %.
Resumen
Otra área potencial que podría verse afectada por este cuello de botella son los secundarios legibles del grupo de disponibilidad. Si una réplica de la base de datos está configurada para que sea legible, todas las consultas en ella utilizan automáticamente el aislamiento de instantáneas y todas las reproducciones de registros desde la principal generarán versiones. Con una carga de trabajo de actualización lo suficientemente alta procedente de la principal y muchas bases de datos configuradas para ser legibles, y con la rehacer paralela como norma para las secundarias del grupo de disponibilidad, el pestillo APPEND_ONLY_STORAGE_INSERT_POINT también podría convertirse en un cuello de botella en una secundaria legible del grupo de disponibilidad, lo que podría conducir a la secundaria quedando atrás de la primaria. No he probado esto, pero es exactamente el mismo mecanismo que describí anteriormente, por lo que parece probable. En ese caso, es posible deshabilitar la rehacer en paralelo usando el indicador de rastreo 3459, pero esto podría conducir a un peor rendimiento general en el secundario.
Dejando a un lado el escenario del grupo de disponibilidad, lamentablemente, no usar el aislamiento de instantáneas es la única forma de evitar por completo este cuello de botella, que no es una opción viable si su carga de trabajo depende de la semántica proporcionada por el aislamiento de instantáneas, o si la necesita para aliviar los problemas de bloqueo. (ya que el aislamiento de instantáneas significa que las consultas de lectura no adquieren bloqueos compartidos que bloquean las consultas de cambio).
Editar:de los comentarios a continuación, * puede * eliminar el cuello de botella del pestillo usando ADR en SQL Server 2019, pero luego el rendimiento es mucho peor debido a la sobrecarga de ADR. El escenario en el que el pestillo se convierte en un cuello de botella debido a la gran carga de trabajo de actualización no es en absoluto un caso de uso válido para ADR.