Glosario de Directorio Activo Azure

Cada subscripcion de Azure tiene Azure AD.

Usuario

Hay 4 tipos de usuarios: Del mismo AD, con cuenta Microsoft, de otro AD o invitado (Sharepoint…).
En el siguiente enlace tenemos la vista de nuestro usuario; donde podemos ver nuestro perfil y modificar nuestras aplicaciones. Tendremos más opciones si nuestro usuario pertenece a un Azure AD.

Aplicación

Cualquier usuario no invitado del AD puede registrar una aplicación. Las aplicaciones pueden ser:

  • Nuestras: Creadas en nuestro AD . Esta aplicación (Aplication Object) se muestra como Service Principal en múltiples ADs (incluido en nuestro AD cuando la usamos).
  • Usamos: Incluye ademas de las nuestras otras creadas en otros AD. Es decir, usamos aplicaciones externas😉. La aplicación de otro AD; aparece como un Service Principal en nuestro AD.

Características de la Aplicación Global

  • Identificador: Guid que identifica la aplicación.
  • Tipo de aplicación
    • Web Aplication/Web Api:
      • Application ID URI: URL que identifica la aplicación.
      • Reply Url: URL que indica donde se envia la respuesta cuando el usuario de AD se autentica.
    • Nativa: aplicaciones desktop, jobs… En este caso solo solicita la Reply Url.
  • Key: Se requiere para el proceso de autenticación.
  • Permisos necesarios de la aplicación:
    Requerir permisos para acceder a determinados servicios del Azure AD que son necesarios para la aplicación. Esta parte solo la pueden configurar administradores. A los usuarios se les pedirá el consentimiento para usar estos derechos la primera vez que se conecten (aunque es posible que un usuario administrador del consentimiento para todos los usuarios del AD).
    Los servicios que se puede acceder son:
    • Microsoft Graph: API permite trabajar con los elementos del AD (usuarios, grupos…).
    • Office 365 Management API
    • Windows Azure Active Directory
    • Windows Azure Service Management API: API que permite trabajar con recursos de azure (Maquinas Virtuales, SQL Azure…).
  • Acceso a las Aplicación
    • Las aplicaciones por defecto pueden ser accedidas por todos los usuarios de nuestro AD. Podemos restringir el acceso, requiriendo la User Assignment Required to Access App en la configuración de la aplicación. Y asignando en la aplicación los usuarios o grupo (el grupo sólo AD Premium).
    • Las aplicaciones no pueden ser accedidas por usuarios de otros AD; podemos activar Aplication is MultiTenant (MultiEmpresa) en la configuración de la aplicación para permitirlo.
      NOTA: La aplicación Multitenant para eliminarla del AD; debemos pasarla a SingleTenant y luego borrarla😉.
    • Las aplicaciones nativas son siempre Multitenant; es decir accesibles por otros AD, no es posible restringir a nuestro AD.

Escenarios de Autenticación

En este enlace se describen los escenarios de uso de autenticación contra un Azure AD. Usar las librerías🙂

Publicado en Azure, Seguridad | Deja un comentario

Active Geo-Replication SQL Azure

En SQL Azure podemos replicar nuestra base de datos (primaria) hasta en cuatro bases de datos (secundarias). Automáticamente los cambios se traspasan de la primaria a las secundarias. Las secundarias pueden ser accedidas en modo lectura o sin lectura (pensada para desastres-failover). El rendimiento (DTU o eDTU) de la secundarias debe ser el suficiente para admitir las actualizaciones de la primaria. Las funcionalidades que aportar son:

  • Balancear Carga: Múltiples bases de datos para leer datos.
  • Protección ante desastres: Cuando falla la principal, una secundaria puede asumir su rol. En este caso si queremos evitar una perdida critica de datos; deberíamos sp_wait_for_database_copy_sync después del commit de la transacción.
  • Proceso de migración o actualización: Permite copiar la base de datos, con un tiempo de parada mínimo.

Operativa

