Agrupación de conexiones en SQL Server (ADO.NET)
Actualización: November 2007
La conexión a un servidor de base de datos consta normalmente de varios pasos que requieren mucho tiempo. Se debe establecer un canal físico, como un socket o una canalización con nombre, debe tener lugar el protocolo de enlace con el servidor, se debe analizar la información de la cadena de conexión, el servidor debe autenticar la conexión, se deben ejecutar comprobaciones para la inscripción en la transacción actual, etc.
En la práctica, la mayoría de las aplicaciones solamente utilizan unas cuantas configuraciones diferentes para las conexiones. Esto significa que durante la ejecución de la aplicación, muchas conexiones idénticas se abrirán y cerrarán de forma repetida. Para reducir el costo de la apertura de conexiones, ADO.NET emplea una técnica de optimización denominada agrupación de conexiones.
La agrupación de conexiones reduce el número de veces que es necesario abrir nuevas conexiones. El concentrador mantiene la propiedad de la conexión física. Para administrar las conexiones, mantiene un conjunto de conexiones activas para cada configuración de conexión dada. Cada vez que un usuario llama a Open en una conexión, el agrupador comprueba si hay una conexión disponible en el grupo. Si hay disponible una conexión agrupada, la devuelve a la persona que llama en lugar de abrir una nueva. Cuando la aplicación llama a Close en la conexión, el agrupador la devuelve al conjunto agrupado de conexiones activas en lugar de cerrarla. Una vez que la conexión vuelve al grupo, ya está preparada para volverse a utilizar en la siguiente llamada a Open.
Sólo se pueden agrupar conexiones con la misma configuración. ADO.NET mantiene varios grupos de forma simultánea, uno para cada configuración. Las conexiones se dividen en grupos por cadena de conexión, y por identidad de Windows si se utiliza seguridad integrada. Las conexiones también se agrupan en función de si están incluidas en una transacción.
La agrupación de conexiones puede mejorar de forma significativa el rendimiento y la escalabilidad de la aplicación. De forma predeterminada, la agrupación de conexiones está habilitada en ADO.NET. A menos que la deshabilite explícitamente, el agrupador optimiza las conexiones a medida que se abren y cierran en la aplicación. También puede proporcionar varios modificadores de cadena de conexión para controlar el comportamiento de agrupación de conexiones. Para obtener más información, vea "Control de la agrupación de conexiones con palabras clave de cadena de conexión" más adelante en este tema.
Creación y asignación del grupo
Cuando se abre una conexión por primera vez, se crea un grupo de conexión basado en un algoritmo de coincidencia exacta que asocia el grupo con la cadena de conexión de la conexión. Cada grupo de conexión se asocia con una cadena de conexión distinta. Si se abre una nueva conexión y la cadena de conexión no coincide exactamente con un grupo existente, se crea un nuevo grupo. Las conexiones se agrupan por proceso, por dominio de aplicación, por cadena de conexión y, cuando se utiliza seguridad integrada, por identidad de Windows. Las cadenas de conexión también deben ser una coincidencia exacta; las palabras clave indicadas en un orden diferente para la misma conexión se agruparán por separado.
En el siguiente ejemplo con C#, se crean tres nuevos objetos SqlConnection, pero sólo se necesitan dos grupos de conexión para administrarlos. Observe que las cadenas de conexión primera y segunda difieren en el valor asignado a Initial Catalog.
using (SqlConnection connection = new SqlConnection(
"Integrated Security=SSPI;Initial Catalog=Northwind"))
{
connection.Open();
// Pool A is created.
}
using (SqlConnection connection = new SqlConnection(
"Integrated Security=SSPI;Initial Catalog=pubs"))
{
connection.Open();
// Pool B is created because the connection strings differ.
}
using (SqlConnection connection = new SqlConnection(
"Integrated Security=SSPI;Initial Catalog=Northwind"))
{
connection.Open();
// The connection string matches pool A.
}
Si no se especifica MinPoolSize en la cadena de conexión o se especifica como cero, las conexiones del grupo se cerrarán tras un período de inactividad. No obstante, si el MinPoolSize especificado es mayor que cero, el grupo de conexión no se destruye hasta que se descarga el AppDomain y finaliza el proceso. El mantenimiento de grupos inactivos o vacíos supone una sobrecarga mínima para el sistema.
Nota: |
---|
El grupo se borra automáticamente cuando se produce un error irrecuperable, como una conmutación por error. |
Adición de conexiones
Por cada cadena de conexión única se crea un grupo de conexión. Cuando se crea un grupo, se crean y agregan al grupo varios objetos de conexión y se satisface así el requisito de tamaño mínimo del grupo. Las conexiones se agregan al grupo cuando es necesario, hasta el tamaño máximo del grupo especificado (100 es el valor predeterminado), y se liberan de nuevo en el grupo cuando se cierran o eliminan.
Cuando se solicita un objeto SqlConnection, se obtiene del grupo si se encuentra disponible una conexión que se pueda utilizar. Una conexión de este tipo debe estar sin utilizar, tener un contexto de transacción coincidente o no estar asociada con ningún contexto de transacción y tener un vínculo válido al servidor.
El concentrador de conexión satisface las solicitudes de conexión al reasignar las conexiones conforme se liberan de nuevo en el grupo. Si se ha alcanzado el tamaño máximo del grupo y no hay disponible ninguna conexión que se pueda utilizar, la solicitud se pone en la cola. A continuación, el concentrador intenta reclamar las conexiones hasta que se agota el tiempo de espera (el valor predeterminado es 15 segundos). Si no puede satisfacer la solicitud antes de que se agote el tiempo de espera de la conexión, se inicia una excepción.
Precaución: |
---|
Se recomienda encarecidamente cerrar siempre la conexión cuando se termine de utilizar para que regrese al grupo. Para ello, puede utilizar los métodos Close o Dispose del objeto Connection, o abrir todas las conexiones dentro de una instrucción using en C#, o de una instrucción Using en Visual Basic. Es posible que las conexiones que no se cierran explícitamente no se puedan agregar ni puedan regresar al grupo. Para obtener más información, vea using (Instrucción, Referencia de C#) o Cómo: Deshacerse de un recurso del sistema para Visual Basic. |
Nota: |
---|
No llame a Close o a Dispose en un objeto Connection, un objeto DataReader o cualquier otro objeto administrado en el método Finalize de la clase. En un finalizador, libere sólo los recursos no administrados que pertenezcan directamente a su clase. Si la clase no dispone de recursos no administrados, no incluya un método Finalize en la definición de clase. Para obtener más información, vea Recolección de elementos no utilizados. |
Nota: |
---|
Los eventos de inicio y cierre de sesión no se producen en el servidor cuando se obtiene una conexión del grupo de conexiones o se devuelve a él. Esto se debe a que la conexión no se cierra realmente cuando se devuelve al grupo de conexiones. Para obtener más información, vea Audit Login (clase de evento) y Audit Logout (clase de evento) en los Libros en pantalla de SQL Server. |
Cómo quitar conexiones
El agrupador de conexiones quita una conexión del grupo después de haber estado inactiva durante un período de tiempo prolongado o si detecta que se ha roto la conexión con el servidor. Tenga en cuenta que una conexión rota sólo puede detectarse después de intentar comunicarse con el servidor. Si se encuentra que una conexión ya no está conectada al servidor, se marca como no válida. Las conexiones no válidas se quitan del grupo de conexión sólo cuando se cierran o reclaman.
Si existe una conexión en un servidor que ha desaparecido, se puede extraer del grupo aunque el agrupador de conexiones no haya detectado la conexión rota y la haya marcado como no válida. El motivo es que la sobrecarga de comprobar que la conexión es aún válida eliminaría los beneficios de tener un concentrador y ocasionaría que se produjera otro viaje de ida y vuelta (round trip) al servidor. Cuando esto ocurre, el primer intento para usar la conexión detectará que ésta se ha roto y se iniciará una excepción.
Borrado del grupo
ADO.NET 2.0 introdujo dos nuevos métodos para borrar el grupo: ClearAllPools y ClearPool. ClearAllPools borra los grupos de conexiones de un proveedor dado y ClearPool borra el grupo de conexiones que está asociado a una conexión concreta. Si en el momento de la llamada se están usando conexiones, se marcan de forma adecuada. Cuando se cierran, se eliminan en lugar de devolverse al grupo.
Compatibilidad con transacciones
Las conexiones se extraen del grupo y se asignan en función del contexto de transacción. A menos que se especifique Enlist=false en la cadena de conexión, el grupo de conexión garantiza que la conexión está dada de alta en el contexto de Current. Cuando se cierra una conexión y se devuelve al grupo con una transacción System.Transactions dada de alta, se reserva de forma que la siguiente solicitud de ese grupo de conexiones con la misma transacción System.Transactions devolverá la misma conexión, si está disponible. Si se emite dicha solicitud y no hay conexiones agrupadas disponibles, se extrae una conexión de la parte sin transacción del grupo y se le da de alta. Si no hay conexiones disponibles en cualquier área del grupo, se crea y da de alta una nueva conexión.
Cuando se cierra una conexión, se libera de nuevo en el grupo y en la subdivisión adecuada en función de su contexto de transacción. Por lo tanto, puede cerrar la conexión sin generar un error, incluso aunque aún haya pendiente una transacción distribuida. Esto permite confirmar o anular la transacción distribuida más adelante.
Control de la agrupación de conexiones con palabras clave de cadena de conexión
La propiedad ConnectionString del objeto SqlConnection admite pares de clave y valor de cadena de conexión que se pueden utilizar para ajustar el comportamiento de la lógica de agrupación de conexiones. Para obtener más información, vea ConnectionString.
Fragmentación de grupos
La fragmentación de grupos es un problema común en muchas aplicaciones web en las que la aplicación puede crear gran cantidad de grupos que no están libres hasta que finaliza el proceso. El resultado es un gran número de conexiones abiertas que consumen memoria, lo que da lugar a un bajo rendimiento.
Fragmentación de grupos debido a la seguridad integrada
Las conexiones se agrupan de acuerdo con la cadena de conexión y la identidad del usuario. Por lo tanto, si utiliza autenticación básica o autenticación de Windows en el sitio web y un inicio de sesión de seguridad integrada, obtendrá un grupo por usuario. Aunque de esta manera se mejora el rendimiento de las posteriores solicitudes de base de datos de un solo usuario, ese usuario no podrá aprovechar las conexiones realizadas por otros usuarios. Además, como resultado habrá una conexión como mínimo por usuario al servidor de base de datos. Se trata de un efecto secundario de una determinada arquitectura de aplicaciones web que los programadores deben sopesar frente a los requisitos de seguridad y auditoría.
Fragmentación de grupos debido a muchas bases de datos
Muchos proveedores de servicios Internet hospedan varios sitios web en un único servidor. Puede que utilicen una sola base de datos para confirmar un inicio de sesión de autenticación de formularios y luego abran una conexión a una base de datos específica para ese usuario o grupo de usuarios. La conexión a la base de datos de autenticación es agrupada y utilizada por todo el mundo. Sin embargo, hay un grupo independiente de conexiones con cada base de datos, lo que implica un aumento del número de conexiones con el servidor.
Este es también un efecto secundario del diseño de la aplicación. Existe, sin embargo, una forma relativamente sencilla de evitarlo sin comprometer la seguridad cuando se establece conexión con SQL Server. En lugar de realizar una conexión a una base de datos diferente por cada usuario o grupo, realice una conexión a la misma base de datos en el servidor y, luego, ejecute la instrucción USE de Transact-SQL para cambiar a la base de datos deseada. En el siguiente fragmento de código se muestra la creación de una conexión inicial con la base de datos master y el cambio a la base de datos deseada especificada en la variable de cadena databaseName.
' Assumes that command is a valid SqlCommand object and that
' connectionString connects to master.
command.Text = "USE DatabaseName"
Using connection As New SqlConnection(connectionString)
connection.Open()
command.ExecuteNonQuery()
End Using
// Assumes that command is a SqlCommand object and that
// connectionString connects to master.
command.Text = "USE DatabaseName";
using (SqlConnection connection = new SqlConnection(
connectionString))
{
connection.Open();
command.ExecuteNonQuery();
}
Funciones de aplicación y agrupación de conexiones
Una vez activada una función de aplicación de SQL Server al llamar al procedimiento almacenado de sistema sp_setapprole, no se puede restablecer el contexto de seguridad de la conexión. Sin embargo, cuando se habilita la agrupación, la conexión se devuelve al grupo y se produce un error al utilizar de nuevo la conexión agrupada. Para obtener más información, vea el artículo de Knowledge Base "SQL application role errors with OLE DB resource pooling".
Alternativas a las funciones de aplicación
Si usa SQL Server 2005, se recomienda aprovechar las ventajas de los nuevos mecanismos de seguridad que se pueden usar en lugar de las funciones de la aplicación. Para obtener más información, vea Crear funciones de aplicación en SQL Server (ADO.NET).
Vea también
Conceptos
Contadores de rendimiento (ADO.NET)