Compartir a través de


Extensiones en AdventureWorks para demostrar In-Memory OLTP

Información general

En este ejemplo se muestra la nueva característica OLTP de In-Memory, que forma parte de SQL Server 2014. Muestra las nuevas tablas optimizadas para memoria y los procedimientos almacenados compilados de forma nativa, y se puede usar para demostrar las ventajas de rendimiento de In-Memory OLTP.

Nota:

Para consultar este tema en SQL Server 2016, vea Extensiones de AdventureWorks para ilustrar In-Memory OLTP.

El ejemplo migra 5 tablas de la base de datos AdventureWorks a tablas optimizadas para memoria e incluye una carga de trabajo de ejemplo para el procesamiento de órdenes de venta. Puede usar esta carga de trabajo de demostración para ver la ventaja de rendimiento de usar In-Memory OLTP en el servidor.

En la descripción del ejemplo se describen los inconvenientes realizados en la migración de las tablas a In-Memory OLTP para tener en cuenta las características que aún no se admiten para las tablas optimizadas para memoria en SQL Server 2014.

La documentación de este ejemplo se estructura de la siguiente manera:

Prerrequisitos

Instalación del ejemplo OLTP In-Memory basado en AdventureWorks

Siga estos pasos para instalar el ejemplo:

  1. Descargue el archivo para obtener la copia de seguridad completa de la base de datos AdventureWorks2014:

    1. Abra lo siguiente: https://msftdbprodsamples.codeplex.com/downloads/get/880661.

    2. Cuando se le pida que guarde el archivo en una carpeta local.

  2. Extraiga el archivo AdventureWorks2014.bak en una carpeta local, por ejemplo "c:\temp".

  3. Restaure la copia de seguridad de la base de datos mediante Transact-SQL o SQL Server Management Studio:

    1. Identifique la carpeta de destino y el nombre de archivo del archivo de datos, por ejemplo.

      'h:\DATA\AdventureWorks2014_Data.mdf'

    2. Identifique la carpeta de destino y el nombre de archivo del archivo de registro, por ejemplo.

      'i:\DATA\AdventureWorks2014_log.ldf'

      1. El archivo de registro debe colocarse en una unidad diferente que el archivo de datos, idealmente una unidad de latencia baja, como un almacenamiento SSD o PCIe, para un rendimiento máximo.

    Script T-SQL de ejemplo:

    RESTORE DATABASE [AdventureWorks2014]   
      FROM DISK = N'C:\temp\AdventureWorks2014.bak'   
        WITH FILE = 1,    
      MOVE N'AdventureWorks2014_Data' TO N'h:\DATA\AdventureWorks2014_Data.mdf',    
      MOVE N'AdventureWorks2014_Log' TO N'i:\DATA\AdventureWorks2014_log.ldf'  
     GO  
    
  4. Cambie el propietario de la base de datos a un inicio de sesión en el servidor; para ello, ejecute el comando siguiente en la ventana de consulta de SQL Server Management Studio:

    ALTER AUTHORIZATION ON DATABASE::AdventureWorks2014 TO [<NewLogin>]  
    
  5. Descargue el script de ejemplo "SQL Server 2014 RTM In-Memory OLTP Sample.sql" de SQL Server 2014 RTM In-Memory ejemplo OLTP en una carpeta local.

  6. Actualice el valor de la variable "checkpoint_files_location" en el script "SQL Server 2014 RTM In-Memory OLTP Sample.sql", para que apunte a la ubicación de destino de los archivos de punto de control olTP de In-Memory. Los archivos de punto de control deben colocarse en una unidad con un buen rendimiento de E/S secuencial.

    Actualice el valor de la variable "database_name" para que apunte a la base de datos AdventureWorks2014.

    1. Asegúrese de incluir la barra diagonal inversa "" como parte del nombre de la ruta de acceso.

    2. Ejemplo:

      :setvar checkpoint_files_location "d:\DBData\"  
      ...  
      :setvar database_name "AdventureWorks2014"  
      
  7. Ejecute el script de ejemplo de una de estas dos maneras:

    1. Uso de la utilidad de línea de comandos sqlcmd. Por ejemplo, ejecutando el siguiente comando desde el símbolo de la línea de comandos de la carpeta que contiene el script:

      sqlcmd -S . -E -i "ssSQL14 RTM hek_2 Sample.sql"  
      
    2. Uso de Management Studio:

      1. Abra el script "SQL Server 2014 RTM In-Memory OLTP Sample.sql" en una ventana de consulta.

      2. Conexión al servidor de destino que contiene la base de datos AdventureWorks2014

      3. Para habilitar el modo SQLCMD, haga clic en "Consulta -> Modo SQLCMD".

      4. Haga clic en el botón "Ejecutar" para ejecutar el script.

Descripción de las tablas y procedimientos de ejemplo

El ejemplo crea nuevas tablas para productos y pedidos de ventas, en función de las tablas existentes en AdventureWorks. El esquema de las nuevas tablas es similar a las tablas existentes, con algunas diferencias, como se explica a continuación.

Las nuevas tablas optimizadas para memoria llevan el sufijo "_inmem". El ejemplo también incluye las tablas correspondientes que llevan el sufijo "_ondisk": estas tablas se pueden usar para realizar una comparación uno a uno entre el rendimiento de las tablas optimizadas para memoria y las tablas basadas en disco en el sistema.

Tenga en cuenta que las tablas optimizadas para memoria usadas en la carga de trabajo para la comparación de rendimiento son totalmente duraderas y totalmente registradas. No sacrifican la durabilidad ni la confiabilidad para lograr la ganancia de rendimiento.

La carga de trabajo objetivo de este ejemplo es el procesamiento de pedidos de ventas, donde también se tiene en cuenta la información sobre los productos y los descuentos. Para ello, las tablas SalesOrderHeader, SalesOrderDetail, Product, SpecialOffer y SpecialOfferProduct.

Se usan dos nuevos procedimientos almacenados, Sales.usp_InsertSalesOrder_inmem y Sales.usp_UpdateSalesOrderShipInfo_inmem, para insertar pedidos de venta y para actualizar la información de envío de un pedido de venta determinado.

El nuevo esquema "Demo" contiene tablas auxiliares y procedimientos almacenados para ejecutar una carga de trabajo de demostración.

Concretamente, el ejemplo de In-Memory OLTP agrega los siguientes objetos a AdventureWorks:

Tablas añadidas por el ejemplo

Las nuevas tablas

Sales.SalesOrderHeader_inmem

  • Información de encabezado sobre pedidos de ventas. Cada pedido de venta tiene una fila en esta tabla.