La operativa se puede realizar en el Portal, Comandos de SQL o script de PowerShell.

  • Creación: Crear una base de datos secundaria. Esta base de datos tendrá el mismo nombre que la primaria pero nos conectarnos a otro servidor
  • Parar replicación: La base de datos secundaria ya no se replica; y se convierte en una base de datos normal (permite escritura de datos).
  • Convertir en primaria: La base de datos secundaria pasa a ser la primaria. En caso de desastres o migraciones.
  • Status de la replicación (únicamente disponible en Comando SQL o Script PowerShell).
Publicado en Sql Azure | Deja un comentario

Múltiples Servicios de Redis en el mismo Servidor

Por defecto Redis se configura en el puerto 6379 (En Azure Redis este puerto deshabilitado por defecto y se configura en el puerto 6380 en SSL). El paquete MSI, instala Redis con la configuración por defecto. Es posible instalar múltiples servicios de Redis en el mismo servidor; instalando en diferentes puertos mediante linea de comandos.

Vamos a instalar un servicio adicional en el puerto 6380😉
En la documentación se indica el siguiente comando, pero al ejecutarlo me muestra un error.

redis-server --service-install --service-name Redis6380 --port 6380 -loglevel verbose

y obtengo😦 :

Invalid argument during startup: Failed to open the .conf file: verbose CWD=C:\P
rogram Files\Redis

Instalar un servicio adicional en el puerto 6380

  • 1.- Creamos el fichero redis6380.windows.conf a partir del redis.windows.conf. Estos ficheros contienen la configuración del servicio Redis, por ejemplo el numero de bases de datos disponibles.
  • 2.- Editamos el fichero redis6380.windows.conf, y en el parámetro port sustituimos 6379 por 6380.
  • 3.- Instalamos el servicio de Redis y el servicio de Windows Asociado; mediante el siguiente comando.
    redis-server --service-install redis6380.windows.conf --service-name Redis6380 --loglevel verbose
    

    y obtenemos:

    [8264] 15 Jul 09:51:43.150 # Granting read/write access to 'NT AUTHORITY\Network
    Service' on: "C:\Program Files\Redis" "C:\Program Files\Redis\"
    [8264] 15 Jul 09:51:43.150 # Redis successfully installed as a service.
    
  • 4.- Ya lo tenemos operativo😉
    RedisOn6380

Para desinstalar el servicio instalado

Ejecutamos el siguiente comando:

redis-server --service-uninstall redis6380.windows.conf -service-name Redis6380 --loglevel verbose

y obtenemos:

[9976] 15 Jul 10:02:40.842 # Redis service successfully uninstalled.
Publicado en Cache, Redis | Deja un comentario

Lua y Redis

Que es Lua?

LualogoLua es un lenguaje de scripting que puede ser usado en Redis. En el siguiente enlace se describe la sintaxis del lenguaje.
Las ventajas que aporta Lua a Redis son:

  • El script de Lua se ejecuta atómicamente. Evita el uso de transacciones (El uso de bloques WATCH/MULTI/EXEC).
  • Es mas eficiente; ya que realiza toda la tarea en el servidor. Evita múltiples conexiones de ida y vuelta con los datos hacia y desde el servidor.

LuaHelloWorld
Ejemplo de Script Hello World

echo local msg = "hello world"; return msg > hello.txt
redis-cli -p 6380 -a [Clave de conexión] --eval hello.txt

Lua en Redis

EVAL "redis.call('SET',KEYS[1],ARGV[1]); return redis.call('GET',KEYS[1])" 1 key valor
  • El comando EVAL permite la ejecución del script. Permite enviar parámetros para la ejecución del script que son obtenidos en el script mediante KEYS o ARGV.
  • Para ejecutar comandos de Redis en el script debemos usar el comando redis.call() o redis.pcall() (este último es el redis.call() con protección frente excepciones). Adjunto enlaces con de ejemplos de scripts.
  • Es posible guardar el script en el servicio (LOAD) y posteriormente ejecutarlo mediante un identificador de carga (EVALSHA). Esto permite:
    • Mejora el rendimiento ya que evita recompilar el script en cada ejecución.
    • Se requiere reenviar el script en cada ejecución (con el consumo de ancho de banda).
Publicado en Azure, Cache, Redis | Deja un comentario

Test de Rendimiento de Aplicaciones Web

Test de Rendimiento

