Den videnskabelige poke-metode, eller hvordan man vælger en databasekonfiguration ved hjælp af benchmarks og en optimeringsalgoritme

Hej

Jeg besluttede at dele mit fund - frugten af ​​tanke, forsøg og fejl.
I det store og hele: dette er selvfølgelig ikke et fund - alt dette burde have været kendt længe for dem, der er involveret i anvendt statistisk databehandling og optimering af nogen systemer, ikke nødvendigvis specifikt DBMS.
Og: ja, de ved det, de skriver interessante artikler om deres forskning, eksempel (UPD.: I kommentarerne pegede de på et meget interessant projekt: ottertune )
På den anden side: umiddelbart ser jeg ikke nogen udbredt omtale eller formidling af denne tilgang på internettet blandt IT-specialister, DBA.

Så til sagen.

Lad os antage, at vi har en opgave: at opsætte et bestemt servicesystem til at servicere en eller anden form for arbejde.

Det er kendt om dette arbejde: hvad det er, hvordan kvaliteten af ​​dette arbejde måles, og hvad er kriteriet for at måle denne kvalitet.

Lad os også antage, at det er mere eller mindre kendt og forstået: præcis hvordan arbejde udføres i (eller med) dette servicesystem.

"Mere eller mindre" - dette betyder, at det er muligt at forberede (eller få det et eller andet sted fra) et bestemt værktøj, hjælpeprogram, service, der kan syntetiseres og anvendes på systemet med en testbelastning, der er tilstrækkelig tilstrækkelig til det, der vil være i produktion, under forhold, der er tilstrækkelige til at arbejde i produktionen.

Nå, lad os antage, at et sæt justeringsparametre for dette servicesystem er kendt, som kan bruges til at konfigurere dette system med hensyn til produktiviteten af ​​dets arbejde.

Og hvad er problemet - der er ikke en tilstrækkelig fuldstændig forståelse af dette servicesystem, et system, der giver dig mulighed for professionelt at konfigurere indstillingerne for dette system til fremtidig belastning på en given platform og få den nødvendige produktivitet af systemet.

Godt. Dette er næsten altid tilfældet.

Hvad kan du gøre her?

Nå, det første, der kommer til at tænke på, er at se på dokumentationen for dette system. Forstå, hvad de acceptable områder er for værdierne af justeringsparametrene. Og for eksempel ved hjælp af koordinatnedstigningsmetoden skal du vælge værdier for systemparametre i test.

De der. give systemet en form for konfiguration i form af et specifikt sæt værdier for dets konfigurationsparametre.

Påfør en testbelastning på den ved at bruge denne belastningsgenerator med værktøj.
Og se på værdien - responsen eller en metrik for systemets kvalitet.

Den anden tanke kan være konklusionen om, at det er meget lang tid.

Nå, det vil sige: hvis der er mange indstillingsparametre, hvis intervallerne for deres værdier, der køres, er store, hvis hver enkelt belastningstest tager meget tid at gennemføre, så: ja, alt dette kan tage en uacceptabel tid lang tid.

Nå, her er hvad du kan forstå og huske.

Du kan finde ud af, at der i værdisættet af parametrene for servicesystemindstillinger er en vektor, som en sekvens af nogle værdier.

Hver sådan vektor, alt andet lige (ved at den ikke er påvirket af denne vektor), svarer til en fuldstændig bestemt værdi af metrikken - en indikator for kvaliteten af ​​systemets drift under en testbelastning.

Ie

Lad os betegne systemkonfigurationsvektoren som Den videnskabelige poke-metode, eller hvordan man vælger en databasekonfiguration ved hjælp af benchmarks og en optimeringsalgoritmeHvor Den videnskabelige poke-metode, eller hvordan man vælger en databasekonfiguration ved hjælp af benchmarks og en optimeringsalgoritme; Hvor Den videnskabelige poke-metode, eller hvordan man vælger en databasekonfiguration ved hjælp af benchmarks og en optimeringsalgoritme — antal systemkonfigurationsparametre, hvor mange af disse parametre er der.

