Dans son blog, R.Foote détaille la structure interne d'un entête de bloc:
- http://richardfoote.wordpress.com/2010/07/20/index-block-dump-block-header-part-i-editions-of-you/
- http://richardfoote.wordpress.com/2010/07/28/index-block-dump-block-header-part-ii-and-read-consistency-i-cant-read/
Puis, il décrit un bloc d'index:
- http://richardfoote.wordpress.com/2010/08/10/index-block-dump-index-only-section-part-i-tvc-15/
- http://richardfoote.wordpress.com/2010/10/07/index-block-dump-index-only-section-part-ii-station-to-station/
- http://richardfoote.wordpress.com/2010/11/04/index-block-dumps-final-demo-come-together/
mercredi 9 mars 2011
jeudi 6 janvier 2011
Event trace 10053
L'event trace 10053 permet de comprendre comment l'optimiseur a créé le plan d'exécution d'une requête. A chaque noeud du plan, on peut voir comment l'optimiseur a pris une décision, par exemple le choix d'un index range scan plutôt qu'un table full scan.
Pour étudier plus facilement ce type de trace, un viewer compatible 10g et 11g est disponible à cette adresse:
http://jonathanlewis.wordpress.com/2010/04/30/10053-viewer/.
Pour tenter de déchiffrer l'event trace 10053, vous pouvez consulter:
- le chapitre 14 du livre Cost Based Oracle Fundamentals écrit par J.Lewis;
- la note Metalink 338137.1: Analyzing 10053 Trace Files;
- la présentation de W.Breitling “A Look under the Hood of CBO – the 10053 Event”.
En 11g, on peut capturer une trace 10053 pour une requête appartenant à un package PL/SQL.
Pour étudier plus facilement ce type de trace, un viewer compatible 10g et 11g est disponible à cette adresse:
http://jonathanlewis.wordpress.com/2010/04/30/10053-viewer/.
Pour tenter de déchiffrer l'event trace 10053, vous pouvez consulter:
- le chapitre 14 du livre Cost Based Oracle Fundamentals écrit par J.Lewis;
- la note Metalink 338137.1: Analyzing 10053 Trace Files;
- la présentation de W.Breitling “A Look under the Hood of CBO – the 10053 Event”.
En 11g, on peut capturer une trace 10053 pour une requête appartenant à un package PL/SQL.
mardi 23 novembre 2010
Prototype d'un pool de connexion au niveau du client
/* Compilation: make -f demo_proc.mk EXE=test_rac_recette_con_cache OBJS=test_rac_recette_con_cache.o build PROCFLAGS="sqlcheck=full userid=xxx/xxx@xxx code=ansi_c define=V8" */
#include stdio.h
#include stdlib.h
#include string.h
#include errno.h
#include unistd.h
#include sys/time.h
#include sys/types.h
#include sys/wait.h
#include sys/ipc.h
#include sys/shm.h
#include sys/sem.h
#include sqlca.h
#include sqlcpr.h
#define UNAME_LEN 30
#define PWD_LEN 30
#define CONNECT_STRING_LEN 128
#define NB_CONNEXION 50
typedef int SEMAPHORE;
/* Contexte de connexion */
EXEC SQL BEGIN DECLARE SECTION;
typedef struct {
sql_context sql_cntxt[NB_CONNEXION];
short h_dspnb[NB_CONNEXION];
} sCntxtConnexion;
sql_context sql_cntxt;
VARCHAR username[UNAME_LEN];
VARCHAR password[PWD_LEN];
VARCHAR connect_string[CONNECT_STRING_LEN];
EXEC SQL END DECLARE SECTION;
sCntxtConnexion *p_cntx;
SEMAPHORE sem;
void sql_error();
int trait_rqt(int i);
int detruire_sem(SEMAPHORE sem);
int changer_sem(SEMAPHORE sem, int val);
SEMAPHORE creer_sem(key_t key);
void P(SEMAPHORE sem);
void V(SEMAPHORE sem);
int main(int argc, char* argv[]) {
int i,j;
int nb_process;
pid_t pid;
int status;
key_t cle, cle_sem;
int id;
int cpt;
if (argc != 5) { printf("test_rac_recette_con ... \n"); return 0; }
printf("Pere: user %s mot de passe %s alias %s \n", argv[1], argv[2], argv[3]);
strcpy((char *) username.arr, argv[1]);
username.len = strlen((char *) username.arr);
strcpy((char *) password.arr, argv[2]);
password.len = strlen((char *) password.arr);
strcpy((char *) connect_string.arr, argv[3]);
connect_string.len = strlen((char *) connect_string.arr);
EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n");
nb_process = atoi(argv[4]);
j = 1;
EXEC SQL ENABLE THREADS;
/* Création du segment de mémoire partagée */
cle = ftok(getenv("HOME"), 'A');
if (cle == -1) {
printf("Pere: pb ftok \n");
return -1;
}
/* 0666: droits */
/* ipcrm -m */
id = shmget(cle, sizeof(sCntxtConnexion), IPC_CREAT | IPC_EXCL | 0666);
if (id == -1) {
switch (errno) {
case EEXIST:
printf("Pere: le segment existe deja \n");
default:
printf("Pere: shmget \n");
return -1;
}
}
p_cntx = (sCntxtConnexion *) shmat(id, NULL, SHM_R | SHM_W);
if (p_cntx == NULL) {
printf("Pere: shmat \n");
return -1;
}
/* Création du pool */
for i in 0..NB_CONNEXION-1
EXEC SQL CONTEXT ALLOCATE :sql_cntxt;
EXEC SQL CONTEXT USE :sql_cntxt;
EXEC SQL CONNECT :username IDENTIFIED BY :password USING :connect_string;
p_cntx->sql_cntxt[i] = sql_cntxt;
p_cntx->h_dspnb[i]=0;
system("ipcs -m");
/* Test du pool sans fork */
sql_cntxt = p_cntx->sql_cntxt[0];
EXEC SQL CONTEXT USE :sql_cntxt;
EXEC SQL select count(*) into :cpt from alphacompactee;
printf("Pere: cpt: %d \n", cpt);
/* Création d'un sémaphore */
cle_sem = ftok(getenv("HOME"), 'B');
if (cle_sem == -1) {
printf("Pere: pb ftok \n");
return -1;
}
sem = creer_sem(cle_sem);
printf("Semaphore: \n");
system("ipcs -s | grep snotter");
/* Fork des processus fils */
for(i = 0; i < nb_process; i++) {
pid = fork();
if (pid == 0) {
/* printf("Fils: %d \n", i); */
trait_rqt(i);
return 0;
}
}
for(i = 0; i < nb_process; i++) {
wait(&status);
}
printf("Pere: fin des fils ... \n");
/* Libération du pool * /
for i in 0..NB_CONNEXION-1
sql_cntxt = p_cntx->sql_cntxt[i];
EXEC SQL CONTEXT USE :sql_cntxt;
/*EXEC SQL COMMIT RELEASE;*/
EXEC SQL CONTEXT FREE :sql_cntxt;
/* Suppression du segment de mémoire partagée */
/* (char *) */
if (shmdt((void *) p_cntx) == -1) {
printf("Pere: shmdt \n");
return -1;
}
if (shmctl(id, IPC_RMID, NULL) == -1) {
printf("Pere: shmctl(remove) \n");
return -1;
}
/* Suppression du sémaphore */
if (detruire_sem(sem) == -1) {
printf("Pere: detruire_sem \n");
return -1;
};
return 0;
}
int trait_rqt(int i) {
int j;
hrtime_t point1, point2;
char tcHeure[20];
struct timeval tv;
struct tm *tm;
int cpt;
gettimeofday(&tv);
tm=localtime(&tv.tv_sec);
memset(tcHeure, '\0', sizeof(tcHeure));
sprintf(tcHeure, " %d:%02d:%02d %03d ", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec/1000);
point1 = gethrtime();
/* Attente sur le sémaphore */
printf("ATT Fils %d wait \n", i);
P(sem);
/* Choix d'une connexion disponible */
for j in 0..NB_CONNEXION-1
if (p_cntx->h_dspnb[j]==0) {
p_cntx->h_dspnb[j]=1;
break;
}
else {
printf("Fils %d Conn %d deja prise \n", i, j);
}
printf("Fils %d Conn %d prise \n", i, j);
/* Libération du sémaphore */
V(sem);
point2 = gethrtime();
printf("Fils Conn %d; %s; %lld ; %lld ; %lld \n", i, tcHeure, (point2 - point1)/1000000, point1, point2);
gettimeofday(&tv);
tm=localtime(&tv.tv_sec);
memset(tcHeure, '\0', sizeof(tcHeure));
sprintf(tcHeure, " %d:%02d:%02d %03d ", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec/1000);
point1 = gethrtime();
sql_cntxt = p_cntx->sql_cntxt[j];
EXEC SQL CONTEXT USE :sql_cntxt;
/* Exécution d'une requête */
EXEC SQL select count(*) into :cpt from alphacompactee;
point2 = gethrtime();
printf("Fils Req %d; %s; %lld ; %lld ; %lld \n", i, tcHeure, (point2 - point1)/1000000, point1, point2);
/* Attente sur le sémaphore */
printf("ATT Fils %d wait \n", i);
P(sem);
/* Libération de la connexion */
p_cntx->h_dspnb[j]=0;
/* Libération du sémaphore */
printf("Fils %d Conn %d liberee\n", i, j);
V(sem);
return 0;
}
void sql_error(char *msg) {
char err_msg[128];
int buf_len, msg_len;
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s\n", msg);
buf_len = sizeof (err_msg);
sqlglm(err_msg, (unsigned int *)&buf_len,(unsigned int *)&msg_len);
if (msg_len > buf_len)
msg_len = buf_len;
printf("%.*s\n", msg_len, err_msg);
EXEC SQL ROLLBACK RELEASE;
exit(1);
}
int detruire_sem(SEMAPHORE sem) {
if (semctl(sem, 0, IPC_RMID, 0) != 0) {
printf("detruire_sem \n");
return -1;
}
return 0;
}
int changer_sem(SEMAPHORE sem, int val) {
struct sembuf sb[1];
sb[0].sem_num = 0;
sb[0].sem_op = val;
sb[0].sem_flg = 0;
if (semop(sem, sb, 1) != 0) {
printf("changer_sem \n");
return -1;
}
return 0;
}
SEMAPHORE creer_sem(key_t key) {
SEMAPHORE sem;
int r;
union semun {
int val;
struct semid_ds *buf;
ushort *array;
} s_ctl;
sem = semget(key, 1, IPC_CREAT | 0666);
if (sem < 0) {
printf("creer_sem \n");
exit(EXIT_FAILURE);
}
s_ctl.val = 1;
r = semctl (sem, 0, SETVAL, s_ctl );
if (r < 0) {
printf("initialisation sémaphore \n");
exit(EXIT_FAILURE);
}
return sem;
}
void P(SEMAPHORE sem) {
changer_sem(sem, -1);
}
void V(SEMAPHORE sem) {
changer_sem(sem, 1);
}
#include stdio.h
#include stdlib.h
#include string.h
#include errno.h
#include unistd.h
#include sys/time.h
#include sys/types.h
#include sys/wait.h
#include sys/ipc.h
#include sys/shm.h
#include sys/sem.h
#include sqlca.h
#include sqlcpr.h
#define UNAME_LEN 30
#define PWD_LEN 30
#define CONNECT_STRING_LEN 128
#define NB_CONNEXION 50
typedef int SEMAPHORE;
/* Contexte de connexion */
EXEC SQL BEGIN DECLARE SECTION;
typedef struct {
sql_context sql_cntxt[NB_CONNEXION];
short h_dspnb[NB_CONNEXION];
} sCntxtConnexion;
sql_context sql_cntxt;
VARCHAR username[UNAME_LEN];
VARCHAR password[PWD_LEN];
VARCHAR connect_string[CONNECT_STRING_LEN];
EXEC SQL END DECLARE SECTION;
sCntxtConnexion *p_cntx;
SEMAPHORE sem;
void sql_error();
int trait_rqt(int i);
int detruire_sem(SEMAPHORE sem);
int changer_sem(SEMAPHORE sem, int val);
SEMAPHORE creer_sem(key_t key);
void P(SEMAPHORE sem);
void V(SEMAPHORE sem);
int main(int argc, char* argv[]) {
int i,j;
int nb_process;
pid_t pid;
int status;
key_t cle, cle_sem;
int id;
int cpt;
if (argc != 5) { printf("test_rac_recette_con
printf("Pere: user %s mot de passe %s alias %s \n", argv[1], argv[2], argv[3]);
strcpy((char *) username.arr, argv[1]);
username.len = strlen((char *) username.arr);
strcpy((char *) password.arr, argv[2]);
password.len = strlen((char *) password.arr);
strcpy((char *) connect_string.arr, argv[3]);
connect_string.len = strlen((char *) connect_string.arr);
EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n");
nb_process = atoi(argv[4]);
j = 1;
EXEC SQL ENABLE THREADS;
cle = ftok(getenv("HOME"), 'A');
if (cle == -1) {
printf("Pere: pb ftok \n");
return -1;
}
/* 0666: droits */
/* ipcrm -m */
id = shmget(cle, sizeof(sCntxtConnexion), IPC_CREAT | IPC_EXCL | 0666);
if (id == -1) {
switch (errno) {
case EEXIST:
printf("Pere: le segment existe deja \n");
default:
printf("Pere: shmget \n");
return -1;
}
}
p_cntx = (sCntxtConnexion *) shmat(id, NULL, SHM_R | SHM_W);
if (p_cntx == NULL) {
printf("Pere: shmat \n");
return -1;
}
/* Création du pool */
for i in 0..NB_CONNEXION
EXEC SQL CONTEXT ALLOCATE :sql_cntxt;
EXEC SQL CONTEXT USE :sql_cntxt;
EXEC SQL CONNECT :username IDENTIFIED BY :password USING :connect_string;
p_cntx->sql_cntxt[i] = sql_cntxt;
p_cntx->h_dspnb[i]=0;
system("ipcs -m");
/* Test du pool sans fork */
sql_cntxt = p_cntx->sql_cntxt[0];
EXEC SQL CONTEXT USE :sql_cntxt;
EXEC SQL select count(*) into :cpt from alphacompactee;
printf("Pere: cpt: %d \n", cpt);
/* Création d'un sémaphore */
cle_sem = ftok(getenv("HOME"), 'B');
if (cle_sem == -1) {
printf("Pere: pb ftok \n");
return -1;
}
sem = creer_sem(cle_sem);
printf("Semaphore: \n");
system("ipcs -s | grep snotter");
for(i = 0; i < nb_process; i++) {
pid = fork();
if (pid == 0) {
/* printf("Fils: %d \n", i); */
trait_rqt(i);
return 0;
}
}
for(i = 0; i < nb_process; i++) {
wait(&status);
}
printf("Pere: fin des fils ... \n");
for i in 0..NB_CONNEXION
EXEC SQL CONTEXT USE :sql_cntxt;
/*EXEC SQL COMMIT RELEASE;*/
EXEC SQL CONTEXT FREE :sql_cntxt;
/* Suppression du segment de mémoire partagée */
/* (char *) */
printf("Pere: shmdt \n");
return -1;
}
if (shmctl(id, IPC_RMID, NULL) == -1) {
printf("Pere: shmctl(remove) \n");
return -1;
}
printf("Pere: detruire_sem \n");
return -1;
};
return 0;
}
int trait_rqt(int i) {
int j;
hrtime_t point1, point2;
char tcHeure[20];
struct timeval tv;
struct tm *tm;
int cpt;
gettimeofday(&tv);
tm=localtime(&tv.tv_sec);
memset(tcHeure, '\0', sizeof(tcHeure));
sprintf(tcHeure, " %d:%02d:%02d %03d ", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec/1000);
point1 = gethrtime();
printf("ATT Fils %d wait \n", i);
P(sem);
/* Choix d'une connexion disponible */
if (p_cntx->h_dspnb[j]==0) {
p_cntx->h_dspnb[j]=1;
break;
}
else {
printf("Fils %d Conn %d deja prise \n", i, j);
}
point2 = gethrtime();
printf("Fils Conn %d; %s; %lld ; %lld ; %lld \n", i, tcHeure, (point2 - point1)/1000000, point1, point2);
gettimeofday(&tv);
tm=localtime(&tv.tv_sec);
memset(tcHeure, '\0', sizeof(tcHeure));
sprintf(tcHeure, " %d:%02d:%02d %03d ", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec/1000);
point1 = gethrtime();
sql_cntxt = p_cntx->sql_cntxt[j];
EXEC SQL CONTEXT USE :sql_cntxt;
/* Exécution d'une requête */
EXEC SQL select count(*) into :cpt from alphacompactee;
point2 = gethrtime();
printf("Fils Req %d; %s; %lld ; %lld ; %lld \n", i, tcHeure, (point2 - point1)/1000000, point1, point2);
/* Attente sur le sémaphore */
P(sem);
p_cntx->h_dspnb[j]=0;
printf("Fils %d Conn %d liberee\n", i, j);
V(sem);
return 0;
}
void sql_error(char *msg) {
char err_msg[128];
int buf_len, msg_len;
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\n%s\n", msg);
buf_len = sizeof (err_msg);
sqlglm(err_msg, (unsigned int *)&buf_len,(unsigned int *)&msg_len);
if (msg_len > buf_len)
msg_len = buf_len;
printf("%.*s\n", msg_len, err_msg);
EXEC SQL ROLLBACK RELEASE;
exit(1);
}
int detruire_sem(SEMAPHORE sem) {
if (semctl(sem, 0, IPC_RMID, 0) != 0) {
printf("detruire_sem \n");
return -1;
}
return 0;
}
int changer_sem(SEMAPHORE sem, int val) {
struct sembuf sb[1];
sb[0].sem_num = 0;
sb[0].sem_op = val;
sb[0].sem_flg = 0;
if (semop(sem, sb, 1) != 0) {
printf("changer_sem \n");
return -1;
}
return 0;
}
SEMAPHORE creer_sem(key_t key) {
SEMAPHORE sem;
int r;
union semun {
int val;
struct semid_ds *buf;
ushort *array;
} s_ctl;
sem = semget(key, 1, IPC_CREAT | 0666);
if (sem < 0) {
printf("creer_sem \n");
exit(EXIT_FAILURE);
}
s_ctl.val = 1;
r = semctl (sem, 0, SETVAL, s_ctl );
if (r < 0) {
printf("initialisation sémaphore \n");
exit(EXIT_FAILURE);
}
return sem;
}
void P(SEMAPHORE sem) {
changer_sem(sem, -1);
}
void V(SEMAPHORE sem) {
changer_sem(sem, 1);
}
mardi 5 octobre 2010
Problème de connexion
Soit un client PRO*C qui a épisodiquement des temps de connexion à une instance Oracle de l'ordre de quelques centaines de ms, voire plusieurs secondes.
La première étape consiste à isoler le problème. Dans le cas présent, la latence est observée au moment de l'exécution de l'instruction CONNECT.
Pour améliorer les temps de connexion, deux paramètres peuvent être intéressants à modifier:
- SDU ( http://www.sun.com/blueprints/1002/817-0370-10.pdf );
- tcp.nodelay ( désactivation de l'algorithme de Nagle lié à la couche TCP ).
Pour aller plus loin, consulter le guide d'administration sur la couche sqlnet.
Si le problème n'est pas résolu, on peut alors poser les traces sqlnet sur le client et ensuite éventuellement sur le listener, voire le dispatcher en cas d'utilisation du mode shared server. Pour les traces sqlnet sur le client, on peut utiliser une configuration spécifique ( sqlnet.ora + tnsnames.ora ) via la variable d'environnement TNS_ADMIN. Le paramétrage des traces du listener s'effectue dans le fichier listener.ora. Puis, on lance le listener pour activer le mode trace ( exemple: set trc_level support ). L'étude du dispatcher se fait via l'event 10248.
Dans le cas présent, aucune erreur ( nserror ) n'a été détectée. Une analyse réseau ( tcpdump sur le client et un noeud du RAC, utilisation de sondes ) a ensuite été menée, ce qui a permis de localiser le problème au niveau de la machine cliente: des paquets de type ACK retardé ou donnée ne sont pas émis immédiatement. Il est à noter que les adresses virtuelles des noeuds du RAC sont résolues directement dans le fichier host.
En changeant de client Oracle ( 9i -> 10g ), le problème a persisté, d'où une mise en cause de la couche système, en particulier au niveau de son paramétrage TCP. Une analyse via l'outil truss n'a pas permis d'obtenir de résultats probants. L'outil Dtrace ne peut pas être utilisé, la machine cliente étant en Solaris 8.
Quelques modifications ont été effectuées au niveau du paramétrage TCP du système, sans grand succès ( exemple: tcp_time_wait_interval ). Pour dédouaner complètement le client Oracle, un client/serveur de type écho à base de socket TCP a été développé ( serveur sur un noeud du RAC ) en se basant sur les sources disponibles à cette adresse : http://cs.baylor.edu/~donahoo/practical/CSockets/textcode.html. Une fois ce dernier installé, on a pu observer que la durée de traitement moyenne de la commande recv du client était de l'ordre de quelques dizaines de micros pour un échange de 8 ko. Cependant, de temps en temps, elle peut excéder la dizaine de ms.
Suite à cette constatation, le problème est étudié par l'équipe système, qui ne trouve pas de solution.
Lors de ces tests, la charge transactionnelle était faible ( 4 transactions par seconde ), d'où une solution possible. Si la charge augmente ou pour éviter ce type de problème sur une machine mal configurée, on peut mettre en place un pool de sockets dédiées aux connexions Oracle. Le principe est le suivant:
- Lors du démarrage du serveur d'application, on crée les sockets entre le client Oracle et l'instance, puis on met en mémoire les pointeurs de connexion dans un segment de mémoire partagée, ce dernier étant protégé par un sémaphore;
- Lors du traitement d'une transaction, le serveur crée un processus fils qui va rechercher dans le segment de mémoire partagée une connexion disponible, si le sémaphore le permet ( unicité d'accès à la ressource ). Une fois une connexion sélectionnée ( temps d'exécution inférieur à 1 ms ), la requête est exécutée et les données transmises au client. La déconnexion, quant à elle, se limite à rendre de nouveau disponible la connexion utilisée.
- Lors de l'arrêt du serveur, les sockets sont closes et les ressources système libérées.
De cette manière, la discontinuité de performance se réduit à l'échange des données entre le client et l'instance après l'exécution d'une requête. Les temps de connexion/déconnexion, quant à eux, sont considérés comme négligeables.
La première étape consiste à isoler le problème. Dans le cas présent, la latence est observée au moment de l'exécution de l'instruction CONNECT.
Pour améliorer les temps de connexion, deux paramètres peuvent être intéressants à modifier:
- SDU ( http://www.sun.com/blueprints/1002/817-0370-10.pdf );
- tcp.nodelay ( désactivation de l'algorithme de Nagle lié à la couche TCP ).
Pour aller plus loin, consulter le guide d'administration sur la couche sqlnet.
Si le problème n'est pas résolu, on peut alors poser les traces sqlnet sur le client et ensuite éventuellement sur le listener, voire le dispatcher en cas d'utilisation du mode shared server. Pour les traces sqlnet sur le client, on peut utiliser une configuration spécifique ( sqlnet.ora + tnsnames.ora ) via la variable d'environnement TNS_ADMIN. Le paramétrage des traces du listener s'effectue dans le fichier listener.ora. Puis, on lance le listener pour activer le mode trace ( exemple: set trc_level support ). L'étude du dispatcher se fait via l'event 10248.
Dans le cas présent, aucune erreur ( nserror ) n'a été détectée. Une analyse réseau ( tcpdump sur le client et un noeud du RAC, utilisation de sondes ) a ensuite été menée, ce qui a permis de localiser le problème au niveau de la machine cliente: des paquets de type ACK retardé ou donnée ne sont pas émis immédiatement. Il est à noter que les adresses virtuelles des noeuds du RAC sont résolues directement dans le fichier host.
En changeant de client Oracle ( 9i -> 10g ), le problème a persisté, d'où une mise en cause de la couche système, en particulier au niveau de son paramétrage TCP. Une analyse via l'outil truss n'a pas permis d'obtenir de résultats probants. L'outil Dtrace ne peut pas être utilisé, la machine cliente étant en Solaris 8.
Quelques modifications ont été effectuées au niveau du paramétrage TCP du système, sans grand succès ( exemple: tcp_time_wait_interval ). Pour dédouaner complètement le client Oracle, un client/serveur de type écho à base de socket TCP a été développé ( serveur sur un noeud du RAC ) en se basant sur les sources disponibles à cette adresse : http://cs.baylor.edu/~donahoo/practical/CSockets/textcode.html. Une fois ce dernier installé, on a pu observer que la durée de traitement moyenne de la commande recv du client était de l'ordre de quelques dizaines de micros pour un échange de 8 ko. Cependant, de temps en temps, elle peut excéder la dizaine de ms.
Suite à cette constatation, le problème est étudié par l'équipe système, qui ne trouve pas de solution.
Lors de ces tests, la charge transactionnelle était faible ( 4 transactions par seconde ), d'où une solution possible. Si la charge augmente ou pour éviter ce type de problème sur une machine mal configurée, on peut mettre en place un pool de sockets dédiées aux connexions Oracle. Le principe est le suivant:
- Lors du démarrage du serveur d'application, on crée les sockets entre le client Oracle et l'instance, puis on met en mémoire les pointeurs de connexion dans un segment de mémoire partagée, ce dernier étant protégé par un sémaphore;
- Lors du traitement d'une transaction, le serveur crée un processus fils qui va rechercher dans le segment de mémoire partagée une connexion disponible, si le sémaphore le permet ( unicité d'accès à la ressource ). Une fois une connexion sélectionnée ( temps d'exécution inférieur à 1 ms ), la requête est exécutée et les données transmises au client. La déconnexion, quant à elle, se limite à rendre de nouveau disponible la connexion utilisée.
- Lors de l'arrêt du serveur, les sockets sont closes et les ressources système libérées.
De cette manière, la discontinuité de performance se réduit à l'échange des données entre le client et l'instance après l'exécution d'une requête. Les temps de connexion/déconnexion, quant à eux, sont considérés comme négligeables.
mardi 24 août 2010
Installation 11gR2 sur Ubuntu ( partie 2 )
Lors du redémarrage d'Ubuntu, j'ai rencontré un problème avec OEM ( Oracle Entreprise Manager ), la console d'administration. L'erreur était la suivante: OC4J Configuration issue. /u01/app/oracle/product/11.2.0/dbhome_1/oc4j/j2ee/OC4J_DBConsole_$HOSTNAME_orcl not found.
Pour corriger ce problème, j'ai effectué les opérations suivantes:
- Suppression du repository d'OEM: emca -deconfig dbcontrol db -repos drop;
- export ORACLE_HOSTNAME=$HOSTNAME;
- Recréation du repository: emca -config dbcontrol db -repos create.
Pour démarrer OEM, on utilise la commande suivante: emctl start dbconsole ( arrêt: emctl stop dbconsole ). Sous Ubuntu, on obtient alors le message suivant: ulimit: 25: bad number. Il peut être ignoré.
En plus de l'outil d'administration, on dispose du client de développement habituel, sqldeveloper ( commande: sh sqldeveloper.sh ). Il peut fonctionner avec OpenJDK ( commande: java -version ).
Pour éviter une fenêtre blanche ( gnome ), il faut désactiver les effets visuels dans la personnalisation de l'apparence du bureau.
Pour corriger ce problème, j'ai effectué les opérations suivantes:
- Suppression du repository d'OEM: emca -deconfig dbcontrol db -repos drop;
- export ORACLE_HOSTNAME=$HOSTNAME;
- Recréation du repository: emca -config dbcontrol db -repos create.
Pour démarrer OEM, on utilise la commande suivante: emctl start dbconsole ( arrêt: emctl stop dbconsole ). Sous Ubuntu, on obtient alors le message suivant: ulimit: 25: bad number. Il peut être ignoré.
En plus de l'outil d'administration, on dispose du client de développement habituel, sqldeveloper ( commande: sh sqldeveloper.sh ). Il peut fonctionner avec OpenJDK ( commande: java -version ).
Pour éviter une fenêtre blanche ( gnome ), il faut désactiver les effets visuels dans la personnalisation de l'apparence du bureau.
mercredi 28 juillet 2010
Installation 11gR2 sur Ubuntu ( partie 1 )
Dernièrement, j'ai acheté un portable Linux ( 64 bits, Ubuntu LTS version 10.04 ) chez Novatux. Pour une fois, je vais faire un peu de pub, car le support technique y est remarquable ( merci Laurent ).
Sur ce dernier, je viens d'installer une 11gR2 en m'appuyant sur les liens suivants:
- http://www.pythian.com/news/2329/installing-oracle-11gr1-on-ubuntu-904-jaunty-jackalope/
- http://www.pythian.com/news/13291/installing-oracle-11gr2-enterprise-edition-on-ubuntu-10-04-lucid-lynx/
L'installation ( instance + database + OEM ) s'est bien déroulée. Quelques conseils:
- Bien respecter la phase préparatoire ( installation des librairies manquantes, création de l'utilisateur oracle, paramétrage système ... );
- Installer le package sux avant de lancer le binaire runinstaller et positionner la variable DISPLAY à :0.0;
- Ne pas tenir compte des erreurs relatives aux packages, OUI ( Oracle Universal Installer ) ne prenant pas en compte Ubuntu;
- Vérifier au préalable votre configuration graphique, vous pouvez en effet rencontrer une fenêtre blanche lors de l'installation et avoir quelques difficultés pour la valider.
Sur ce dernier, je viens d'installer une 11gR2 en m'appuyant sur les liens suivants:
- http://www.pythian.com/news/2329/installing-oracle-11gr1-on-ubuntu-904-jaunty-jackalope/
- http://www.pythian.com/news/13291/installing-oracle-11gr2-enterprise-edition-on-ubuntu-10-04-lucid-lynx/
L'installation ( instance + database + OEM ) s'est bien déroulée. Quelques conseils:
- Bien respecter la phase préparatoire ( installation des librairies manquantes, création de l'utilisateur oracle, paramétrage système ... );
- Installer le package sux avant de lancer le binaire runinstaller et positionner la variable DISPLAY à :0.0;
- Ne pas tenir compte des erreurs relatives aux packages, OUI ( Oracle Universal Installer ) ne prenant pas en compte Ubuntu;
- Vérifier au préalable votre configuration graphique, vous pouvez en effet rencontrer une fenêtre blanche lors de l'installation et avoir quelques difficultés pour la valider.
jeudi 1 juillet 2010
Analyse d'une requête
Ce lien donne une liste d'outils permettant d'effectuer un diagnostic sur une requête SQL: http://oracle-randolf.blogspot.com/2009/02/basic-sql-statement-performance.html.
Inscription à :
Articles (Atom)