Los tests de rendimiento de una aplicación Web nos sirven para determinar tres items:

  • Velocidad: ¿Que rápida va la aplicación?
  • Escalabilidad: ¿Como se degrada el rendimiento al incrementarse las visitas?
  • Estabilidad: ¿Genera errores la aplicación ante picos de visitas?

Podemos elaborar diferentes test de rendimiento, en función del objetivo:

  • Humo: Test rápido para ver por encima como se comporta la aplicación.
  • Rendimiento: Nos indica como se comporta la aplicación ante una carga normal. Nos permite validar los tres items (velocidad, escalabilidad y/o estabilidad).
  • Stress: Nos indica como se comporta la aplicación ante un numero de visitas elevadas. Permite aflorar problemas de estabilidad.
  • Capacidad: Nos indica los límites que puede soportar nuestra aplicación. Permite determinar los recursos críticos de la aplicación.

En el siguiente enlace se muestra más información sobre metodología de elaboración de test de rendimiento. A continuación se describen los diferentes pasos.

1.- Captura del trafico de la aplicación Web

El primer paso a realizar es la obtención de las peticiones a la aplicación web. Se describen dos tipos de ficheros que contienen la navegación de URLs que realiza el usuario:

  • WebTest File: es el fichero de Visual Studio. Se puede obtener:
  • HTTP Archive Format (HAR) es el fichero estándar W3C. Se puede obtener:
    • Con Fiddler: Capturando tráfico filtrado por el host de la aplicación Web y exportandolo a HTTPArchive v1.2 (opción del menú Export -> All Sessions).
    • Con Chrome: En el menu contextual de la pestaña Network de las Developer Tools disponemos de la opción save as HAR with content.

2.- Determinar escenario de carga

No hemos solo de contemplar las peticiones a la Web.
\text{usuarios}\cdot \text{peticiones de usuario por minuto}=  \text{Total de peticiones por minuto}
Es clave determinar los dos factores que aparecen en la formula anterior:

  • Usuarios concurrentes: cada usuario genera en la Web su cookie, sesión y variables internas que consumen recursos en la aplicación. En un test de rendimiento estándar deberíamos considerar el doble o triple del numero de usuarios en la Web.
  • Peticiones por usuario: Por ejemplo, no es realista 200 peticiones usuario por minuto.

3.- Realización del Test de Rendimiento

Para realizar los Test de Rendimiento necesitamos Visual Studio Team Services; donde se van almacenar los resultados de los tests. El principal parámetro del test es la captura realizada. También se podría indicar las URLs manualmente pero esto es bastante engorroso😦 .
Disponemos diferentes opciones de menor a mayor complejidad y potencia:

  • Performance Test Tool: Para App Service en Azure disponemos de una herramienta para realizar el test de rendimiento desde el mismo portal de Azure disponible en Tools -> Performance Test. A partir una WebTest File, solo permite configurar el numero de usuarios, el tiempo y la ubicación para realizar el test de carga.
  • Visual Studio Team Services: Podemos realizar directamente un test de carga a partir de HAR con una configuración más avanzada.
  • Un Web Performance And Load Test Project en Visual Studio:
    NewPerformandAndLoadTestProject
    Generamos un LoadTest donde indicamos el escenario a testear (usuarios, tiempo…). Añadimos un WebTest File (Web Performance Test) en el TestMix del LoadTest.NewLoadTestWizard
    Podemos ejecutar el test en una infraestructura de maquinas que generan las peticiones (controladores y agentes de Test); aunque la manera más simple es usar el servicio Cloud-based Load Testing service de Azure. En este caso, se asigna por defecto una maquina de 2 cores (agente de test) por cada 500 usuarios.
    Las ventajas que aporta el test en Visual Studio son:

    • Permite un escenario de test de rendimiento más complejo de las pruebas de carga; con Warning Up, patrón peticiones, distribución de navegadores o conectividad.
    • Permite generar tests dinámicos. Por ejemplo, simulando escenarios con parámetros de búsqueda diferentes obtenidos de un csv o base de datos.
    • Permite la utilización de parámetros dinámicos. Por ejemplo, parámetros de sesión.
    • Permite añadir información adicional. Al usar Application Insights. De esta manera podemos analizar en mayor profundidad las causas de los resultados. Podemos detectar los patrones de uso y también el consumo de recursos. Disponible en el menú contextual del LoadTest.
      ActivateApplicationInsightsOnLoadTest

