Den vitenskapelige poke-metoden, eller hvordan velge en databasekonfigurasjon ved hjelp av benchmarks og en optimaliseringsalgoritme

Hallo

Jeg bestemte meg for å dele funnet - frukten av tanke, prøving og feiling.
I det store og hele: dette er ikke et funn, selvfølgelig - alt dette burde vært kjent i lang tid, for de som er involvert i anvendt statistisk databehandling og optimalisering av noen systemer, ikke nødvendigvis spesifikt DBMS.
Og: ja, de vet, de skriver interessante artikler om forskningen deres, eksempel (UPD.: i kommentarfeltet pekte de på et veldig interessant prosjekt: ottertune )
På den annen side: umiddelbart ser jeg ingen utbredt omtale eller spredning av denne tilnærmingen på Internett blant IT-spesialister, DBA.

Så, til poenget.

La oss anta at vi har en oppgave: å sette opp et bestemt tjenestesystem for å betjene en eller annen form for arbeid.

Det er kjent om dette arbeidet: hva det er, hvordan kvaliteten på dette arbeidet måles, og hva er kriteriet for å måle denne kvaliteten.

La oss også anta at det er mer eller mindre kjent og forstått: nøyaktig hvordan arbeid utføres i (eller med) dette tjenestesystemet.

"Mer eller mindre" - dette betyr at det er mulig å forberede (eller få det fra et sted) et bestemt verktøy, verktøy, tjeneste som kan syntetiseres og brukes på systemet med en testbelastning tilstrekkelig tilstrekkelig til det som vil være i produksjon, under forhold som er tilstrekkelig til å arbeide i produksjonen.

Vel, la oss anta at et sett med justeringsparametere for dette tjenestesystemet er kjent, som kan brukes til å konfigurere dette systemet når det gjelder produktiviteten til arbeidet.

Og hva er problemet - det er ikke en tilstrekkelig fullstendig forståelse av dette tjenestesystemet, en som lar deg ekspertkonfigurere innstillingene til dette systemet for fremtidig belastning på en gitt plattform og få den nødvendige produktiviteten til systemet.

Vi vil. Dette er nesten alltid tilfelle.

Hva kan du gjøre her?

Vel, det første du tenker på er å se på dokumentasjonen for dette systemet. Forstå hva de akseptable områdene er for verdiene til justeringsparametrene. Og, for eksempel, ved å bruke koordinatnedstigningsmetoden, velg verdier for systemparametere i tester.

De. gi systemet en form for konfigurasjon, i form av et spesifikt sett med verdier for dets konfigurasjonsparametere.

Påfør en testbelastning på den ved å bruke denne lastgeneratoren med verktøy.
Og se på verdien - responsen, eller en beregning av kvaliteten på systemet.

Den andre tanken kan være konklusjonen om at dette er veldig lang tid.

Vel, det vil si: hvis det er mange innstillingsparametere, hvis rekkevidden til verdiene deres som kjøres er store, hvis hver enkelt belastningstest tar mye tid å fullføre, så: ja, alt dette kan ta en uakseptabel lang tid.

Vel, her er hva du kan forstå og huske.

Du kan finne ut at i settet med verdier av parametere for servicesysteminnstillinger er det en vektor, som en sekvens av noen verdier.

Hver slik vektor, alt annet like (ved at den ikke påvirkes av denne vektoren), tilsvarer en helt bestemt metrisk verdi - en indikator på kvaliteten på systemdriften under testbelastning.

Dvs.

La oss betegne systemkonfigurasjonsvektoren som Den vitenskapelige poke-metoden, eller hvordan velge en databasekonfigurasjon ved hjelp av benchmarks og en optimaliseringsalgoritmeDer Den vitenskapelige poke-metoden, eller hvordan velge en databasekonfigurasjon ved hjelp av benchmarks og en optimaliseringsalgoritme; Hvor Den vitenskapelige poke-metoden, eller hvordan velge en databasekonfigurasjon ved hjelp av benchmarks og en optimaliseringsalgoritme — antall systemkonfigurasjonsparametere, hvor mange av disse parameterne det er.

Og verdien av metrikken som tilsvarer dette Den vitenskapelige poke-metoden, eller hvordan velge en databasekonfigurasjon ved hjelp av benchmarks og en optimaliseringsalgoritme la oss betegne det som
Den vitenskapelige poke-metoden, eller hvordan velge en databasekonfigurasjon ved hjelp av benchmarks og en optimaliseringsalgoritme, så får vi en funksjon: Den vitenskapelige poke-metoden, eller hvordan velge en databasekonfigurasjon ved hjelp av benchmarks og en optimaliseringsalgoritme

