Funcionamiento del Redo en el RAC

Hoy vamos con otra de las entradas para dummies, viendo un poco el funcionamiento del redo en el RAC.
Cada instancia dentro del RAC debe de tener su propio espacio de redo (que se corresponderá con un número único de thread para toda la instancia) y undo.

Pero que ocurre si muere un nodo?
¿Que pasa con los datos que están en esos redos?

En un entorno de RAC, todas las instancias de la base de datos tienen acceso a todos los redo logs de todos los nodos, de esta manera, si uno de los nodos muere, uno de los nodos vivos accederá a el redo de la instancia caída y aplicará de manera automática los cambios de la misma manera que se haría un instance recovery a la hora de arrancar la base de datos. Con lo que los datos en disco siempre estarán consistentes.

¿Que ocurre si caen todos a la vez?
Si todas las instancias cayeran el instance recovery sería llevado a cabo por la primera de las instancias que se levantara, esta sería la encargad de hacer el instance recovery de todos los redos de todas las instancias del rac.

Como veis, a pesar de la complejidad del RAC, el funcionamiento no deja de ser muy sencillo, al menos, visto desde arriba 😉

Purgando diag con adrci

Hoy vamos a ver una entrada sencillita en la que haremos un bash-script que nos limpie de manera ordenada el contenido del diag

El comando ADRCi permite la ejecución de comandos batch bien sea mediante la cláusula exec o bien contenidos en un fichero de texto separados por punto y coma.
Lo que vamos ha hacer es ejecutar para cada uno de los homes del la base de datos, asm y listener, el comando purge para cada uno de los tipos de elementos que guarda el diagnostics_dest

En nuestro caso, usaremos el modo script pero podíamos haber puesto todos los comandos en una línea separada por puntos y coma y el funcionamiento sería el mismo.

El script que vacia el diagnostics_dest para todo lo anterior a X días es:

#!/bin/bash
#
# Script que purga del ADRCLI  para  $1 DIAS
#
#
#
#
export ORAENV_ASK=NO
. oraenv  1>/dev/null
# Salida de debug
DEBUG=0

 #Comprobamos que se ha llamado de manera correcta