Sales.SalesOrderDetail_inmem

  • Detalles de los pedidos de venta. Cada elemento de línea de un pedido de ventas tiene una fila en esta tabla.

Ventas.OfertaEspecial_inmem

  • Información sobre ofertas especiales, incluido el porcentaje de descuento asociado a cada oferta especial.

Sales.SpecialOfferProduct_inmem (ProductoOfertaEspecial_enmemoria)

  • Tabla de referencia entre ofertas especiales y productos. Cada oferta especial puede presentar cero o más productos, y cada producto puede aparecer en cero o más ofertas especiales.

Producción.Product_inmem

  • Información sobre los productos, incluido su precio de lista.

Demo.DemoSalesOrderDetailSeed

  • Se utiliza en la carga de trabajo de demostración para crear órdenes de venta de muestra.

Variaciones basadas en disco de las tablas:

  • Sales.SalesOrderHeader_ondisk

  • Sales.SalesOrderDetail_ondisk

  • Venta.OfertaEspecial_enDisco

  • Sales.SpecialOfferProduct_ondisk

  • Production.Product_ondisk

Diferencias entre las tablas originales basadas en disco y las nuevas tablas optimizadas para memoria

En la mayor parte, las nuevas tablas introducidas por este ejemplo usan las mismas columnas y los mismos tipos de datos que las tablas originales. Sin embargo, hay algunas diferencias. A continuación se enumeran las diferencias, junto con una justificación para los cambios.

Sales.SalesOrderHeader_inmem

  • Las restricciones predeterminadas se admiten para las tablas optimizadas para memoria y la mayoría de las restricciones predeterminadas que migramos tal cual. Sin embargo, la tabla original Sales.SalesOrderHeader contiene dos restricciones predeterminadas que recuperan la fecha actual, para las columnas OrderDate y ModifiedDate. En una carga de trabajo de procesamiento de pedidos de alto procesamiento con mucha concurrencia, cualquier recurso global puede convertirse en un punto de disputa. La hora del sistema es un recurso global, y hemos observado que puede convertirse en un cuello de botella al ejecutar una carga de trabajo OLTP de In-Memory que inserta pedidos de ventas, en particular si es necesario recuperar la hora del sistema para varias columnas en el encabezado del pedido, así como en los detalles del pedido. El problema se soluciona en este ejemplo obteniendo la hora del sistema solo una vez para cada orden de venta insertada y usando ese valor para las columnas datetime en SalesOrderHeader_inmem y SalesOrderDetail_inmem, en el procedimiento almacenado Sales.usp_InsertSalesOrder_inmem.

  • UDT de alias : la tabla original usa dos tipos de datos definidos por el usuario (UDT) de alias dbo. OrderNumber y dbo. AccountNumber, para las columnas PurchaseOrderNumber y AccountNumber, respectivamente. SQL Server 2014 no admite el UDT de alias para tablas optimizadas para memoria, por lo que las nuevas tablas usan tipos de datos del sistema nvarchar(25) y nvarchar(15), respectivamente.

  • Columnas que aceptan valores NULL en las claves de índice: en la tabla original, la columna SalesPersonID admite valores NULL, mientras que en las nuevas tablas la columna no admite valores NULL y tiene una restricción predeterminada con el valor (-1). Esto se debe a que los índices de las tablas optimizadas para memoria no pueden tener columnas que aceptan valores NULL en la clave de índice; -1 es un suplente para NULL en este caso.

  • Columnas calculadas: las columnas calculadas SalesOrderNumber y TotalDue se omiten, ya que SQL Server 2014 no admite columnas calculadas en tablas optimizadas para memoria. La nueva vista Sales.vSalesOrderHeader_extended_inmem refleja las columnas SalesOrderNumber y TotalDue. Por lo tanto, puede usar esta vista si se necesitan estas columnas.

  • No se admiten restricciones de clave externa para tablas optimizadas para memoria en SQL Server 2014. Además, SalesOrderHeader_inmem es una tabla activa en la carga de trabajo de ejemplo y las restricciones de claves externas requieren procesamiento adicional para todas las operaciones DML, ya que requiere búsquedas en todas las demás tablas a las que se hace referencia en estas restricciones. Por lo tanto, se supone que la aplicación garantiza la integridad referencial y esta no se valida cuando se insertan filas. La integridad referencial de los datos de esta tabla se puede comprobar mediante el procedimiento almacenado dbo.usp_ValidateIntegrity, mediante el siguiente script:

    DECLARE @o int = object_id(N'Sales.SalesOrderHeader_inmem')  
    EXEC dbo.usp_ValidateIntegrity @o  
    
  • No se admiten restricciones de comprobación para tablas optimizadas para memoria en SQ Server 2014. La integridad del dominio se valida junto con la integridad referencial mediante este script:

    DECLARE @o int = object_id(N'Sales.SalesOrderHeader_inmem')  
    EXEC dbo.usp_ValidateIntegrity @o  
    
  • Rowguid : se omite la columna rowguid. Aunque uniqueidentifier es compatible con tablas optimizadas para memoria, la opción ROWGUIDCOL no se admite en SQL Server 2014. Las columnas de este tipo se usan normalmente para la replicación de mezcla o las tablas que tienen columnas de secuencia de archivos. Este ejemplo no incluye ninguno.

Ventas.DetallePedidoVentas

  • Restricciones predeterminadas : similares a SalesOrderHeader, la restricción predeterminada que requiere la fecha y hora del sistema no se migra, sino que el procedimiento almacenado que inserta pedidos de ventas se encarga de insertar la fecha y hora actuales del sistema en la primera inserción.

  • Columnas calculadas : la columna calculada LineTotal no se migró, ya que las columnas calculadas no se admiten con tablas optimizadas para memoria en SQL Server 2014. Para acceder a esta columna, use la vista Sales.vSalesOrderDetail_extended_inmem.

  • Rowguid : se omite la columna rowguid. Para obtener más información, consulte la descripción de la tabla SalesOrderHeader.

  • Para las restricciones de comprobación y de clave externa, consulte la descripción de SalesOrderHeader. El siguiente script se puede usar para comprobar la integridad de dominio y referencial de esta tabla:

    DECLARE @o int = object_id(N'Sales.SalesOrderHeader_inmem')  
    EXEC dbo.usp_ValidateIntegrity @o  
    

