La méthode scientifique poke, ou comment sélectionner une configuration de base de données à l'aide de benchmarks et d'un algorithme d'optimisation

Bonjour

J'ai décidé de partager ma trouvaille - le fruit de réflexions, d'essais et d'erreurs.
Dans l'ensemble : ce n'est pas une trouvaille, bien sûr - tout cela aurait dû être connu depuis longtemps, par ceux qui sont impliqués dans le traitement des données statistiques appliquées et dans l'optimisation de tout système, pas nécessairement spécifiquement du SGBD.
Et : oui, ils le savent, ils écrivent des articles intéressants sur leurs recherches, exemple (UPD. : dans les commentaires, ils ont souligné un projet très intéressant : ottune )
En revanche : d’emblée, je ne vois aucune mention ou diffusion généralisée de cette approche sur Internet parmi les informaticiens, les DBA.

Donc, au point.

Supposons que nous ayons une tâche : mettre en place un certain système de services pour assurer un certain type de travail.

On connaît ce travail : de quoi s'agit-il, comment la qualité de ce travail est mesurée, et quel est le critère de mesure de cette qualité.

Supposons également que la manière exacte dont le travail est effectué dans (ou avec) ce système de service soit plus ou moins connue et comprise.

"Plus ou moins" - cela signifie qu'il est possible de préparer (ou de l'obtenir de quelque part) un certain outil, utilitaire, service qui peut être synthétisé et appliqué au système avec une charge de test suffisamment adéquate à ce qui sera en production, dans des conditions suffisamment adéquates pour travailler en production.

Eh bien, supposons qu'un ensemble de paramètres de réglage pour ce système de service soit connu, qui peuvent être utilisés pour configurer ce système en termes de productivité de son travail.

Et quel est le problème - il n'y a pas une compréhension suffisamment complète de ce système de service, qui vous permet de configurer de manière experte les paramètres de ce système pour une charge future sur une plate-forme donnée et d'obtenir la productivité requise du système.

Bien. C'est presque toujours le cas.

Que pouvez-vous faire ici?

Eh bien, la première chose qui me vient à l’esprit est de consulter la documentation de ce système. Comprenez quelles sont les plages acceptables pour les valeurs des paramètres de réglage. Et, par exemple, en utilisant la méthode de descente de coordonnées, sélectionnez les valeurs des paramètres du système dans les tests.

Ceux. donner au système une sorte de configuration, sous la forme d'un ensemble spécifique de valeurs pour ses paramètres de configuration.

Appliquez-lui une charge de test, à l'aide de cet outil-utilitaire, générateur de charge.
Et regardez la valeur – la réponse, ou une mesure de la qualité du système.

La deuxième réflexion pourrait être de conclure que cela prend beaucoup de temps.

Eh bien, c'est-à-dire : s'il y a beaucoup de paramètres de réglage, si les plages de leurs valeurs couvertes sont larges, si chaque test de charge individuel prend beaucoup de temps, alors : oui, tout cela peut prendre une quantité inacceptable de temps.

Eh bien, voici ce que vous pouvez comprendre et retenir.

Vous pouvez découvrir que dans l'ensemble des valeurs des paramètres de configuration du système de service, il existe un vecteur, sous la forme d'une séquence de certaines valeurs.

Chacun de ces vecteurs, toutes choses étant égales par ailleurs (en ce sens qu'il n'est pas affecté par ce vecteur), correspond à une valeur métrique complètement définie - un indicateur de la qualité du fonctionnement du système sous charge de test.

C.-à-

Notons le vecteur de configuration du système comme La méthode scientifique poke, ou comment sélectionner une configuration de base de données à l'aide de benchmarks et d'un algorithme d'optimisationLa méthode scientifique poke, ou comment sélectionner une configuration de base de données à l'aide de benchmarks et d'un algorithme d'optimisation; Où La méthode scientifique poke, ou comment sélectionner une configuration de base de données à l'aide de benchmarks et d'un algorithme d'optimisation — nombre de paramètres de configuration du système, combien de ces paramètres existent.

Et la valeur de la métrique correspondant à cela La méthode scientifique poke, ou comment sélectionner une configuration de base de données à l'aide de benchmarks et d'un algorithme d'optimisation notons-le comme
La méthode scientifique poke, ou comment sélectionner une configuration de base de données à l'aide de benchmarks et d'un algorithme d'optimisation, alors on obtient une fonction : La méthode scientifique poke, ou comment sélectionner une configuration de base de données à l'aide de benchmarks et d'un algorithme d'optimisation

Eh bien : tout se résume immédiatement, dans mon cas : presque oubliés de mes années d'étudiant, aux algorithmes de recherche de l'extremum d'une fonction.