Og værdien af ​​metrikken, der svarer til dette Den videnskabelige poke-metode, eller hvordan man vælger en databasekonfiguration ved hjælp af benchmarks og en optimeringsalgoritme lad os betegne det som
Den videnskabelige poke-metode, eller hvordan man vælger en databasekonfiguration ved hjælp af benchmarks og en optimeringsalgoritme, så får vi en funktion: Den videnskabelige poke-metode, eller hvordan man vælger en databasekonfiguration ved hjælp af benchmarks og en optimeringsalgoritme

Nå, så: alt kommer straks ned på, i mit tilfælde: næsten glemt fra min studietid, algoritmer til at søge efter en funktions yderpunkt.

Okay, men her opstår et organisatorisk og anvendt spørgsmål: hvilken algoritme man skal bruge.

  1. I den forstand - så man kan kode mindre i hånden.
  2. Og for at det skal virke, dvs. fandt ekstremum (hvis der er et), ja, i hvert fald hurtigere end koordinatnedstigning.

Det første punkt antyder, at vi skal se mod nogle miljøer, hvor sådanne algoritmer allerede er blevet implementeret, og i en eller anden form er klar til brug i kode.
Jeg ved det godt python и cran-r

Det andet punkt betyder, at du skal læse om selve algoritmerne, hvad de er, hvad deres krav er, og funktionerne i deres arbejde.

Og hvad de giver kan være nyttige bivirkninger - resultater eller direkte fra selve algoritmen.

Eller de kan fås fra resultaterne af algoritmen.

Meget afhænger af inputbetingelserne.

For eksempel, hvis du af en eller anden grund har brug for at få et resultat hurtigere, ja, du skal se mod gradient-descent-algoritmer og vælge en af ​​dem.

Eller, hvis tiden ikke er så vigtig, kan du for eksempel bruge stokastiske optimeringsmetoder, såsom en genetisk algoritme.

Jeg foreslår at overveje arbejdet med denne tilgang, valg af systemkonfiguration ved hjælp af en genetisk algoritme, i det næste, så at sige: laboratoriearbejde.

Original:

  1. Lad der være, som et servicesystem: oracle xe 18c
  2. Lad det tjene transaktionsaktivitet og målet: at opnå den højest mulige gennemstrømning af underdatabasen, i transaktioner/sek.
  3. Transaktioner kan være meget forskellige i karakteren af ​​arbejdet med data og konteksten af ​​arbejdet.
    Lad os blive enige om, at det er transaktioner, der ikke behandler en stor mængde tabeldata.
    I den forstand, at de ikke genererer flere fortryd-data end at gøre om og ikke behandler store procentdele af rækker og store tabeller.

Det er transaktioner, der ændrer en række i en mere eller mindre stor tabel, med et lille antal indekser på denne tabel.

I denne situation: produktiviteten af ​​underdatabasen til behandling af transaktioner vil, med et forbehold, blive bestemt af kvaliteten af ​​behandlingen af ​​redoxdatabasen.

Ansvarsfraskrivelse - hvis vi taler specifikt om subdb-indstillingerne.

For i det generelle tilfælde kan der for eksempel være transaktionslåse mellem SQL-sessioner, på grund af designet af brugerens arbejde med tabeldata og/eller tabelmodellen.

Hvilket selvfølgelig vil have en deprimerende effekt på TPS-metrikken, og dette vil være en eksogen faktor, i forhold til subdatabasen: Nå, det er sådan tabelmodellen blev designet og arbejdet med data i den, at blokeringer opstår.

Derfor vil vi for eksperimentets renhed udelukke denne faktor, og nedenfor vil jeg præcisere præcis hvordan.

  1. Lad os antage, at 100 % af de SQL-kommandoer, der sendes til databasen, er DML-kommandoer.
    Lad egenskaberne for brugerarbejde med underdatabasen være de samme i test.
    Nemlig: antallet af skl-sessioner, tabeldata, hvordan skl-sessioner fungerer med dem.
  2. Subd virker i FORCE LOGGING, ARCHIVELOG mods. Flashback-databasetilstand er slået fra på subd-niveau.
  3. Redo logs: placeret i et separat filsystem, på en separat "disk";
    Resten af ​​den fysiske komponent af databasen: i et andet, separat filsystem, på en separat "disk":