Vel, da: alt kommer umiddelbart ned til, i mitt tilfelle: nesten glemt fra studietiden, algoritmer for å søke etter ytterpunktet til en funksjon.

Ok, men her dukker det opp et organisatorisk og anvendt spørsmål: hvilken algoritme du skal bruke.

  1. I betydningen – slik at du kan kode mindre for hånd.
  2. Og for at det skal fungere, dvs. fant ekstremum (hvis det er en), vel, i det minste raskere enn koordinatnedstigning.

Det første punktet antyder at vi må se mot noen miljøer der slike algoritmer allerede er implementert, og i en eller annen form er klare til bruk i kode.
Vel, jeg vet python и cran-r

Det andre punktet betyr at du må lese om selve algoritmene, hva de er, hvilke krav de har og funksjonene i arbeidet deres.

Og det de gir kan være nyttige bivirkninger - resultater, eller direkte fra selve algoritmen.

Eller de kan fås fra resultatene av algoritmen.

Mye avhenger av inngangsforholdene.

For eksempel, hvis du av en eller annen grunn trenger å få et resultat raskere, vel, du må se mot gradient-nedstigningsalgoritmer og velge en av dem.

Eller, hvis tiden ikke er så viktig, kan du for eksempel bruke stokastiske optimaliseringsmetoder, for eksempel en genetisk algoritme.

Jeg foreslår å vurdere arbeidet med denne tilnærmingen, velge systemkonfigurasjon, ved å bruke en genetisk algoritme, i det neste, så å si: laboratoriearbeid.

Opprinnelig:

  1. La det være, som et servicesystem: oracle xe 18c
  2. La den tjene transaksjonsaktivitet og målet: å oppnå høyest mulig gjennomstrømning av subdatabasen, i transaksjoner/sek.
  3. Transaksjoner kan være svært forskjellige når det gjelder arbeid med data og arbeidskontekst.
    La oss bli enige om at dette er transaksjoner som ikke behandler en stor mengde tabelldata.
    I den forstand at de ikke genererer mer angredata enn å gjøre om og ikke behandler store prosentandeler av rader og store tabeller.

Dette er transaksjoner som endrer én rad i en mer eller mindre stor tabell, med et lite antall indekser på denne tabellen.

I denne situasjonen: produktiviteten til underdatabasen for behandling av transaksjoner vil, med en reservasjon, bli bestemt av kvaliteten på behandlingen av redoksdatabasen.

Ansvarsfraskrivelse - hvis vi snakker spesifikt om subdb-innstillingene.

For i det generelle tilfellet kan det for eksempel være transaksjonslåser mellom SQL-sesjoner, på grunn av utformingen av brukerarbeid med tabelldata og/eller tabellmodellen.

Noe som selvfølgelig vil ha en deprimerende effekt på TPS-metrikken og dette vil være en eksogen faktor, i forhold til subdatabasen: vel, det er slik tabellmodellen ble utformet og arbeidet med data i den at blokkeringer oppstår.

Derfor, for renheten til eksperimentet, vil vi ekskludere denne faktoren, og nedenfor vil jeg avklare nøyaktig hvordan.

  1. La oss anta at 100 % av SQL-kommandoene som sendes til databasen er DML-kommandoer.
    La egenskapene til brukerarbeid med deldatabasen være de samme i tester.
    Nemlig: antall skl-økter, tabelldata, hvordan skl-økter fungerer med dem.
  2. Subd fungerer i FORCE LOGGING, ARCHIVELOG mods. Flashback-database-modus er slått av på subd-nivå.
  3. Gjenta logger: ligger i et eget filsystem, på en separat "disk";
    Resten av den fysiske komponenten av databasen: i et annet, separat filsystem, på en separat "disk":

Flere detaljer om den fysiske enheten. 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 utgangspunktet, under disse belastningsforholdene, ønsket jeg å bruke transaksjonssubd SLOB-verktøy
Den har en så fantastisk funksjon, jeg skal sitere forfatteren:

I hjertet av SLOB er "SLOB-metoden." SLOB-metoden har som mål å teste plattformer
uten søknadspåstand. Man kan ikke kjøre maksimal maskinvareytelse
bruke applikasjonskode som for eksempel er bundet av applikasjonslåsing eller til og med
deling av Oracle-databaseblokker. Det er riktig – det er overhead når du deler data
i datablokker! Men SLOB – i sin standarddistribusjon – er immun mot slike påstander.