Producción.Producto

  • Alias de UDTs: la tabla original usa el tipo de datos definido por el usuario dbo.Flag, que es equivalente al tipo de datos del sistema bit. En su lugar, la tabla migrada usa el tipo de datos bit.

  • Intercalación BIN2 : las columnas Name y ProductNumber se incluyen en las claves de índice y, por tanto, deben tener intercalaciones BIN2 en SQL Server 2014. En este caso, la suposición es que la aplicación no se basa en detalles de intercalación, como la insensibilidad entre mayúsculas y minúsculas.

  • Rowguid : se omite la columna rowguid. Para obtener más información, consulte la descripción de la tabla SalesOrderHeader.

  • Las restricciones únicas, Check y Clave Foránea se consideran de dos maneras: los procedimientos almacenados Product.usp_InsertProduct_inmem y Product.usp_DeleteProduct_inmem se pueden usar para insertar y eliminar productos; estos procedimientos validan el dominio y la integridad referencial, y fallarán si se viola la integridad. Además, el script siguiente se puede usar para validar la integridad de dominio y referencial tal como es:

    DECLARE @o int = object_id(N'Production.Product')  
    EXEC dbo.usp_ValidateIntegrity @o  
    
    • Tenga en cuenta que los procedimientos almacenados usp_InsertProduct_inmem y usp_DeleteProduct_inmem solo consideran las claves externas entre las tablas migradas. No se tienen en cuenta las referencias a otras tablas ProductModel, ProductSubcategory y UnitMeasure.

Ventas.OfertaEspecial

  • Las restricciones Check y Foreign Key se tienen en cuenta de dos maneras: los procedimientos almacenados Sales.usp_InsertSpecialOffer_inmem y Sales.usp_DeleteSpecialOffer_inmem se pueden usar para insertar y eliminar ofertas especiales; estos procedimientos validan el dominio y la integridad referencial, y producirán un error si se infringe la integridad. Además, el siguiente script se puede usar para validar la integridad de dominio y referencial tal como está.

    DECLARE @o int = object_id(N'Sales.SpecialOffer_inmem')  
    EXEC dbo.usp_ValidateIntegrity @o  
    
  • Rowguid : se omite la columna rowguid. Para obtener más información, consulte la descripción de la tabla SalesOrderHeader.

ProductoDeOfertaEspecial

  • Las restricciones de clave externa se tienen en cuenta de dos maneras: el procedimiento almacenado Sales.usp_InsertSpecialOfferProduct_inmem se puede usar para insertar relaciones entre ofertas especiales y productos; este procedimiento valida la integridad referencial y producirá un error si se infringe la integridad. Además, el script siguiente se puede usar para validar la integridad referencial tal como es:

    DECLARE @o int = object_id(N'Sales.SpecialOfferProduct_inmem')  
    EXEC dbo.usp_ValidateIntegrity @o  
    
  • Rowguid : se omite la columna rowguid. Para obtener más información, consulte la descripción de la tabla SalesOrderHeader.

Consideraciones para índices en tablas optimizadas para memoria

El índice base para las tablas optimizadas para memoria es el índice NO AGRUPADO, que admite búsquedas puntuales (búsqueda del índice en predicado de igualdad), exploraciones de rango (búsqueda del índice en predicado de desigualdad), exploraciones completas del índice y exploraciones ordenadas. Además, los índices NONCLUSTERED admiten la búsqueda en columnas iniciales de la clave de índice. De hecho, los índices NONCLUSTERED optimizados para memoria admiten todas las operaciones que admiten los índices NONCLUSTERED basados en disco, exceptuando los escaneos hacia atrás. Por lo tanto, el uso de índices no agrupados es una opción segura para tus índices.

Los índices HASH se pueden usar para optimizar aún más la carga de trabajo. Están especialmente optimizados para consultas puntuales e inserciones de fila. Sin embargo, hay que tener en cuenta que no admiten escaneos de rango, escaneos ordenados ni búsqueda en columnas iniciales de clave de índice. Por lo tanto, es necesario tener cuidado al usar estos índices. Además, es necesario especificar el bucket_count en tiempo de creación. Normalmente, debe establecerse entre uno y dos veces el número de valores de clave de índice, pero la sobreestimación normalmente no es un problema.

Consulte Libros en línea para obtener más detalles sobre las directrices de índice y las directrices para elegir el bucket_count correcto.

Los índices de las tablas migradas se han optimizado para la carga de trabajo de procesamiento de pedidos de ventas de demostración. La carga de trabajo se basa en inserciones y búsquedas de puntos en las tablas Sales.SalesOrderHeader_inmem y Sales.SalesOrderDetail_inmem, y también se basa en búsquedas de puntos en las columnas de clave principal de las tablas Production.Product_inmem y Sales.SpecialOffer_inmem.

Sales.SalesOrderHeader_inmem tiene tres índices, los cuales son todos índices HASH por motivos de rendimiento y porque no se necesitan escaneos ordenados o de rango para la carga de trabajo.

  • Índice HASH en (SalesOrderID): bucket_count tiene un tamaño de 10 millones (redondeado hasta 16 millones), ya que el número esperado de pedidos de ventas es de 10 millones.

  • Índice HASH en (SalesPersonID): bucket_count es de 1 millón. El conjunto de datos proporcionado no tiene muchos vendedores, pero esto permite un crecimiento futuro, además de no pagar una penalización de rendimiento por consultas de puntos si el bucket_count es excesivo en tamaño.

  • Índice HASH en (CustomerID): bucket_count es de 1 millón. El conjunto de datos proporcionado no tiene muchos clientes, pero esto permite un crecimiento futuro.

Sales.SalesOrderDetail_inmem tiene tres índices, que todos son índices HASH por razones de rendimiento, y porque no se necesitan escanes ordenados o de intervalo para la carga de trabajo.

  • Índice HASH en (SalesOrderID, SalesOrderDetailID): este es el índice de clave principal y, aunque las búsquedas en (SalesOrderID, SalesOrderDetailID) sean poco frecuentes, el uso de un índice hash para la clave acelera las inserciones de filas. El bucket_count tiene un tamaño de 50 millones (redondeado hasta 67 millones): el número esperado de pedidos de ventas es de 10 millones y tiene un tamaño medio de 5 artículos por pedido.

  • Índice HASH en (SalesOrderID): las búsquedas por pedidos de ventas son frecuentes; querrá encontrar todas las partidas correspondientes a un solo pedido. bucket_count tiene un tamaño de 10 millones (redondeado hasta 16 millones), porque el número esperado de pedidos de ventas es de 10 millones.

  • Índice HASH en (ProductID): bucket_count es de 1 millón. El conjunto de datos proporcionado no tiene mucho producto, pero esto permite un crecimiento futuro.

