Acerca de admin

Tras más de 20 años trabajando con tecnologías Oracle, me decidí a recopilar en un Blog algunas de las cosillas útiles para el día a día.

expdp de una particion

Hoy vamos a ver otra entrada para dummies, esta vez una entrada rápida y sencilla sobre el exdp

¿Como podemos hacer para exportar los datos de una partición de una de las tablas de la base de datos?

La respuesta es muy sencilla, ya que, la opción TABLE nos permite mediante el uso de : indicar que lo que queremos extraer de esa tabla es una partición determinada.
Así pues para sacar una o varias particiones de una tabla, el comando sería :

expdp dba_user/password dumpfile=expdppart.dmp 
tables=(
schema_name.tablename:partition_name1,\
schema_name.tablename:partition_name2,\
schema_name.tablename:partition_name3,\
schema_name2.tablename:partition_name \
)

IMPORTANTE
Recordad que, a pesar de que hacer el export es tan sencillo, como vimos en una entrada anterior No se puede importar una partición por separado

Capturar errores de usuario en la base de datos

Hoy vamos a ver como facilitarnos un poco el control del código de aplicación de la base de datos.

En muchas ocasiones, los desarrolladores no llevan a cabo correctamente la captura de errores , lo que produce una serie quejas «no me va y no me da error».
Para lidiar con estas situaciones podemos crear un pequeño trigger que registre en la base de datos que y quien genera errores en la base de datos, para poder seguirlos y depurar estos errores.

Vamos a suponer que:

  • Tenemos un tablespace específico llamado CONTROL donde guardaremos nuestros datos
  • Tenemos un usuario llamado CONTROL que será el propietario del código

Primero crearemos la tabla que contendrán los errores:

-- Creamos la tabla 
create table CONTROL.oraerror (
identificador   NUMBER,
FECHA           DATE,
ESQUEMA         VARCHAR2(30),
USUARIO         VARCHAR2(50),
SQL_ID          VARCHAR2 (13) DEFAULT NULL,
SQL_HASH_VALUE  NUMBER DEFAULT NULL,
err_nr          NUMBER(10),
err_msg         VARCHAR2(4000),
stmt            CLOB
) TABLESPACE CONTROL;

--
-- Creamos una secuencia para el oraerror
create sequence CONTROL.oraerror_seq
start with 1
increment by 1
minvalue 1
nomaxvalue
nocache
nocycle;

Y después crearemos nuestro trigguer

CREATE OR REPLACE TRIGGER CONTROL.after_error
 AFTER SERVERERROR ON DATABASE
 DECLARE
 id             NUMBER;
 vSerial        NUMBER DEFAULT NULL;
 ESQUEMA        VARCHAR2(30);
USUARIO         VARCHAR2(50);
 SQL_ID         VARCHAR2 (13) DEFAULT NULL;
 SQL_HASH_VALUE NUMBER DEFAULT NULL;
  sql_text      ORA_NAME_LIST_T;
 v_stmt         CLOB;
 n              NUMBER;
 vsid           NUMBER DEFAULT NULL;
BEGIN
 -- BUscamos ID 
SELECT dbms_debug_jdwp.current_session_serial serial# INTO vSerial 
      FROM DUAL;
SELECT oraerror_seq.nextval,SYS_CONTEXT ('USERENV', 'sid') INTO  id, vsid
     FROM dual;
SELECT  SQL_ID, SQL_HASH_VALUE,USERNAME,OSUSER INTO SQL_ID, SQL_HASH_VALUE,ESQUEMA,USUARIO
   FROM sys.v_$session   WHERE sid = vsid AND serial# = vSerial;
n:= ora_sql_txt(sql_text);

IF n >= 1
 THEN
 FOR i IN 1..n LOOP
 v_stmt := v_stmt || sql_text(i);
 END LOOP;
END IF;
--
 FOR n IN 1..ora_server_error_depth LOOP
      INSERT  /*+ APPEND */ 
      INTO CONTROL.oraerror VALUES(id, sysdate, ESQUEMA, USUARIO, SQL_ID,SQL_HASH_VALUE,  ora_server_error(n), ora_server_error_msg(n), v_stmt);
  END LOOP;
 --
END ;

Con esto tendrémos en nuestra tabla todos los errores que dela base de datos.
Hay que tener en cuenta que, muchos de los errores pueden ser correctos, como los errores :

  • ORA-01017: nombre de usuario/contraseña no válidos; conexión denegada
  • ORA-00001: unique constraint (XXXX) violated (error asumible en cargas masivas)
  • ORA-03113: end-of-file on communication channel ORA-03113: end-of-file on communication channel

Con lo que habrá que seguirlos con cuidado y ver en que caso son reprochables o asumibles

Vuelta tras de una base de datos a una fecha con rman

Revisando las entradas de RMAN veo que nos falta una entrada para el caso mas común y mas sencillo de todos, volver la base de datos a una determinada fecha.
La manera mas cómoda de hacer esto desde la versión 11g es hacerlo con un flashback database pero, por si no pudiese hacerse, vamos a explicar la recuperación mas sencilla que hay.

Supuesto

Nos encontramos en el caso en el que no hemos perdido nada en la base de datos pero debemos de hacer una marcha atrás en el tiempo de la base de datos a un momento anterior a 7 días(o la etencion del backup del controlfile).
En este caso disponemos en el servidor de todos los elementos de la base de datos ( passwd,spfile,controlfile….) pero los datos no no son válidos.

Pasos previos

En este caso y para garantizar que si fallamos en el proceso podemos repetirlo guardaremos el controlfile.
Este paso es de suma importancia ya que, al no tener base de datos de catálogo de RMAN si perdiésemos el controlfile perderíamos toda la información del RMAN
Para ello haremos dos acciones:

Copia del controlfile a texto

alter database backup controlfile to  trace as ‘….\CONTROLFILE.TXT’;

Copia física del controlfile

Como decíamos anteriormente, el controlfile el único elemento de la base de datos en el que mantenemos la información de donde esta el catálogo de rman, así que, pararemos la base de datos y copiaremos los 3 controlfiles desde su ubicación en los discos a un directorio dedicado creado para esta copia
El contenido de los 3 controlfiles es exactamente el mismo, con lo que, al copiar los 3 estamos haciendo 3 backups

Recuperacion

Una vez hemos guardado nuestros controlfiles para tener las espaldas cubiertas, procederemos a recuperar la base de datos a el momento en que queremos.
Para ello, crearemos un script de rman llamado recuperacion.cmd con el contenido :

startup mount;
RUN {
SET until time="TO_DATE('23/02/15 21:00:00','DD/MM/YY hh24:mi:ss')";
allocate channel DEV0 type SBT_TAPE PARMS 'ENV=(XXXXXXXXXXXXXXX)';
restore database;
recover database;
 }

Donde ENV=(XXXXXXXXXXXXXXXXXX) dependerá de la integración de backup que se use.
Y lo ejecutaremos con el comando

ORACLE_SID=XXX
rman cmdfile restauracion.cmd log=estado_restauracion.log

Apertura

Al estar haciendo una recuperación de la base de datos incompleta deberemos de abrir la base de datos en modo resetlogs, para ello, desde la línea de comandos

 ORACLE_SID=XXX
Sqlplus “/as sysdba”
ALTER DATABASE OPEN RESETLOGS;

Y con esto tendremos la base de datos recuperada a la fecha que buscábamos.
Como veis, al no tener que conocer DBIDs, ni recuperar controlfiles u spfiles, el

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