4.- Análisis del Test de Rendimiento

En Visual Studio Team Services podemos ver los resultados de la ejecución del Test. La misma vista obtendremos en el Test Manager de Visual Studio😉.

  • En Summary y Charts nos muestra los resultados del test en modo numérico y gráfico respectivamente.
  • En Diagnostics nos muestra información del proceso de configuración del entorno de test.
  • En Logs nos muestra las peticiones que han ido mal. Si no hay nada, todo ok🙂

El objetivo es definir un test base para permitir realizar un seguimiento de los procesos optimización realizados en la aplicación web. A partir de aquí podemos hacer comparaciones entre los test; y observar las diferencias.
CompareRuns

Publicado en Test, Visual Studio | Deja un comentario

Azure Redis Cache

¿Que es Redis?

redis-whiteRedis es un repositorio pares clave-valor cuyo uso principal es ser un servicio de cache. El repositorio se almacena en memoria con lo que el rendimiento es optimo. Este repositorio soporta replicación en nodos secundarios o escalado particionado en múltiples nodos.
El valor es un bloque de bytes que tiene conversión implícita desde/hacia una cadena de caracteres. En conclusión tanto bytes como cadenas pueden ser usados indistintamente.
Las ventajas/funcionalidades que aportar frente a otros servicios de cache son:

  • Soporta transacciones al acceder a los datos a nivel básico (no hay rollback ni bloqueo optimista)
  • Soporta operaciones atómicas sobre valores: incrementos (Comando TTL) , decrementos (Comando DECR) y múltiples asignaciones (Comando MSET) o lecturas (Comando MGET).
  • Soporta expiración de los datos (Comando TTL).
  • Soporta crear una clave que contiene un conjuntos de valores (Tipo de Datos Set y Comando SADD). Por ejemplo, usadas para crear relaciones entre claves.
  • Soporta crear una clave que contiene una cola de valores (Tipo de Datos List y Comando LPUSH). Por ejemplo, para obtener últimos encolados.
  • Soporta crear una clave que incorpora una valor adicional de orden (Tipo de Datos Sorted sets y Comando ZADD).
  • Soporta usar una clave para crear un canal de comunicación del tipo publicación (Comando PUBLISH) y subscripción (Comando SUBSCRIBE).
  • Soporta algun tipo de scripting.

¿Que es Azure Redis Cache?

AzureRedisEs la distribución de Redis pensada para Azure que se ejecuta como un servicio PAAS. Ofrece tres niveles de servicio: Basic, Standard y Premium.

Uso de Redis en NET

Hay múltiples librerías clientes para variedad de lenguajes que permiten acceder al servicio de Redis. Para NET se recomienda StackExchange.Redis client library. En el siguiente enlace se describen las practicas recomendadas con ejemplos usando StackExchange.Redis.
A nivel de desarrollo no hay ninguna diferencia entre usar Redis o Azure Redis; únicamente la cadena de conexión debe incorporar las siguiente flags:

También se puede usar Redis en aplicaciones ASP .NET para funciones concretas:

Enlaces Útiles

Publicado en Azure, Cache, Redis | 1 Comentario

Monitorización de SQL II

En esta entrada se describen problemas y recomendaciones relacionadas con la monitorización de SQL.

Estado de la Servicio

El siguiente enlace podemos ver el estado de los servicios de SQL Azure para determinar si hay alguna incidencia en la plataforma.

Falta de Tunning

