Seiten

Dienstag, 5. November 2013

APEX - Passwort speichern in der Login-Form erlauben

Standardmäßig verhindert APEX das Abspeichern von Passwörtern im Browser. Die Suche nach der Einstellung, die geändert werden muss, war etwas mühselig. Damit der nächste nicht so lang wie ich suchen muss, hier die Lösung: Das Speichern des Passwortes wird durch den Parameter autocomplete der Login-Form von APEX verhindert, der immer auf "off" gesetzt ist. Den Parameter ändert man weder im Template, noch in der Region und auch nicht über irgend welche CSS/HTML-Spielereien am Item. Nein, man geht im Applicationbuilder auf die Loginseite der Application und über Edit Page -> Security -> Form Auto Complete ändert man den Wert "off" auf "on". Apply Changes. Fertig.
Die Lösung findet man auch im Application Builder User's Guide - wenn man weiß, wonach man suchen muss.

Montag, 28. Oktober 2013

CRSCTL – RAC-Statusausgabe und -verwertung

Eine einfache Möglichkeit, den Status eines RAC auszugeben, bietet CRSCTL.
D:\>crsctl status resource –t 
oder kürzer:
D:\>crsctl stat res –t
--------------------------------------------------------------------------------
NAME           TARGET  STATE        SERVER                   STATE_DETAILS
--------------------------------------------------------------------------------
Local Resources
--------------------------------------------------------------------------------
ora.LISTENER.lsnr
               ONLINE  OFFLINE      rac2
--------------------------------------------------------------------------------
Cluster Resources
--------------------------------------------------------------------------------
ora.LISTENER_SCAN3.lsnr
      1        ONLINE  OFFLINE
ora.orcl.db
      1        OFFLINE OFFLINE                               Instance Shutdown
      2        OFFLINE OFFLINE
Dieser Befehl liefert - gesteuert durch den Parameter "-t" eine eingeschränkte tabellarische Ansicht aller Ressourcen. "-h" liefert möglicher weitere Parameter.
     resName [...]     Ein oder mehrere durch Leerzeichen getrennte Ressourcennamen
     -w                Ressourcenfilter (Beispiel: "TYPE = ora.database.type")
     -p                Gibt statische Konfiguration aus
     -v                Gibt Laufzeitkonfiguration aus\r
     -e                Wertet Sonderwerte einer Ressourceninstanz aus\r
     -f                Gibt vollständige Konfiguration aus\r
     -l                Gibt alle Kardinalitäts- und Grad-Member aus
     -g                Prüft, ob Ressourcen registriert sind\r
     -k                Kardinalitäts-ID\r
     -d                Grad-ID\r
     -n                Servername\r
     -s                Ruft Zielserver für Umspeichern ab\r
     -t                Tabellarische Anzeige
Will man das Ergebnis verskripten, ist die tabellarische Ausgabe ungeeignet. Man lässt besser den Schalter „-t“ weg.
D:\>crsctl stat res
NAME=ora.LISTENER.lsnr
TYPE=ora.listener.type
TARGET=ONLINE
STATE=OFFLINE

NAME=ora.LISTENER_SCAN3.lsnr
TYPE=ora.scan_listener.type
TARGET=ONLINE
STATE=OFFLINE

NAME=ora.orcl.db
TYPE=ora.database.type
TARGET=OFFLINE, OFFLINE
STATE=OFFLINE, OFFLINE
Dieses Ergebnis kann man schon besser in z.B. der Powershell verarbeiten.
Die Ausgabe ist allerdings im Normalfall allumfassend.

Filter - Ich will nur Probleme sehen!

Wenn man nicht die Dinge sehen will, die in Ordnung sind, sondern nur die, die Probleme bedeuten, kann die Ausgabe mit „-w“ gefiltert werden.
D:\skripte>crsctl stat res -t -w "STATE != ONLINE"
Oder
D:\skripte>crsctl stat res -t -w "TARGET != ONLINE"
Das lässt sich hervorragend kombinieren:
D:\skripte>crsctl stat res -t -w "(TARGET != ONLINE OR STATE != ONLINE)"
--------------------------------------------------------------------------------
NAME           TARGET  STATE        SERVER                   STATE_DETAILS
--------------------------------------------------------------------------------
Local Resources
--------------------------------------------------------------------------------
ora.LISTENER.lsnr
               ONLINE  OFFLINE      rac2
ora.gsd
               OFFLINE OFFLINE      rac1
               OFFLINE OFFLINE      rac2
ora.ons
               ONLINE  OFFLINE      rac2
--------------------------------------------------------------------------------
Cluster Resources
--------------------------------------------------------------------------------
ora.LISTENER_SCAN3.lsnr
      1        ONLINE  OFFLINE
ora.orcl.db
      1        OFFLINE OFFLINE                               Instance Shutdown
      2        OFFLINE OFFLINE
GSD und ONS sind uninteressant? OK:
D:\skripte>crsctl stat res -t -w "((TARGET != ONLINE) OR (STATE != ONLINE)) AND (NAME != ora.gsd) AND (NAME != ora.ons)"
Besser als NAME ist die Verwendung von TYPE:
D:\skripte>crsctl stat res -t -w "((TARGET != ONLINE) OR (STATE != ONLINE)) AND (TYPE != ora.gsd.type) AND (TYPE != ora.ons.type)"
--------------------------------------------------------------------------------
NAME           TARGET  STATE        SERVER                   STATE_DETAILS
--------------------------------------------------------------------------------
Local Resources
--------------------------------------------------------------------------------
ora.LISTENER.lsnr
               ONLINE  OFFLINE      rac2
-------------------------------------------------------------------------------
Cluster Resources
--------------------------------------------------------------------------------
ora.LISTENER_SCAN3.lsnr
      1        ONLINE  OFFLINE
ora.orcl.db
      1        OFFLINE OFFLINE                               Instance Shutdown
      2        OFFLINE OFFLINE 
Und das Ganze noch einmal ohne "-t":
D:\skripte>crsctl stat res -w "((TARGET != ONLINE) OR (STATE != ONLINE)) AND (TYPE != ora.gsd.type) AND (TYPE != ora.ons.type)"
NAME=ora.LISTENER.lsnr
TYPE=ora.listener.type
TARGET=ONLINE
STATE=OFFLINE

NAME=ora.LISTENER_SCAN3.lsnr
TYPE=ora.scan_listener.type
TARGET=ONLINE
STATE=OFFLINE

NAME=ora.orcl.db
TYPE=ora.database.type
TARGET=OFFLINE, OFFLINE
STATE=OFFLINE, OFFLINE

Weiterverarbeitung in der Powershell

Auf Grundlage einer gefilterten crsctl-Ausgabe lassen sich verschiedene Möglichkeiten umsetzen.
So könnte man einen Listener, der OFFLINE gegangen ist, neu starten oder den Admin per Email über Probleme benachrichtigen lassen.
Hier ein sehr minimalistisches Beispiel:
$a = crsctl stat res -w "((TARGET != ONLINE) OR (STATE != ONLINE)) AND ((TYPE = ora.listener.type) OR (TYPE = ora.scan_listener.type))";
$to      = "admin@meinefirma.de"
$from    = "rac@meinefirma.de"
$smtp    = "email.meinefirma.de"
$body    = '';
$subject = '';
 
if($a){
    $subject = "Listener OFFLINE!";
    $body    = "Einer oder mehrere Listener sind offline!`r`n`r`n"
    $body   += [string]$a -replace " ", "`r`n"
}
else{}

send-mailmessage -to $to -from $from -body $body -subject $subject -smtpserver $smtp
Das Skript führt zunächst crsctl aus und filtert nach Listenern, die nicht ONLINE sind.
Die Rückgabe wird in eine Email gepackt und versendet.

Dienstag, 24. September 2013

Korrupte Blöcke bekämpfen mit DBMS_REPAIR

Eine plötzliche Blockcorruption ist keine angenehme Überraschung. Wenn sich die Malaise nicht per Recovery beheben lässt, kann dbms_repair helfen.
Also.
Der Admin stellt auf einmal fest, dass Tabelle xyz_table dank einer blockcorruption nicht komplett lesbar ist oder sich nicht mit dpexp/exp exportieren lässt. (z.B.: ORA-31693: ...ORA-02354: ...ORA-00600: internal error code, arguments: [25027], [4], [394435]...)
Ein Recovery wird verworfen, da die Koruption schon zu lange existiert und andere Daten verloren gingen.
Die Lösung ist DBMS_REPAIR:
Zunächst werden die korrupten Blöcke mit dbms_repair markiert, damit die betroffene Tabelle wieder komplett gelesen/exportiert werden kann.
DBMS_REPAIR benötigt dafür eine Repairtabelle. Diese legt man hiermit an:
SET SERVEROUTPUT ON
DECLARE num_corrupt INT;
BEGIN

num_corrupt := 0;

DBMS_REPAIR.CHECK_OBJECT (
     SCHEMA_NAME => 'BEISPIEL',
     OBJECT_NAME => 'XYZ_TABLE',
     REPAIR_TABLE_NAME => 'REPAIR_TABLE',
     CORRUPT_COUNT =>  num_corrupt);
DBMS_OUTPUT.PUT_LINE('number corrupt: ' || TO_CHAR (num_corrupt));

END;
/

Anschließend jagt man DBMS_REPAIR über die kaputte Tabelle:

SET SERVEROUTPUT ON
DECLARE num_corrupt INT;
BEGIN

