Den vetenskapliga petningsmetoden, eller hur man väljer en databaskonfiguration med hjälp av benchmarks och en optimeringsalgoritm

Hallå

Jag bestämde mig för att dela med mig av mitt fynd - frukten av tanke, försök och misstag.
I det stora hela: detta är inte ett fynd, naturligtvis - allt detta borde ha varit känt sedan länge, för dem som är involverade i tillämpad statistisk databehandling och optimering av alla system, inte nödvändigtvis specifikt DBMS.
Och: ja, de vet, de skriver intressanta artiklar om sin forskning, exempel (UPD.: i kommentarerna påpekade de ett mycket intressant projekt: ottertune )
Å andra sidan: direkt ser jag inte något utbrett omnämnande eller spridning av detta tillvägagångssätt på Internet bland IT-specialister, DBA.

Så, till saken.

Låt oss anta att vi har en uppgift: att sätta upp ett visst servicesystem för att betjäna något slags arbete.

Det är känt om detta arbete: vad det är, hur kvaliteten på detta arbete mäts och vad är kriteriet för att mäta denna kvalitet.

Låt oss också anta att det är mer eller mindre känt och förstått: exakt hur arbete utförs i (eller med) detta servicesystem.

"Mer eller mindre" - detta betyder att det är möjligt att förbereda (eller få det någonstans ifrån) ett visst verktyg, verktyg, tjänst som kan syntetiseras och appliceras på systemet med en testbelastning som är tillräckligt tillräcklig för vad som kommer att vara i produktion, under förhållanden som är tillräckliga för att arbeta i produktionen.

Tja, låt oss anta att en uppsättning justeringsparametrar för detta servicesystem är kända, som kan användas för att konfigurera detta system när det gäller produktiviteten i dess arbete.

Och vad är problemet - det finns inte en tillräckligt fullständig förståelse av detta tjänstesystem, en som låter dig sakkunnigt konfigurera inställningarna för detta system för framtida belastning på en given plattform och få den erforderliga produktiviteten i systemet.

Väl. Detta är nästan alltid fallet.

Vad kan du göra här?

Tja, det första som kommer att tänka på är att titta på dokumentationen för detta system. Förstå vad de acceptabla intervallen är för värdena för justeringsparametrarna. Och, till exempel, med hjälp av koordinatnedstigningsmetoden, välj värden för systemparametrar i tester.

De där. ge systemet någon form av konfiguration, i form av en specifik uppsättning värden för dess konfigurationsparametrar.

Applicera en testbelastning på den med hjälp av denna belastningsgenerator.
Och titta på värdet - svaret, eller ett mått på kvaliteten på systemet.

Den andra tanken kan vara slutsatsen att det här är väldigt lång tid.

Tja, det vill säga: om det finns många inställningsparametrar, om intervallen för deras värden som körs är stora, om varje enskilt belastningstest tar mycket tid att slutföra, då: ja, allt detta kan ta ett oacceptabelt länge sedan.

Tja, här är vad du kan förstå och komma ihåg.

Du kan ta reda på att i uppsättningen värden för parametrarna för servicesysteminställningar finns en vektor, som en sekvens av vissa värden.

Varje sådan vektor, om andra saker är lika (eftersom den inte påverkas av denna vektor), motsvarar ett helt bestämt värde på måtten - en indikator på kvaliteten på systemets drift under en testbelastning.

Dvs

Låt oss beteckna systemkonfigurationsvektorn som Den vetenskapliga petningsmetoden, eller hur man väljer en databaskonfiguration med hjälp av benchmarks och en optimeringsalgoritmvar Den vetenskapliga petningsmetoden, eller hur man väljer en databaskonfiguration med hjälp av benchmarks och en optimeringsalgoritm; Var Den vetenskapliga petningsmetoden, eller hur man väljer en databaskonfiguration med hjälp av benchmarks och en optimeringsalgoritm — Antal systemkonfigurationsparametrar, hur många av dessa parametrar det finns.

Och värdet på måtten som motsvarar detta Den vetenskapliga petningsmetoden, eller hur man väljer en databaskonfiguration med hjälp av benchmarks och en optimeringsalgoritm låt oss beteckna det som
Den vetenskapliga petningsmetoden, eller hur man väljer en databaskonfiguration med hjälp av benchmarks och en optimeringsalgoritm, då får vi en funktion: Den vetenskapliga petningsmetoden, eller hur man väljer en databaskonfiguration med hjälp av benchmarks och en optimeringsalgoritm

Jo, då: allt handlar omedelbart om, i mitt fall: nästan bortglömt från min studenttid, algoritmer för att söka efter en funktions yttersta punkt.