Falta de un diseño adecuado (indices, consultas y planes de ejecución ineficientes…)

  • SQL Database Advisor
  • Analizando Statistics IO/Time de las consultas
  • Analizando Execution Planes de las consultas
  • Consulta DMV que nos suguiere indices que nos faltan (ojo, hay usar el sentido comun para determinar cuales aplicamos).
    SELECT CONVERT (varchar, getdate(), 126) AS runtime,
    mig.index_group_handle,
    mid.index_handle,
    CONVERT (decimal (28,1),
    migs.avg_total_user_cost *
    migs.avg_user_impact *
    (migs.user_seeks + migs.user_scans))
    AS improvement_measure,
    'CREATE INDEX missing_index_' +
    CONVERT (varchar, mig.index_group_handle) +
    '_' +
    CONVERT (varchar, mid.index_handle) +
    ' ON ' +
    mid.statement +
    ' (' + ISNULL (mid.equality_columns,'') +
    CASE WHEN mid.equality_columns IS NOT NULL
    AND mid.inequality_columns IS NOT NULL
    THEN ','
    ELSE ''
    END + ISNULL (mid.inequality_columns, '') +
    ')' +
    ISNULL (' INCLUDE (' + mid.included_columns + ')',
    '') AS create_index_statement,
    migs.*,
    mid.database_id, mid.[object_id]
    FROM sys.dm_db_missing_index_groups mig
    INNER JOIN sys.dm_db_missing_index_group_stats migs
    ON migs.group_handle = mig.index_group_handle
    INNER JOIN sys.dm_db_missing_index_details mid
    ON mig.index_handle = mid.index_handle
    WHERE CONVERT (decimal (28,1),
    migs.avg_total_user_cost *
    migs.avg_user_impact *
    (migs.user_seeks + migs.user_scans)) > 10
    ORDER BY migs.avg_total_user_cost *
    migs.avg_user_impact *
    (migs.user_seeks + migs.user_scans) DESC
    

Consumo de Recursos