num_corrupt := 0;

DBMS_REPAIR.CHECK_OBJECT (
     SCHEMA_NAME => 'BEISPIEL',
     OBJECT_NAME => 'XYZ_TABLE',
     REPAIR_TABLE_NAME => 'REPAIR_TABLE',
     CORRUPT_COUNT =>  num_corrupt);
DBMS_OUTPUT.PUT_LINE('number corrupt: ' || TO_CHAR (num_corrupt));

END;
/

Jetzt kann man in der Repairtabelle nachsehen, was im Argen liegt:
SELECT OBJECT_ID, TABLESPACE_ID tsid,
   RELATIVE_FILE_ID fileid, BLOCK_ID,
   CORRUPT_TYPE, CORRUPT_DESCRIPTION, REPAIR_DESCRIPTION
FROM REPAIR_TABLE;

Hier die ausführliche CORRUPT_DESCRIPTION:

Block Checking: DBA = 16780739, Block Type = KTB-managed data block
data header at 0xf25ec07c
kdbchk: row locked by non-existent transaction
        table=0   slot=1
        lockid=6   ktbbhitc=3

Jetzt, wo man weiß, welche Block-ID – in diesem Fall die 3523 – betroffen ist, kann man mit diesem Select sogar den Inhalt finden und anzeigen:
SELECT * FROM XYZ_TABLE
WHERE DBMS_ROWID.ROWID_BLOCK_NUMBER(rowid) = 3523

Das sieht wirklich kaputt aus... aber mit ein wenig Kenntnis des Datenmodells und der Anwendung weiß man jetzt vielleicht sogar, wie man an die korrekten Daten kommt.
Anschließend kann die Tabelle korrigiert werden - z.B. mittels DBMS_REPAIR.FIX_CORRUPT_BLOCKS().

Dienstag, 20. August 2013

Thoth - APEX mit dem IIS

Oracle bietet von Haus aus bereits 3 verschiedene Web-Listener für den DB-Zugriff über APEX:
  1. das embedded PL/SQL Gateway,
  2. den APEX-Listener und
  3. Oracle HTTP Server (OHS) mit mod_plsql - einem Apache-Webserver.
Das sollte an und für sich genügen.

Aber: Sofern die eigene Firma den Einsatz eines Microsoft IIS vorschreibt, sieht man alt aus.
Morten Braten hat hier glücklicherweise einen Ausweg parat: Thoth Gateway - PL/SQL Gateway Module for Microsoft IIS
Wenn man die Installation hinter sich gebracht hat, tut Thoth klaglos seinen Dienst.

Man umgeht durch den Einsatz von Thoth aber andere Probleme in Bezug auf Lizenzkosten und Virtualisierung:
Falls man den OHS nicht auf einem DB-Server einsetzen möchte - und das dürfte im produktiven Einsatz die Regel sein - muss z.B. Oracle Web Tier erworben werden. Das kann sehr schnell kostspielig werden, da hier wieder die üblichen Fallen lauern. (Prozessoranzahl, Virtualisierung...)

Ein Nachteil von Thoth, der nicht verschwiegen werden soll:
Das Gateway wird derzeit von nur einem Entwickler gepflegt, ist dafür aber Open Source und bereits in Version 1.3.7 verfügbar.

Dienstag, 18. Juni 2013

Wie stoppt man ein "EXPIRE(GRACE)" nach einer PROFILE-Panne?

Wenn man das Default-Profile ändert oder der falschen Usergruppe das falsche Profile zuordnet, haben ganz schnell eine Menge User ein ablaufendes Passwort.

SQL >select count(*) from dba_users
  2  where  account_status = 'EXPIRE(GRACE)';

  COUNT(*)
----------
        78

1 Zeile wurde ausgewählt.

Jetzt ist guter Rat teuer. Ein "alter user account_status 'OPEN';" gibt es nicht. Die Passwörter laufen ab. Man kann nichts dagegen tun, außer das Passwort zu ändern.
Da man die Passwörter aller User für gewöhnlich nicht kennt, hilft hier nur die Passwortänderung mit dem Passwordhash.
Diesen bekommt man in 11g aus der sys.user$. Mit diesem Skript lässt man sich in sqlplus gleich noch ein sql-Skript erstellen, welches die Passworthashes schreibt und damit den Accountstatus zurücksetzt:
rem unexpire_users.sql, MW, 18.06.2013

spool alter_user_unexpire.sql
set echo off
set heading off
set linesize 200
set feedback off