if  [ $# -ne 1 ]; then
echo "Uso Purgar_logs DIAS "
exit 3;
fi

# Inicializamos valores para el script
export TEMPORAL=/var/tmp/purgar_temporal_$$.log
export DIAS=$(($1*1440))

## Comenzamos la ejecucion
if [ $DEBUG -eq 1 ]; then
        echo "DEBUG: ORACLE_BASE=$ORACLE_BASE"
        echo "DEBUG: TEMPORAL= ${TEMPORAL} "
        echo "DEBUG: Dias= $2   en min $DIAS"
fi
# Comenzamos las iteraciones
adrci_homes=( $(adrci exec="show homes" | grep -e rdbms -e asm -e tnslsnr))
   for adrci_home in ${adrci_homes[@]}
   do
          #Generamos el fichero de comandos
         echo "set home ${adrci_home} ;" > ${TEMPORAL}
         echo "echo \"Purgamos en  ${adrci_home} \""  >> ${TEMPORAL}
         echo "purge -age ${DIAS} -type ALERT ;" >> ${TEMPORAL}
         echo "purge -age ${DIAS} -type TRACE ;" >> ${TEMPORAL}
         echo "purge -age ${DIAS} -type INCIDENT ;" >> ${TEMPORAL}
         echo "purge -age ${DIAS} -type HM ;" >> ${TEMPORAL}
         echo "purge -age ${DIAS} -type  utscdmp;" >> ${TEMPORAL}
         echo "purge -age ${DIAS} -type  cdump;" >> ${TEMPORAL}
         echo "exit; " >> ${TEMPORAL}

       if [ $DEBUG -eq 1 ]; then
        echo "DEBUG: "
        echo "DEBUG:  adrci SCRIPT=${TEMPORAL}"
        echo "DEBUG: EL etemporal es "
        echo "DEBUG: -inicio temporal-"
         cat ${TEMPORAL}
         echo "DEBUG: -fin temporal-"
        fi
        adrci  SCRIPT=${TEMPORAL}
         rm ${TEMPORAL}
    done #Fin del bucle

Como véis, es extremadamente sencillo, y bastante mas limpio que hacer el borrado a mano

Uso de mayúsculas/minúsculas en ASM

Vamos a ver una entrada muy cortita sobre el uso del ASM.

¿Son los ficheros/directorios del ASM sensibles a las mayúsculas y minúsculas?

Si miramos la documentación del ASM de Oracle nos dice una frase algo enigmática
«Only the forward slash (/) is supported by ASMCMD. Filenames are not case sensitive, but are case retentive. If you type a path name as lowercase, ASMCMD retains the lowercase. «

Lo que quiere decir es que, los nombres de los ficheros (y directorios) de ASM no son case sensitive, pero, las cadenas de texto con la que nos lo muestra si.

Vamos a verlo con un ejemplo:

Supongamos tenemos ASM con una base de datos llamada SID.
He creado dos tablespaces con datafiles en SID y siD y no me ha dado error en ninguno de los dos

create tablespace prueba1 datafile '+DATA/SID/prueba1.dbf' size 3m;
create tablespace prueba2 datafile '+DATA/siD/prueba2.dbf' size 3m;

Sin embargo, si intento crear

create tablespace prueba3 datafile '+DATA/SID/prueba2.dbf' size 3m;

Me da error por que ya existe el mismo fichero con el directorio siD

Conclusión: ASM no es «case sensitive», pero hemos de ser muy cuidadosos a la hora de elegir una política para la notación de los ficheros ya que el uso indiscriminado de unos y otros puede provocarnos errrores.

Sintaxis del EXDP desde comando vs parfile

Volvemos a una entrada de ayuda para dummies.
Muchas veces perdemos un montón de tiempo haciendo prueba y error con las comillas en los scripts de expdp e impdp
En esta entrada cortita, vamos a poner el mismo script de import en parfile y linea de comandos

Parfile

$ORACLE_HOME/bin/expdp system/blabla parfile  fichero_opciones.par 

Donde el parfile es :

DIRECTORY=DATA_PUMP_DIR
DUMPFILE=fichero_expdp.dmp
LOGFILE=expdp.log
FULL=Y
EXCLUDE=TABLE:"IN ('NOMINAS', 'CHISTES' , 'GAZAPOS')"
EXCLUDE=SCHEMA:"IN ('BLOG','PORRA')"

Línea de comandos
En línea de comandos, simplemente escaparemos las comillas simples y dobles (especialmente si vamos a incluirlo en algún script)

$ORACLE_HOME/bin/expdp system/blabla DIRECTORY=DATA_PUMP_DIR DUMPFILE=fichero_expdp.dmp LOGFILE=expdp.log FULL=Y EXCLUDE=TABLE:\"IN (\'NOMINAS\', \'CHISTES\' , \'GAZAPOS\')\"  EXCLUDE=SCHEMA:\"IN (\'BLOG\',\'PORRA\')\"

Escribir la salida de un sql en un fichero con UTL_FILE

Vamos con otra entrada básica para dummies.

Supongamos que queremos hacer implantar algún tipo de sonda que nos haga una pregunta a la base de datos y nos deje el resultado en un directorio.
Vamos a crear un pequeño procedimiento que, a modo de ejemplo nos saque algunos parámetros del v$session y nos lo deje en un directorio previamente definido llamado SONDA

CREATE OR REPLACE PROCEDURE proc_a_file AS
     fichero utl_file.file_type;
     nombre_fichero  VARCHAR2(255);
     linea_resul  VARCHAR2(255);
        BEGIN
  -- Definimos el nombre del log de salida
     select  'sonda_'||
              to_char(TO_CHAR(sysdate, 'YYYYMMDDHHmiss'))||
             '.log'
              into nombre_fichero  from dual  ;
  -- Abrimos fichero      
       fichero := utl_file.fopen('SONDA', nombre_fichero, 'A');
  -- Añadimos encabezado   
           UTL_FILE.PUT_LINE ( fichero ,'SID, SERIAL,TERMINAL,OSUSER');
  -- Lanzamos consulta        
                     FOR ciclo IN (
                         SELECT
                              select sid SID,
                              serial#  SERIAL,
                              terminal TEMINAL,
                              osuser  USER  
                         from v$session
                               )
                  LOOP  --Bucle de scritura de datos 
                 UTL_FILE.PUT_LINE ( fichero ,ciclo.SID||
                                              chr(44)|| 
                                             ciclo.SERIAL||
                                              chr(44)|| 
                                            ciclo.TERMINAL||
                                            chr(44)||
                                            ciclo.USER);
                 END LOOP;         
-- Limpiamos y cerramos
          utl_file.fflush(fichero);
        utl_file.fclose(fichero);
-- Excepciones de error en el alert.log
    EXCEPTION
    WHEN UTL_FILE.WRITE_ERROR THEN
    dbms_system.ksdwrt(2, 'ORA-XXXX  Error en procedure proc_a_file escrbiendo en fichero de log.');
    WHEN others THEN
       dbms_system.ksdwrt(2, 'ORA-XXXX  Error genérico en procedure   proc_a_file. '||SQLERRM);
      END proc_a_file; -- En del procedure

Ahora ya podemos lanzar nuestro procedimiento, bien desde el código de aplicación, cron, task manager, trigger o desde el lugar que deseemos