Okej, men här uppstår en organisatorisk och tillämpad fråga: vilken algoritm som ska användas.

  1. På så sätt - så att du kan koda mindre för hand.
  2. Och för att det ska fungera, d.v.s. hittade extremumet (om det finns ett), ja, åtminstone snabbare än koordinatnedstigningen.

Den första punkten antyder att vi måste titta mot vissa miljöer där sådana algoritmer redan har implementerats, och i någon form är redo att användas i kod.
Jag vet python и cran-r

Den andra punkten innebär att du måste läsa om själva algoritmerna, vad de är, vilka krav de har och funktionerna i deras arbete.

Och vad de ger kan vara användbara biverkningar - resultat, eller direkt från själva algoritmen.

Eller så kan de erhållas från resultaten av algoritmen.

Mycket beror på ingångsförhållandena.

Till exempel, om du av någon anledning behöver få ett resultat snabbare, ja, du måste titta mot algoritmer för gradientnedstigning och välja en av dem.

Eller, om tiden inte är så viktig, kan du till exempel använda stokastiska optimeringsmetoder, som en genetisk algoritm.

Jag föreslår att man överväger arbetet med detta tillvägagångssätt, att välja systemkonfiguration, med hjälp av en genetisk algoritm, i nästa, så att säga: laboratoriearbete.

Första:

  1. Låt det finnas, som ett servicesystem: oracle xe 18c
  2. Låt den tjäna transaktionsaktivitet och målet: att få högsta möjliga genomströmning av underdatabasen, i transaktioner/sek.
  3. Transaktioner kan skilja sig mycket åt vad gäller arbetet med data och arbetskontexten.
    Låt oss komma överens om att det här är transaktioner som inte behandlar en stor mängd tabelldata.
    I den meningen att de inte genererar mer ångra data än gör om och inte bearbetar stora andelar rader och stora tabeller.

Detta är transaktioner som ändrar en rad i en mer eller mindre stor tabell, med ett litet antal index på denna tabell.

I denna situation: produktiviteten hos underdatabasen för bearbetning av transaktioner kommer, med en reservation, att bestämmas av kvaliteten på bearbetningen av redoxdatabasen.

Ansvarsfriskrivning - om vi pratar specifikt om subdb-inställningarna.

Eftersom det i det allmänna fallet kan finnas till exempel transaktionslås mellan SQL-sessioner, på grund av utformningen av användararbetet med tabelldata och/eller tabellmodellen.

Vilket naturligtvis kommer att ha en deprimerande effekt på TPS-måttet och detta kommer att vara en exogen faktor, relativt subdatabasen: ja, det är så här tabellmodellen utformades och arbetet med data i den som blockeringar uppstår.

Därför, för experimentets renhet, kommer vi att utesluta denna faktor, och nedan kommer jag att klargöra exakt hur.

  1. Låt oss för säkerhets skull anta att 100 % av SQL-kommandona som skickas till databasen är DML-kommandon.
    Låt egenskaperna för användararbete med deldatabasen vara desamma i tester.
    Nämligen: antalet skl-sessioner, tabelldata, hur skl-sessioner fungerar med dem.
  2. Subd fungerar i FORCE LOGGING, ARCHIVELOG mods. Flashback-databasläget är avstängt på subd-nivå.
  3. Gör om loggar: finns i ett separat filsystem, på en separat "disk";
    Resten av den fysiska komponenten i databasen: i ett annat, separat filsystem, på en separat "disk":

Mer information om den fysiska enheten. laboratoriedatabaskomponenter

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

Från början, under dessa belastningsförhållanden, ville jag använda transaktionssubd SLOB-verktyg
Den har en så underbar funktion, jag ska citera författaren:

I hjärtat av SLOB är "SLOB-metoden." SLOB-metoden syftar till att testa plattformar
utan ansökningsstrid. Man kan inte köra maximal hårdvaruprestanda
använda applikationskod som till exempel är bunden av applikationslåsning eller till och med
dela Oracle Database-block. Det stämmer – det finns overhead när man delar data
i datablock! Men SLOB – i sin standardinstallation – är immun mot sådana påståenden.

Denna förklaring: motsvarar, det är.
Det är bekvämt att reglera graden av parallellitet för cl-sessioner, detta är nyckeln -t starta verktyget runit.sh från SLOB
Procentandelen DML-kommandon regleras, i antalet textmeddelanden som skickas till subd, varje textsession, parameter UPDATE_PCT
Separat och mycket bekvämt: SLOB själv, före och efter laddningssessionen - förbereder ett statspack, eller awr-snapshots (vad som är inställt på att förberedas).