SELECT 'ALTER USER ' || DU.USERNAME || ' IDENTIFIED BY VALUES '''|| SU.PASSWORD ||''';' 
FROM SYS.USER$ SU
INNER JOIN SYS.DBA_USERS DU ON DU.USERNAME = SU.NAME
WHERE DU.ACCOUNT_STATUS = 'EXPIRED(GRACE)'
ORDER BY DU.PROFILE, DU.DEFAULT_TABLESPACE, DU.USERNAME;

spool off
exit

Und jetzt das erstellte SQL-Skript ausführen und fertig:
SQL >@alter_user_unexpire
...
...
SQL >select count(*) from dba_users
  2  where  account_status = 'EXPIRE(GRACE)';

  COUNT(*)
----------
         0

1 Zeile wurde ausgewählt.

Donnerstag, 13. Juni 2013

Clonen einer DB auf dem RAC

Das Duplizieren von Datenbanken auf einem RAC ist gar nicht so schwer.
Das erste, was man sich vergegenwärtigen muss, wenn man auf dem RAC eine DB clonen/duplizieren möchte ist: Es ist im Prinzip das gleiche wie auf einem "normalen" Server - normal meint hier: Single-Node - nur irgendwie komplizierter. Aber wen wundert das schon. ;)
Im Prinzip steht alles, was man wissen muss hier drin: Oracle® Database Backup and Recovery User's Guide
Speziell diese Kapitel sollen helfen:
Wer schon einmal eine DB auf einem "normalen" Server dupliziert hat, der weiß im Prinzip schon alles. Er musste dabei auf diverse Details seiner Systemumgebung achten - zum Beispiel:
  • Wird von ASM nach ASM, von ASM ins Filesystem oder umgekehrt dupliziert?
  • Wird OMF von target oder von auxiliary oder von beiden genutzt?
  • Duplizieren wir "backup based", "without/with target connection", "with/without recovery catalog" oder lieber nur "from active database"? 
  • ...Hä? ;)
...und das Ganze jetzt auch noch auf dem RAC...

Das Wichtigste gleich vorneweg:
Es ist unmöglich von RAC-DB zu RAC-DB zu duplizieren.
Aber: Man kann eine RAC-DB in eine Single-Instance-DB duplizieren und diese anschließend zu einer RAC-DB machen.
Na geht doch. 

Ausgangslage

Ich mache es mir einfach:
Wir haben einen 2-Knoten-RAC mit ASM und OMF - keine ausgefallenen Geschichten. Im Falle eines Standard-Edition-Systems und noch dazu auf Windows sollte dies in 99% der Fälle passen.
Auf dem RAC läuft die zu duplizierende DB (target), die auf dem selben RAC dupliziert werden soll. In der ASM ist genug Platz. Die DB läuft im Archivelog-Modus und ein RMAN-Backup ist eingerichtet.
Die Duplizierung läuft "backup based", "with target connection" und "without recovery catalog".
Das Server-OS ist (leider) ein WindowsServer 2008 R2. (englisch) 

Vorbereitung

Die neue Instanz (auxiliary) muss eingerichet werden. Hierfür sind eine Reihe von Schritten notwendig.

1. Dienst und Passwortdatei anlegen

D:\>set ORACLE_HOME=D:\oracle\product\11.2.0.3\dbhome_1

D:\>oradim -new -sid DUPLO
Instanz erstellt.

D:\>orapwd file=d:\oracle\product\11.2.0.3\dbhome_1\database\pwdDUPLO.ora
password=<SYSPWD>
Es bietet sich an, hier das gleiche Passwort zu nutzen, wie in der target-DB.

2. Parameterfile anlegen

Am besten kopiert man die init.ora der target-Instanz und kommentiert alle RAC-Parameter aus oder entfernt sie.
Für eine initiale Duplizierung sind nur wenige Parameter notwendig.
#initDUPLO.ora
### notwendige Parameter ###
db_name='DUPLO'
db_create_file_dest='+DATA'
#Falls Datafile-Konvertierungen nötig sind: DB_FILE_NAME_CONVERT
#DB_FILE_NAME_CONVERT='+DUPSOURCE','+DUPDEST'
control_files='+DATA(CONTROLFILE)/duplo/duplo_control01.ctl','+FRA(CONTROLFILE)/duplo/duplo_control02.ctl'
db_recovery_file_dest='+FRA'
db_recovery_file_dest_size=52428800000
#Falls Logfile-Konvertierungen nötig sind: LOG_FILE_NAME_CONVERT
#LOG_FILE_NAME_CONVERT='+DGDUPSOURCE','+DGDUPDEST'
log_archive_dest='+FRA'

### Hier die RAC-Parameter ###
cluster_database=false
#cluster_database_instances=2
#duplo1.instance_number=1
#duplo2.instance_number=2

### optionale Parameter ###
db_domain=''
db_block_size=8192
sga_target=3969908736
pga_aggregate_target=1323302912
log_archive_format='ARC%S_%R.%T'
db_create_online_log_dest_1='+FRA'
archive_lag_target=1800
[edit] hinzugefügt am 10.09.2013:

3. Die Datenbank und die Instanz in der Clusterware registrieren

Auf einem normalen DB-Server ohne Grid-Infrastruktur macht man die Instanzen über einen Eintrag in der listener.ora bekannt.
Auf dem RAC bemüht man eine Reihe von srvctl-Kommandos:
D:\>set ORACLE_HOME=D:\oragrid\11.2.0.3

D:\>srvctl add database -d DUPLO -o d:\oracle\product\11.2.0.3\dbhome_1\ -c SINGLE -x racnode01

rem D:\>srvctl add instance -d DUPLO -i DUPLO -n racnode01

D:\>srvctl config database -d DUPLO
Eindeutiger Datenbankname: DUPLO
Datenbankname:
Oracle-Standardverzeichnis: d:\oracle\pr
Oracle-Benutzer: nt authority\system
Spfile:
Domain:
Startoptionen: open
Stoppoptionen: immediate
Datenbankrolle: PRIMARY
Verwaltungs-Policy: AUTOMATIC
Server-Pools: DUPLO
Datenbankinstanz: DUPLO
DatentrΣgergruppen:
Mount Point-Pfade:
Services:
Typ: SINGLE
Die Datenbank ist administratorverwaltet
[/edit]

4. spfile anlegen

Zunächst startet man die Instanz der auxiliary-DB im nomount-Modus und erzeugt das spfile in der ASM.
Instanz starten:
D:\>set ORACLE_SID=DUPLO

D:\>set ORACLE_UNQNAME=DUPLO

D:\>sqlplus /nolog

SQL*Plus: Release 11.2.0.3.0 Production on Di Jun 11 20:16:35 2013

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

SQL >conn / as sysdba
Bei einer nicht hochgefahrenen Instanz angemeldet.

SQL >startup nomount pfile='D:\oracle\product\11.2.0.3\dbhome_1\database\initDUPLO.ora';
ORACLE-Instanz hochgefahren.

Total System Global Area 3958439936 bytes
Fixed Size                  2261280 bytes
Variable Size             771755744 bytes
Database Buffers         3170893824 bytes
Redo Buffers               13529088 bytes
11.06.13 SYS AS SYSDBA@DUPLO>
SPFILE in der ASM erzeugen:
SQL >create spfile='+DATA' from pfile='d:\oracle\product\
11.2.0.3\dbhome_1\database\initDUPLO.ora';

Datei erstellt.

Abgelaufen: 00:00:00.32
11.06.13 SYS AS SYSDBA@DUPLO> 

Ein must have (1)

Hier halten wir kurz inne und nehmen uns Zeit für ein wenig Feinschliff.
Das spfile wird in der ASM automatisch unter '+DATA/DUPLO/PARAMETERFILE/' angelegt und bekommt wegen OMF einen nicht merkbaren Namen:
ASMCMD [+] > ls +data/duplo/parameterfile/
spfile.303.817894325
Es ist schöner, wenn man mit einem spfile arbeiten kann, das nicht diese Zahlen hat. Wenn man das trotz OMF haben will, ist MKALIAS in ASMCMD das Mittel der Wahl - also ein Link:
ASMCMD [+] > mkalias +DATA/DUPLO/PARAMETERFILE/spfile.303.817894325 +DATA/DUPLO/spfileduplo.ora
Hier sieht man dann schön das Ergebnis:
ASMCMD [+] > ls +data/duplo/
CONTROLFILE/
DATAFILE/
PARAMETERFILE/
duplo_control01.ctl
spfileduplo.ora
ASMCMD [+] >
Hier fehlt noch ein Detail: Die Instanz weiß nicht wo das spfile liegt und bedient sich weiter gemäß den Defaultvorgaben. Dazu aber später mehr.

 Die Duplizierung

1. RMAN - connect & duplicate

Jetzt kann man sich mit RMAN einloggen und die DB duplizieren:
D:\>rman target sys/<SYSPWD>@orcl auxiliary /

Recovery Manager: Release 11.2.0.3.0 - Production on Di Jun 11 20:52:29 2013

Copyright (c) 1982, 2011, Oracle and/or its affiliates.  All rights reserved.

Mit Ziel-Datenbank verbunden: ORCL (DBID=4167151783)
Bei Hilfsdatenbank angemeldet: DUPLO (nicht mit MOUNT angeschlossen)

RMAN> duplicate target database to DUPLO until time "TO_DATE('11.06.2013:20:30','DD.MM.YYYY:HH24:MI')";

Starten Duplicate Db um 11.06.13
Kontrolldatei der Zieldatenbank wird anstelle des Recovery-Katalogs verwendet
Zugewiesener Kanal: ORA_AUX_DISK_1
Kanal ORA_AUX_DISK_1: SID=922 Device-Typ=DISK

...
...

Inhalt von Speicher-Skript:
{
   Alter clone database open resetlogs;
}
Speicher-Skript wird ausgeführt

Datenbank geöffnet
Beendet Duplicate Db um 11.06.13

RMAN>

An dieser Stelle wäre noch Platz für Nacharbeiten bzw. Umkonfigurieren der DB. (Onlinelogs hinzufügen, Pfadanpassungen...)

2. Aus 1 mach 2 (1)

Das pfile muss jetzt angepasst werden um aus der Singleinstancedatenbank eine RAC-DB machen zu können.
Hierzu werden dien RAC-Parameter in der init.ora editiert. Anschließend wird mit dem pfile das spfile in der ASM erzeugt.
Das reicht aber noch nicht aus. Die DB muss dann in den Cluster integriert werden
RAC-Parameter im pfile:
#initDUPLO.ora
...

### Hier die RAC-Parameter anpassen###
cluster_database=true
cluster_database_instances=2
duplo1.instance_number=1
duplo2.instance_number=2

...
jetzt das spfile erzeugen:
SQL >create spfile='+DATA/DUPLO/spfileduplo.ora' from pfile='d:\oracle\product\11.2.0.3\dbhome_1\database\initDUPLO.ora';

Datei erstellt.

Abgelaufen: 00:00:00.21
SQL >shu immediate
Datenbank geschlossen.
Datenbank dismounted.
ORACLE-Instanz heruntergefahren.
SQL >
Ohne den Alias auf das spfile hätte hier die Pfadangabe auf das bestehende spfile mit dem OMF-Namen erfolgen müssen. Zum Beispiel:
SQL >create spfile='+DATA/DUPLO/PARAMETERFILE/spfile.303.817894325' from pfile='d:\oracle\product\11.2.0.3\dbhome_1\database\initDUPLO.ora';

3. Ab in den Cluster

Jetzt wird zunächst das ORACLE_HOME aufs Grid geschwenkt und die Datenbank registriert:
D:\>set ORACLE_HOME=D:\oragrid\11.2.0.3

D:\>srvctl add database -d DUPLO -o d:\oracle\product\11.2.0.3\dbhome_1\
Die Prüfung der Konfiguration ergibt, dass die DB registriert ist, aber keine Datenbankinstanzen besitzt.
D:\>srvctl config database -d DUPLO
Eindeutiger Datenbankname: DUPLO
Datenbankname:
Oracle-Standardverzeichnis: d:\oracle\product\11.2.0.3\dbhome_1\
Oracle-Benutzer: nt authority\system
Spfile:
Domain:
Startoptionen: open
Stoppoptionen: immediate
Datenbankrolle: PRIMARY
Verwaltungs-Policy: AUTOMATIC
Server-Pools: DUPLO
Datenbankinstanzen:
Datenträgergruppen:
Mount Point-Pfade:
Services:
Typ: RAC
Die Datenbank ist administratorverwaltet

D:\>
Die Registrierung der Datenbankinstanzen ist der nächste Schritt. Die notwendige Syntax erhält man von SRVCTL, wenn man mit ihm spricht:
D:\>srvctl add instance -h

Fügt eine Datenbankinstanzkonfiguration zu Oracle Clusterware hinzu.

Verwendung: srvctl add instance -d  -i  -n  [-f]
    -d            Eindeutiger Name für die Datenbank
    -i            Instanzname
    -n            Knotenname
    -f                       Hinzufügen-Vorgang erzwingen, selbst wenn einige Ressourcen gestoppt werden
    -h                  Verwendung drucken
Kommunikation ist eben alles. ;)
Man kann sich übrigens mit fast allen Oracle-Kommadozeilenprogrammen so angeregt unterhalten und spart sich auf diese Weise den zeitraubenden Blick in Handbücher.
D:\>srvctl add instance -d DUPLO -i DUPLO1 -n racnode01

D:\>srvctl add instance -d DUPLO -i DUPLO2 -n racnode02

Das must have (2)

Jetzt sagen wir der DB noch schnell, wo das spfile - bzw. sein Alias - liegt:
D:\>srvctl modify database -d duplo -p +DATA/duplo/spfileduplo.ora
Konfiguration prüfen:
D:\>srvctl config database -d DUPLO
Eindeutiger Datenbankname: DUPLO
Datenbankname:
Oracle-Standardverzeichnis: d:\oracle\product\11.2.0.3\dbhome_1\
Oracle-Benutzer: nt authority\system
Spfile: +DATA/duplo/spfileduplo.ora
Domain:
Startoptionen: open
Stoppoptionen: immediate
Datenbankrolle: PRIMARY
Verwaltungs-Policy: AUTOMATIC
Server-Pools: DUPLO
Datenbankinstanzen: DUPLO1,DUPLO2
Datenträgergruppen:
Mount Point-Pfade:
Services:
Typ: RAC
Die Datenbank ist administratorverwaltet

D:\> 
Das spfile in der ASM ist bekannt und die Instanzen ebenfalls - perfekt.

Abschlussarbeiten und DB-Start

Jetzt sind beide Instanzen registriert, wie man sieht.
Die Instanzen kann man aber noch nicht starten. Hierfür sind noch zusätzliche Handgriffe zu erledigen.
  • Auf jedem Knoten muss ein pfile für die jeweilige Instanz existieren.
  • Jedes dieser pfiles kann auf "seine" Instanz angepasst sein. Es ist beispielsweise möglich, jeder Instanz unterschiedliche Mengen an Speicher zur Verfügung zu stellen. Best Practice ist aber: Alle Knoten sind möglichst identisch konfiguriert.
  • Die pfiles benötigen den Pfad zum gemeinsamen spfile in der ASM.

Aus 1 mach 2 (2)

Zunächst wird also auf jedem Knoten unter %ORACLE_HOME%/database ein pfile angelegt. Die pfiles sind einfache eine Kopie des bisher verwendeten pfile mit dem jeweiligen Instanznamen:
  1. Knoten 1: initDUPLO.ora -> initDUPLO1.ora
  2. Knoten 2: initDUPLO.ora -> initDUPLO2.ora
Der Pfad zum spfile wird ganz am Ende des pfiles eingetragen:
#initDUPLO.ora
### notwendige Parameter ###
db_name='DUPLO' 

...
...

SPFILE='+DATA/duplo/spfileduplo.ora'
Ein pfile, das nur den Verweis auf das spfile in der ASM enthält, reicht auch aus.

Fazit

Im Prinzip ganz einfach. Man muss es nur machen.:)
Das Clonen von RAC-DB in anderen Konstellationen - z.B. von einem RAC auf einen zweiten Cluster - funktioniert in leicht abgewandelter Form, wenn man das backup based duplicate durchführt.
Leider ist das Duplizieren "from active database" nicht so einfach, weil es da nicht ausreicht, sich per
External User Authentication mit der DB zu verbinden.

Mittwoch, 12. Juni 2013

Mobile-Apex-Tutorial

Carsten Czarski hat in der Apex Community ein erstklassiges Tutorial veröffentlicht, das zeigt, wie schnell und einfach Webseiten für mobile Engeräte mit APEX erstellt werden können.
Workshopartig, über 10 Aufgaben verteilt, wird umfassend und in anschaulicher Weise der Aufbau einer kleinen WebApp gezeigt.

Prädikat: Äußerst empfehlenswert.

(An Aufgabe 08 habe ich einen kleinen Anteil - das freut mich natürlich besonders. :))

Freitag, 31. Mai 2013

DB-Version und -Patchstand erfragen

Wenn man die Oracle-DB gepatcht hat - oder noch patchen möchte - möchte man vorher oder nachher sicher gern wissen, wie es in der DB versionsmäßig aussieht. Dafür reicht die v$version-View nicht - was man vielleicht erwarten würde.
--Versionsstand der Installation(en)
select * from v$version;

--oder für RAC
SELECT * FROM GV$VERSION;
Will man den Patchstand der DB erfahren, muss man in die DBA_REGISTRY sehen
--Patchstand der DB
SELECT * FROM DBA_REGISTRY_HISTORY
Weitere Views, die irgendwie REGISTRY-Bezug haben, findet man im Datadictionary.
SELECT * FROM DICTIONARY
WHERE TABLE_NAME LIKE 'DBA_REGISTRY%'
Interessant sind vielleicht diese 2:
--auf welcher Plattform läuft die DB?
SELECT * FROM DBA_REGISTRY_DATABASE

--geladene Komponenten
SELECT * FROM DBA_REGISTRY

Montag, 6. Mai 2013

APEX mobil - Zugriff auf's Smartphone-GPS

Mit Apex 4.2 ist es sehr einfach, Webseiten für mobile devices zu erstellen.
Sogar der Zugriff auf einige Smartphone-Funktionen ist über den Browser möglich.
Ich habe beispielhaft den Zugriff auf den Standort implementiert:
  • Auf einer Webseite soll die Position des Smartphones dargestellt werden.
  • Über den Klick eines Buttons wird der Browser die API ansprechen und Höhe und Breite abrufen.
  • Höhe und Breite werden zusätzlich angezeigt, damit man auch Änderungen registrieren kann.
Zunächst habe ich mir auf http://apex.oracle.com einen kostenlosen Workspace angelegt und anschließend schnell mal eine Anwendung mit dem Mobile-Template zusammengeklickt. Wie man das macht, ist in diesem Tipp der deutschsprachigen APEX und PL/SQL Community beschrieben.
Hier im Schnelldurchlauf:
  • Den Wizard für eine neue Applikation aufrufen und eine Datenbankapplikation auswählen.
  • Name, Schema, Optionen, Template auswählen
Die obligatorischen Angaben machen und weiter
  • Im Wizard bis zum Schluss immer nur auf "Weiter" klicken und Anwendung erstellen. Ob man - wie ich hier - ohne Login arbeiten möchte ist natürlich Geschmackssache.
Das Ergebnis
  • Jetzt vergeben wir noch schnell einen Aliasnamen, damit man sich nicht die Anwendungs-ID merken muss.
Damit steht das Grundgerüst und kann mit dieser URL aufgerufen werden: http://apex.oracle.com/pls/apex/f?p=gpstest
Auf der Home-Seite wird ein Button platziert, der bei einem Klick eine Dynamic Action auslöst. Hinter dieser liegt etwas JavaScript, welches das GPS-Modul des Smartphones abfragt.
Den Button bekommt man, indem man nacheinander:
  1. eine Region erstellt und dann dort
  2. den Button erstellt
Rechtsklick auf "Regionen"
In der Region muss noch HTML eingetragen werden, welches später per JavaScript manipuliert wird:
Wer schon mal mit JavaScript Webseiten manipuliert hat, der weiß, dass die ID eines Elementes für den gezielten Zugriff auf selbiges benötigt wird.
Jetzt per Rechtsklick auf die neue Region den Wizard für die Buttonerstellung starten.
Dem neuen Button verpassen wir - ebenfalls per Rechtsklick - eine "dynamic action"
Benamsen und weiter
weiter
Mussfelder sinnvoll füllen und weiter

Den JavaScript-Code einfügen, weiter zum letzten Schritt und dann fertigstellen.

Das Javascript findet man bequem per Google mit diesen Suchbegriffen: jquery mobile geolocation und kann ihn dann modifizieren.
navigator.geolocation.getCurrentPosition (function (pos)
{
  var lat = pos.coords.latitude;
  var lng = pos.coords.longitude;
  var t = new Date();
  var Monat = new Array("01", "02", "03", "04", "05", "06",
                        "07", "08", "09", "10", "11", "12")
  var Tag = t.toGMTString();
  var Tag = t.getDate() + "." + Monat[t.getMonth()] + "." + t.getFullYear()        + " "  + t.getHours() + ":" + t.getMinutes() + ":" +
          + t.getSeconds();
  var Tag = t.toLocaleString() ;
  var ts = t.getTime();
  $("#time").text (Tag);
  $("#ts").text (ts);
  $("#lat").text (lat);
  $("#lng").text (lng);
});

Hier kann man sich das unspektakuläre Ergebnis mit dem Iphone/Androiden/Blackberry/WindowsPhone ansehen: http://apex.oracle.com/pls/apex/f?p=gpstest
Eine Fehlerbehandlung habe ich mir gespart. Deshalb wird auch nichts passieren, sofern man gerade keinen freien Himmel hat.
Dieses Beispiel ließe sich jetzt bequem erweitern:
  • wegschreiben des Standpunktes in die DB (GPS-Tracking)
  • Darstellung des Standortes auf einem Kartendienst
  • ...

Fazit:

Mit APEX ist eine mobile Webseite extrem schnell "zusammengeklickt". Natürlich muss man für komplexe Anwendungen unter APEX mehr Aufwand investieren, dafür muss man sich nicht mit lästigen Arbeiten rund um Login, Datenbankzugriff, Browserkompatibilitäten... herumschlagen.
Schneller als mit APEX kommt man wohl nicht zu einer mobile-app-ähnlichen Webseite für Smartphones - erst recht nicht, wenn man sich darauf beschränkt, Datenbankinhalte auf dem Smartphone "nur" darzustellen.
Auch wenn man mehr machen möchte, kommt man sehr schnell zu einem guten Ergebnis.

Derzeit ist geolocation aber die einzige interessantere herstellerübergreifende Gerätefunktion, auf die der Smartphone-Browser zugreifen kann. Richtig interessant wird es erst, wenn der Browser Zugriff auf z.B. die Kamera bekommt. Dann ließen sich tolle Anwendungen in APEX erstellen, die bisher den nativen Apps vorbehalten sind.
Auf http://mobilehtml5.org/ werden die Fähigkeiten der gängigen Smartphone-OS aufgelistet. Bisher können nur Blackberry (Z10), sowie Android & Symbian (Opera Mobile) mit der Unterstützung von getUserMedia aufwarten - also dem Zugriff auf die Kamera.

Donnerstag, 25. April 2013

Bug im jQuery Mobile von APEX 4.2.1: "Error loading page"

Das jQuery Mobile, welches mit APEX 4.2.1 ausgeliefert wird, hat einen gemeinen Bug (bug# 16184694), der zuschlägt, sobald man nicht direkt auf den Webserver/APEXListener zugreift, sondern der Zugriff per redirect durch einen Proxy geleitet wird - das ist z.B. dann der Fall, wenn per Smartphone auf die DB zugegriffen werden soll und der Webserver in der DMZ steht.
Bei mir verhinderte der Bug bereits das Login in die Applikation.

Lösung:
Enter

$("#wwvFlowForm", apex.gPageContext$).attr("data-ajax", false);

into "Execute when Page Loads" attribute of page which contains the select list page item to not use an AJAX call for the submit. Avoiding the AJAX call will trigger a full page refresh which will also cleanup the jQuery Mobile call stack.
Quelle: https://forums.oracle.com/forums/thread.jspa?messageID=10857686&tstart=0

Freitag, 19. April 2013

Systemstatistiken

In der 01/2013 der DOAG News ist ein sehr empfehlenswerter Artikel von Thorsten W. Grebe über die Oracle Systemstatistiken zu finden: “Glücksspiel Systemstatistiken - Das Märchen vom typischen Workload“ (S.52) Auf  www.twg-it.de ist dazu sein Vortrag auf der DOAG-Konferenz zu finden.
Hr. Grebe stellt in dem DOAG-News-Artikel sehr gut dar, dass es nicht so einfach ist, an verlässliche Systemstatistiken zu kommen, wie Oracle es in seiner Doku unterstellt.
Hier sieht man die Defaultwerte einer unberührten DB:

SQL> set wrap off;
SQL> col sname format a20;
SQL> col pname format a20;
SQL> col pval2 format a20;
SQL> select * from aux_stats$;

SNAME                PNAME                     PVAL1 PVAL2
-------------------- -------------------- ---------- --------------------
SYSSTATS_INFO        STATUS                          COMPLETED
SYSSTATS_INFO        DSTART                          11-03-2011 06:38
SYSSTATS_INFO        DSTOP                           11-03-2011 06:38
SYSSTATS_INFO        FLAGS                         1
SYSSTATS_MAIN        CPUSPEEDNW           1720,20725
SYSSTATS_MAIN        IOSEEKTIM                    10
SYSSTATS_MAIN        IOTFRSPEED                 4096
SYSSTATS_MAIN        SREADTIM
SYSSTATS_MAIN        MREADTIM
SYSSTATS_MAIN        CPUSPEED
SYSSTATS_MAIN        MBRC
SYSSTATS_MAIN        MAXTHR
SYSSTATS_MAIN        SLAVETHR

13 Zeilen ausgewählt.

Abgelaufen: 00:00:00.01
SQL>
Die No-Workloadwerte sind IOSEEKTIM und IOTFRSPEED.

Die Workloadstatistiken waren mir schon lange suspekt. Weil ich mir bisher nie sicher war, wann und wie lang ich die Workloadstatistiken aufzeichnen soll, habe ich es lieber ganz sein lassen. Und das ist bis auf weiteres auch in Ordnung. Für gute Workloadstatistiken muss mehr Aufwand betrieben werden, als Oracle Glauben machen will.
Und hier gilt diesmal im Gegensatz zu den Objektstatistiken: Lieber Defaultstatistiken als schlechte Statistiken.

Wobei: Für die No-Workloadstatistiken gilt das nicht ganz, bzw. ist hier zumindest der Aufwand nicht so hoch, wie für die Generierung plausibler Workloadstatistiken.
Die No-Workload-Statistiken kann man ruhig generieren, wenn man auch tatsächlich prüft, ob die Werte sinnvoll sind. Wird der DB-Server gerade stark beansprucht, während die No-Workloadwerte generiert werden, können auch diese Statistiken verfälscht werden.

No-Workloadstatistiken generiert man mit dem Package DBMS_STATS:
SQL> exec DBMS_STATS.GATHER_SYSTEM_STATS();

PL/SQL-Prozedur erfolgreich abgeschlossen.

Abgelaufen: 00:00:08.95
SQL> select * from aux_stats$;

SNAME                PNAME                     PVAL1 PVAL2
-------------------- -------------------- ---------- --------------------
SYSSTATS_INFO        STATUS                          COMPLETED
SYSSTATS_INFO        DSTART                          04-19-2013 07:32
SYSSTATS_INFO        DSTOP                           04-19-2013 07:32
SYSSTATS_INFO        FLAGS                         1
SYSSTATS_MAIN        CPUSPEEDNW                 1460
SYSSTATS_MAIN        IOSEEKTIM                     7
SYSSTATS_MAIN        IOTFRSPEED                28796
SYSSTATS_MAIN        SREADTIM
SYSSTATS_MAIN        MREADTIM
SYSSTATS_MAIN        CPUSPEED
SYSSTATS_MAIN        MBRC
SYSSTATS_MAIN        MAXTHR
SYSSTATS_MAIN        SLAVETHR

13 Zeilen ausgewählt.

Abgelaufen: 00:00:00.00
SQL>

Workloadstatistiken lassen sich natürlich auch manuell einstellen (dbms_stats.set_systems_stats(...)) oder auf Default zurücksetzen (dbms_stats.delete_system_stats()):
SQL> exec DBMS_STATS.DELETE_SYSTEM_STATS ();

PL/SQL-Prozedur erfolgreich abgeschlossen.

Abgelaufen: 00:00:00.01
SQL> select * from aux_stats$;

SNAME                PNAME                     PVAL1 PVAL2
-------------------- -------------------- ---------- --------------------
SYSSTATS_INFO        STATUS                          COMPLETED
SYSSTATS_INFO        DSTART                          04-19-2013 07:38
SYSSTATS_INFO        DSTOP                           04-19-2013 07:38
SYSSTATS_INFO        FLAGS                         0
SYSSTATS_MAIN        CPUSPEEDNW                 1460
SYSSTATS_MAIN        IOSEEKTIM                    10
SYSSTATS_MAIN        IOTFRSPEED                 4096
SYSSTATS_MAIN        SREADTIM
SYSSTATS_MAIN        MREADTIM
SYSSTATS_MAIN        CPUSPEED
SYSSTATS_MAIN        MBRC
SYSSTATS_MAIN        MAXTHR
SYSSTATS_MAIN        SLAVETHR

13 Zeilen ausgewählt.

Abgelaufen: 00:00:00.00
SQL>

Beruhigend: Erst wenn man tatsächlich Performanceprobleme und die Systemstatistiken als Ursache identifiziert hat, muss man sich eingehend mit dem Thema befassen.
Bis dahin gilt weiter: Ursache Nr.1 für Performanceprobleme in der Anwendung ist schlechtes SQL.

Mittwoch, 13. März 2013

Tabellenimport auf die Schnelle mit APEX

APEX bietet mit seinen Websheets eine einfache Möglichkeit gemeinsam in einem Team/ einem Projekt Daten im Browser zu bearbeiten. Wie einfach das funktioniert, kann man sich in diesem Youtube-Video der MT AG ansehen.

Das ist schon mal ganz nett, aber man möchte die gleichen Daten vielleicht viel lieber in einer APEX-Applikation verarbeiten und benötigt sie direkt in einer Tabelle.
Mit APEX geht das sehr schnell und sehr leicht.
Wie das geht, ist hier ausführlich beschrieben.
Unter "SQL Workshop">"Data Workshop" findet man den Startpunkt für Export/Import.
Hier wird man von Wizards an die Hand genommen und Schritt für Schritt zum Ziel geführt.

Man kann Daten sowohl in eine vorhandene Tabelle importieren, als auch eine komplett neue Tabelle über den Import generieren. Letzteres ist sehr nützlich, wenn man "mal schnell" Tabellen und Daten für einen Prototypen benötigt und sich nicht ausführlich mit dem Entwurf eines Datenmodells befassen möchte.
Als Datenquelle können CSV- und XML-Files dienen. Per copy&paste kann man die Daten ebenfalls nach APEX bringen.

Mittwoch, 27. Februar 2013

APEX - About Choosing a Web Listener

Laut Doku kann APEX über 3 Möglichkeiten mit dem Browser sprechen:
  1. Oracle Application Express Listener
  2. Embedded PL/SQL Gateway
  3. Oracle HTTP Server (OHS)
Wenn man den OHS (ist ein Apache) nutzen möchte, darf man der Doku nicht glauben. Tut man das doch und nimmt den Punkt Step 5: Configure Oracle HTTP Server Distributed with Oracle Database 11g or Oracle Application Server 10g für bare Münze, so sucht man vergeblich in der DB-Installation nach dem OHS.
Im OTN bin ich dann fündig geworden: Der OHS ist nicht mehr Teil der Datenbankinstallation und muss jetzt separat heruntergeladen und installiert werden. (hier)
Jetzt stellt sich natürlich wieder die Frage der Lizensierung des OHS. Wahrscheinlich ist die Nutzung über die Datenbank lizensiert und auf den Server beschränkt. Besser man fragt Oracle direkt.
Einstweilen nutze ich weiter den APEX Listener.

Freitag, 15. Februar 2013

Flashback mit der Standard Edition

Flashback ist eine coole Sache. Wenn man auf einer Enterprise Edition (EE) arbeiten kann, schöpft man natürlich aus dem Vollen:
  • Flashback Query, Flashback expdp/exp
  • Flashback Version Query
  • Flashback Drop
  • Flashback Transaction Query (wirklich nur EE?)
  • Flashback Table (nur EE)
  • Flashback Database (nur EE)
  • Flashback Transaction (nur EE)
  • Total Recall: Flashback Data Archive (ab 11g, natürlich nur EE und das auch nur als Option)
In der Standard Edition (SE) kann man leider nur die ersten 3 Features nutzen. Aber bereits diese können sehr wertvoll sein und sind in vielen Fällen völlig ausreichend.
Wie also nutzt man Flashback in der SE?
Bevor man überhaupt daran denken kann, Flashback zu nutzen, sollte man sicher gehen, dass das dafür Nötige konfiguriert ist.
Die Parameter UNDO RETENTION und RECYCLEBIN müssen entsprechend gesetzt sein und der UNDO-Tablespace muss über ausreichend Platz verfügen.
Will man Flashback Transaction Query nutzen, muss außerdem Supplemental Logging eingeschaltet sein. Aber dazu unten mehr.

UNDO RETENTION

SQL> show parameter undo_retention

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------
undo_retention                       integer     900
900 ist der Standardwert in Sekunden. Diese 15min sind kein wirklich beruhigendes Zeitpolster. Wir setzen den Wert also etwas etwas höher - z.B. auf 7200 (2 Stunden).
SQL> ALTER SYSTEM SET UNDO_RETENTION=7200;
Problem: Der UNDO_RETENTION-Wert ist nur das Ziel der DB und wird nicht garantiert. Es ist möglich bzw. in einer produktiven DB wahrscheinlich, dass durch DML zu viel UNDO benötigt wird und damit das tatsächlich nutzbare Zeitpolster schrumpft. In diesem Fall bekommt ein FLASHBACK QUERY möglicherweise einen "ORA-01555 snapshot too old...".
Wenn man das verhindern möchte, kann man zusätzlich die RETENTION GUARANTEE auf dem UNDO-Tablespace setzen.
Das wiederum zieht dann möglicherweise ein Folgeproblem nach sich:
Ist der UNDO aufgebraucht und weitere DML-Operationen laufen auf, werden diese Operationen mangels UNDO fehlschlagen.
Dagegen hilft dann natürlich nur ein ausreichend großer UNDO-Tablespace.
Wir sehen nach, ob die Garantie konfiguriert ist:
SQL> SELECT TABLESPACE_NAME, RETENTION FROM DBA_TABLESPACES WHERE CONTENTS = 'UNDO';

TABLESPACE_NAME                RETENTION
------------------------------ -----------
UNDOTBS1                       NOGUARANTEE
Und so verschafft man sich diese Garantie:
SQL> ALTER TABLESPACE UNDOTBS1 RETENTION GUARANTEE;

PAPIERKORB

Den Papierkorb/Recyclebin benötigt man für die Wiederherstellung versehentlich gelöschter Tabellen.
Ist der Papierkorb an?
SQL> show parameter recyclebin;

NAME                                 TYPE        VALUE
------------------------------------ ----------- -----
recyclebin                           string      on
Wenn nicht, dann:
SQL> ALTER SYSTEM SET RECYCLEBIN='ON'

Flashback Query

Jemand setzt ein DELETE oder UPDATE mit der der falschen oder gar keiner WHERE-Bedingung ab und schon herrscht Panik. Sofort wird ein DB-Recovery verlangt.
Ein incomplete recovery ist natürlich selten eine gute Lösung für dieses Problem.
Sofern der faux pas nicht zu weit zurückliegt (UNDO RETENTION!), lassen sich die korrekten Daten per FLASHBACK QUERY wieder hervorzaubern.
Da die Uhr tickt und der Ablauf UNDO RETENTION wie ein Damokleschwert über dem unglücklichen User schwebt, sichert man am besten den Datenbestand per CTAS in eine Tabelle.
SQL> CREATE TABLE jobs_rettung AS (SELECT * FROM jobs AS OF TIMESTAMP to_timestamp('10.02.2013 13:00','dd.mm.yyyy hh24:mi:ss'));
Von hier aus kann man die Daten in Ruhe wieder reparieren.
Eine andere Methode, den vergangenen Augenblick zu sichern bieten expdp und das alte exp.
Mit dem Parameter FLASHBACK_TIME/FLASHBACK_SCN weist man expdp/exp an, die Vergangenheit in ein dump-file zu pressen.
expdp system/@orcl dumpfile=jobs_flashback.dmp logfile=jobs_flashback_exp.log directory=expdp schemas=hr flashback_time='2013-02-10-13:00:00'
ODER:
expdp system/@orcl dumpfile=jobs_flashback.dmp logfile=jobs_flashback_exp.log directory=expdp schemas=hr flashback_scn='3416833'

Flashback Versions Query

Der User, der das verhängnisvolle DELETE/UPDATE abgesetzt hat, weiß unter Umständen gar nicht, wann genau das war. Er weiß vielleicht auch nur die ungefähre Zeit. Das ist uns alles zu ungenau. Wir hätten gern den genauen Zeitpunkt - besser noch die SCN. Um das zu erfahren, hat Oracle Flashback Versions Query und ein paar nützliche Pseudospalten spendiert.
Wenn man den genauen Zeitpunkt erfahren möchte, zu dem Werte geändert wurden, stürzt man sich auf die Spalten VERSIONS_STARTSCN und VERSIONS_STARTTIME der FLASHBACK VERSIONS QUERY.
Wir ändern als Beispiel einfach mal das Gehalt in der Tabelle jobs:
SQL> update jobs set min_salary=7777 where job_title='Accountant';

1 Zeile wurde aktualisiert.

Abgelaufen: 00:00:00.00
SQL> commit;

Transaktion mit COMMIT abgeschlossen.

Abgelaufen: 00:00:00.00
SQL> update jobs set max_salary=11111 where job_title='Accountant';

1 Zeile wurde aktualisiert.

Abgelaufen: 00:00:00.01
SQL> commit;

Transaktion mit COMMIT abgeschlossen.

Abgelaufen: 00:00:00.00
Und jetzt sehen wir uns den Änderungsverlauf an:
SQL> COL VERSIONS_XID FORMAT A17
SQL> COL VERSIONS_STARTTIME FORMAT A18
SQL> COL JOB_TITLE FORMAT A10
SQL> SELECT VERSIONS_XID, VERSIONS_OPERATION,
  2  VERSIONS_STARTSCN , VERSIONS_STARTTIME,
  3  --VERSIONS_ENDSCN, VERSIONS_ENDTIME,
  4  JOB_TITLE, MIN_SALARY, MAX_SALARY
  5  FROM JOBS VERSIONS BETWEEN SCN MINVALUE AND MAXVALUE
  6  WHERE JOB_TITLE='Accountant'
  7  ORDER BY VERSIONS_STARTSCN;

VERSIONS_XID      V VERSIONS_STARTSCN VERSIONS_STARTTIME JOB_TITLE  MIN_SALARY MAX_SALARY
----------------- - ----------------- ------------------ ---------- ---------- ----------
02002100270B0000  U           3538077 10.02.13 13:00:46  Accountant       7777       9000
010020007D0A0000  U           3538081 10.02.13 13:00:48  Accountant       7777      11111
                                                         Accountant       4200       9000

3 Zeilen ausgewählt.
Die letzte Zeile hat keine Werte in den Spalten VERSIONS_XID, V, VERSIONS_STARTSCN und VERSIONS_STARTTIME. Das ist der Ursprungszustand. In Spalte V wird über die Kürzel I(nsert), U(pdate) und D(elete) die Änderungsoperation angegeben. Mit diesen Angaben kann man dann FLASHBACK QUERY wie oben nutzen, um die alten Werte zu ziehen und weiter zu verarbeiten.

Flashback Drop

Per FLASHBACK DROP kann ein DROP TABLE rückgängig gemacht werden. Dabei wird sowohl die Tabelle, als auch deren Indizes, Constraints usw. gerettet.
Eine gelöschte Tabelle kann solange wieder hergestellt werden, wie der von ihr genutzte Platz im Tablespace nicht benötigt wird. Die UNDO RETENTION spielt hier ausnahmsweise keine Rolle.
Dieses Flashback-Feature ist meiner Meinung nach das langweiligste. Das liegt aber vielleicht daran, dass ich noch nie aus Versehen eine Tabelle gelöscht habe. :)
SQL> DROP TABLE jobs;

Tabelle wurde gelöscht.

Abgelaufen: 00:00:00.18
SQL> SELECT object_name, original_name, type, droptime
  2       FROM user_recyclebin;

OBJECT_NAME                    ORIGINAL_NAME                    TYPE
DROPTIME
------------------------------ -------------------------------- ---------
BIN$/KCe7FP4T/6HZfUeTGb53A==$0 JOBS                             TABLE
2013-02-10:13:00:46
SYS_LOB0000830301C00003$$      SYS_LOB0000830301C00003$$        LOB
2013-02-10:12:37:46
SYS_IL0000830301C00003$$       SYS_IL0000830301C00003$$         LOB INDEX
2013-02-10:12:37:46

3 Zeilen ausgewählt.

Abgelaufen: 00:00:00.02

SQL> FLASHBACK TABLE "BIN$/KCe7FP4T/6HZfUeTGb53A==$0" TO BEFORE DROP RENAME TO jobs;

Flashback abgeschlossen.

Abgelaufen: 00:00:00.04

Flashback Transaction Query

Eigentlich ist hier Schluss mit den SE-Flashback-Features, denn Flashback Transaction Query ist laut Oracle ein EE-Feature.
Aber beim Testen ist mir aufgefallen, dass man auch in der SE die Möglichkeit besitzt, Flashback Transaction Query zu nutzen.
Einzige Voraussetzung: Man muss SUPPLEMENTAL LOGGING aktivieren.
Ich bin davon ausgegangen, dass das in der SE gar nicht funktioniert, aber naja...
Also: Ist das Logging eingeschaltet?
SQL> select SUPPLEMENTAL_LOG_DATA_MIN from v$database;

SUPPLEME
--------
NO

1 Zeile wurde ausgewõhlt.
Supplemental Logging ist standardmäßig abgeschaltet. Und so schaltet man es ein:
SQL> alter database add supplemental log data;
Dieses Logging schreibt zusätzliche Informationen in die DB. Per Flashback Transaction Query kann man sich damit DML ausgeben lassen, welche die geloggten Operationen rückgängig machen. Aus einem DELETE wird ein INSERT und umgekehrt, ein UPDATE-Befehl findet seine entsprechende Korrektur usw.
Logging bedeutet auch zusätzliche Last auf der DB. Das muss man sich natürlich vor Augen führen, bevor man die Entscheidung zur Aktivierung trifft.
Und so holt man sich das UNDO_SQL:
SQL> conn / as sysdba
Connect durchgeführt.
Abgelaufen: 00:00:00.00

GLOBAL_NAME
--------------------------------------------------
sys@ORCL 10.02.2013 13:30:34
Abgelaufen: 00:00:00.00

SQL> SELECT UNDO_SQL FROM FLASHBACK_TRANSACTION_QUERY WHERE XID = '02002100270B0000';

UNDO_SQL
--------------------------------------------------------------------------------
update "HR"."JOBS" set "MIN_SALARY" = '4200' where ROWID = 'AAASbGAAFAAAAC/AAE';

2 Zeilen ausgewählt.

Abgelaufen: 00:00:06.83
SQL> update "HR"."JOBS" set "MIN_SALARY" = '4200' where ROWID = 'AAASbGAAFAAAAC/AAE';

1 Zeile wurde aktualisiert.

Abgelaufen: 00:00:00.00
Wie gesagt, handelt es sich hier um ein EE-Feature. In der SE sollte man es keinesfalls nutzen. Wahrscheinlich ist bereits das Einschalten des SUPPLEMENTAL LOGGING lizenzpflichtig.

Fazit

Flashback ist ein tolles Feature der Oracle-DB. Bereits mit den Möglichkeiten, die die SE bietet, kommt man sehr weit:
Es ist recht einfach möglich, den genauen Fehlerzeitpunkt zu bestimmen (Flashback Versions Query) und die Daten lassen sich dann damit einfach aus der Vergangenheit in die Gegenwart holen.

Samstag, 26. Januar 2013

APEX 4.2: Apex-Listener-Installationshürde "images"

Ich habe diese Woche erstmals APEX 4.2 auf einer 11.2.0.3-DB installiert. Obwohl die Installation fehlerfrei durchlief, wurde die Loginseite nicht angezeigt bzw. lieferte einen Javascriptfehler: "...'apex' is undefined..."
[edit]04.02.2013: Ich war etwas ungenau. Die APEX-Installation selbst war sehr einfach und lief fehlerfrei durch. Probleme bereitete nur der APEX-Listener, den ich statt des HTTP-Servers nutze.[/edit]
Das hat mich Nerven gekostet, obwohl ich früh die richtige Idee hatte: APEX Der APEX-Listener kannte das Verzeichnis nicht, in dem sich die Grafiken/images befanden.
Hätte ich diesen niegelnagelneuen Community-Tip gekannt, wäre ich ein paar graue Haare ärmer. :)
Update:
Das hatte ich ganz vergessen zu schreiben: Die Lösung des Problems war, das images-Verzeichnis aus dem Installationspaket an die richtige Stelle im ORACLE_HOME zu kopieren:
Das alte Verzeichnis habe ich sicherheitshalber gesichert. (hier als images_11.2.0.3)

Update (19.03.2013): Der Vollständigkeit halber hier noch eine Ergänzung zur Konfiguration des APEX-Listeners

Der APEX-Listener legt seine Dateien in einem eigenen Verzeichnis an, welches man beim ersten Start in einem Dialog konfiguriert.
In diesem Verzeichnis liegen unter anderem diese beiden interessanten Dateien:
<LISTENER_CONFIGFOLDER>\apex\apex.properties
<LISTENER_CONFIGFOLDER>\defaults.XML

in der Datei apex.properties findet sich die Pfadeinstellung zu den Images und die Portnummer:
#Tue Mar 18 18:05:16 CET 2013
#apex.images.do.not.prompt=true
apex.images=d\:\\oracle\\product\\11.2.0\\dbhome_1\\apex\\images\\
http.port=8080

Donnerstag, 24. Januar 2013

Soft Skill

"One reason that women love men who are oracle professionals: we're not afraid to 'commit'."
( Steve O'Hearn, "SQL Certified Expert Exam Guide", S.112, Chapter 3)
Ein Fall von Zweckoptimismus? :)

Sonntag, 13. Januar 2013

SAP HANA

SAP macht mit HANA Schlagzeilen und hat es damit sogar in die FAZ geschafft. ("Die neue Walldorfschule" - 05.01.2013) Es ist also Zeit, sich mal genauer damit zu befassen.
Worum geht es? Zunächst: HANA ist nicht nur eine Datenbanksoftware, sondern eine Appliance. Man kauft also Soft- und Hardware von SAP:
"SAP In-Memory Appliance (SAP HANA) ist eine flexible, vielfältig einsatzfähige In-Memory Appliance. Sie umfasst eine Kombination aus unterschiedlichen SAP-Software-Komponenten und maßgeschneiderter Hardware, die führende SAP-Partner bereitstellen." (SAP)
Alles was auf dem System gelesen, geschrieben, berechnet wird, passiert im RAM, welches auf der HANA bis 2 TB groß sein kann. Die Hardware ist letztlich dafür da, die Schreiboperationen sicher von der oder auf die Platte zu bringen.
Das In-Memory-Konzept ist nicht wirklich neu und schon gar keine SAP-Erfindung. SAP setzt die Konzepte aber konsequent um:
  • In-Memory - alles liegt im RAM und wird dort verarbeitet.
  • Spaltenbasierte Verarbeitung der Datensätze - nicht zeilenweise wie bei RDBMS. Spalteninhalte lassen sich so schneller auswerten. Das ist ein klares Plus für die Auswertung großer Datenmengen. Geht es um einzelne Datensätze, bei denen man eine eine ganze Zeile aus dem System ziehen muss - wie bei OLTP, ist die zeilenorientierte Verarbeitung eines RDBMS natürlich keine schlechte Option.
  • Kompression - Die Spalteninhalte werden komprimiert. Kompression wird auch in RDBMS genutzt. RDBMS komprimieren aber den Inhalt der Datenblöcke und nicht die Spalten.
  • Insert (keine Updates) - Die HANA ändert also keine Datensätze, sie legt sie nur neu am Ende der Spalte an. Die Spalten beinhalten also mehrere Versionen eines Datensatzes und wachsen bei Änderungen schnell an.
Da SAP kein eigenes RDBMS im Angebot hat, dem HANA Konkurrenz im eigenen Haus machen könnte, muss SAP auch auf nichts Rücksicht nehmen und keine faulen Kompromisse eingehen, wenn es auf diese DB-Technologie setzt. Bei Oracle, Microsoft oder IBM mit ihren RDBMS sieht das etwas anders aus.
HANA ist also schnell. Okay... Die naheliegende Managerfrage lautet demnach: Ist mein SAP damit schneller? Nein. Wer sein gutes altes SAP-ERP im Einsatz hat, dem nützt HANA erst einmal nichts. SAP müsste zunächst seine eigene Software komplett portieren. Das kann dauern und wird kosten.

Oracles Antwort: Exalytics

Oracle gefällt der Hype um HANA natürlich nicht. Als Reaktion auf HANA wurde Exalytics auf den Weg gebracht. 
Exalytics ist ebenfalls eine Appliance-Lösung. Einen Vergleich versucht www.silicon.de. Der Titel des Artikels "Äpfel mit Birnen" sagt es schon: Die Exalytics ist konzeptionell etwas anderes als HANA.
In der Exalytics werden RDBMS und In-Memory mit einander kombiniert - mit allen Vor- und Nachteilen, die ein solcher Kompromiss mit sich bringt.
Mit der Exadata X3, die von Oracle als "InMemory Machine" beworben wird, sieht das ähnlich aus.
Oracle ist offensichtlich nervös und sieht HANA als Gefahr für das eigene Geschäft. Die Exalytics muss die Kundschaft sichern, bis Oracle ein echtes Konkurrenzpaket im Angebot hat.

Fazit

Eigentlich geht es um nichts Neues. Im Kern dreht sich alles um die Frage, ob In-Memory-Datenbanken die klassischen RDBMS ablösen können und wie die großen Hersteller (Oracle, IBM, Microsoft) damit umgehen.
HANA konkurriert bis auf weiteres eher mit Produkten wie Exasolution oder Terracotta und wildert damit im Segment der Analyse- und DataWarehouse-Lösungen.
Achja: Teuer ist der InMemory-Spaß auch. Die Lizenzpreise stellen die Enterprise-Versionen von Oracle 11g, MS SQLServer und IBMs DB2 in den Schatten.

Mittwoch, 9. Januar 2013

RAC Housekeeping unter Windows

Wie man seinen RAC sauber hält, wird in diesem Artikel der Deutschsprachigen DBA-Community von Sebastian Solbach beschrieben: Housekeeping 11gR2 RAC
Wie fast immer wird sich auf Linux bezogen. Ich fasse das Ganze für Windowsserver zusammen.
Ich bin einer der [ironie]Glücklichen[/ironie], der einen RAC auf WindowsServer2008 administriert.
Der Betrieb des RAC verläuft recht problemlos, was dazu verleitet, den vielen Log-Dateien nicht die Aufmerksamkeit zu schenken, die sie verdienen – und sei es nur, sie zu löschen.
Log- und Tracedateien fallen im Grunde in 2 Bereichen an:
  • In der Grid-Infrastruktur
  • Datenbanken

GRID-Infrastruktur

Ein Großteil der Logdateien wird von der Clusterware verwaltet. Unterhalb von %GRID_HOME%\log\<host>\ liegen diverse Unterverzeichnisse mit rollierenden Logdateien. Bis auf 2 Ausnahmen muss man sich um nichts kümmern.
Folgende Logs auf jedem Node müssen manuell behandelt werden:
  • Das Clusterware-alert.log: %GRID_HOME%\log\<host>\alert<host>.log
  • Die Logs unterhalb: %GRID_HOME%\log\<host>\client\*

Alert.log - Clusterware

Das alert<host>.log wächst langsam. Auf meinem Beispielcluster mit 2 Knoten sind es nach einem Jahr Betrieb 15 bzw. 50MB.
Unter Windows kann das Logfile aber nicht wie unter Linux bei laufender Clusterware rolliert werden, da sich die Datei im Zugriff befindet.
Es bietet sich also an, das Log manuell im Zuge eines Wartungsfensters bei Windowsupdates zu rollieren.
Eine jährliche Rotation sollte ausreichen.
Ein Skript könnte so aussehen und im Zuge der Wartung per Windows-Scheduler beim Serverstart ausgeführt werden:
set GRID_HOME=D:\oragrid\11.2.0.3
set GRID_HOST=RAC1
set LOGPATH=%GRID_HOME%\log\%GRID_HOST%
set jahr=%date:~-4%
set monat=%date:~-7,2%
set tag=%date:~-10,2%
set LOGDATE=%jahr%%monat%%tag%

copy %LOGPATH%\alert%GRID_HOST%.log %LOGPATH%\alert%GRID_HOST%_%LOGDATE%.log
type nul > %LOGPATH%\alert%GRID_HOST%.log

OCRDUMP, OCRCHECK, OCRCONFIG, CRSCTL

Unterhalb %GRID_HOME%\log\<host>\client\* finden sich viele kleine Logdateien, die bei administrative Aufrufen von z.B. ocrcheck erzeugt werden.
Auf meinem Produktivsystem summierte sich der Platzbedarf innerhalb eines Jahres auf 200-250MB je Node. Mit diesem Skript kann man der Plage Herr werden:
set GRID_HOME=D:\oragrid\11.2.0.3
set GRID_HOST=RAC1
set LOGPATH=%GRID_HOME%\log\%GRID_HOST%

forfiles /P %LOGPATH%\client\ /S /M *.* /D -7 /C "cmd /c del /q @path"
Es löscht alle Dateien älter als 7 Tage. Wenn man das täglich oder monatlich über den Scheduler startet, hat man seine Ruhe.

Listener.log, listener_scan<#>.log rollieren

Auf jedem Knoten die Listener.log- und listener_scan1.log bis listener_scan3.log-Dateien umbenennen. Zum Beispiel:
%GRID_HOME%\log\diag\tnslsnr\<host>\listener\trace
%GRID_HOME%\log\diag\tnslsnr\<host>\listener_scan1\trace
%GRID_HOME%\log\diag\tnslsnr\<host>\listener_scan2\trace
%GRID_HOME%\log\diag\tnslsnr\<host>\listener_scan3\trace
Es genügt, die ursprüngliche log umzubenennen/löschen. Beim nächsten Eintrag wird eine neue Datei generiert.
Die Größe der Logs ist natürlich abhängig von der Aktivität auf dem Cluster. Auf meinem Beispielsystem liegen nach einem Jahr insgesamt 1,5GB an Listener-Logs vor.
Eine monatliche Rotation ist empfehlenswert.

ASM

Die Log- und Trace-Files der ASM-Instanzen werden im Automatic Diagnostic Repository gespeichert.
Das Housekeeping ist automatisiert.
Einstellungen zum Löschen alter Informationen:
  • Default 30 Tage
  • 365 Tage für das mechanische Alert File
Das Alert-log der ASM-Instanzen findet sich hier:
%ORACLE_BASE%\diag\asm\+asm\asm<#>\trace\alert_ asm<#>.log
Zum rollieren einfach
  1. die alert_ asm<#>.log in alert_ asm<#>_YYYYMMDD.log umbennennen und
  2. eine neue alert_ asm<#>.log anlegen.
Außerdem muss man sich um die Tracefiles unter %GRID_HOME%\RDBMS\trace selbst kümmern.

Datenbanken

Die RAC-DB verhalten sich wie die ASM-Instanzen:
Das Housekeeping wird vom ADR erledigt und um das alert.log muss sich der Admin kümmern.

Alert.log

Wie bei der ASM-Instanz auf allen Knoten unter %ORACLE_BASE%\diag\rdbms\<database>\<instance>\trace:
  1. alert_<instance>.log in alert_<instance>_YYYYMMDD.log umbennennen und
  2. neue alert_<instance>.log anlegen.

Sonstiges

OCR

Weiterhin gibt es laut Housekeeping 11gR2 RAC  unter %GRID_HOME%\cdata\<cluster>\ Sicherungen der OCR, die periodisch erzeugt und gelöscht werden müssen. In meiner WindowsServer-Installation existieren die angesprochenen Sicherungen nicht an dieser Stelle oder anderswo im GRID_HOME. Ich sehe bei Gelegenheit nach, ob diese in der ASM zu finden sind.

Auditfiles der ASM- und Datenbankinstanzen

Anders als in Housekeeping 11gR2 RACbeschrieben, existieren unter Windows diese Logs nicht im Filesystem. Dieses Auditing findet man in der Ereignisanzeige:
Event Viewer

Enterprise Manager DB Console / Enterprise Management Agent

Unterhalb %ORACLE_HOME%\<knoten>_<dbuniquename>\sysman\log befinden sich logs und tracefiles der DBCansole. Innerhalb eines Jahres kamen hier 120MB zusammen.
Monatliches Rollieren und Löschen kann nicht schaden.

cfgtoollogs

Hier zitiere ich direkt aus Housekeeping 11gR2 RAC :
Zu den bisher erwähnten Dateien sollte man noch auf das cfgtoollogs Verzeichnis (im $ORACLE_BASE und $ORACLE_HOME) achten. Je nach dem ob man häufiger mit OPatch die Oracle Homes überprüft, EMCA verwendet, um den Enterprise Manager umzukonfigurieren, oder den DBCA um Datenbanken anzulegen. Jedes dieser Tools schreibt ein Log in das entsprechende cfgtoollogs Verzeichnis. Sollte man in Skripten oder ähnlichem diese Tools periodisch verwenden, sollte auch das cfgtoollogs Verzeichnis von den anfallenden Logfiles befreit werden.
Je nach Aufkommen monatliches oder jährliches Löschen.

Fazit

Ob es sich lohnt bei einem 2-Knoten-RAC alle Einzelschritte zu verskripten ist Geschmackssache. Da das Datenaufkommen so gering ist, dass es genügt, die logs monatlich zu rollieren oder zu löschen, ist es zu verschmerzen, dies manuell zu erledigen. Netter Nebeneffekt: Man hat regelmäßig die Verzeichnisstruktur vor Augen.
Wenn man das Ganze in ein oder mehrere Skripte gießen und das gleich richtig machen will, bietet sich auf einem WindowsServer natürlich PowerShell an, statt lauter Batchdateien anzulegen.

Montag, 7. Januar 2013

ORA_ROWSCN - Datensatzänderungen ermitteln

In einer von mir administrierten DB wird durch eine Web-Anwendung umfangreich Gebrauch von der Pseudospalte ORA_ROWSCN gemacht.
Diese Spalte gibt die SCN der letzten Änderung aus.
In der Anwendung soll über die ORA_ROWSCN ermittelt werden, ob ein bestimmter Datensatz seit dem letzten Zugriff geändert wurde.
Hierbei ist aber zu beachten, dass standardmäßig die SCN der letzten Änderung des Blocks ausgegeben wird. Da in einem Block für gewöhnlich mehrere Datensätze gespeichert sind, gehören zu einer ORA_ROWSCN also auch mehr als 1 Datensatz.
Das Ganze ist demnach erstmal recht unscharf.
Will man die ORA_ROWSCN datensatzscharf haben, muss die betroffene Tabelle korrekt angelegt werden:
CREATE TABLE test
   (col1 number, col2 varchar2(10), 
   constraint test_pk primary key(col1))
   ROWDEPENDENCIES;

Über ALTER TABLE lässt sich eine Tabelle leider nicht "datensatzscharf" schalten.

Natürlich lässt sich über die SCN auch der Zeitpunkt der Änderung ermitteln.

Quellen:
Oracle: http://docs.oracle.com/cd/E11882_01/server.112/e26088/pseudocolumns007.htm#SQLRF50953
AskTom: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:517105100346104196