Flere detaljer om den fysiske enhed. laboratoriedatabasekomponenter

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

I første omgang, under disse belastningsforhold, ønskede jeg at bruge transaktion subd SLOB-værktøj
Det har sådan en vidunderlig funktion, jeg vil citere forfatteren:

I hjertet af SLOB er "SLOB-metoden." SLOB-metoden har til formål at teste platforme
uden ansøgningspåstand. Man kan ikke køre maksimal hardwareydelse
ved hjælp af applikationskode, der for eksempel er bundet af applikationslåsning eller endda
deling af Oracle-databaseblokke. Det er rigtigt – der er overhead, når du deler data
i datablokke! Men SLOB - i sin standardimplementering - er immun over for en sådan påstand.

Denne erklæring: svarer, det er.
Det er praktisk at regulere graden af ​​parallelitet af cl-sessioner, dette er nøglen -t starte værktøjet runit.sh fra SLOB
Procentdelen af ​​DML-kommandoer er reguleret i antallet af tekstbeskeder, der sendes til subd, hver tekstsession, parameter UPDATE_PCT
Separat og meget bekvemt: SLOB selv, før og efter indlæsningssessionen - forbereder en statspakke eller awr-snapshots (hvad der er indstillet til at blive forberedt).

Det viste det sig dog SLOB understøtter ikke SQL-sessioner med en varighed på mindre end 30 sekunder.
Derfor kodede jeg først min egen, arbejder-bonde-version af læsseren, og så forblev den i drift.

Lad mig præcisere, hvad læsseren gør, og hvordan den gør det, for klarhedens skyld.
I bund og grund ser læsseren sådan ud:

Arbejderkode

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

Arbejdere lanceres på denne måde:

Løbende arbejdere

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

Og tabeller til arbejdere er forberedt på denne måde:

Oprettelse af tabeller

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"