Det visade det sig dock SLOB stöder inte SQL-sessioner med en varaktighet på mindre än 30 sekunder.
Därför kodade jag först min egen, arbetar-bondeversion av lastaren, och sedan förblev den i drift.

Låt mig förtydliga vad lastaren gör och hur den gör det, för tydlighetens skull.
I princip ser lastaren ut så här:

Arbetarkod

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

Arbetare lanseras på detta sätt:

Körande arbetare

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

Och bord för arbetare är förberedda så här:

Skapa 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 där. För varje arbetare (praktiskt taget: en separat SQL-session i DB) skapas en separat tabell som arbetaren arbetar med.

Detta säkerställer frånvaron av transaktionslås mellan arbetarsessioner.
Varje arbetare: gör samma sak, med sitt eget bord är alla tabeller likadana.
Alla arbetare utför arbete under lika lång tid.
Dessutom under tillräckligt lång tid för att till exempel en loggväxling definitivt skulle inträffa, och mer än en gång.
Jo, följaktligen uppstod associerade kostnader och effekter.
I mitt fall konfigurerade jag varaktigheten av arbetarnas arbete till 8 minuter.

En del av en statspack-rapport som beskriver funktionen av 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

Återgår till laboratoriearbetet.
Vi kommer, allt annat lika, att variera värdena för följande parametrar i laboratorieunderdatabasen:

  1. Storlek på databaslogggrupper. värdeintervall: [32, 1024] MB;
  2. Antal journalgrupper i databasen. värdeintervall: [2,32];
  3. log_archive_max_processes värdeområde: [1,8];
  4. commit_logging två värden är tillåtna: batch|immediate;
  5. commit_wait två värden är tillåtna: wait|nowait;
  6. log_buffer värdeintervall: [2,128] MB.
  7. log_checkpoint_timeout värdeintervall: [60,1200] sekunder
  8. db_writer_processes värdeintervall: [1,4]
  9. undo_retention värdeområde: [30;300] sekunder
  10. transactions_per_rollback_segment värdeintervall: [1,8]
  11. disk_asynch_io två värden är tillåtna: true|false;
  12. filesystemio_options följande värden är tillåtna: none|setall|directIO|asynch;
  13. db_block_checking följande värden är tillåtna: OFF|LOW|MEDIUM|FULL;
  14. db_block_checksum följande värden är tillåtna: OFF|TYPICAL|FULL;

En person med erfarenhet av att underhålla Oracle-databaser kan säkert redan säga vad och till vilka värden som ska ställas in, från de angivna parametrarna och deras acceptabla värden, för att få större produktivitet i databasen för arbetet med data som anges av applikationskoden , här ovan.

Men.

Poängen med laboratoriearbetet är att visa att själva optimeringsalgoritmen kommer att klargöra detta för oss relativt snabbt.

För oss återstår bara att titta in i dokumentet, genom det anpassningsbara systemet, precis tillräckligt för att ta reda på vilka parametrar som ska ändras och inom vilka intervall.
Och även: koda koden som kommer att användas för att arbeta med det anpassade systemet för den valda optimeringsalgoritmen.

Så nu om koden.
Jag pratade ovan om cran-r, dvs: alla manipulationer med det anpassade systemet är orkestrerade i form av ett R-skript.

Den faktiska uppgiften, analys, urval efter metriskt värde, systemtillståndsvektorer: detta är ett paket GA (dokumentationen)
Paketet, i det här fallet, är inte särskilt lämpligt, i den meningen att det förväntar sig att vektorer (kromosomer, om i termer av paketet) ska specificeras i form av strängar av tal med en bråkdel.

Och min vektor, från värdena för inställningsparametrarna: dessa är 14 kvantiteter - heltal och strängvärden.

Problemet kan naturligtvis lätt undvikas genom att tilldela några specifika nummer till strängvärden.

Så till slut ser huvuddelen av R-skriptet ut så här:

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

Här, med hjälp lower и upper subrutinattribut ga i huvudsak är ett område av sökutrymmet specificerat, inom vilket en sökning kommer att utföras efter en sådan vektor (eller vektorer) för vilken det maximala värdet för fitnessfunktionen kommer att erhållas.

Ga-subrutinen utför en sökning som maximerar fitnessfunktionen.

Tja, då visar det sig att i det här fallet är det nödvändigt att fitnessfunktionen, som förstår vektorn som en uppsättning värden för vissa parametrar i subd, får ett mått från subd.