Production.Product_inmem tiene tres índices

  • Índice HASH en (ProductID): las búsquedas en ProductID se encuentran en la ruta de acceso crítica para la carga de trabajo de demostración, por lo que se trata de un índice hash.

  • Índice NONCLUSTERED en (Nombre): esto permitirá recorridos ordenados de nombres de productos.

  • Índice no agrupado en (ProductNumber): esto permitirá escaneos ordenados de los números de producto.

Sales.SpecialOffer_inmem tiene un índice HASH en (SpecialOfferID): las búsquedas puntuales de ofertas especiales se encuentran en la parte crítica de la carga de trabajo de demostración. El bucket_count tiene un tamaño de 1 millón para permitir el crecimiento futuro.

No se hace referencia a Sales.SpecialOfferProduct_inmem en la carga de trabajo de demostración, y por tanto, no hay una necesidad aparente de usar índices hash en esta tabla para optimizar la carga de trabajo. Los índices en (SpecialOfferID, ProductID) y (ProductID) son no agrupados (NONCLUSTERED).

Tenga en cuenta que en los bucket_counts anteriores, algunos tienen un tamaño excesivo, pero no es el caso de los bucket_counts de los índices en SalesOrderHeader_inmem y SalesOrderDetail_inmem: están dimensionados para solo 10 millones de pedidos de ventas. Esto se ha hecho para permitir la instalación del ejemplo en sistemas con baja disponibilidad de memoria, aunque en esos casos se producirá un error en la carga de trabajo de demostración con memoria insuficiente. Si desea escalar mucho más allá de 10 millones de pedidos de ventas, puede aumentar los números de contenedores en consecuencia.

Consideraciones sobre el uso de memoria

El uso de memoria en la base de datos de ejemplo, tanto antes como después de ejecutar la carga de trabajo de demostración, se describe en la sección Uso de memoria para las tablas optimizadas para memoria.

Procedimientos almacenados añadidos por el ejemplo

Los dos procedimientos almacenados clave para insertar el pedido de venta y actualizar los detalles del envío son los siguientes:

  • Sales.usp_InsertSalesOrder_inmem

    • Inserta un nuevo pedido de ventas en la base de datos y genera salesOrderID para ese pedido de ventas. Como parámetros de entrada, toma detalles para el encabezado del pedido de ventas, así como las líneas de pedido.

    • Parámetro de salida:

      • @SalesOrderID int: salesOrderID para el pedido de venta que se acaba de insertar
    • Parámetros de entrada (obligatorios):

      • @DueDate datetime2

      • @CustomerID Int

      • @BillToAddressID [int]

      • @ShipToAddressID [int]

      • @ShipMethodID [int]

      • @SalesOrderDetails Sales.SalesOrderDetailType_inmem: TVP que contiene los elementos de línea del pedido

    • Parámetros de entrada (opcional):

      • @Status [tinyint]

      • @OnlineOrderFlag [bit]

      • @PurchaseOrderNumber [nvarchar](25)

      • @AccountNumber [nvarchar](15)

      • @SalesPersonID [int]

      • @TerritoryID [int]

      • @CreditCardID [int]

      • @CreditCardApprovalCode [varchar](15)

      • @CurrencyRateID [int]

      • @Comment nvarchar(128)

  • Sales.usp_UpdateSalesOrderShipInfo_inmem

    • Actualice la información de envío de un pedido de venta determinado. Esto también actualizará la información de envío de todos los artículos de línea del pedido de venta.

    • Se trata de un procedimiento envoltorio para los procedimientos almacenados compilados de forma nativa, Sales.usp_UpdateSalesOrderShipInfo_native, con lógica de reintento para tratar posibles conflictos inesperados con transacciones simultáneas que actualizan el mismo pedido. Para obtener más información sobre la lógica de reintento, consulte el tema en Documentación en línea aquí.

  • Sales.usp_ActualizarInformaciónEnvíoPedido_native

    • Este es el procedimiento almacenado compilado de forma nativa que procesa realmente la actualización a la información de envío. Está destinado a ser llamado desde el procedimiento almacenado Sales.usp_UpdateSalesOrderShipInfo_inmem. Si el cliente puede tratar errores y implemente la lógica de reintento, puede llamar a este procedimiento directamente, en lugar de usar el procedimiento almacenado que contiene.

El siguiente procedimiento almacenado se usa para la carga de trabajo de demostración.

  • Demo.usp_DemoReset

    • Restablece la demostración vacíando y reiniciando las tablas SalesOrderHeader y SalesOrderDetail.

Los siguientes procedimientos almacenados se usan para insertar y eliminar de tablas optimizadas para memoria, al tiempo que garantizan la integridad referencial y del dominio.

  • Production.usp_InsertProduct_inmem

  • Production.usp_DeleteProduct_inmem

  • Ventas.usp_InsertarOfertaEspecial_enmemoria

  • Sales.usp_DeleteSpecialOffer_inmem

  • Sales.usp_InsertSpecialOfferProduct_inmem

Por último, se usa el siguiente procedimiento almacenado para comprobar la integridad de dominio y referencial.

  1. dbo.usp_ValidateIntegrity

    • Parámetro opcional: : @object_id identificador del objeto para validar la integridad de

    • Este procedimiento se basa en las tablas dbo. DomainIntegrity, dbo. ReferencialIntegrity y dbo. UniqueIntegrity para las reglas de integridad que deben comprobarse: el ejemplo rellena estas tablas en función de las restricciones check, foreign key y unique que existen para las tablas originales de la base de datos AdventureWorks.

    • Se basa en los procedimientos auxiliares dbo.usp_GenerateCKCheck, dbo.usp_GenerateFKCheck y dbo. GenerateUQCheck para generar el T-SQL necesario para realizar las comprobaciones de integridad.

Medidas de rendimiento mediante la carga de trabajo de demostración

Ostress es una herramienta de línea de comandos desarrollada por el equipo de soporte técnico de MICROSOFT CSS SQL Server. Esta herramienta se puede usar para ejecutar consultas o ejecutar procedimientos almacenados en paralelo. Puede configurar el número de subprocesos para ejecutar una instrucción T-SQL determinada en paralelo y puede especificar cuántas veces se debe ejecutar la instrucción en este subproceso; ostress pondrá en marcha los subprocesos y ejecutará la instrucción en todos los subprocesos en paralelo. Una vez finalizada la ejecución de todos los subprocesos, ostress notificará el tiempo necesario para que todos los subprocesos finalicen la ejecución.

Instalando ostress

Ostress se instala como parte de las utilidades RML; no hay ninguna instalación independiente para ostress.