De der. For hver arbejder (praktisk talt: en separat SQL-session i DB'en) oprettes en separat tabel, som arbejderen arbejder med.

Dette sikrer fravær af transaktionslåse mellem arbejdersessioner.
Hver arbejder: gør det samme, med sit eget bord er bordene alle ens.
Alle arbejdere udfører arbejde i samme tid.
Desuden i lang nok tid til, at der for eksempel helt sikkert ville opstå en log switch, og mere end én gang.
Derfor opstod der dermed forbundne omkostninger og effekter.
I mit tilfælde konfigurerede jeg varigheden af ​​arbejdernes arbejde til 8 minutter.

Et stykke af en statspack-rapport, der beskriver driften af ​​en underdatabase under load

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

Vender tilbage til laboratoriearbejdet.
Vi vil, alt andet lige, variere værdierne af følgende parametre i laboratorieunderdatabasen:

  1. Størrelse på databaseloggrupper. værdiområde: [32, 1024] MB;
  2. Antal journalgrupper i databasen. værdiområde: [2,32];
  3. log_archive_max_processes værdiområde: [1,8];
  4. commit_logging to værdier er tilladt: batch|immediate;
  5. commit_wait to værdier er tilladt: wait|nowait;
  6. log_buffer værdiområde: [2,128] MB.
  7. log_checkpoint_timeout værdiområde: [60,1200] sekunder
  8. db_writer_processes værdiområde: [1,4]
  9. undo_retention værdiområde: [30;300] sekunder
  10. transactions_per_rollback_segment værdiområde: [1,8]
  11. disk_asynch_io to værdier er tilladt: true|false;
  12. filesystemio_options følgende værdier er tilladt: none|setall|directIO|asynch;
  13. db_block_checking følgende værdier er tilladt: OFF|LOW|MEDIUM|FULL;
  14. db_block_checksum følgende værdier er tilladt: OFF|TYPICAL|FULL;

En person med erfaring i at vedligeholde Oracle-databaser kan helt sikkert allerede sige, hvad og til hvilke værdier der skal indstilles, ud fra de angivne parametre og deres acceptable værdier, for at opnå større produktivitet af databasen for arbejdet med data, der er angivet af applikationskoden her ovenfor.

Men.

Pointen med laboratoriearbejdet er at vise, at selve optimeringsalgoritmen relativt hurtigt vil afklare dette for os.

For os er der kun tilbage at kigge i dokumentet gennem det tilpassede system, lige nok til at finde ud af, hvilke parametre der skal ændres og i hvilke områder.
Og også: kode koden, der skal bruges til at arbejde med det brugerdefinerede system for den valgte optimeringsalgoritme.

Så nu om koden.
Jeg talte ovenfor om cran-r, dvs.: alle manipulationer med det tilpassede system er orkestreret i form af et R-script.

Den faktiske opgave, analyse, udvælgelse efter metrisk værdi, systemtilstandsvektorer: dette er en pakke GA (dokumentation)
Pakken, i dette tilfælde, er ikke særlig egnet, i den forstand, at den forventer, at vektorer (kromosomer, hvis det er i form af pakken) specificeres i form af talstrenge med en brøkdel.

Og min vektor, fra værdierne af indstillingsparametrene: disse er 14 mængder - heltal og strengværdier.

Problemet kan selvfølgelig nemt undgås ved at tildele nogle specifikke tal til strengværdier.

I sidste ende ser hoveddelen af ​​R-scriptet således ud:

Ring til 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

Her med hjælp lower и upper subrutine attributter ga i det væsentlige er et område af søgerummet specificeret, inden for hvilket en søgning vil blive udført efter en sådan vektor (eller vektorer), for hvilken den maksimale værdi af fitnessfunktionen vil blive opnået.

Ga-underrutinen udfører en søgning, der maksimerer fitnessfunktionen.

Nå, så viser det sig, at det i dette tilfælde er nødvendigt, at fitnessfunktionen, der forstår vektoren som et sæt værdier for visse parametre for subd, modtager en metrik fra subd.

Det vil sige: hvor mange, med en given subd-opsætning og en given belastning på subd'en: subd'en behandler transaktioner pr. sekund.

Det vil sige, at ved udfoldning skal følgende multi-trin udføres inde i fitnessfunktionen:

  1. Behandling af inputvektoren for tal - konvertering af den til værdier for underdataparametrene.
  2. Et forsøg på at oprette et givet antal gentag-grupper af en given størrelse. Desuden kan forsøget være mislykket.
    Magasingrupper, der allerede eksisterede i subd, i en vis mængde og af en vis størrelse, for forsøgets renhed - d.b. slettet.
  3. Hvis det forrige punkt er vellykket: angivelse af værdierne for konfigurationsparametre til databasen (igen: der kan være en fejl)
  4. Hvis det forrige trin er vellykket: stop subd, start subd, så de nyligt angivne parameterværdier træder i kraft. (igen: der kan være en fejl)
  5. Hvis det forrige trin er vellykket: Udfør en belastningstest. få metrics fra subd.
  6. Returner subd til sin oprindelige tilstand, dvs. slet yderligere loggrupper, returner den oprindelige underdatabasekonfiguration til at fungere.

Kode til fitnessfunktion

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)
}

At. alt arbejde: udføres i fitnessfunktionen.

Ga-subrutinen behandler vektorer eller mere korrekt kromosomer.
I hvilket, hvad der er vigtigst for os, er udvælgelsen af ​​kromosomer med gener, for hvilke fitnessfunktionen producerer store værdier.

Dette er i bund og grund processen med at søge efter det optimale sæt kromosomer ved hjælp af en vektor i et N-dimensionelt søgerum.

