viernes, 19 de septiembre de 2008

Oracle RAC - Balanceo de Sesiones

Uno de los puntos clave para llevar a cabo una implementación exitosa de una aplicación conectada a una base de datos Oracle en Real Application Cluster es entender y configurar el balanceo de las sesiones y la alta disponibilidad que el ambiente RAC nos ofrece.

Este post esta dedicado a entender el balanceo de sesiones. La configuración de Alta Disponibilidad será tratada en otro momento.

Comunmente, pueden existir problemas debidos a las diferentes aplicaciones existentes y a su naturaleza de conexión con la base de datos. Entre los problemas más comunes que me ha tocado observar se encuentran los siguientes:

  • Las aplicaciones no pueden conectarse al ambiente en modo RAC. Únicamente se pueden conectar a uno de los nodos.
  • Uno de los nodos es quien procesa la mayor parte de la carga mientras que el/los otros(s) nodo(s) prácticamente no realizan ningún trabajo.

A continuación explicaré de que forma Oracle realiza el balanceo de carga en un entorno Real Application Clusters, y para ello partiré de las siguientes primicias:

  • Todos los tipos de balanceo disponibles (9i – 10g, ya que no he probado en 11g) ocurren al momento de iniciar la conexión.
  • Las buenas aplicaciones se conectan una sola vez y permanecen conectadas (El costo de establecer una conexión con la base de datos es muy alto).

Ahora bien, los diferentes tipos de balanceo existentes son:

  • Aleatorio. Es configurado a nivel cliente en la cadena de conexión o mediante hardware de balanceo y aleatoriamente distribuyen la conexión a las diferentes instancias. La parte negativa de este método es que no se toma en cuenta la carga en el nodo seleccionado o bien si el nodo está disponible, por lo cual pudieran causarse 'timeouts' a nivel TCP/IP.
    Para confiugurar este método se debe crear una cadena de conexión como la siguiente:
    RAC10G =
    (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = srvrac1vip.oratest.com)(PORT = 1521))
    (ADDRESS = (PROTOCOL = TCP)(HOST = srvrac2vip.oratest.com)(PORT = 1521))
    (LOAD_BALANCE=yes)
    (CONNECT_DATA =
    (SERVER = DEDICATED)
    (SERVICE_NAME = rac10g.oratest.com)
    )
    )

  • Basado en la carga del nodo. El balanceo se realiza a nivel listener y es el método predefinido de Oracle. Este método redirige las conexiones dependiendo del perfil de carga que el PMON de cada una de las instancias reporta dinámicamente al listener. La frecuencia de actualización del valor del perfil de carga depende de la carga misma, es decir, entre mayor sea la carga mayor es la frecuencia con la que el PMON actualiza el perfil de carga. Sin embargo, lo anterior está basado en la carga total en el NODO, no en la carga de las sesiones.
    Este método puede resultar muy bueno para conexiones que tienen una vida corta pero puede degradar el desempeño para las conexiones persistentes cuando la carga cambia a través del tiempo, es decir, el balanco inicial puede resultar finalmente desbalanceado.
    Este metodo no se recomienda para servidores de aplicación o metodos de conexión a través de un 'pool' de conexiones.

  • Basado en el número de sesiones. El balanceo se realiza a nivel listener y es utilizado para distribuir el número de conexiones en cada instancia tomando en cuenta únicamente el número de sesiones conectadas a cada uno de los nodos.
    Este método permite evitar las llamadas “tormentas de conexiones” las cuales no son más que muchas conexiones haciendo una operación de 'logon' en un intervalo muy pequeño de tiempo.
    PMON registra la información de carga de nodo aproximadamente cada 60 segundos en promedio y es por este motivo que las "tormentas de conexiones" no pueden ser bien balanceadas mediante el metodo basado en la carga del nodo. El impacto directo de las "tormentas de conexiones" es una gran pérdida de rendimiento en el nodo afectado y como consecuencia una pérdida general de rendimiento y escalabilidad del cluster.
    Para configurar este método es necesario definir el siguiente parámetro del listener.
    PREFER_LEAST_LOADED_NODE_ =OFF

Ahora bien, entendamos como funciona el proceso de conexión a una base de datos en RAC:

  1. El cliente desea iniciar una conexión a la base de datos y busca en su cadena de conexión la dirección correspondiente a la instancia.
    Si la cadena de conexión tiene configurado un balanceo, aleatoriamente seleccionará uno de los destinos configurados y enviará la petición de conexión.
  2. Cuando el listener recibe la petición de conexión a la instancia, verificará de acuerdo al método configurado (B o C) a cual de las instancias se debe asignar la conexión.
  3. Si la instancia en el nodo local es la candidata para la conexión, entonces el listener inicia el proceso de autenticación.
  4. Si la instacia candidata para la conexión se encuentra en otro nodo, el listener regresa la cadena de conexión hacia el listener correspondiente al que el cliente deberá intentar conectarse e iniciar el proceso de autenticación.

En conclusión, es necesario llevar a cabo un estudio en función del tipo de aplicaciones que se desplieguen en un entorno RAC y cuidadosamente seleccionar la opción a utilizar. Aquí, la recomendación por mi parte es utilizar una combinación de la opción A con alguna de las otras dos opciones (B o C).


Referencias
=========

  • Metalink nota 300903.1
  • Oracle® Database Net Services Administrator’s Guide

6 comentarios:

Unknown dijo...

Hola Juan Jose,

Acabo de leer tus dos documentos sobre Oracle RAC: TAF y Balanceo de Sesiones. Me parecen dos artículos muy precisos.