Pasos de instalación:

  1. Descargue y ejecute el paquete de instalación x64 para las utilidades rmL desde la página siguiente: https://blogs.msdn.com/b/psssql/archive/2013/10/29/cumulative-update-2-to-the-rml-utilities-for-microsoft-sql-server-released.aspx

  2. Si hay un cuadro de diálogo que indica que determinados archivos están en uso, haga clic en "Continuar".

Ejecución de ostress

Ostress se ejecuta desde el símbolo de la línea de comandos. Es más conveniente ejecutar la herramienta desde "RML Cmd Prompt", que se instala como parte de las utilidades de RML.

Para abrir el símbolo del sistema de RML Cmd, siga estas instrucciones:

En Windows Server 2012 [R2] y en Windows 8 y 8.1, abra el menú Inicio haciendo clic en la tecla Windows y escriba "rml". Haga clic en "RmL Cmd Prompt", que estará en la lista de resultados de búsqueda.

Asegúrese de que el símbolo del sistema se encuentra en la carpeta de instalación de utilidades de RML. Por ejemplo:

Las opciones de línea de comandos para "ostress" se pueden ver al ejecutar simplemente ostress.exe sin ninguna opción de línea de comandos. Las principales opciones que se deben tener en cuenta para ejecutar ostress con este ejemplo son:

  • -S nombre de la instancia de MicrosoftSQL Server a la que conectarse

  • -E usa la autenticación de Windows para conectarse (valor predeterminado); Si usa la autenticación de SQL Server, use las opciones -U y -P para especificar el nombre de usuario y la contraseña, respectivamente.

  • -d nombre de la base de datos, para este ejemplo AdventureWorks2014

  • -Q la instrucción T-SQL que se va a ejecutar

  • -n número de conexiones que procesan cada archivo o consulta de entrada

  • :son el número de iteraciones de cada conexión para ejecutar cada archivo o consulta de entrada.

Carga de trabajo de demostración

El procedimiento almacenado principal que se usa en la carga de trabajo de demostración es Sales.usp_InsertSalesOrder_inmem/ondisk. En el siguiente script se construye un parámetro de tabla (TVP) con datos de ejemplo y se llama al procedimiento para insertar un pedido de ventas con 5 líneas de artículos.

La herramienta ostress se usa para ejecutar las llamadas a procedimientos almacenados en paralelo, para simular que los clientes insertan pedidos de ventas simultáneamente.

Restablezca la demostración después de cada ejecución de prueba de estrés ejecutando Demo.usp_DemoReset. Este procedimiento elimina las filas de las tablas optimizadas para memoria, trunca las tablas basadas en disco y ejecuta un punto de control de base de datos.

El siguiente script se ejecuta simultáneamente para simular una carga de trabajo de procesamiento de pedidos de ventas:

DECLARE   
      @i int = 0,   
      @od Sales.SalesOrderDetailType_inmem,   
      @SalesOrderID int,   
      @DueDate datetime2 = sysdatetime(),   
      @CustomerID int = rand() * 8000,   
      @BillToAddressID int = rand() * 10000,   
      @ShipToAddressID int = rand() * 10000,   
      @ShipMethodID int = (rand() * 5) + 1;   
  
INSERT INTO @od   
SELECT OrderQty, ProductID, SpecialOfferID   
FROM Demo.DemoSalesOrderDetailSeed   
WHERE OrderID= cast((rand()*106) + 1 as int);   
  
WHILE (@i < 20)   
BEGIN;   
      EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od;   
      SET @i += 1   
END  
  

Con este script, cada orden de ejemplo que se construye se inserta 20 veces a través de 20 procedimientos almacenados ejecutados en un bucle WHILE. El bucle se utiliza para tener en cuenta que la base de datos se emplea para construir el orden de la muestra. En entornos de producción típicos, la aplicación de nivel medio construirá el pedido de ventas que se va a insertar.

El script anterior inserta pedidos de ventas en tablas optimizadas para memoria. El script para insertar órdenes de venta en tablas basadas en disco se deriva al reemplazar las dos apariciones de "_inmem" por "_ondisk".

Usaremos la herramienta ostress para ejecutar los scripts mediante varias conexiones simultáneas. Usaremos el parámetro "-n" para controlar el número de conexiones y el parámetro "r" para controlar cuántas veces se ejecuta el script en cada conexión.

Validación funcional de la carga de trabajo

Para comprobar que todo funciona, empezaremos con una prueba de ejemplo, usando 10 conexiones simultáneas y 5 iteraciones, insertando un total de 10 * 5 * 20 = 1000 pedidos de ventas.

Con el comando siguiente se supone que se usa la instancia predeterminada en el equipo local. Si usa una instancia con nombre o usa un servidor remoto, cambie el nombre del servidor en consecuencia mediante el parámetro -S.

Inserte 1000 órdenes de venta en tablas optimizadas para memoria, y utilice el siguiente comando en el símbolo del sistema de RML Cmd:

Haga clic en el botón Copiar para copiar el comando y péguelo en el símbolo del sistema de utilidades de RML.

ostress.exe -n10 -r5 -S. -E -dAdventureWorks2014 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_inmem, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"  

Si todo funciona según lo previsto, la ventana de comandos tendrá un aspecto similar al siguiente. No se esperan mensajes de error.

Valide que también la carga de trabajo funcione según lo previsto para las tablas basadas en disco mediante la ejecución del siguiente comando en el símbolo del sistema de CMD de RML:

Haga clic en el botón Copiar para copiar el comando y péguelo en el símbolo del sistema de utilidades de RML.

ostress.exe -n10 -r5 -S. -E -dAdventureWorks2014 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_ondisk, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_ondisk @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"  

Ejecución de la carga de trabajo

Para probar a escala, insertamos 10 millones de pedidos de ventas mediante 100 conexiones. Esta prueba se realiza razonablemente en un servidor modesto (por ejemplo, 8 núcleos físicos, 16 núcleos lógicos) y almacenamiento SSD básico para el registro. Si la prueba no funciona bien en el hardware, eche un vistazo a la sección Solución de problemas de pruebas de ejecución lenta. Si desea reducir el nivel de esfuerzo de esta prueba, reduzca el número de conexiones cambiando el parámetro "-n". Por ejemplo, para reducir el número de conexiones a 40, cambie el parámetro "-n100" a "-n40".

Como medida de rendimiento para la carga de trabajo, usamos el tiempo transcurrido según lo notificado por ostress.exe después de ejecutar la carga de trabajo.

Tablas optimizadas para la memoria