Problemas de rendimiento en la ejecución de las consultas.

  • En el panel de SQL Database Performance Insight
  • Consulta DMV donde vemos durante la ultima hora el estado cada 15 segundos del recursos consumidos por la base de datos.

    select * from Sys.dm_db_Resource_stats 
    order by end_time desc
    
  • Consulta DMV a dm_db_wait_stats donde informa los tipos de espera relevantes más frecuentes que se producen en la ejecución de consultas. El siguiente enlace muestra los diferentes tipos de espera. En el siguiente documento SQL Server Performance Tuning Using Wait Statistics nos indica como actuar ante tiempos de espera elevados.
    WITH [Waits] AS
        (SELECT
            [wait_type],
            [wait_time_ms] / 1000.0 AS [WaitS],
            ([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [ResourceS],
            [signal_wait_time_ms] / 1000.0 AS [SignalS],
            [waiting_tasks_count] AS [WaitCount],
           100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],
            ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
        FROM sys.dm_os_wait_stats
        WHERE [wait_type] NOT IN (
            N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR',
            N'BROKER_TASK_STOP', N'BROKER_TO_FLUSH',
            N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
            N'CHKPT', N'CLR_AUTO_EVENT',
            N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
     
            -- Maybe uncomment these four if you have mirroring issues
            N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE',
            N'DBMIRROR_WORKER_QUEUE', N'DBMIRRORING_CMD',
     
            N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
            N'EXECSYNC', N'FSAGENT',
            N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
     
            -- Maybe uncomment these six if you have AG issues
            N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION',
            N'HADR_LOGCAPTURE_WAIT', N'HADR_NOTIFICATION_DEQUEUE',
            N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
     
            N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP',
            N'LOGMGR_QUEUE', N'MEMORY_ALLOCATION_EXT',
            N'ONDEMAND_TASK_QUEUE',
            N'PREEMPTIVE_XE_GETTARGETSTATE',
            N'PWAIT_ALL_COMPONENTS_INITIALIZED',
            N'PWAIT_DIRECTLOGCONSUMER_GETNEXT',
            N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP', N'QDS_ASYNC_QUEUE',
            N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP',
            N'QDS_SHUTDOWN_QUEUE', N'REDO_THREAD_PENDING_WORK',
            N'REQUEST_FOR_DEADLOCK_SEARCH', N'RESOURCE_QUEUE',
            N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH',
            N'SLEEP_DBSTARTUP', N'SLEEP_DCOMSTARTUP',
            N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
            N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP',
            N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
            N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT',
            N'SP_SERVER_DIAGNOSTICS_SLEEP', N'SQLTRACE_BUFFER_FLUSH',
            N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
            N'SQLTRACE_WAIT_ENTRIES', N'WAIT_FOR_RESULTS',
            N'WAITFOR', N'WAITFOR_TASKSHUTDOWN',
            N'WAIT_XTP_RECOVERY',
            N'WAIT_XTP_HOST_WAIT', N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG',
            N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
            N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT')
        AND [waiting_tasks_count] > 0
        )
    SELECT
        MAX ([W1].[wait_type]) AS [WaitType],
        CAST (MAX ([W1].[WaitS]) AS DECIMAL (16,2)) AS [Wait_S],
        CAST (MAX ([W1].[ResourceS]) AS DECIMAL (16,2)) AS [Resource_S],
        CAST (MAX ([W1].[SignalS]) AS DECIMAL (16,2)) AS [Signal_S],
        MAX ([W1].[WaitCount]) AS [WaitCount],
        CAST (MAX ([W1].[Percentage]) AS DECIMAL (5,2)) AS [Percentage],
        CAST ((MAX ([W1].[WaitS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgWait_S],
        CAST ((MAX ([W1].[ResourceS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgRes_S],
        CAST ((MAX ([W1].[SignalS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgSig_S],
        CAST ('https://www.sqlskills.com/help/waits/' + MAX ([W1].[wait_type]) as XML) AS [Help/Info URL]
    FROM [Waits] AS [W1]
    INNER JOIN [Waits] AS [W2]
        ON [W2].[RowNum] <= [W1].[RowNum]
    GROUP BY [W1].[RowNum]
    HAVING SUM ([W2].[Percentage]) - MAX( [W1].[Percentage] ) < 95; -- percentage threshold
    
  • Consultas DMV que nos indican las consultas que más tiempo consumen o se ejecutan con más frecuencia sobre la base de datos:
    -- Top CPU Time Querys 
    SELECT TOP 10
    GETDATE() AS "Collection Date",
    qs.execution_count AS "Execution Count",
    SUBSTRING(qt.text,qs.statement_start_offset/2 +1, 
                    (CASE WHEN qs.statement_end_offset = -1 
                        THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2 
                        ELSE qs.statement_end_offset END -
                            qs.statement_start_offset
                    )/2
                ) AS "Query Text", 
        DB_NAME(qt.dbid) AS "DB Name",
        qs.total_worker_time AS "Total CPU Time",
        qs.total_worker_time/qs.execution_count AS "Avg CPU Time (ms)",     
        qs.total_physical_reads AS "Total Physical Reads",
        qs.total_physical_reads/qs.execution_count AS "Avg Physical Reads",
        qs.total_logical_reads AS "Total Logical Reads",
        qs.total_logical_reads/qs.execution_count AS "Avg Logical Reads",
        qs.total_logical_writes AS "Total Logical Writes",
        qs.total_logical_writes/qs.execution_count AS "Avg Logical Writes",
        qs.total_elapsed_time AS "Total Duration",
        qs.total_elapsed_time/qs.execution_count AS "Avg Duration (ms)",
        qp.query_plan AS "Plan"
    FROM sys.dm_exec_query_stats AS qs 
    CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt 
    CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) AS qp
    ORDER BY qs.total_worker_time DESC
    --- Change Order to get Top Number executed querys. 
    --- ORDER BY qs.EXECUTION_COUNT DESC
    
  • Consulta DMV que nos indica cuantas lecturas y como se realizan (seek, scan y read) de los indices de las tablas; así mismo también las escrituras. Permite detectar indices que se uso no es correcto, ineficiente o simplemente no se usan. Permite ver que indices producen overhead de cara eliminarlos😉.
    SELECT  OBJECT_NAME(ddius.[object_id], ddius.database_id) AS [object_name] ,		
            ddius.index_id ,
    	i.[name] AS [index_name],
            ddius.user_seeks ,
            ddius.user_scans ,
            ddius.user_lookups ,
            ddius.user_seeks + ddius.user_scans + ddius.user_lookups 
                                                         AS user_reads ,
            ddius.user_updates AS user_writes ,
            ddius.last_user_scan ,
            ddius.last_user_update
    FROM    sys.dm_db_index_usage_stats ddius
     INNER JOIN sys.indexes i ON ddius.[object_id] = i.[object_id]
                                         AND i.[index_id] = ddius.[index_id]
    WHERE   ddius.database_id > 4 -- filter out system tables
            AND OBJECTPROPERTY(ddius.OBJECT_ID, 'IsUserTable') = 1
            AND ddius.index_id > 0  -- filter out heaps 		
    ORDER BY ddius.user_scans DESC
    

Bloqueos

Hay datos que son actualizados por varias sesiones concurrentemente, lo que provoca esperas😦
Consulta DMV para detectarlos:

select
conn.session_id as blockerSession,
conn2.session_id as BlockedSession,
req.wait_time as Waiting_Time_ms,
cast((req.wait_time/1000.) as decimal(18,2)) as Waiting_Time_secs,
cast((req.wait_time/1000./60.) as decimal(18,2)) as Waiting_Time_mins,
t.text as BlockerQuery,
t2.text as BlockedQuery
from sys.dm_exec_requests as req
inner join sys.dm_exec_connections as conn
	on req.blocking_session_id=conn.session_id
inner join sys.dm_exec_connections as conn2
	on req.session_id=conn2.session_id
cross apply sys.dm_exec_sql_text(conn.most_recent_sql_handle) as t
cross apply sys.dm_exec_sql_text(conn2.most_recent_sql_handle) as t2

Timeouts

Consultas que tardan en exceso o bloqueos que duran en exceso (deadlocks).

Falta de Mantenimiento

Con el uso la base de datos se debe realizar acciones de mantenimiento, para evitar perdida de rendimiento.

  • Mantenimiento de indices. Al realizar inserciones y borrados de datos los indices se van degradando (fragmentando). La siguiente consulta permite obtener el grado de fragmentación de los indices de la base de datos. Si este es elevado (por ejemplo, superior al 20%) deberíamos reconstruir el indice (ALTER INDEX {0} ON {1} REBUILD WITH (STATISTICS_NORECOMPUTE = ON, ONLINE=ON). Más información en el enlace Mantenimiento de indices.

    --- For Instance, indexes fragmented over 20 percent
    SELECT name, avg_fragmentation_in_percent
    FROM sys.dm_db_index_physical_stats (
           DB_ID(N'DatabaseName')
         , OBJECT_ID('[NombreDeBaseDatos]')
         , NULL
         , NULL
         , NULL) AS a
    JOIN sys.indexes AS b 
    ON a.object_id = b.object_id AND a.index_id = b.index_id
    where avg_fragmentation_in_percent >20
    order by avg_fragmentation_in_percent DESC
    

    También es importante que las estadísticas del indice estén actualizadas, de cara a elaborar planes de ejecución óptimos.

Conexiones

Establecer conexiones requiere de múltiples idas y vueltas en la red; proceso más lento en la nube. Además SQL Azure aborta (throttle) las conexiones si hay un numero elevado o estas consumen elevados recursos. Debemos proteger la aplicación ante el aborte de las conexiones:

Debemos evitar en lo posible el throttling ya que requiere de restablecer la conexión y volver a ejecutar la consulta. La información de conexiones la tenemos disponible:

  • En el SQL Database Advisor podemos añadir las métricas conexiones activas, falladas y deadlocks.
  • Consulta que muestra el sumario de las conexiones cada 5 minutos.
    -- Execute on Master DB
    select *
    from sys.database_connection_stats 
    where database_name = '[NombreDeBaseDatos]'
    order by start_time desc
    

    Información más detallada por grupo de conexión para encontrar las causas😉

    -- Execute on Master DB
    select *
    from sys.event_log 
    where database_name = '[NombreDeBaseDatos]'
    order by start_time desc
    

Aplicar Funciones Avanzadas

  • Optimización en Memoria (In-Memory OLTP): Obtiene mejoras de rendimiento al trabajar en memoria: tablas (Memory-optimized tables) y la compilación nativa de stores procedures (Natively compiled modules).
  • Active Geo-Replication: Se replica la base de datos activa y se crean hasta 4 base de datos secundarias en modo lectura.
Publicado en Sql Azure, Sql Server | 1 Comentario