Det vill säga: hur många, med en given subd-inställning och en given belastning på subd:n: subd bearbetar transaktioner per sekund.

Det vill säga, vid utfällning måste följande flersteg utföras i fitnessfunktionen:

  1. Bearbeta indatavektorn av siffror - omvandla den till värden för underdataparametrarna.
  2. Ett försök att skapa ett givet antal redo-grupper av en given storlek. Dessutom kan försöket misslyckas.
    Magasingrupper som redan fanns i subd, i viss mängd och av någon storlek, för experimentets renhet - d.b. raderade.
  3. Om föregående punkt är framgångsrik: ange värdena för konfigurationsparametrar till databasen (igen: det kan vara ett fel)
  4. Om föregående steg är framgångsrikt: stoppa subd, starta subd så att de nyligen angivna parametervärdena träder i kraft. (igen: det kan finnas ett fel)
  5. Om föregående steg är framgångsrikt: utför ett belastningstest. hämta mätvärden från subd.
  6. Återställ subd till sitt ursprungliga tillstånd, dvs. ta bort ytterligare logggrupper, återställ den ursprungliga underdatabaskonfigurationen för att fungera.

Kod för träningsfunktion

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

Den där. allt arbete: utförs i fitnessfunktionen.

Ga-subrutinen bearbetar vektorer, eller, mer korrekt, kromosomer.
Där, det som är viktigast för oss, är urvalet av kromosomer med gener för vilka fitnessfunktionen producerar stora värden.

Detta är i huvudsak processen att söka efter den optimala uppsättningen kromosomer med hjälp av en vektor i ett N-dimensionellt sökutrymme.

Mycket tydlig, detaljerad en förklaring, med exempel på R-kod, arbetet med en genetisk algoritm.

Jag vill separat notera två tekniska punkter.

Hjälpanrop från funktionen evaluate, till exempel, stopp-start, inställning av värdet på subd-parametern, utförs baserat på cran-r funktioner system2

Med hjälp av vilken: något bash-skript eller kommando anropas.

Till exempel:

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

Den andra punkten är linjen, evaluate funktioner, med att spara ett specifikt metriskt värde och dess motsvarande inställningsvektor till en loggfil:

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

Detta är viktigt, eftersom det från denna datamatris kommer att vara möjligt att erhålla ytterligare information om vilken av komponenterna i avstämningsvektorn som har en större eller mindre effekt på det metriska värdet.

Det vill säga: det kommer att vara möjligt att utföra attribut-importamce-analys.

Så vad kan hända?

I grafform, om du beställer testerna i stigande metrisk ordning, ser bilden ut som följer:

Den vetenskapliga petningsmetoden, eller hur man väljer en databaskonfiguration med hjälp av benchmarks och en optimeringsalgoritm

Vissa data som motsvarar de extrema värdena för måttet:
Den vetenskapliga petningsmetoden, eller hur man väljer en databaskonfiguration med hjälp av benchmarks och en optimeringsalgoritm
Här, i skärmdumpen med resultaten, ska jag förtydliga: värdena för inställningsvektorn ges i termer av fitnessfunktionskoden, inte i termer av nummerlistan över parametrar/intervall av parametervärden, som formulerades ovan i texten.

Väl. Är det mycket eller lite, ~8 tusen tps: en separat fråga.
Inom ramen för laborationer är denna siffra inte viktig, det viktiga är dynamiken, hur detta värde förändras.

Dynamiken här är bra.
Det är uppenbart att åtminstone en faktor avsevärt påverkar värdet av metriken, ga-algoritmen, som sorterar genom kromosomvektorerna: täckt.
Att döma av den ganska kraftiga dynamiken i kurvvärdena finns det åtminstone en faktor till som, även om den är betydligt mindre, påverkar.

Det är här du behöver det attribute-importance analys för att förstå vilka attribut (ja, i det här fallet, komponenter i avstämningsvektorn) och hur mycket de påverkar det metriska värdet.
Och från denna information: förstå vilka faktorer som påverkades av förändringar i betydande attribut.

Kör attribute-importance möjligt på olika sätt.

För dessa ändamål gillar jag algoritmen randomForest R-paket med samma namn (dokumentationen)
randomForest, som jag förstår hans arbete i allmänhet och hans inställning till att bedöma vikten av attribut i synnerhet, bygger en viss modell av responsvariabelns beroende av attributen.

I vårt fall är svarsvariabeln ett mått som erhålls från databasen i belastningstester: tps;
Och attribut är komponenter i inställningsvektorn.

Så här randomForest utvärderar vikten av varje modellattribut med två siffror: %IncMSE — hur närvaron/frånvaron av detta attribut i en modell ändrar MSE-kvaliteten för denna modell (Mean Squared Error);