Denne erklæringen: samsvarer, det er.
Det er praktisk å regulere graden av parallellitet til cl-økter, dette er nøkkelen -t starte verktøyet runit.sh fra SLOB
Prosentandelen av DML-kommandoer er regulert, i antall tekstmeldinger som sendes til subd, hver tekstøkt, parameter UPDATE_PCT
Separat og veldig praktisk: SLOB seg selv, før og etter innlastingsøkten - forbereder en statspack, eller awr-snapshots (det som er satt til å bli forberedt).

Det viste det seg imidlertid SLOB støtter ikke SQL-økter med en varighet på mindre enn 30 sekunder.
Derfor kodet jeg først min egen, arbeider-bonde-versjon av lasteren, og så forble den i drift.

La meg avklare hva lasteren gjør og hvordan den gjør det, for klarhetens skyld.
I hovedsak ser lasteren slik ut:

Arbeiderkode

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

Arbeidere lanseres på denne måten:

Løpende arbeidere

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 for arbeidere er forberedt slik:

Lage 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. For hver arbeider (praktisk talt: en egen SQL-sesjon i DB) opprettes det en egen tabell som arbeideren arbeider med.

Dette sikrer fravær av transaksjonslåser mellom arbeiderøkter.
Hver arbeider: gjør det samme, med sitt eget bord er alle bordene like.
Alle arbeidere utfører arbeid like lang tid.
Dessuten i lang nok tid til at for eksempel en loggbryter definitivt vil oppstå, og mer enn én gang.
Vel, følgelig oppsto tilhørende kostnader og effekter.
I mitt tilfelle konfigurerte jeg varigheten av arbeidernes arbeid til 8 minutter.

En del av en statspack-rapport som beskriver operasjonen til subd under belastning

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

Tilbake til laboratoriearbeidet.
Vi vil, ellers likt, variere verdiene til følgende parametere i laboratorieunderdatabasen:

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

En person med erfaring med å vedlikeholde Oracle-databaser kan sikkert allerede si hva og til hvilke verdier som bør settes, fra de spesifiserte parameterne og deres akseptable verdier, for å oppnå større produktivitet av databasen for arbeidet med data som er indikert av applikasjonskoden , her ovenfor.

Men.

Poenget med laboratoriearbeidet er å vise at selve optimaliseringsalgoritmen vil avklare dette for oss relativt raskt.

For oss gjenstår det bare å se inn i dokumentet, gjennom det tilpassbare systemet, akkurat nok til å finne ut hvilke parametere som skal endres og i hvilke områder.
Og også: kode koden som skal brukes til å jobbe med det tilpassede systemet til den valgte optimaliseringsalgoritmen.

Så nå om koden.
Jeg snakket ovenfor om cran-r, dvs.: alle manipulasjoner med det tilpassede systemet er orkestrert i form av et R-skript.

Selve oppgaven, analyse, valg etter metrisk verdi, systemtilstandsvektorer: dette er en pakke GA (dokumentasjonen)
Pakken, i dette tilfellet, er ikke særlig egnet, i den forstand at den forventer at vektorer (kromosomer, hvis det gjelder pakken) spesifiseres i form av tallstrenger med en brøkdel.

Og vektoren min, fra verdiene til innstillingsparametrene: dette er 14 mengder - heltall og strengverdier.

Problemet kan selvfølgelig enkelt unngås ved å tilordne noen spesifikke tall til strengverdier.

Dermed, til slutt, ser hoveddelen av R-skriptet slik ut:

Ring 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 hjelp lower и upper subrutineattributter ga i hovedsak er et område av søkeområdet spesifisert, innenfor hvilket et søk vil bli utført for en slik vektor (eller vektorer) som maksimalverdien til treningsfunksjonen vil bli oppnådd for.

Ga-subrutinen utfører et søk som maksimerer treningsfunksjonen.

Vel, så viser det seg at i dette tilfellet er det nødvendig at fitnessfunksjonen, som forstår vektoren som et sett med verdier for visse parametere til subd, mottar en beregning fra subd.

Det vil si: hvor mange, med et gitt subd oppsett og en gitt belastning på subd: subd behandler transaksjoner per sekund.