Empezaremos ejecutando la carga de trabajo en tablas optimizadas para memoria. El siguiente comando abre 100 subprocesos, cada uno ejecutándose durante 5000 iteraciones. Cada iteración inserta 20 pedidos de ventas en transacciones independientes. Hay 20 inserciones por iteración para compensar el hecho de que la base de datos se usa para generar los datos que se van a insertar. Esto produce un total de 20 * 5000 * 100 = 10 000 000 000 inserciones de pedido de ventas.

Abra el símbolo del sistema de comandos de RML y ejecute el siguiente comando:

Haga clic en el botón Copiar para copiar el comando y luego péguelo en el indicador de comandos de las utilidades de RML.

ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2014 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_inmem, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_inmem @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"  

En un servidor de prueba con un número total de 8 núcleos físicos (16 lógicos), esto tardó 2 minutos y 5 segundos. En un segundo servidor de prueba con 24 núcleos físicos (48 lógicos), esto tardó 1 minuto y 0 segundos.

Observe el uso de la CPU mientras se ejecuta la carga de trabajo, por ejemplo, mediante el administrador de tareas. Verá que el uso de cpu está cerca de 100%. Si no es así, tiene un cuello de botella de entrada/salida de registros. Consulte también Solución de problemas de pruebas de ejecución lenta.

Tablas basadas en disco

El siguiente comando ejecutará la carga de trabajo en tablas basadas en disco. Tenga en cuenta que esta carga de trabajo puede tardar un tiempo en ejecutarse, lo que se debe principalmente a la contención de cerrojos en el sistema. Las tablas optimizadas para memoria no tienen bloqueos y, por tanto, no sufren este problema.

Abra la ventana de comandos de RML y ejecute el siguiente comando.

Haga clic en el botón Copiar para copiar el comando y péguelo en el símbolo del sistema de utilidades de RML.

ostress.exe -n100 -r5000 -S. -E -dAdventureWorks2014 -q -Q"DECLARE @i int = 0, @od Sales.SalesOrderDetailType_ondisk, @SalesOrderID int, @DueDate datetime2 = sysdatetime(), @CustomerID int = rand() * 8000, @BillToAddressID int = rand() * 10000, @ShipToAddressID int = rand() * 10000, @ShipMethodID int = (rand() * 5) + 1; INSERT INTO @od SELECT OrderQty, ProductID, SpecialOfferID FROM Demo.DemoSalesOrderDetailSeed WHERE OrderID= cast((rand()*106) + 1 as int); while (@i < 20) begin; EXEC Sales.usp_InsertSalesOrder_ondisk @SalesOrderID OUTPUT, @DueDate, @CustomerID, @BillToAddressID, @ShipToAddressID, @ShipMethodID, @od; set @i += 1 end"  

En un servidor de prueba con un número total de 8 núcleos físicos (16 lógicos), esto tardó 41 minutos y 25 segundos. En un segundo servidor de prueba con 24 núcleos físicos (48 lógicos), esto tardó 52 minutos y 16 segundos.

El factor principal de la diferencia de rendimiento entre las tablas optimizadas para memoria y las tablas basadas en disco en esta prueba es el hecho de que cuando se usan tablas basadas en disco, SQL Server no puede utilizar completamente la CPU. La razón es la contención de cerrojo: las transacciones concurrentes intentan escribir en la misma página de datos; los cerrojos se utilizan para garantizar que solo una transacción a la vez pueda escribir en una página. El motor OLTP de In-Memory está libre de bloqueos temporales y las filas de datos no se organizan en páginas. Por lo tanto, las transacciones simultáneas no bloquean las inserciones entre sí, lo que permite que SQL Server use completamente la CPU.

Puede observar el uso de la CPU mientras se ejecuta la carga de trabajo, por ejemplo, mediante el administrador de tareas. Verá con las tablas basadas en disco que el uso de la CPU está lejos de ser del 100%%. En una configuración de prueba con 16 procesadores lógicos, el uso se mantenería alrededor de 24%.

Opcionalmente, puede ver el número de esperas de bloqueo temporal por segundo mediante el Monitor de rendimiento, con el contador de rendimiento '\SQL Server:Latches\Latch Waits/sec'.

Restablecer la demostración

Para restaurar la demostración, abra el indicador de comandos de RML y ejecute el siguiente comando.

ostress.exe -S. -E -dAdventureWorks2014 -Q"EXEC Demo.usp_DemoReset"  

Dependiendo del hardware, esto puede tardar unos minutos en ejecutarse.

Se recomienda restablecer después de cada ejecución de demostración. Dado que esta carga de trabajo es de solo inserción, cada ejecución consumirá más memoria y, por tanto, se requiere un restablecimiento para evitar que se agote la memoria. La cantidad de memoria consumida después de una ejecución se describe en Uso de memoria de sección después de ejecutar la carga de trabajo.

Solución de problemas de pruebas de ejecución lenta

Los resultados de las pruebas suelen variar con el hardware y también el nivel de simultaneidad que se usa en la ejecución de pruebas. Un par de cosas que se deben buscar si los resultados no son los esperados:

  • Número de transacciones simultáneas: al ejecutar la carga de trabajo en un único subproceso, es probable que la ganancia de rendimiento con In-Memory OLTP sea menor que 2X. La contención de bloqueos es solo un gran problema si hay un alto nivel de concurrencia.

  • Número bajo de núcleos disponibles para SQL Server: esto significa que habrá un bajo nivel de simultaneidad en el sistema, ya que solo puede haber tantas transacciones que se ejecuten simultáneamente como hay núcleos disponibles para SQL.

    • Síntoma: si la utilización de la CPU es elevada al ejecutar la carga de trabajo en tablas que se almacenan en disco, esto indica que no hay mucha contención, lo que apunta a una falta de concurrencia.
  • Velocidad de la unidad de registro: si la unidad de registro no puede mantenerse al día con el nivel de rendimiento de la transacción en el sistema, la carga de trabajo se convierte en un cuello de botella en la E/S del registro. Aunque el registro es más eficaz con In-Memory OLTP, si la entrada/salida de registros es un cuello de botella, la mejora potencial del rendimiento es limitada.

    • Síntoma: si el uso de la CPU no está cerca de 100% o está muy esporádico al ejecutar la carga de trabajo en tablas optimizadas para memoria, es posible que haya un cuello de botella de E/S de registro. Para confirmarlo, abra Resource Monitor y examine la longitud de la cola de la unidad de registro.

Uso de memoria y espacio en disco en el ejemplo

En la parte siguiente se describe qué esperar en términos de uso de espacio en disco y memoria para la base de datos de ejemplo. También se muestran los resultados que hemos visto en un servidor de prueba con 16 núcleos lógicos.

Uso de memoria para las tablas optimizadas para memoria

Uso general de la base de datos