Meget tydelig, detaljeret forklaring, med eksempler på R-kode, arbejdet med en genetisk algoritme.

Jeg vil gerne særskilt bemærke to tekniske punkter.

Hjælpeopkald fra funktionen evaluate, for eksempel stop-start, indstilling af værdien af ​​subd parameteren, udføres baseret på cran-r funktioner system2

Ved hjælp af hvilken: kaldes et bash-script eller kommando.

For eksempel:

sæt_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)
}
}

Det andet punkt er linjen, evaluate funktioner, med at gemme en specifik metrisk værdi og dens tilsvarende tuning vektor til en logfil:

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

Dette er vigtigt, fordi det fra dette dataarray vil være muligt at opnå yderligere information om, hvilke af komponenterne i tuning-vektoren, der har en større eller mindre effekt på den metriske værdi.

Det vil sige: det vil være muligt at udføre attribut-importamce-analyse.

Så hvad kan der ske?

I grafform, hvis du bestiller testene i stigende metrisk rækkefølge, er billedet som følger:

Den videnskabelige poke-metode, eller hvordan man vælger en databasekonfiguration ved hjælp af benchmarks og en optimeringsalgoritme

Nogle data, der svarer til metrikkens ekstreme værdier:
Den videnskabelige poke-metode, eller hvordan man vælger en databasekonfiguration ved hjælp af benchmarks og en optimeringsalgoritme
Her, i skærmbilledet med resultaterne, vil jeg præcisere: værdierne af tuning-vektoren er givet i form af fitnessfunktionskoden, ikke i form af nummerlisten over parametre/intervaller af parameterværdier, som blev formuleret ovenfor i teksten.

Godt. Er det meget eller lidt, ~8 tusinde tps: et separat spørgsmål.
Inden for rammerne af laboratoriearbejde er dette tal ikke vigtigt, det vigtige er dynamikken, hvordan denne værdi ændres.

Dynamikken her er god.
Det er indlysende, at mindst én faktor signifikant påvirker værdien af ​​metrikken, ga-algoritmen, der sorterer gennem kromosomvektorerne: dækket.
At dømme efter kurveværdiernes ret kraftige dynamik er der mindst en faktor mere, som, selvom den er væsentlig mindre, har indflydelse.

Det er her, du har brug for det attribute-importance analyse for at forstå, hvilke attributter (nå, i dette tilfælde, komponenter i tuning-vektoren) og hvor meget de påvirker den metriske værdi.
Og fra denne information: forstå, hvilke faktorer der blev påvirket af ændringer i væsentlige egenskaber.

Udfør attribute-importance muligt på forskellige måder.

Til disse formål kan jeg godt lide algoritmen randomForest R-pakke af samme navn (dokumentation)
randomForest, som jeg forstår hans arbejde generelt og hans tilgang til at vurdere vigtigheden af ​​attributter i særdeleshed, bygger en bestemt model for responsvariablens afhængighed af attributterne.

I vores tilfælde er svarvariablen en metrik, der er hentet fra databasen i belastningstest: tps;
Og attributter er komponenter i tuning-vektoren.

Så her er det randomForest evaluerer vigtigheden af ​​hver modelattribut med to tal: %IncMSE — hvordan tilstedeværelsen/fraværet af denne attribut i en model ændrer denne models MSE-kvalitet (Mean Squared Error);

Og IncNodePurity er et tal, der afspejler, hvor godt, baseret på værdierne af denne attribut, et datasæt med observationer kan opdeles, så der i den ene del er data med én værdi af metrikken, der forklares, og i den anden med en anden værdi af metrikken.
Nå, det vil sige: i hvor høj grad er dette en klassificeringsegenskab (jeg så den mest klare, russisksprogede forklaring på RandomForest her).

Arbejder-bonde R-kode til behandling af et datasæt med resultaterne af belastningstest:

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

Du kan direkte vælge algoritmens hyperparametre med dine hænder og med fokus på modellens kvalitet vælge en model, der mere præcist opfylder forudsigelserne på valideringsdatasættet.
Du kan skrive en form for funktion til dette arbejde (forresten, igen ved at bruge en form for optimeringsalgoritme).