Det vil si at ved utfolding må følgende flertrinn utføres inne i treningsfunksjonen:

  1. Behandle inngangsvektoren til tall - konvertere den til verdier for underdataparameterne.
  2. Et forsøk på å lage et gitt antall gjenta grupper av en gitt størrelse. Dessuten kan forsøket være mislykket.
    Magasingrupper som allerede fantes i subd, i en viss mengde og av en eller annen størrelse, for forsøkets renhet - d.b. slettet.
  3. Hvis forrige punkt er vellykket: spesifisere verdiene for konfigurasjonsparametere til databasen (igjen: det kan være en feil)
  4. Hvis forrige trinn er vellykket: stoppe subd, start subd slik at de nylig spesifiserte parameterverdiene trer i kraft. (igjen: det kan være en feil)
  5. Hvis forrige trinn er vellykket: utfør en belastningstest. få beregninger fra subd.
  6. Returner subd til sin opprinnelige tilstand, dvs. slett flere logggrupper, returner den opprinnelige underdatabasekonfigurasjonen til å fungere.

Kode for treningsfunksjon

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 arbeid: utføres i treningsfunksjonen.

Ga-subrutinen behandler vektorer, eller, mer korrekt, kromosomer.
I hvilke, det som er viktigst for oss, er utvalget av kromosomer med gener som fitnessfunksjonen produserer store verdier for.

Dette er i hovedsak prosessen med å søke etter det optimale settet med kromosomer ved å bruke en vektor i et N-dimensjonalt søkerom.

Veldig tydelig, detaljert forklaring, med eksempler på R-kode, arbeidet til en genetisk algoritme.

Jeg vil særskilt bemerke to tekniske punkter.

Hjelpeanrop fra funksjonen evaluate, for eksempel, stopp-start, innstilling av verdien til subd-parameteren, utføres basert på cran-r funksjoner system2

Ved hjelp av dette: kalles et bash-skript eller kommando.

For eksempel:

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

Det andre punktet er linjen, evaluate funksjoner, med lagring av en spesifikk metrisk verdi og dens tilsvarende innstillingsvektor til en loggfil:

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

Dette er viktig, fordi det fra denne datamatrisen vil være mulig å få ytterligere informasjon om hvilke av komponentene i avstemningsvektoren som har en større eller mindre effekt på den metriske verdien.

Det vil si: det vil være mulig å utføre attributt-importamce-analyse.

Så hva kan skje?

I grafform, hvis du bestiller testene i stigende metrisk rekkefølge, er bildet som følger:

Den vitenskapelige poke-metoden, eller hvordan velge en databasekonfigurasjon ved hjelp av benchmarks og en optimaliseringsalgoritme

Noen data som tilsvarer de ekstreme verdiene til beregningen:
Den vitenskapelige poke-metoden, eller hvordan velge en databasekonfigurasjon ved hjelp av benchmarks og en optimaliseringsalgoritme
Her, i skjermbildet med resultatene, skal jeg avklare: verdiene til tuning-vektoren er gitt i form av kondisjonsfunksjonskoden, ikke i form av nummerlisten over parametere/intervaller av parameterverdier, som ble formulert over i teksten.

Vi vil. Er det mye eller lite, ~8 tusen tps: et eget spørsmål.
Innenfor rammen av laboratoriearbeid er denne figuren ikke viktig, det som er viktig er dynamikken, hvordan denne verdien endres.

Dynamikken her er god.
Det er åpenbart at minst én faktor i betydelig grad påvirker verdien av metrikken, ga-algoritmen, som sorterer gjennom kromosomvektorene: dekket.
Å dømme etter den ganske kraftige dynamikken til kurveverdiene, er det minst en faktor til som, selv om den er betydelig mindre, har innflytelse.

Det er her du trenger det attribute-importance analyse for å forstå hvilke attributter (vel, i dette tilfellet, komponenter i tuning-vektoren) og hvor mye de påvirker den metriske verdien.
Og fra denne informasjonen: forstå hvilke faktorer som ble påvirket av endringer i betydelige attributter.

Henrette attribute-importance mulig på forskjellige måter.

For disse formålene liker jeg algoritmen randomForest R-pakke med samme navn (dokumentasjonen)
randomForest, slik jeg forstår hans arbeid generelt og hans tilnærming til å vurdere viktigheten av attributter spesielt, bygger en viss modell av responsvariabelens avhengighet av attributtene.

I vårt tilfelle er responsvariabelen en beregning hentet fra databasen i belastningstester: tps;
Og attributter er komponenter av tuning-vektoren.

så her randomForest evaluerer viktigheten av hvert modellattributt med to tall: %IncMSE – hvordan tilstedeværelsen/fraværet av dette attributtet i en modell endrer MSE-kvaliteten til denne modellen (Mean Squared Error);