La consulta siguiente se puede usar para obtener el uso total de memoria para In-Memory OLTP en el sistema.

SELECT type  
   , name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  

Instantánea después de que se acaba de crear la base de datos:

tipo nombre pages_MB
MEMORYCLERK_XTP Predeterminado 94
MEMORYCLERK_XTP DB_ID_5 877
MEMORYCLERK_XTP Predeterminado 0
MEMORYCLERK_XTP Predeterminado 0

Los administradores de memoria predeterminados contienen estructuras de memoria de todo el sistema y son relativamente pequeños. El administrador de memoria de la base de datos de usuario, en este caso, la base de datos con ID 5, es de aproximadamente 900 MB.

Uso de memoria por tabla

La consulta siguiente se puede usar para explorar en profundidad el uso de memoria de las tablas individuales y sus índices:

SELECT object_name(t.object_id) AS [Table Name]  
     , memory_allocated_for_table_kb  
 , memory_allocated_for_indexes_kb  
FROM sys.dm_db_xtp_table_memory_stats dms JOIN sys.tables t   
ON dms.object_id=t.object_id  
WHERE t.type='U'  

A continuación se muestran los resultados de esta consulta para una instalación nueva del ejemplo:

Nombre de tabla memoria_asignada_para_tabla_kb memoria_asignada_para_indices_kb
SpecialOfferProduct_inmem 64 3840
DemoSalesOrderHeaderSeed 1984 5504
SalesOrderDetail_inmem 15316 663552
DemoSalesOrderDetailSeed 64 10432
SpecialOffer_inmem 3 8192
SalesOrderHeader_inmem 7168 147456
Product_inmem 124 12352

Como puede ver, las tablas son bastante pequeñas: SalesOrderHeader_inmem tiene aproximadamente 7 MB y SalesOrderDetail_inmem tiene un tamaño de aproximadamente 15 MB.

Lo que resulta sorprendente aquí es el tamaño de la memoria asignada para los índices, en comparación con el tamaño de los datos de la tabla. Esto se debe a que los índices hash de la muestra están dimensionados previamente para un tamaño de datos mayor. Tenga en cuenta que los índices hash tienen un tamaño fijo y, por tanto, su tamaño no aumentará con el tamaño de los datos de la tabla.

Uso de memoria después de ejecutar la carga de trabajo

Después de insertar 10 millones de pedidos de ventas, el uso de memoria total es similar al siguiente:

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  
tipo nombre pages_MB
MEMORYCLERK_XTP Predeterminado 146
MEMORYCLERK_XTP DB_ID_5 7374
MEMORYCLERK_XTP Predeterminado 0
MEMORYCLERK_XTP Predeterminado 0

Como puede ver, SQL Server usa un poco menos de 8 GB para las tablas e índices optimizados para memoria en la base de datos de ejemplo.

Examine el uso detallado de memoria por tabla después de una ejecución de ejemplo:

SELECT object_name(t.object_id) AS [Table Name]  
     , memory_allocated_for_table_kb  
 , memory_allocated_for_indexes_kb  
FROM sys.dm_db_xtp_table_memory_stats dms JOIN sys.tables t   
ON dms.object_id=t.object_id  
WHERE t.type='U'  
Nombre de tabla memoria_asignada_para_tabla_kb memoria_asignada_para_indices_kb
SalesOrderDetail_inmem 5113761 663552
DetalleDeOrdenDeVentasDeDemostraciónInicial 64 10368
SpecialOffer_inmem 2 8192
SalesOrderHeader_inmem 1575679 147456
Product_inmem 111 12032
SpecialOfferProduct_inmem 64 3712
DemoSalesOrderHeaderSeed 1984 5504

Podemos ver un total de aproximadamente 6,5 GB de datos. Observe que el tamaño de los índices de la tabla SalesOrderHeader_inmem y SalesOrderDetail_inmem es el mismo que el tamaño de los índices antes de insertar los pedidos de ventas. El tamaño del índice no cambió porque ambas tablas usan índices hash y los índices hash son estáticos.

Después del restablecimiento de la demostración

El procedimiento almacenado Demo.usp_DemoReset se puede usar para restablecer la demostración. Elimina los datos de las tablas SalesOrderHeader_inmem y SalesOrderDetail_inmem, y reestablece los datos de las tablas originales SalesOrderHeader y SalesOrderDetail.

Ahora, aunque se han eliminado las filas de las tablas, esto no significa que la memoria se recupere inmediatamente. SQL Server reclama memoria de filas eliminadas en tablas optimizadas para memoria en segundo plano, según sea necesario. Verá que inmediatamente después del reinicio de la demostración, sin carga transaccional en el sistema, la memoria de las filas eliminadas aún no se ha recuperado.

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  
tipo nombre pages_MB
MEMORYCLERK_XTP Predeterminado 2261
MEMORYCLERK_XTP DB_ID_5 7396
MEMORYCLERK_XTP Predeterminado 0
MEMORYCLERK_XTP Predeterminado 0

Esto se espera: la memoria se recuperará cuando se ejecute la carga de trabajo transaccional.

Si inicia una segunda ejecución de la carga de trabajo de demostración, verá que el uso de memoria disminuye inicialmente, ya que las filas eliminadas anteriormente se limpian. En algún momento, el tamaño de memoria volverá a aumentar hasta que finalice la carga de trabajo. Después de insertar 10 millones de filas después del restablecimiento de demostración, el uso de memoria será muy similar al uso después de la primera ejecución. Por ejemplo:

SELECT type  
, name  
, pages_kb/1024 AS pages_MB   
FROM sys.dm_os_memory_clerks WHERE type LIKE '%xtp%'  
tipo nombre pages_MB
MEMORYCLERK_XTP Predeterminado 1863
MEMORYCLERK_XTP DB_ID_5 7390
MEMORYCLERK_XTP Predeterminado 0
MEMORYCLERK_XTP Predeterminado 0

Uso de disco para tablas optimizadas para memoria

El tamaño general en disco para los archivos de punto de comprobación de una base de datos en un momento dado se puede encontrar mediante la consulta:

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX'  
  

Estado inicial

Cuando se crean inicialmente el grupo de archivos de ejemplo y las tablas optimizadas para memoria de ejemplo, se crean previamente varios archivos de punto de control y el sistema comienza a rellenar los archivos; el número de archivos de punto de control creados previamente depende del número de procesadores lógicos del sistema. Como el ejemplo es inicialmente muy pequeño, los archivos creados previamente estarán principalmente vacíos después de la creación inicial.

A continuación se muestra el tamaño inicial en disco del ejemplo en una máquina con 16 procesadores lógicos:

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX'  
Tamaño en disco en MB
2312