Me queda una pequeña duda: ¿hay que configurar los listener? ¿cómo?

Muchas gracias,
Jose

Juan J. Martínez dijo...

Hola Jose Luis,

Si el tipo de balanceo que quieres hacer en el RAC es por carga en los nodos, no habría que configurar nada a nivel listener, ya que este es el balanceo por defecto, unicamente es necesario asegurarnos que el parámetro remote_listener de las instancias tenga el valor adecuado. Este parametro hace que la instancia se registre con cada uno de los listeners del RAC.

Por el contrario, si deseas balanceo por numero de sesiones, si necesitas agregar el siguiente parametro en el listener.ora PREFER_LEAST_LOADED_NODE_ =OFF

Para 10gR2 y posteriores, se incluyó el "Load Balancing Advisory" a nivel instancia. Más información al respecto puede ser encontrada en la nota 552609.1 de metalink.

Saludos,

Juan J. Martínez dijo...

Un ejemplo de configuración de balanceo por carga para un RAC de 2nodos:

1) INFORMACIÓN GENERAL
Nodo1:
Hostname: node1.idc.oracle.com
VIP Hostname: node1-vip.idc.oracle.com
Database Service_names: service.idc.oracle.com
SID: sid1

Node2:
Hostname: node2.idc.oracle.com
VIP Hostname: node2-vip.idc.oracle.com
Database Service_names: service.idc.oracle.com
SID: sid2


2) CONFIGURACIÓN DE NODO1
Listener.ora
LISTENER_NODE1=
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = node1-vip.idc.oracle.com)(PORT = 1521)(IP=FIRST))
(ADDRESS = (PROTOCOL = TCP)(HOST = node1.idc.oracle.com)(PORT = 1521)(IP=FIRST))
)
)

Tnsnames.ora
NODE1_LOCAL=
(ADDRESS = (PROTOCOL = TCP)(HOST=node1-vip.idc.oracle.com)(PORT = 1521))

Configurar el local_listener para el nodo1
SQL > alter system set LOCAL_LISTENER='node1_local' scope=both sid='sid1' ;


3) CONFIGURACIÓN DE NODO2
Listener.ora
LISTENER_NODE2=
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS=(PROTOCOL=TCP)(HOST=node2.idc.oracle.com)(PORT=1521)(IP=FIRST))
(ADDRESS=(PROTOCOL=TCP)(HOST=node2-vip.idc.oracle.com)(PORT=1521)(IP=FIRST))
)
)

Tnsnames.ora
NODE2_LOCAL=
(ADDRESS = (PROTOCOL = TCP)(HOST = node2-vip.idc.oracle.com)(PORT = 1521))

Configurar el local_listener para el nodo2
SQL > alter system set LOCAL_LISTENER='node2_local' scope=both sid='sid2' ;

4)CONFIGURACION DE BALANCEO POR CARGA
Añadir la siguiente entrada al tnsnames.ora de ambos nodos:

NODE_REMOTE =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST=node1-vip.idc.oracle.com)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST=node2-vip.idc.oracle.com)(PORT = 1521))
)
)

Configurar el parametro REMOTE_LISTENER
SQL> alter system set REMOTE_LISTENER='node_remote' scope=both;



Más información puede encontrarse en la nota 453293.1

Unknown dijo...

Juan Jose,

Gracias de nuevo ampliar la información.

Acabo de realizar una primera instalación de ORACLE RAC 11g y estos aspectos son nuevos para mí. Creo que la información que me has aportado me será de gran ayuda.

Saludos,
Jose Luis

Unknown dijo...

Hola Juan Jose,

Hoy he revisado la configuración de mi instalación y después de darle vueltas no estoy muy seguro de haber acertado...

Te adjunto la configuración de mis tnsnames y listener para que me la verifiques, sí no es mucha molestia.

Muchas gracias,
José Luis
TnsNames.ora en cliente
=======================
CBD =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD20-vip)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD21-vip)(PORT = 1521))
(LOAD_BALANCE = yes)
(FAILOVER = yes)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = CBD)
(failover_mode =
(type = select)
(method = basic)
(retries = 180)
(delay = 5)
)
)
)

CBD2 =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD21-vip)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = CBD)
(INSTANCE_NAME = CBD2)
)
)

CBD1 =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD20-vip)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = CBD)
(INSTANCE_NAME = CBD1)
)
)


TnsNames.ora en cada nodo
=========================
LISTENERS_CBD =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD20-vip)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD21-vip)(PORT = 1521))
)

CBD =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD20-vip)(PORT = 1521))
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD21-vip)(PORT = 1521))
(LOAD_BALANCE = yes)
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = CBD)


)
)

CBD2 =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD21-vip)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = CBD)
(INSTANCE_NAME = CBD2)
)
)

CBD1 =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD20-vip)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = CBD)
(INSTANCE_NAME = CBD1)
)
)



Listener.ora en cada nodo:
===========================
LISTENER_SSCCBD20 =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = sscCBD20-vip)(PORT = 1521)(IP = FIRST))
(ADDRESS = (PROTOCOL = TCP)(HOST = 10.30.121.20)(PORT = 1521)(IP = FIRST))
)
)

Unknown dijo...

Hola Juan Jose,

El ejemplo que te adjunte es válido según las pruebas que he realizado. Gestiona correctamente el balanceo por carga en los nodos y realiza correctamente el failover en caso de caida por el método select.

Gracias por compartir y "escuchar"...
José Luis