Du kan bruge R-pakken caret, ikke pointen er vigtig.

Som et resultat, i dette tilfælde, opnås følgende resultat for at vurdere graden af ​​vigtighed af attributterne:

Den videnskabelige poke-metode, eller hvordan man vælger en databasekonfiguration ved hjælp af benchmarks og en optimeringsalgoritme

Godt. Således kan vi begynde global refleksion:

  1. Det viser sig, at den mest betydningsfulde, under disse testbetingelser, var parameteren commit_wait
    Teknisk specificerer den udførelsestilstanden for io-operationen med at skrive redo-data fra subdb-logbufferen til den aktuelle loggruppe: synkron eller asynkron.
    Value nowait hvilket resulterer i en næsten lodret, multipel stigning i værdien af ​​tps-metrikken: dette er inklusion af den asynkrone io-tilstand i redo-grupper.
    Et særskilt spørgsmål er, om du skal gøre dette i en fødevaredatabase. Her begrænser jeg mig til blot at sige: dette er en væsentlig faktor.
  2. Det er logisk, at størrelsen af ​​logbufferen for subd: viser sig at være en væsentlig faktor.
    Jo mindre størrelsen af ​​log-bufferen er, jo mindre er dens bufferkapacitet, jo oftere flyder den over og/eller manglende evne til at allokere et ledigt område i den til en del af nye redoxdata.
    Dette betyder: forsinkelser i forbindelse med tildeling af plads i logbufferen og/eller dumpning af redo-data fra den i redo-grupper.
    Disse forsinkelser bør og påvirker naturligvis databasens gennemstrømning af transaktioner.
  3. Parameter db_block_checksum: godt, også, generelt er det klart - transaktionsbehandling fører til dannelsen af ​​darty-blokke i underdatabasens buffercache.
    Hvilket, når kontrol af kontrolsummer af datablokke er aktiveret, databasen skal behandle - beregn disse kontrolsummer fra kroppen af ​​datablokken, kontroller dem med hvad der er skrevet i datablokoverskriften: matcher/matcher ikke.
    Sådant arbejde kan igen ikke andet end at forsinke databehandlingen, og derfor viser parameteren og mekanismen, der sætter denne parameter, sig at være betydelige.
    Det er derfor, leverandøren i dokumentationen for denne parameter tilbyder forskellige værdier for den (parameteren) og bemærker, at ja, der vil være en indvirkning, men godt, du kan vælge forskellige værdier, op til "fra" og forskellige påvirkninger.

Nå, en global konklusion.

Tilgangen viser sig generelt at være ganske fungerende.

Han tillader sig selv, i de tidlige stadier af belastningstestning af et bestemt servicesystem, for at vælge dets (system) optimale konfiguration for belastningen, ikke at dykke for meget i detaljerne ved opsætning af systemet til belastningen.

Men det udelukker det ikke helt - i det mindste på forståelsesniveauet: Systemet skal være kendt om "justeringsknapperne" og de tilladte rotationsområder for disse knapper.

Fremgangsmåden kan så relativt hurtigt finde den optimale systemkonfiguration.
Og baseret på resultaterne af test er det muligt at få information om arten af ​​forholdet mellem systemydelsesmålingerne og værdierne af systemindstillingernes parametre.

Hvilket selvfølgelig skulle bidrage til fremkomsten af ​​denne meget dybe forståelse af systemet, dets drift, i hvert fald under en given belastning.

I praksis er dette en udveksling af omkostningerne ved at forstå det skræddersyede system med omkostningerne ved at forberede en sådan test af systemet.

Jeg vil gerne bemærke separat: I denne tilgang er graden af ​​tilstrækkelighed af systemtestning til de driftsbetingelser, som det vil have i kommerciel drift, af afgørende betydning.

Tak for din opmærksomhed og tid.

Kilde: www.habr.com

Tilføj en kommentar