Och IncNodePurity är ett tal som återspeglar hur väl, baserat på värdena för detta attribut, en datauppsättning med observationer kan delas, så att det i en del finns data med ett värde av måtten som förklaras, och i den andra med ett annat värde av måtten.
Tja, det vill säga: i vilken utsträckning är detta ett klassificerande attribut (jag såg den mest tydliga, ryskspråkiga förklaringen på RandomForest här).

Arbetar-bonde R-kod för bearbetning av en datauppsättning med resultaten 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 direkt välja hyperparametrarna för algoritmen med händerna och, med fokus på modellens kvalitet, välja en modell som mer exakt uppfyller förutsägelserna i valideringsdataset.
Du kan skriva någon form av funktion för detta arbete (förresten, återigen, med någon form av optimeringsalgoritm).

Du kan använda R-paketet caret, inte poängen är viktig.

Som ett resultat, i det här fallet, erhålls följande resultat för att bedöma graden av betydelse för attributen:

Den vetenskapliga petningsmetoden, eller hur man väljer en databaskonfiguration med hjälp av benchmarks och en optimeringsalgoritm

Väl. Således kan vi börja global reflektion:

  1. Det visar sig att den viktigaste, under dessa testförhållanden, var parametern commit_wait
    Tekniskt sett specificerar den exekveringsläget för io-operationen för att skriva omdata från subdb-loggbufferten till den aktuella logggruppen: synkron eller asynkron.
    Värde nowait vilket resulterar i en nästan vertikal, multipel ökning av värdet på tps-måttet: detta är inkluderingen av det asynkrona io-läget i redo-grupper.
    En separat fråga är om du ska göra detta i en livsmedelsdatabas. Här begränsar jag mig till att bara konstatera: detta är en betydande faktor.
  2. Det är logiskt att storleken på loggbufferten för subd: visar sig vara en betydande faktor.
    Ju mindre storleken på loggbufferten är, desto mindre är dess buffringskapacitet, desto oftare svämmar den över och/eller oförmågan att allokera ett fritt område i den för en del av ny redoxdata.
    Detta innebär: fördröjningar i samband med tilldelning av utrymme i loggbufferten och/eller dumpning av redo-data från den till redo-grupper.
    Dessa förseningar bör och påverkar naturligtvis genomströmningen av databasen för transaktioner.
  3. Parameter db_block_checksum: ja, också, i allmänhet är det tydligt - transaktionsbearbetning leder till bildandet av darty-block i underdatabasens buffertcache.
    Vilket, när kontroll av kontrollsummor av datablock är aktiverat, databasen måste bearbeta - beräkna dessa kontrollsummor från datablockets kropp, kontrollera dem med vad som står i datablockshuvudet: matchar/matchar inte.
    Sådant arbete, återigen, kan inte annat än fördröja databehandlingen, och följaktligen visar sig parametern och mekanismen som ställer in denna parameter vara betydande.
    Det är därför leverantören, i dokumentationen för denna parameter, erbjuder olika värden för den (parametern) och noterar att ja, det kommer att bli en påverkan, men, ja, du kan välja olika värden, upp till "av" och olika effekter.

Tja, en global slutsats.

Tillvägagångssättet visar sig i allmänhet fungera ganska bra.

Han tillåter sig själv, i de tidiga stadierna av lasttestning av ett visst servicesystem, för att välja dess (system) optimala konfiguration för lasten, att inte fördjupa sig för mycket i detaljerna för att ställa in systemet för lasten.

Men det utesluter det inte helt - åtminstone på förståelsenivån: systemet måste vara känt om "justeringsrattarna" och de tillåtna rotationsintervallen för dessa rattar.

Tillvägagångssättet kan då relativt snabbt hitta den optimala systemkonfigurationen.
Och baserat på testresultaten är det möjligt att få information om arten av förhållandet mellan systemprestandamåtten och värdena för systeminställningsparametrarna.

Vilket naturligtvis borde bidra till uppkomsten av denna mycket djupa förståelse av systemet, dess funktion, åtminstone under en given belastning.

I praktiken är detta ett utbyte av kostnaderna för att förstå det skräddarsydda systemet mot kostnaderna för att förbereda sådan testning av systemet.

Jag skulle vilja notera separat: i detta tillvägagångssätt är graden av lämplighet hos systemtestning för de driftsförhållanden som kommer att ha i kommersiell drift av avgörande betydelse.

Tack för din uppmärksamhet och tid.

Källa: will.com

Lägg en kommentar