D’accord, mais ici se pose une question organisationnelle et appliquée : quel algorithme utiliser.

  1. Dans le sens - pour que vous puissiez moins coder à la main.
  2. Et pour que ça marche, c'est à dire trouvé l'extremum (s'il y en a un), enfin, au moins plus rapidement que la descente coordonnée.

Le premier point suggère que nous devons nous tourner vers certains environnements dans lesquels de tels algorithmes ont déjà été implémentés et sont, sous une forme ou une autre, prêts à être utilisés dans le code.
Oui je sais python и cran-r

Le deuxième point signifie que vous devez vous renseigner sur les algorithmes eux-mêmes, ce qu'ils sont, quelles sont leurs exigences, les caractéristiques de leur travail.

Et ce qu'ils donnent peuvent être des effets secondaires utiles - des résultats, ou directement issus de l'algorithme lui-même.

Ou bien ils peuvent être obtenus à partir des résultats de l’algorithme.

Cela dépend beaucoup des conditions d'entrée.

Par exemple, si, pour une raison quelconque, vous avez besoin d’obtenir un résultat plus rapidement, vous devez vous tourner vers les algorithmes de descente de gradient et en choisir un.

Ou, si le temps n’est pas si important, vous pouvez, par exemple, utiliser des méthodes d’optimisation stochastique, comme un algorithme génétique.

Je propose d'envisager le travail de cette approche, en sélectionnant la configuration du système, à l'aide d'un algorithme génétique, dans le prochain, pour ainsi dire : le travail en laboratoire.

Source:

  1. Soit, en tant que système de service : oracle xe 18c
  2. Laissez-le servir l'activité transactionnelle et l'objectif : obtenir le débit le plus élevé possible de la sous-base de données, en transactions/s.
  3. Les transactions peuvent être très différentes dans la nature du travail avec les données et dans le contexte du travail.
    Admettons qu'il s'agit de transactions qui ne traitent pas une grande quantité de données tabulaires.
    Dans le sens où ils ne génèrent pas plus de données d'annulation que de restauration et ne traitent pas de grands pourcentages de lignes et de grandes tables.

Ce sont des transactions qui modifient une ligne dans une table plus ou moins grande, avec un petit nombre d'index sur cette table.

Dans cette situation : la productivité de la sous-base de données de traitement des transactions sera, sous réserve, déterminée par la qualité du traitement par la base de données redox.

Avis de non-responsabilité - si nous parlons spécifiquement des paramètres de la subdb.

Car, dans le cas général, il peut y avoir, par exemple, des verrous transactionnels entre sessions SQL, dus à la conception du travail des utilisateurs avec des données tabulaires et/ou au modèle tabulaire.

Ce qui, bien sûr, aura un effet déprimant sur la métrique TPS et ce sera un facteur exogène, par rapport à la sous-base de données : eh bien, c'est ainsi que le modèle tabulaire a été conçu et le travail avec les données qu'il contient entraîne des blocages.

Par conséquent, pour la pureté de l'expérience, nous exclurons ce facteur, et ci-dessous je clarifierai exactement comment.

  1. Supposons, pour être précis, que 100 % des commandes SQL soumises à la base de données sont des commandes DML.
    Laissez les caractéristiques du travail de l'utilisateur avec la sous-base de données être les mêmes dans les tests.
    A savoir : le nombre de sessions skl, les données tabulaires, comment les sessions skl fonctionnent avec elles.
  2. Subd travaille dans FORCE LOGGING, ARCHIVELOG modifs. Le mode base de données Flashback est désactivé, au niveau subd.
  3. Redo logs : situés dans un système de fichiers distinct, sur un « disque » distinct ;
    Le reste du composant physique de la base de données : dans un autre système de fichiers distinct, sur un « disque » distinct :

Plus de détails sur l'appareil physique. composants de base de données de laboratoire

SQL> select status||' '||name from v$controlfile;
 /db/u14/oradata/XE/control01.ctl
SQL> select GROUP#||' '||MEMBER from v$logfile;
1 /db/u02/oradata/XE/redo01_01.log
2 /db/u02/oradata/XE/redo02_01.log
SQL> select FILE_ID||' '||TABLESPACE_NAME||' '||round(BYTES/1024/1024,2)||' '||FILE_NAME as col from dba_data_files;
4 UNDOTBS1 2208 /db/u14/oradata/XE/undotbs1_01.dbf
2 SLOB 128 /db/u14/oradata/XE/slob01.dbf
7 USERS 5 /db/u14/oradata/XE/users01.dbf
1 SYSTEM 860 /db/u14/oradata/XE/system01.dbf
3 SYSAUX 550 /db/u14/oradata/XE/sysaux01.dbf
5 MONITOR 128 /db/u14/oradata/XE/monitor.dbf
SQL> !cat /proc/mounts | egrep "/db/u[0-2]"
/dev/vda1 /db/u14 ext4 rw,noatime,nodiratime,data=ordered 0 0
/dev/mapper/vgsys-ora_redo /db/u02 xfs rw,noatime,nodiratime,attr2,nobarrier,inode64,logbsize=256k,noquota 0 0

Au départ, dans ces conditions de charge, je voulais utiliser transaction subd Utilitaire SLOB
Il a une fonctionnalité tellement merveilleuse que je citerai l’auteur :

Au cœur de SLOB se trouve la « méthode SLOB ». La Méthode SLOB vise à tester les plateformes
sans conflit d'application. On ne peut pas générer des performances matérielles maximales
en utilisant du code d'application qui est, par exemple, lié au verrouillage d'application ou même
partageant des blocs de base de données Oracle. C'est vrai : il y a une surcharge lors du partage de données
dans des blocs de données ! Mais SLOB, dans son déploiement par défaut, est à l'abri d'un tel conflit.

Cette déclaration : correspond, elle l'est.
Il est pratique de régler le degré de parallélisme des sessions cl, c'est la clé -t lancez l'utilitaire runit.sh de SLOB
Le pourcentage de commandes DML est régulé, en nombre de messages texte envoyés au subd, chaque session de texte, paramètre UPDATE_PCT
Séparément et très commodément : SLOB lui-même, avant et après la session de chargement - prépare un pack de statistiques ou des instantanés awr (ce qui doit être préparé).

Cependant, il s'est avéré que SLOB ne prend pas en charge les sessions SQL d'une durée inférieure à 30 secondes.
Par conséquent, j’ai d’abord codé ma propre version ouvrière-paysanne du chargeur, puis elle est restée en service.

Permettez-moi de clarifier ce que fait le chargeur et comment il le fait, pour plus de clarté.
En gros, le chargeur ressemble à ceci :

Code du travailleur

function dotx()
{
local v_period="$2"
[ -z "v_period" ] && v_period="0"
source "/home/oracle/testingredotracе/config.conf"

$ORACLE_HOME/bin/sqlplus -S system/${v_system_pwd} << __EOF__
whenever sqlerror exit failure
set verify off
set echo off
set feedback off

define wnum="$1"
define period="$v_period"
set appinfo worker_&&wnum

declare
 v_upto number;
 v_key  number;
 v_tots number;
 v_cts  number;
begin
 select max(col1) into v_upto from system.testtab_&&wnum;
 SELECT (( SYSDATE - DATE '1970-01-01' ) * 86400 ) into v_cts FROM DUAL;
 v_tots := &&period + v_cts;
 while v_cts <= v_tots
 loop
  v_key:=abs(mod(dbms_random.random,v_upto));
  if v_key=0 then
   v_key:=1;
  end if;
  update system.testtab_&&wnum t
  set t.object_name=translate(dbms_random.string('a', 120), 'abcXYZ', '158249')
  where t.col1=v_key
  ;
  commit;
  SELECT (( SYSDATE - DATE '1970-01-01' ) * 86400 ) into v_cts FROM DUAL;
 end loop;
end;
/

exit
__EOF__
}
export -f dotx

Les ouvriers sont lancés de cette façon :

Travailleurs en marche

echo "starting test, duration: ${TEST_DURATION}" >> "$v_logfile"
for((i=1;i<="$SQLSESS_COUNT";i++))
do
 echo "sql-session: ${i}" >> "$v_logfile"
 dotx "$i" "${TEST_DURATION}" &
done
echo "waiting..." >> "$v_logfile"
wait

Et les tables pour les ouvriers sont préparées comme ceci :

Création de tableaux

function createtable() {
source "/home/oracle/testingredotracе/config.conf"
$ORACLE_HOME/bin/sqlplus -S system/${v_system_pwd} << __EOF__
whenever sqlerror continue
set verify off
set echo off
set feedback off

define wnum="$1"
define ts_name="slob"

begin
 execute immediate 'drop table system.testtab_&&wnum';
exception when others then null;
end;
/

create table system.testtab_&&wnum tablespace &&ts_name as
select rownum as col1, t.*
from sys.dba_objects t
where rownum<1000
;
create index testtab_&&wnum._idx on system.testtab_&&wnum (col1);
--alter table system.testtab_&&wnum nologging;
--alter index system.testtab_&&wnum._idx nologging;
exit
__EOF__
}
export -f createtable

seq 1 1 "$SQLSESS_COUNT" | xargs -n 1 -P 4 -I {} -t bash -c "createtable "{}"" | tee -a "$v_logfile"
echo "createtable done" >> "$v_logfile"

Ceux. Pour chaque travailleur (en pratique : une session SQL distincte dans la base de données), une table distincte est créée, avec laquelle le travailleur travaille.

Cela garantit l'absence de verrous transactionnels entre les sessions de travail.
Chaque ouvrier : fait la même chose, avec sa propre table, les tables sont toutes pareilles.
Tous les travailleurs effectuent un travail pendant la même durée.
De plus, pendant une période suffisamment longue pour que, par exemple, un changement de journal se produise définitivement, et plus d'une fois.
En conséquence, des coûts et des effets associés sont apparus.
Dans mon cas, j’ai configuré la durée de travail des ouvriers à 8 minutes.

Un extrait d'un rapport statspack décrivant le fonctionnement du subd sous charge

Database    DB Id    Instance     Inst Num  Startup Time   Release     RAC
~~~~~~~~ ----------- ------------ -------- --------------- ----------- ---
          2929910313 XE                  1 07-Sep-20 23:12 18.0.0.0.0  NO

Host Name             Platform                CPUs Cores Sockets   Memory (G)
~~~~ ---------------- ---------------------- ----- ----- ------- ------------
     billing.izhevsk1 Linux x86 64-bit           2     2       1         15.6

Snapshot       Snap Id     Snap Time      Sessions Curs/Sess Comment
~~~~~~~~    ---------- ------------------ -------- --------- ------------------
Begin Snap:       1630 07-Sep-20 23:12:27       55        .7
  End Snap:       1631 07-Sep-20 23:20:29       62        .6
   Elapsed:       8.03 (mins) Av Act Sess:       8.4
   DB time:      67.31 (mins)      DB CPU:      15.01 (mins)

Cache Sizes            Begin        End
~~~~~~~~~~~       ---------- ----------
    Buffer Cache:     1,392M              Std Block Size:         8K
     Shared Pool:       288M                  Log Buffer:   103,424K

Load Profile              Per Second    Per Transaction    Per Exec    Per Call
~~~~~~~~~~~~      ------------------  ----------------- ----------- -----------
      DB time(s):                8.4                0.0        0.00        0.20
       DB CPU(s):                1.9                0.0        0.00        0.04
       Redo size:        7,685,765.6              978.4
   Logical reads:           60,447.0                7.7
   Block changes:           47,167.3                6.0
  Physical reads:                8.3                0.0
 Physical writes:              253.4                0.0
      User calls:               42.6                0.0
          Parses:               23.2                0.0
     Hard parses:                1.2                0.0
W/A MB processed:                1.0                0.0
          Logons:                0.5                0.0
        Executes:           15,756.5                2.0
       Rollbacks:                0.0                0.0
    Transactions:            7,855.1

Retour au travail de laboratoire.
Nous allons, toutes choses égales par ailleurs, faire varier les valeurs des paramètres suivants de la sous-base de données du laboratoire :

  1. Taille des groupes de journaux de base de données. plage de valeurs : [32, 1024] Mo ;
  2. Nombre de groupes de journaux dans la base de données. plage de valeurs : [2,32] ;
  3. log_archive_max_processes plage de valeurs : [1,8] ;
  4. commit_logging deux valeurs sont autorisées : batch|immediate;
  5. commit_wait deux valeurs sont autorisées : wait|nowait;
  6. log_buffer plage de valeurs : [2,128] Mo.
  7. log_checkpoint_timeout plage de valeurs : [60,1200] secondes
  8. db_writer_processes plage de valeurs : [1,4]
  9. undo_retention plage de valeurs : [30;300] secondes
  10. transactions_per_rollback_segment plage de valeurs : [1,8]
  11. disk_asynch_io deux valeurs sont autorisées : true|false;
  12. filesystemio_options les valeurs suivantes sont autorisées : none|setall|directIO|asynch;
  13. db_block_checking les valeurs suivantes sont autorisées : OFF|LOW|MEDIUM|FULL;
  14. db_block_checksum les valeurs suivantes sont autorisées : OFF|TYPICAL|FULL;

Une personne expérimentée dans la maintenance des bases de données Oracle peut certainement déjà dire quoi et sur quelles valeurs doit être définie, à partir des paramètres spécifiés et de leurs valeurs acceptables, afin d'obtenir une plus grande productivité de la base de données pour le travail avec les données indiquées par le code de l'application, ci-dessus.

Mais.

Le but du travail de laboratoire est de montrer que l’algorithme d’optimisation lui-même nous éclairera assez rapidement sur ce point.

Il ne nous reste plus qu'à examiner le document, grâce au système personnalisable, juste ce qu'il faut pour savoir quels paramètres modifier et dans quelles plages.
Et aussi : codez le code qui sera utilisé pour travailler avec le système personnalisé de l'algorithme d'optimisation sélectionné.

Alors maintenant, parlons du code.
J'en ai parlé plus haut cran-r, c'est à dire : toutes les manipulations avec le système personnalisé sont orchestrées sous la forme d'un script R.

La tâche réelle, l'analyse, la sélection par valeur métrique, les vecteurs d'état du système : il s'agit d'un package GA (documentation)
Le package, dans ce cas, n'est pas très approprié, dans le sens où il s'attend à ce que les vecteurs (les chromosomes, si l'on parle du package) soient spécifiés sous la forme de chaînes de nombres avec une partie fractionnaire.

Et mon vecteur, à partir des valeurs des paramètres de réglage : ce sont 14 quantités - entiers et valeurs de chaîne.

Bien entendu, le problème peut être facilement évité en attribuant des nombres spécifiques aux valeurs de chaîne.

Ainsi, au final, la pièce principale du script R ressemble à ceci :

Appeler GA : ga

cat( "", file=v_logfile, sep="n", append=F)

pSize = 10
elitism_value=1
pmutation_coef=0.8
pcrossover_coef=0.1
iterations=50

gam=GA::ga(type="real-valued", fitness=evaluate,
lower=c(32,2, 1,1,1,2,60,1,30,1,0,0, 0,0), upper=c(1024,32, 8,10,10,128,800,4,300,8,10,40, 40,30),
popSize=pSize,
pcrossover = pcrossover_coef,
pmutation = pmutation_coef,
maxiter=iterations,
run=4,
keepBest=T)
cat( "GA-session is done" , file=v_logfile, sep="n", append=T)
gam@solution

Ici, avec l'aide lower и upper attributs de sous-programme ga essentiellement, une zone de l'espace de recherche est spécifiée, dans laquelle une recherche sera effectuée pour un tel vecteur (ou des vecteurs) pour lesquels la valeur maximale de la fonction de fitness sera obtenue.

Le sous-programme ga effectue une recherche maximisant la fonction fitness.

Eh bien, il s'avère que, dans ce cas, il est nécessaire que la fonction de fitness, comprenant le vecteur comme un ensemble de valeurs pour certains paramètres du subd, reçoive une métrique du subd.

C'est-à-dire : combien, avec une configuration de subd donnée et une charge donnée sur le subd : le subd traite les transactions par seconde.

Autrement dit, lors du dépliage, les étapes multiples suivantes doivent être effectuées dans la fonction fitness :

  1. Traitement du vecteur d'entrée des nombres - conversion en valeurs pour les paramètres de sous-données.
  2. Une tentative de créer un nombre donné de groupes de restauration d'une taille donnée. De plus, la tentative peut échouer.
    Groupes de magazines qui existaient déjà dans le subd, en une certaine quantité et d'une certaine taille, pour la pureté de l'expérience - d.b. supprimé.
  3. Si le point précédent est réussi : spécifier les valeurs des paramètres de configuration à la base de données (encore une fois : il peut y avoir un échec)
  4. Si l'étape précédente réussit : arrêter le subd, démarrer le subd pour que les valeurs des paramètres nouvellement spécifiées prennent effet. (encore une fois : il peut y avoir un problème)
  5. Si l'étape précédente réussit : effectuez un test de charge. obtenir des métriques de subd.
  6. Remettez le subd à son état d'origine, c'est-à-dire supprimez des groupes de journaux supplémentaires et faites fonctionner la configuration de sous-base de données d'origine.

Code de fonction de remise en forme

evaluate=function(p_par) {
v_module="evaluate"
v_metric=0
opn=NULL
opn$rg_size=round(p_par[1],digit=0)
opn$rg_count=round(p_par[2],digit=0)
opn$log_archive_max_processes=round(p_par[3],digit=0)
opn$commit_logging="BATCH"
if ( round(p_par[4],digit=0) > 5 ) {
 opn$commit_logging="IMMEDIATE"
}
opn$commit_logging=paste("'", opn$commit_logging, "'",sep="")

opn$commit_wait="WAIT"
if ( round(p_par[5],digit=0) > 5 ) {
 opn$commit_wait="NOWAIT"
}
opn$commit_wait=paste("'", opn$commit_wait, "'",sep="")

opn$log_buffer=paste(round(p_par[6],digit=0),"m",sep="")
opn$log_checkpoint_timeout=round(p_par[7],digit=0)
opn$db_writer_processes=round(p_par[8],digit=0)
opn$undo_retention=round(p_par[9],digit=0)
opn$transactions_per_rollback_segment=round(p_par[10],digit=0)
opn$disk_asynch_io="true"
if ( round(p_par[11],digit=0) > 5 ) {
 opn$disk_asynch_io="false"
} 

opn$filesystemio_options="none"
if ( round(p_par[12],digit=0) > 10 && round(p_par[12],digit=0) <= 20 ) {
 opn$filesystemio_options="setall"
}
if ( round(p_par[12],digit=0) > 20 && round(p_par[12],digit=0) <= 30 ) {
 opn$filesystemio_options="directIO"
}
if ( round(p_par[12],digit=0) > 30 ) {
 opn$filesystemio_options="asynch"
}

opn$db_block_checking="OFF"
if ( round(p_par[13],digit=0) > 10 && round(p_par[13],digit=0) <= 20 ) {
 opn$db_block_checking="LOW"
}
if ( round(p_par[13],digit=0) > 20 && round(p_par[13],digit=0) <= 30 ) {
 opn$db_block_checking="MEDIUM"
}
if ( round(p_par[13],digit=0) > 30 ) {
 opn$db_block_checking="FULL"
}

opn$db_block_checksum="OFF"
if ( round(p_par[14],digit=0) > 10 && round(p_par[14],digit=0) <= 20 ) {
 opn$db_block_checksum="TYPICAL"
}
if ( round(p_par[14],digit=0) > 20 ) {
 opn$db_block_checksum="FULL"
}

v_vector=paste(round(p_par[1],digit=0),round(p_par[2],digit=0),round(p_par[3],digit=0),round(p_par[4],digit=0),round(p_par[5],digit=0),round(p_par[6],digit=0),round(p_par[7],digit=0),round(p_par[8],digit=0),round(p_par[9],digit=0),round(p_par[10],digit=0),round(p_par[11],digit=0),round(p_par[12],digit=0),round(p_par[13],digit=0),round(p_par[14],digit=0),sep=";")
cat( paste(v_module," try to evaluate vector: ", v_vector,sep="") , file=v_logfile, sep="n", append=T)

rc=make_additional_rgroups(opn)
if ( rc!=0 ) {
 cat( paste(v_module,"make_additional_rgroups failed",sep="") , file=v_logfile, sep="n", append=T)
 return (0)
}

v_rc=0
rc=set_db_parameter("log_archive_max_processes", opn$log_archive_max_processes)
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("commit_logging", opn$commit_logging )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("commit_wait", opn$commit_wait )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("log_buffer", opn$log_buffer )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("log_checkpoint_timeout", opn$log_checkpoint_timeout )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("db_writer_processes", opn$db_writer_processes )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("undo_retention", opn$undo_retention )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("transactions_per_rollback_segment", opn$transactions_per_rollback_segment )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("disk_asynch_io", opn$disk_asynch_io )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("filesystemio_options", opn$filesystemio_options )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("db_block_checking", opn$db_block_checking )
if ( rc != 0 ) {  v_rc=1 }
rc=set_db_parameter("db_block_checksum", opn$db_block_checksum )
if ( rc != 0 ) {  v_rc=1 }

if ( rc!=0 ) {
 cat( paste(v_module," can not startup db with that vector of settings",sep="") , file=v_logfile, sep="n", append=T)
 rc=stop_db("immediate")
 rc=create_spfile()
 rc=start_db("")
 rc=remove_additional_rgroups(opn)
 return (0)
}

rc=stop_db("immediate")
rc=start_db("")
if ( rc!=0 ) {
 cat( paste(v_module," can not startup db with that vector of settings",sep="") , file=v_logfile, sep="n", append=T)
 rc=stop_db("abort")
 rc=create_spfile()
 rc=start_db("")
 rc=remove_additional_rgroups(opn)
 return (0)
}

rc=run_test()
v_metric=getmetric()

rc=stop_db("immediate")
rc=create_spfile()
rc=start_db("")
rc=remove_additional_rgroups(opn)

cat( paste("result: ",v_metric," ",v_vector,sep="") , file=v_logfile, sep="n", append=T)
return (v_metric)
}

Que. tout le travail : effectué dans la fonction fitness.

Le sous-programme ga traite les vecteurs ou, plus exactement, les chromosomes.
Dans lequel, ce qui est le plus important pour nous est la sélection de chromosomes avec des gènes pour lesquels la fonction fitness produit des valeurs élevées.

Il s’agit essentiellement du processus de recherche de l’ensemble optimal de chromosomes à l’aide d’un vecteur dans un espace de recherche à N dimensions.

Très clair, détaillé une explication, avec des exemples de R-code, le travail d'un algorithme génétique.

Je voudrais souligner séparément deux points techniques.

Appels auxiliaires de la fonction evaluate, par exemple, l'arrêt-démarrage, définissant la valeur du paramètre subd, est effectué en fonction de cran-r les fonctions system2

A l'aide de quoi : un script ou une commande bash est appelé.

Par exemple:

set_db_parameter

set_db_parameter=function(p1, p2) {
v_module="set_db_parameter"
v_cmd="/home/oracle/testingredotracе/set_db_parameter.sh"
v_args=paste(p1," ",p2,sep="")

x=system2(v_cmd, args=v_args, stdout=T, stderr=T, wait=T)
if ( length(attributes(x)) > 0 ) {
 cat(paste(v_module," failed with: ",attributes(x)$status," ",v_cmd," ",v_args,sep=""), file=v_logfile, sep="n", append=T)
 return (attributes(x)$status)
}
else {
 cat(paste(v_module," ok: ",v_cmd," ",v_args,sep=""), file=v_logfile, sep="n", append=T)
 return (0)
}
}

Le deuxième point est la ligne, evaluate fonctions, avec enregistrement d'une valeur métrique spécifique et de son vecteur de réglage correspondant dans un fichier journal :

cat( paste("result: ",v_metric," ",v_vector,sep="") , file=v_logfile, sep="n", append=T)

Ceci est important, car à partir de ce tableau de données, il sera possible d'obtenir des informations supplémentaires sur laquelle des composantes du vecteur de réglage a un effet plus ou moins important sur la valeur métrique.

Autrement dit : il sera possible d’effectuer une analyse de l’importance des attributs.

Alors que peut-il arriver ?

Sous forme graphique, si vous classez les tests par ordre métrique croissant, l’image est la suivante :

La méthode scientifique poke, ou comment sélectionner une configuration de base de données à l'aide de benchmarks et d'un algorithme d'optimisation

Quelques données correspondant aux valeurs extrêmes de la métrique :
La méthode scientifique poke, ou comment sélectionner une configuration de base de données à l'aide de benchmarks et d'un algorithme d'optimisation
Ici, dans la capture d'écran avec les résultats, je vais clarifier : les valeurs du vecteur de réglage sont données en termes de code de fonction de fitness, et non en termes de liste de numéros de paramètres/plages de valeurs de paramètres, qui a été formulée ci-dessus dans le texte.

Bien. Est-ce beaucoup ou un peu, ~8 XNUMX tps : une question distincte.
Dans le cadre de travaux de laboratoire, ce chiffre n'a pas d'importance, ce qui est important c'est la dynamique, comment cette valeur évolue.

La dynamique ici est bonne.
Il est évident qu'au moins un facteur influence de manière significative la valeur de la métrique, l'algorithme ga, triant les vecteurs chromosomiques : couverts.
À en juger par la dynamique assez vigoureuse des valeurs de la courbe, il existe au moins un autre facteur qui, bien que nettement plus petit, a une influence.

C'est là que tu en as besoin attribute-importance analyse pour comprendre quels attributs (enfin, dans ce cas, les composants du vecteur de réglage) et dans quelle mesure ils influencent la valeur métrique.
Et à partir de ces informations : comprenez quels facteurs ont été affectés par les changements dans les attributs significatifs.

Exécuter attribute-importance possible de différentes manières.

À ces fins, j'aime l'algorithme randomForest Package R du même nom (documentation)
randomForest, si je comprends son travail en général et son approche pour évaluer l'importance des attributs en particulier, construit un certain modèle de dépendance de la variable de réponse aux attributs.

Dans notre cas, la variable de réponse est une métrique obtenue à partir de la base de données lors des tests de charge : tps;
Et les attributs sont des composants du vecteur de réglage.

Donc randomForest évalue l'importance de chaque attribut du modèle avec deux nombres : %IncMSE — comment la présence/absence de cet attribut dans un modèle modifie la qualité MSE de ce modèle (Mean Squared Error) ;

Et IncNodePurity est un nombre qui reflète dans quelle mesure, en fonction des valeurs de cet attribut, un ensemble de données avec des observations peut être divisé, de sorte que dans une partie il y ait des données avec une valeur de la métrique expliquée, et dans l'autre avec une autre valeur de la métrique.
Eh bien, c'est-à-dire : dans quelle mesure s'agit-il d'un attribut de classification (j'ai vu l'explication la plus claire en russe sur RandomForest ici).

R-code ouvrier-paysan pour le traitement d'un ensemble de données avec les résultats des tests de charge :

x=NULL
v_data_file=paste('/tmp/data1.dat',sep="")
x=read.table(v_data_file, header = TRUE, sep = ";", dec=",", quote = ""'", stringsAsFactors=FALSE)
colnames(x)=c('metric','rgsize','rgcount','lamp','cmtl','cmtw','lgbffr','lct','dbwrp','undo_retention','tprs','disk_async_io','filesystemio_options','db_block_checking','db_block_checksum')

idxTrain=sample(nrow(x),as.integer(nrow(x)*0.7))
idxNotTrain=which(! 1:nrow(x) %in% idxTrain )
TrainDS=x[idxTrain,]
ValidateDS=x[idxNotTrain,]

library(randomForest)
#mtry=as.integer( sqrt(dim(x)[2]-1) )
rf=randomForest(metric ~ ., data=TrainDS, ntree=40, mtry=3, replace=T, nodesize=2, importance=T, do.trace=10, localImp=F)
ValidateDS$predicted=predict(rf, newdata=ValidateDS[,colnames(ValidateDS)!="metric"], type="response")
sum((ValidateDS$metric-ValidateDS$predicted)^2)
rf$importance

Vous pouvez sélectionner directement les hyperparamètres de l'algorithme avec vos mains et, en vous concentrant sur la qualité du modèle, sélectionner un modèle qui répond plus précisément aux prédictions de l'ensemble de données de validation.
Vous pouvez écrire une sorte de fonction pour ce travail (d'ailleurs, encore une fois, en utilisant une sorte d'algorithme d'optimisation).

Vous pouvez utiliser le package R caret, ce n'est pas le point qui est important.

En conséquence, dans ce cas, le résultat suivant est obtenu pour évaluer le degré d'importance des attributs :

La méthode scientifique poke, ou comment sélectionner une configuration de base de données à l'aide de benchmarks et d'un algorithme d'optimisation

Bien. Ainsi, nous pouvons entamer une réflexion globale :

  1. Il s’avère que le plus significatif, dans ces conditions de tests, était le paramètre commit_wait
    Techniquement, il spécifie le mode d'exécution de l'opération io d'écriture des données de rétablissement du tampon de journal de la sous-base de données vers le groupe de journaux actuel : synchrone ou asynchrone.
    Valeur nowait ce qui se traduit par une augmentation multiple presque verticale de la valeur de la métrique tps : il s'agit de l'inclusion du mode io asynchrone dans les groupes redo.
    Une autre question est de savoir si vous devez ou non faire cela dans une base de données alimentaire. Ici, je me limite à dire : c'est un facteur important.
  2. Il est logique que la taille du tampon de journal du subd: s'avère être un facteur important.
    Plus la taille du tampon de journal est petite, plus sa capacité de tamponnage est faible, plus il déborde souvent et/ou il est impossible d'y allouer une zone libre pour une partie des nouvelles données redox.
    Cela signifie : les délais associés à l'allocation d'espace dans le tampon de journal et/ou au transfert des données de rétablissement vers des groupes de rétablissement.
    Bien entendu, ces retards devraient affecter et affectent effectivement le débit de la base de données pour les transactions.
  3. Paramètre db_block_checksum: eh bien, aussi, en général, c'est clair - le traitement des transactions conduit à la formation de blocs Darty dans le cache tampon de la sous-base de données.
    Ce qui, lorsque la vérification des sommes de contrôle des blocs de données est activée, la base de données doit traiter - calculer ces sommes de contrôle à partir du corps du bloc de données, les vérifier avec ce qui est écrit dans l'en-tête du bloc de données : correspond/ne correspond pas.
    Encore une fois, un tel travail ne peut que retarder le traitement des données et, par conséquent, le paramètre et le mécanisme qui définit ce paramètre s'avèrent importants.
    C'est pourquoi le vendeur propose, dans la documentation de ce paramètre, différentes valeurs et note que oui, il y aura un impact, mais vous pouvez choisir différentes valeurs, même « off » et différents impacts.

Eh bien, une conclusion globale.

L’approche, en général, s’avère assez efficace.

Il se permet tout à fait, dès les premiers stades des tests de charge d'un certain système de service, afin de sélectionner sa configuration (du système) optimale pour la charge, de ne pas trop approfondir les spécificités de la configuration du système pour la charge.

Mais cela ne l'exclut pas complètement - du moins au niveau de la compréhension : le système doit connaître les « boutons de réglage » et les plages de rotation autorisées de ces boutons.

L’approche permet alors de trouver relativement rapidement la configuration optimale du système.
Et sur la base des résultats des tests, il est possible d'obtenir des informations sur la nature de la relation entre les mesures de performances du système et les valeurs des paramètres de configuration du système.

Ce qui, bien entendu, devrait contribuer à l’émergence de cette compréhension très approfondie du système, de son fonctionnement, du moins sous une charge donnée.

En pratique, il s’agit d’un échange des coûts de compréhension du système personnalisé contre les coûts de préparation de ces tests du système.

Je voudrais noter séparément : dans cette approche, le degré d'adéquation des tests du système aux conditions de fonctionnement qu'il aura en exploitation commerciale est d'une importance cruciale.

Merci pour votre attention et votre temps.

Source: habr.com

Ajouter un commentaire