Como puede ver, hay una gran discrepancia entre el tamaño en disco de los archivos de punto de control, que es de 2,3 GB y el tamaño real de los datos, que está más cerca de 30 MB.

Si observa más cerca de dónde procede el uso del espacio en disco, puede usar la consulta siguiente. El tamaño del disco devuelto por esta consulta es aproximado para los archivos con estado en 5 (OBLIGATORIO PARA BACKUP/HA), 6 (IN TRANSITION TO TOMBSTONE) o 7 (TOMBSTONE).

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type  

Para el estado inicial del ejemplo, el resultado tendrá un aspecto similar al de un servidor con 16 procesadores lógicos:

state_desc descripción_tipo_de_archivo contar Tamaño en disco: MB
PRECONFIGURADO DATOS 16 2048
PRECONFIGURADO DELTA 16 128
EN OBRA DATOS 1 128
EN OBRA DELTA 1 8

Como puede ver, la mayoría del espacio se usa mediante datos creados previamente y archivos delta. SQL Server creó previamente un par de archivos (datos, delta) por procesador lógico. Además, los archivos de datos tienen un tamaño previo de 128 MB y archivos delta a 8 MB, con el fin de que la inserción de datos en estos archivos sea más eficaz.

Los datos reales de las tablas optimizadas para memoria se encuentra en el único archivo de datos.

Después de ejecutar la carga de trabajo

Después de una sola ejecución de prueba que inserta 10 millones de pedidos de ventas, el tamaño general del disco tiene un aspecto similar al siguiente (para un servidor de prueba de 16 núcleos):

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX'  
Tamaño en disco en MB
8828

El tamaño del disco está cerca de 9 GB, que se acerca al tamaño en memoria de los datos.

Examinando con más detalle los tamaños de los archivos de punto de comprobación en los distintos estados:

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type  
state_desc file_type_desc contar Tamaño en disco MB
PRECONFIGURADO DATOS 16 2048
PRECONFIGURADO DELTA 16 128
EN OBRA DATOS 1 128
EN OBRA DELTA 1 8

Todavía tenemos 16 pares de archivos creados previamente, listos para ir a medida que se cierran los puntos de control.

Hay un par en construcción, que se usa hasta que se cierra el punto de control actual. Junto con los archivos de punto de comprobación activos, esto proporciona aproximadamente 6,5 GB de uso de disco para 6,5 GB de datos en memoria. Recuerde que los índices no se conservan en el disco y, por tanto, el tamaño general del disco es menor que el tamaño en la memoria en este caso.

Después del restablecimiento de la demostración

Después del restablecimiento de demostración, el espacio en disco no se recupera inmediatamente si no hay ninguna carga de trabajo transaccional en el sistema y no hay puntos de comprobación de base de datos. Para que los archivos de punto de control se muevan a través de sus distintas fases y finalmente se descarten, es necesario que se produzcan varios puntos de comprobación y eventos de truncamiento de registros, para iniciar la combinación de archivos de punto de control, así como para iniciar la recolección de elementos no utilizados. Esto ocurrirá automáticamente si tiene una carga de trabajo transaccional en el sistema [y realiza copias de seguridad de registros normales, en caso de que use el modelo de recuperación COMPLETA], pero no cuando el sistema esté inactivo, como en un escenario de demostración.

En el ejemplo, después del reinicio de la demostración, podría ver algo parecido a

SELECT SUM(df.size) * 8 / 1024 AS [On-disk size in MB]  
FROM sys.filegroups f JOIN sys.database_files df   
   ON f.data_space_id=df.data_space_id  
WHERE f.type=N'FX'  
Tamaño en disco en MB
11839

Con casi 12 GB, esto es significativamente más que los 9 GB que tuvimos antes del restablecimiento de la demostración. Esto se debe a que se han iniciado algunas combinaciones de archivos de punto de control, pero algunos de los destinos de combinación aún no se han instalado y algunos de los archivos de origen de combinación aún no se han limpiado, como se puede ver en lo siguiente:

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type  
state_desc file_type_desc contar Tamaño en disco MB
PRECONFIGURADO DATOS 16 2048
PRECONFIGURADO DELTA 16 128
ACTIVO DATOS 38 5152
ACTIVO DELTA 38 1331
OBJETIVO DE FUSIÓN DATOS 7 896
OBJETIVO DE FUSIÓN DELTA 7 56
ORIGEN COMBINADO DATOS 13 1772
ORIGEN COMBINADO DELTA 13 4:55

Los destinos de combinación se instalan y el origen combinado se limpia a medida que se produce actividad transaccional en el sistema.

Después de ejecutar por segunda vez la carga de trabajo de demostración, e insertar 10 millones de pedidos de ventas tras el restablecimiento de la demostración, verá que los archivos creados durante la primera ejecución de la carga de trabajo se han eliminado. Si ejecuta la consulta anterior varias veces mientras se ejecuta la carga de trabajo, puede ver que los archivos de punto de control pasan por las distintas fases.

Después de insertar 10 millones de pedidos de ventas en la segunda ejecución de la carga de trabajo, verá que la utilización del disco es muy similar, aunque no necesariamente igual que después de la primera ejecución, ya que el sistema es dinámico por naturaleza. Por ejemplo:

SELECT state_desc  
 , file_type_desc  
 , COUNT(*) AS [count]  
 , SUM(CASE  
   WHEN state = 5 AND file_type=0 THEN 128*1024*1024  
   WHEN state = 5 AND file_type=1 THEN 8*1024*1024  
   WHEN state IN (6,7) THEN 68*1024*1024  
   ELSE file_size_in_bytes  
    END) / 1024 / 1024 AS [on-disk size MB]   
FROM sys.dm_db_xtp_checkpoint_files  
GROUP BY state, state_desc, file_type, file_type_desc  
ORDER BY state, file_type  
state_desc file_type_desc contar Tamaño en disco MB
PRECONFIGURADO DATOS 16 2048
PRECONFIGURADO DELTA 16 128
EN OBRA DATOS 2 268
EN OBRA DELTA 2 16
ACTIVO DATOS 41 5608
ACTIVO DELTA 41 328

En este caso, hay dos pares de archivos de punto de control en el estado "en construcción", lo que significa que varios pares de archivos se movieron al estado "en construcción", probablemente debido al alto nivel de concurrencia en la carga de trabajo. Varios hilos concurrentes requirieron un nuevo par de archivos al mismo tiempo y, por lo tanto, movieron un par de 'precreado' a 'en construcción'.

Véase también

In-Memory OLTP ( optimización deIn-Memory)