Og IncNodePurity er et tall som gjenspeiler hvor godt, basert på verdiene til dette attributtet, et datasett med observasjoner kan deles, slik at det i den ene delen er data med en verdi av metrikken som forklares, og i den andre med en annen verdi av metrikken.
Vel, det vil si: i hvilken grad er dette et klassifiserende attributt (jeg så den mest klare, russiskspråklige forklaringen på RandomForest her).

Arbeider-bonde R-kode for behandling av et datasett med resultater av belastningstester:

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 velge hyperparametrene til algoritmen direkte med hendene og, med fokus på kvaliteten på modellen, velge en modell som mer nøyaktig oppfyller spådommene på valideringsdatasettet.
Du kan skrive en slags funksjon for dette arbeidet (forresten, igjen, ved å bruke en slags optimaliseringsalgoritme).

Du kan bruke R-pakken caret, ikke poenget er viktig.

Som et resultat, i dette tilfellet, oppnås følgende resultat for å vurdere graden av viktighet av attributtene:

Den vitenskapelige poke-metoden, eller hvordan velge en databasekonfigurasjon ved hjelp av benchmarks og en optimaliseringsalgoritme

Vi vil. Dermed kan vi begynne global refleksjon:

  1. Det viser seg at den mest betydningsfulle, under disse testforholdene, var parameteren commit_wait
    Teknisk spesifiserer den utførelsesmodusen for io-operasjonen for å skrive redo data fra subdb-loggbufferen til gjeldende logggruppe: synkron eller asynkron.
    Verdi nowait som resulterer i en nesten vertikal, multippel økning i verdien av tps-metrikken: dette er inkluderingen av den asynkrone io-modusen i redo-grupper.
    Et eget spørsmål er om du bør gjøre dette i en matdatabase. Her begrenser jeg meg til å bare slå fast: dette er en vesentlig faktor.
  2. Det er logisk at størrelsen på loggbufferen til subd: viser seg å være en vesentlig faktor.
    Jo mindre størrelsen på loggbufferen er, jo mindre bufferkapasitet er den, jo oftere renner den over og/eller manglende evne til å tildele et ledig område i den for en del av nye redoksdata.
    Dette betyr: forsinkelser knyttet til tildeling av plass i loggbufferen og/eller dumping av gjenta data fra den til gjenta grupper.
    Disse forsinkelsene bør og påvirker selvfølgelig gjennomstrømningen av databasen for transaksjoner.
  3. Parameter db_block_checksum: vel, også, generelt er det klart - transaksjonsbehandling fører til dannelse av darty-blokker i subdatabasens bufferbuffer.
    Som når sjekking av sjekksummer for datablokker er aktivert, må databasen behandle - beregn disse sjekksummene fra kroppen til datablokken, sjekk dem med det som er skrevet i datablokkoverskriften: matcher/ stemmer ikke.
    Slikt arbeid kan igjen ikke annet enn å forsinke databehandlingen, og følgelig viser parameteren og mekanismen som setter denne parameteren seg å være betydelig.
    Det er derfor leverandøren tilbyr, i dokumentasjonen for denne parameteren, forskjellige verdier og merker at ja, det vil være en innvirkning, men du kan velge forskjellige verdier, til og med "av" og forskjellige virkninger.

Vel, en global konklusjon.

Tilnærmingen viser seg generelt å være ganske fungerende.

Han tillater seg selv, i de tidlige stadiene av lasttesting av et visst servicesystem, for å velge dets (system) optimale konfigurasjon for lasten, ikke fordype seg for mye i detaljene ved å sette opp systemet for lasten.

Men det utelukker det ikke helt - i det minste på forståelsesnivået: systemet må være kjent om "justeringsknottene" og de tillatte rotasjonsområdene til disse knottene.

Tilnærmingen kan da relativt raskt finne den optimale systemkonfigurasjonen.
Og basert på resultatene av testingen er det mulig å få informasjon om arten av forholdet mellom systemytelsesmålingene og verdiene til systeminnstillingsparametrene.

Noe som selvfølgelig burde bidra til fremveksten av denne svært dype forståelsen av systemet, dets drift, i det minste under en gitt belastning.

I praksis er dette en utveksling av kostnadene ved å forstå det tilpassede systemet mot kostnadene ved å utarbeide slik testing av systemet.

Jeg vil merke separat: I denne tilnærmingen er graden av tilstrekkelighet av systemtesting til driftsforholdene det vil ha i kommersiell drift kritisk viktig.

Takk for oppmerksomheten og tiden.

Kilde: www.habr.com

Legg til en kommentar