Die Methode des wissenschaftlichen Stocherns oder wie man Subdatenbanken mit Benchmarks und Optimierungsalgorithmen konfiguriert

Hallo.

Ich habe beschlossen, meine Entdeckung zu teilen – das Produkt von Überlegungen, Versuchen und Fehlern.
Im Großen und Ganzen: das ist natĂŒrlich keine echte Entdeckung – all dies sollte lĂ€ngst bekannt sein, denjenigen, die sich mit angewandter Statistik-Datenverarbeitung und der Optimierung von irgendwelchen Systemen beschĂ€ftigen, nicht unbedingt nur DBMS.
Und: Ja, sie wissen es, schreiben interessante Artikel ĂŒber ihre Forschungen, Beispiel (UPD.: In den Kommentaren wurde auf ein sehr interessantes Projekt hingewiesen: ottertune )
Andererseits: Auf den ersten Blick sehe ich kein breites ErwÀhnen oder Verbreiten eines solchen Ansatzes im Internet unter IT-Spezialisten, DBA.

Also, zum Wesentlichen.

Angenommen, wir haben die Aufgabe: ein gewisses Dienstsystem fĂŒr die DurchfĂŒhrung einer bestimmten Arbeit einzurichten.

Über diese Arbeit – es ist bekannt: wie sie aussieht, wie die QualitĂ€t dieser Arbeit gemessen wird und welcher Kriteriumswert fĂŒr die Messungen dieser QualitĂ€t gilt.

Angenommen, es ist mehr oder weniger bekannt, wie genau die Arbeit in (oder mit) diesem Dienstsystem ausgefĂŒhrt wird.

"Mehr oder weniger" bedeutet, dass es die Möglichkeit gibt, (oder irgendwo zu bekommen) ein gewisses Tool, eine Dienstprogramm, einen Service vorzubereiten, mit dem man eine Testlast synthetisieren und auf das System eine ausreichend angemessene Last aufbringen kann, die dem entspricht, was in der Produktion passieren wird, unter Bedingungen, die fĂŒr die Arbeit in der Produktion ausreichend geeignet sind.

Nun, und stellen wir uns vor, dass eine Reihe von Regelparametern dieses Dienstsystems bekannt ist, mit denen man dieses System in Bezug auf seine ProduktivitÀt einstellen kann.

Und, wo das Problem liegt – es gibt kein ausreichendes VerstĂ€ndnis dieses Dienstsystems, das einen Experten in die Lage versetzt, die Einstellungen dieses Systems fĂŒr die zukĂŒnftige Last auf dieser Plattform vorzunehmen und die erforderliche ProduktivitĂ€t des Systems zu erhalten.

Nun. So geht es fast immer.

Was kann man hier tun.

Nun, das Erste, was mir in den Sinn kommt: Ein Blick in die Dokumentation zu diesem System. Verstehen – welche zulĂ€ssigen Bereiche fĂŒr die Werte der Regelparameter dort vorhanden sind. Und zum Beispiel durch den Koordinatensprungmethode Werte fĂŒr die Systemparameter in den Tests auswĂ€hlen.

Das heißt, dem System eine bestimmte Konfiguration in Form eines konkreten Satzes seiner Einstellwerte zuzuweisen.

Daraufhin eine Testlast mit diesem Tool-Dienstprogramm, Last-Generator einzugeben.
Und die GrĂ¶ĂŸe zu beobachten – die Reaktion, nun oder die Metrik der QualitĂ€t der Systemarbeit.

Der zweite Gedanke könnte folgendermassen lauten: das dauert doch sehr lange.

Das heißt: Wenn es viele Einstellungen gibt, wenn die Wertebereiche groß sind, wenn jeder einzelne Lasttest viel Zeit in Anspruch nimmt, dann: ja, das kann alles unangemessen viel Zeit in Anspruch nehmen.

Und hier kann man verstehen und sich erinnern, was möglich ist.

Man kann erfahren, dass in der Menge der Werte fĂŒr die Konfigurationsparameter des Dienstsystems ein Vektor vorhanden ist, als eine Reihenfolge bestimmter Werte.

Jeder solchen Vektor entspricht, unter sonst gleichen Bedingungen (sofern dieser Vektor nicht betroffen ist), einem ziemlich bestimmten Wert der Metrik — dem Maß fĂŒr die QualitĂ€t der Systemarbeit unter Testlast.

Das heißt.

Lassen Sie uns den Konfigurationsvektor des Systems bezeichnen als Die Methode des wissenschaftlichen Stocherns oder wie man Subdatenbanken mit Benchmarks und Optimierungsalgorithmen konfiguriert

Die Methode des wissenschaftlichen Stocherns oder wie man Subdatenbanken mit Benchmarks und Optimierungsalgorithmen konfiguriert; Wo Die Methode des wissenschaftlichen Stocherns oder wie man Subdatenbanken mit Benchmarks und Optimierungsalgorithmen konfiguriert — die Anzahl der Konfigurationsparameter des Systems ist, wie viele es gibt, diese Parameter.

Und der Metrikwert, der diesem Die Methode des wissenschaftlichen Stocherns oder wie man Subdatenbanken mit Benchmarks und Optimierungsalgorithmen konfiguriert entspricht, wird bezeichnet als
Die Methode des wissenschaftlichen Stocherns oder wie man Subdatenbanken mit Benchmarks und Optimierungsalgorithmen konfiguriert, also ergibt sich fĂŒr uns eine Funktion: Die Methode des wissenschaftlichen Stocherns oder wie man Subdatenbanken mit Benchmarks und Optimierungsalgorithmen konfiguriert

Nun, und dann: alles reduziert sich sofort auf, in meinem Fall: fast vergessene Algorithmen zur Extremwertsuche, die ich wÀhrend meines Studiums gelernt habe.

Gut, aber hier stellt sich die organisatorische und praktische Frage: Welchen Algorithmus soll man verwenden?

  1. Im Sinne von — um selbst weniger hĂ€ndisch zu codieren.
  2. Und damit es funktioniert, d.h. das Extremum findet (wenn es existiert), zumindest schneller als der Gradientenabstieg.

Der erste Punkt deutet darauf hin, dass man in Richtung bestimmter Umgebungen schauen muss, in denen solche Algorithmen bereits implementiert sind und in irgendeiner Form bereit zur Verwendung im Code.
Nun, ich kenne python und cran-r

Der zweite Punkt bedeutet, dass man ĂŒber die Algorithmen selbst lesen muss, was fĂŒr sie existiert, welche Anforderungen sie haben, Besonderheiten in der Arbeit.

Und was sie bringen, können nĂŒtzliche Nebenwirkungen-Ergebnisse sein, entweder direkt vom Algorithmus.

Oder sie können aus den Ergebnissen der Algorithmusarbeit erhalten werden.

Hier hÀngt vieles von den Eingangsvoraussetzungen ab.

Zum Beispiel, wenn man aus bestimmten GrĂŒnden schneller ein Ergebnis benötigt, sollte man in Richtung der Gradientenabfallalgorithmen schauen und einen von ihnen auswĂ€hlen.

Oder, wenn die Zeit nicht so wichtig ist, kann man zum Beispiel stochastische Optimierungsmethoden verwenden, etwa den genetischen Algorithmus.

Ich schlage vor, die Arbeit mit einem solchen Ansatz zur Konfigurationsauswahl des Systems unter Verwendung des genetischen Algorithmus in der folgenden, sozusagen: Laborarbeit zu betrachten.

Ausgangsdaten:

  1. Lass es eine Servicelösung geben: oracle xe 18c
  2. Lass sie die TransaktionsaktivitÀt verwalten und das Ziel: eine möglichst hohe DurchsatzkapazitÀt der Datenbank pro Transaktionen/Sekunde zu erreichen.
  3. Transaktionen sind in ihrem Charakter und Kontext der Datenverarbeitung sehr unterschiedlich.
    Vereinbaren wir, dass diese Transaktionen keine große Menge an Tabellendaten verarbeiten.
    Im Sinne von, dass sie nicht mehr Undo-Daten erzeugen als Redo und nicht hohe ProzentsĂ€tze von Zeilen großer Tabellen verarbeiten.

Dies sind Transaktionen, die eine Zeile in einer mehr oder weniger großen Tabelle mit einer geringen Anzahl von Indizes ĂŒber dieser Tabelle Ă€ndern.

In diesem Zusammenhang wird die ProduktivitÀt der Datenbank bei der Verarbeitung von Transaktionen unter der Voraussetzung durch die QualitÀt der Verarbeitung der Redo-Daten bestimmt.

Vorausgesetzt, wir sprechen konkret ĂŒber die Einstellungen der Datenbank.

Denn im allgemeinen Fall können z.B. Transaktionssperren zwischen SQL-Sitzungen auftreten, aufgrund des Designs der Benutzerinteraktion mit Tabellendaten und/oder des Tabellenmodells.

Diese werden sich natĂŒrlich negativ auf die TPS-Metrik auswirken, und dies wird ein exogener, relativ zur Datenbank, Faktor sein: so wurde das Tabellenmodell und die Arbeit mit den Daten so gestaltet, dass Sperren entstehen.

Deshalb, um die Reinheit des Experiments zu wahren, lassen wir diesen Faktor außen vor, ich werde spĂ€ter nĂ€her erlĂ€utern, wie genau.

  1. Nehmen wir zur Klarheit an, dass 100% der in die Datenbank eingespeisten SQL-Befehle: DML-Befehle sind.
    Lass die Merkmale der Benutzerinteraktion mit der Datenbank: gleich bleiben in den Tests.
    NĂ€mlich: die Anzahl der SQL-Sitzungen, die Tabellendaten, wie die SQL-Sitzungen mit ihnen arbeiten.
  2. Die Datenbank arbeitet in FORCE LOGGING, ARCHIVELOG Modi. Der Flashback-Datenbankmodus ist auf Ebene der Datenbank deaktiviert.
  3. Redo-Logs: befinden sich in einem separaten Dateisystem, auf einer separaten "Festplatte";
    Der gesamte andere Teil der physischen Komponente der Datenbank: in einem anderen, separaten FS, auf einer separaten "Festplatte":

Mehr dazu, wie die physische Komponente der Labor-DB strukturiert ist.

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

UrsprĂŒnglich wollte ich unter diesen Lastbedingungen eine Transaktionsdatenbank verwenden. SLOB-Utility
Es hat so eine wunderbare Eigenschaft, die ich zitiere:

Im Kern von SLOB steht die „SLOB-Methode.“ Die SLOB-Methode zielt darauf ab, Plattformen zu testen
ohne Anwendungsinhalt. Man kann nicht die maximale Hardware-Leistung ausschöpfen,
wenn der Anwendungscode, zum Beispiel durch Anwendungs-Blocking oder sogar
durch das Teilen von Oracle Database-Blöcken gebunden ist. Das stimmt—es gibt Overhead beim Teilen von Daten
in Datenblöcken! Aber SLOB—in seiner Standardbereitstellung—ist immun gegen solche Konflikte.

Diese Aussage: trifft zu, das ist wahr.
Es ist bequem, das Maß an ParallelitĂ€t der SQL-Sitzungen zu regulieren, das ist der SchlĂŒssel -t zum Start der Utility runit.sh aus dem SLOB-Paket.
Es wird der Prozentsatz der DML-Befehle reguliert, in der Menge der SQL-Anfragen, die an die DB gesendet werden, von jeder SQL-Sitzung, der Parameter UPDATE_PCT
Separat und sehr bequem: SLOB selbst, vor und nach der Lastsitzung — erstellt Statspak oder AWR-Snapshots (was konfiguriert ist).

Es stellte sich jedoch heraus, dass SLOB es nicht unterstĂŒtzt, wenn SQL-Sitzungen weniger als 30 Sekunden dauern.
Daher habe ich zuerst meine eigene, handwerkliche Variante des Lastgenerators programmiert, und dann blieb er so im Einsatz.

Ich möchte klarstellen, was der Lastgenerator tut, um Klarheit zu schaffen.
Im Wesentlichen sieht der Lastgenerator so aus:

Worker-Code

function dotx()
{
local v_period="$2"
[ -z "v_period" ] && v_period="0"
source "/home/oracle/testingredotrace/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

Worker werden auf folgende Weise gestartet:

Start von Workern

echo "Starte Test, Dauer: ${TEST_DURATION}" >> "$v_logfile"
for((i=1;i> "$v_logfile"
 dotx "$i" "${TEST_DURATION}" &
done
echo "Warte..." >> "$v_logfile"
wait

Die Tabellen fĂŒr die Worker werden so vorbereitet:

Tabellen erstellen

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> "$v_logfile"

Das heißt, fĂŒr jeden Worker (praktisch: eine separate SQL-Session in der DB) wird eine eigene Tabelle erstellt, mit der der Worker arbeitet.

Damit wird sichergestellt, dass es keine Transaktionssperren zwischen den SQL-Sessions der Worker gibt.
Jeder Worker: macht dasselbe mit seiner eigenen Tabelle, alle Tabellen sind gleich.
Alle Worker fĂŒhren die Arbeit ĂŒber denselben Zeitraum hinweg aus.
Und zwar ĂŒber einen ausreichend langen Zeitraum, damit beispielsweise ein Logging-Switching definitiv stattfindet und das nicht nur einmal.
Und in der Folge entstehen damit verbundene Kosten und Effekte.
In meinem Fall habe ich die Laufzeit der Worker auf 8 Minuten eingestellt.

Ein Auszug des Statspack-Berichts mit einer Beschreibung der DB-Arbeit unter Last.

Datenbank    DB Id    Instanz     Inst Num  Startzeit   Release     RAC
~~~~~~~~ ----------- ------------ -------- --------------- ----------- ---
          2929910313 XE                  1 07-Sep-20 23:12 18.0.0.0.0  NEIN

Hostname             Plattform                CPUs Kerne Sockets   Speicher (G)
~~~~ ---------------- ---------------------- ----- ----- ------- ------------
     billing.izhevsk1 Linux x86 64-Bit           2     2       1         15.6

Snapshot       Snap Id     Snap Zeit      Sitzungen Curs/Sitzung Kommentar
~~~~~~~~    ---------- ------------------ -------- --------- ------------------
Begin Snapshot:       1630 07-Sep-20 23:12:27       55        .7
  End Snapshot:       1631 07-Sep-20 23:20:29       62        .6
   Verstrichene Zeit:       8.03 (Minuten) Durchschnittliche aktive Sitzungen:       8.4
   DB-Zeit:      67.31 (Minuten)      DB-CPU:      15.01 (Minuten)

Cache-GrĂ¶ĂŸen            Beginn        Ende
~~~~~~~~~~~       ---------- ----------
    Puffer-Cache:     1.392M              Std BlockgrĂ¶ĂŸe:         8K
     Shared Pool:       288M                  Protokoll-Puffer:   103.424K

Ladeprofil              Pro Sekunde    Pro Transaktion    Pro AusfĂŒhrung    Pro Aufruf
~~~~~~~~~~~~      ------------------  ----------------- ----------- -----------
      DB-Zeit(s):                8.4                0.0        0.00        0.20
       DB-CPU(s):                1.9                0.0        0.00        0.04
       Redo-GrĂ¶ĂŸe:        7.685.765.6              978.4
   Logische LesevorgÀnge:           60.447.0                7.7
   BlockÀnderungen:           47.167.3                6.0
  Physische LesevorgÀnge:                8.3                0.0
 Physische SchreibvorgÀnge:              253.4                0.0
      Benutzeraufrufe:               42.6                0.0
          Analysen:               23.2                0.0
     Harte Analysen:                1.2                0.0
W/A MB bearbeitet:                1.0                0.0
          Anmeldungen:                0.5                0.0
        AusfĂŒhrungen:           15.756.5                2.0
       Rollbacks:                0.0                0.0
    Transaktionen:            7.855.1

Kehren wir zur Formulierung der Laborarbeit zurĂŒck.
Wir werden, bei gleichen Bedingungen, die Werte solcher Parameter der Labor-DB variieren:

  1. GrĂ¶ĂŸe der Datenbank-Protokollgruppen. Wertebereich: [32, 1024] MB;
  2. Anzahl der Datenbank-Protokollgruppen. Wertebereich: [2, 32];
  3. log_archive_max_processes Wertebereich: [1, 8];
  4. commit_logging es sind zwei Werte erlaubt: batch|immediate;
  5. commit_wait es sind zwei Werte erlaubt: wait|nowait;
  6. log_buffer Wertebereich: [2, 128] MB.
  7. log_checkpoint_timeout Wertebereich: [60, 1200] Sekunden
  8. db_writer_processes Wertebereich: [1, 4]
  9. undo_retention Wertebereich: [30, 300] Sekunden
  10. transactions_per_rollback_segment Wertebereich: [1, 8]
  11. disk_asynch_io es sind zwei Werte erlaubt: true|false;
  12. filesystemio_options Folgende Werte sind erlaubt: none|setall|directIO|asynch;
  13. db_block_checking Folgende Werte sind erlaubt: OFF|LOW|MEDIUM|FULL;
  14. db_block_checksum Folgende Werte sind erlaubt: OFF|TYPICAL|FULL;

Ein Mensch mit Erfahrung in der Betreuung von Oracle-Datenbanken kann sicherlich bereits jetzt sagen – welche Werte fĂŒr die angegebenen Parameter und deren zulĂ€ssigen Werte eingestellt werden mĂŒssen, um eine höhere ProduktivitĂ€t der DB fĂŒr die dort beschriebene Arbeit mit Daten zu erzielen, die in dem oben genannten Anwendungscode angegeben ist.

Aber.

Der Sinn der Laborarbeit ist zu zeigen, dass der Optimierungsalgorithmus selbst relativ schnell ist, was uns klÀren wird.

Uns bleibt nur, einen Blick in die Dokumentation des anpassbaren Systems zu werfen, genau so viel, wie nötig ist, um herauszufinden, welche Parameter und in welchen Bereichen geĂ€ndert werden mĂŒssen.
Und auch: den Code zu kodieren, mit dem die Arbeit mit dem gewÀhlten Optimierungsalgorithmus des anpassbaren Systems umgesetzt wird.

Also, jetzt zum Code.
Ich habe oben ĂŒber cran-r, d.h.: alle Manipulationen mit dem anpassbaren System werden in Form eines R-Skripts orchestriert.

TatsÀchlich sieht die Aufgabe, Analyse, Auswahl nach dem Wert der Metrik, Zustandsvektoren des Systems: so aus, dass es ein Paket ist GA (Dokumentation)
Das Paket passt in diesem Fall nicht wirklich, da es erwartet, dass die Vektoren (Chromosomen, wenn wir von dem Paket sprechen) in Form von reellen Zahlen mit Dezimalanteilen vorliegen.

Mein Vektor der Einstellparameterwerte besteht jedoch aus 14 Werten - Ganzzahlen und Zeichenfolgen.

Das Problem lĂ€sst sich natĂŒrlich leicht umgehen, indem bestimmten Zahlen den Zeichenfolgenwerten zugewiesen werden.

So sieht also schließlich der Hauptteil des R-Skripts so aus:

Aufruf 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 ist beendet", file=v_logfile, sep="n", append=T)
gam@solution

Hiermit wird mithilfe von lower und upper Attributen des Unterprogramms ga im Wesentlichen der Suchraum definiert, innerhalb dessen nach einem Vektor (oder Vektoren) gesucht wird, fĂŒr den das maximalste Fitness-Funktionswert erzielt wird.

Die ga-Unterroutine sucht, indem sie die Fitness-Funktion maximiert.

Das bedeutet, dass in diesem Fall die Fitness-Funktion, die den Vektor als eine Menge von Werten fĂŒr bestimmte Subdatenbanken versteht, eine Metrik von der Subdatenbank erhĂ€lt.

D.h.: wie viele Transaktionen pro Sekunde die Subdatenbank bei dieser Konfiguration der Subdatenbank und dieser Last auf der Subdatenbank verarbeitet.

Das bedeutet, dass innerhalb der Fitness-Funktion eine mehrschichtige Logik durchgefĂŒhrt werden muss:

  1. Verarbeitung des Eingangsvektors von Zahlen - Umwandlung in Werte fĂŒr die Parameter der Subdatenbank.
  2. Ein Versuch, eine bestimmte Anzahl von Redo-Gruppen mit einer bestimmten GrĂ¶ĂŸe zu erstellen. Dabei kann der Versuch fehlschlagen.
    Bereits vorhandene Journalgruppen in der Datenbank, in einer bestimmten Anzahl und einer bestimmten GrĂ¶ĂŸe, sollten zur Sauberkeit des Experiments entfernt werden.
  3. Bei Erfolg des vorherigen Punktes: Festlegung der Datenbankwerte fĂŒr die Konfigurationsparameter (erneut: es kann zu einem Fehler kommen)
  4. Bei Erfolg des vorherigen Punktes: Stoppen der Datenbank, Neustart der Datenbank, damit die neu festgelegten Parameterwerte wirksam werden. (erneut: es kann zu einem Fehler kommen)
  5. Bei Erfolg des vorherigen Punktes: DurchfĂŒhrung eines Lasttests. Metriken von der Datenbank erhalten.
  6. Datenbank in den Ursprungszustand zurĂŒckversetzen, d.h. zusĂ€tzliche Journalgruppen entfernen und die ursprĂŒngliche Konfiguration der Datenbank wiederherstellen.

Code der Fitness-Funktion

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 && 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 && 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="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," versucht, Vektor zu bewerten: ", 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 fehlgeschlagen",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," kann die Datenbank mit diesem Vektor von Einstellungen nicht starten",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," kann die Datenbank mit diesem Vektor von Einstellungen nicht starten",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("Ergebnis: ",v_metric," ",v_vector,sep="") , file=v_logfile, sep="n", append=T)
return (v_metric)
}

So wird die gesamte Arbeit im Fitness-Funktion durchgefĂŒhrt.

Die ga-Unterfunktion verarbeitet Vektoren oder, besser gesagt, Chromosomen.
Dabei ist uns besonders wichtig: die Selektion von Chromosomen mit solchen Genen, bei denen die Fitness-Funktion große Werte zurĂŒckgibt.

Das ist im Wesentlichen der Prozess der Suche nach dem optimalen Satz von Chromosomen als Vektor im N-dimensionalen Suchraum.

Sehr klar, detailliert ErlÀuterung, mit Beispielen von R-Code, der Arbeit des genetischen Algorithmus.

Ich möchte zwei technische Punkte gesondert erwÀhnen.

Hilfsaufrufe aus der Funktion evaluate, zum Beispiel Stop-Start, das Festlegen eines Wertes fĂŒr den Subd-Parameter, werden basierend auf cran-r der Funktion system2

Mit der verwendet wird: um ein bestimmtes Bash-Skript oder einen Befehl aufzurufen.

Zum Beispiel:

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

Der zweite Punkt ist die Zeile evaluate der Funktion, die den spezifischen Wert der Metrik und den entsprechenden Einstellungsvektor in eine Log-Datei speichert:

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

Das ist wichtig, denn aus diesem Datenarray kann man zusĂ€tzliche Informationen darĂŒber erhalten, welcher der Komponenten des Einstellungsvektors mehr oder weniger Einfluss auf den Wert der Metrik hat.

Das heißt: Es kann eine Analyse der Attribut-Bedeutung durchgefĂŒhrt werden.

Also, was könnte herauskommen.

In Form eines Diagramms, wenn man die Tests nach aufsteigender Metrik ordnet, sieht es so aus:

Die Methode des wissenschaftlichen Stocherns oder wie man Subdatenbanken mit Benchmarks und Optimierungsalgorithmen konfiguriert

Einige Daten, die den extremen Werten der Metrik entsprechen:
Die Methode des wissenschaftlichen Stocherns oder wie man Subdatenbanken mit Benchmarks und Optimierungsalgorithmen konfiguriert
Hier möchte ich im Screenshot mit den Ergebnissen klarstellen: Die Werte des Einstellungsvektors sind in den Begriffen des Codes der Fitness-Funktion angegeben, nicht in den Begriffen der Nummernliste der Parameter/Wertebereiche, die ich weiter oben im Text formuliert habe.

Nun, ist das viel oder wenig, ~8.000 tps: eine eigene Frage.
Im Rahmen der Laborarbeit ist diese Zahl nicht entscheidend, wichtig ist die Dynamik, wie sich dieser Wert verÀndert.

Die Dynamik hier ist gut.
Offensichtlich gibt es mindestens einen Faktor, der signifikant den Wert der Metrik beeinflusst, der den Vektor-Chromosomen durchsucht.
Angesichts der relativ dynamischen Werte der Kurve gibt es mindestens einen weiteren Faktor, der, obwohl er erheblich geringer ist, Einfluss hat.

Hier benötigt man attribute-importance eine Analyse, um zu verstehen, welche Attribute (nun, in diesem Fall — Komponenten des Einstellvektors) und wie stark sie den Wert der Metrik beeinflussen.
Von dieser Information aus: verstehen, welche Faktoren durch VerĂ€nderungen der bedeutenden Attribute berĂŒhrt wurden.

AusfĂŒhren attribute-importance kann auf verschiedene Weisen erfolgen.

FĂŒr diese Zwecke gefĂ€llt mir der Algorithmus randomForest des gleichnamigen R-Pakets (Dokumentation)
randomForest, soweit ich seine Funktionsweise allgemein und sein Verfahren zur Bewertung der Bedeutung von Attributen im Besonderen verstehe, erstellt er ein Modell der AbhÀngigkeit der Antwortvariablen von den Attributen.

In unserem Fall ist die Antwortvariable die Metrik, die von der Datenbank in Belastungstests erhalten wird: tps;
Und die Attribute sind die Komponenten des Einstellvektors.

So bewertet er randomForest die Wichtigkeit jedes Attributs im Modell mit zwei Zahlen: %IncMSE — wie das Vorhandensein/Fehlen dieses Attributs im Modell die MSE-QualitĂ€t dieses Modells (Mean Squared Error) Ă€ndert;

Und IncNodePurity — das ist eine Zahl, die zeigt, wie gut man den Datensatz mit Beobachtungen nach den Werten dieses Attributs trennen kann, so dass auf der einen Seite Daten mit einem bestimmten Wert der erklĂ€rten Metrik und auf der anderen mit einem anderen Wert der Metrik landen.
Nun, d.h.: wie klassifizierend dieses Attribut ist (die klarste, russischsprachige ErklÀrung zu random forest habe ich gesehen. hier).

Arbeitsbauern-R-Code, um einen Datensatz mit den Ergebnissen von Belastungstests zu bearbeiten:

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

Man kann die Hyperparameter des Algorithmus direkt manuell anpassen und, basierend auf der QualitÀt des Modells, ein genaueres Modell auswÀhlen, das Vorhersagen im Validierungsdatensatz macht.
Man kann auch eine Funktion fĂŒr diese Arbeit schreiben (ĂŒbrigens — wieder auf einem Optimierungsalgorithmus basierend).

Man kann das R-Paket caret, das ist nicht wichtig.

Letztendlich ergibt sich in diesem Fall folgendes Ergebnis zur Bewertung der Wichtigkeit der Attribute:

Die Methode des wissenschaftlichen Stocherns oder wie man Subdatenbanken mit Benchmarks und Optimierungsalgorithmen konfiguriert

Nun, somit kann man zu globalen Überlegungen ĂŒbergehen:

  1. Es stellt sich heraus, dass der bedeutendste Parameter unter den gegebenen Testbedingungen die commit_wait
    Technisch legt er den AusfĂŒhrungsmodus der IO-Operation zum Schreiben von Redo-Daten aus dem Log-Puffer der DB in die aktuelle Journierungsgruppe fest: synchron oder asynchron.
    Wert nowait , bei dem ein praktisch vertikaler, mehrfacher Anstieg der TPS-Metrik erzielt wird: dies ist die Aktivierung des asynchronen IO-Modus in den Redo-Gruppen.
    Eine separate Frage ist, ob man dies in der Produktionsdatenbank so machen sollte oder nicht. Ich beschrÀnke mich hier auf die Feststellung: Das ist ein bedeutender Faktor.
  2. Es ist logisch, dass die GrĂ¶ĂŸe des Log-Puffers der DB ein bedeutender Faktor ist.
    Je kleiner die GrĂ¶ĂŸe des Log-Puffers, desto geringer ist seine PufferkapazitĂ€t, desto hĂ€ufiger kommt es zu ÜberlĂ€ufen und/oder dazu, dass man keinen freien Bereich fĂŒr eine Charge neuer Redo-Daten bereitstellen kann.
    Das bedeutet: Verzögerungen, die mit der Zuweisung von Speicherplatz im Log-Puffer und/oder dem ZurĂŒckschreiben von Redo-Daten aus ihm in die Redo-Gruppen verbunden sind.
    Diese Verzögerungen sollten natĂŒrlich die DurchsatzkapazitĂ€t der DB hinsichtlich Transaktionen beeinflussen und tun dies auch.
  3. Parameter db_block_checksum: Nun, das ist auch grundsĂ€tzlich verstĂ€ndlich - die Verarbeitung von Transaktionen fĂŒhrt zur Bildung von Dirty-Blöcken im Puffer-Cache der DB.
    Die, bei aktivierter ÜberprĂŒfung der PrĂŒfziffern der Datenblöcke, von der DB verarbeitet werden mĂŒssen – diese PrĂŒfziffern mĂŒssen aus dem Body des Datenblocks berechnet und mit dem verglichen werden, was im Header des Datenblocks steht: ĂŒbereinstimmt/nicht ĂŒbereinstimmt.
    Eine solche Arbeit kann wiederum nicht die Datenverarbeitung verlangsamen, und folglich werden der Parameter und der Mechanismus, den dieser Parameter festlegt, bedeutend.
    Aus diesem Grund schlĂ€gt der Anbieter in der Dokumentation zu diesem Parameter verschiedene Werte vor und merkt an, dass es einen Einfluss geben wird, aber Sie können zwischen verschiedenen Werten, bis hin zu „deaktiviert“, mit unterschiedlichem Einfluss wĂ€hlen.

Nun und die globale Schlussfolgerung.

Der Ansatz erweist sich insgesamt als durchaus funktional.

Er ermöglicht es, in den frĂŒhen Phasen von Lasttests eines Service-Systems die optimale Konfiguration des Systems unter Last auszuwĂ€hlen, ohne sich allzu sehr mit den Besonderheiten der Systemkonfiguration unter Last zu befassen.

Es schließt jedoch nichts aus – zumindest auf der Ebene des VerstĂ€ndnisses: "RegelknĂ€ufen" und den zulĂ€ssigen Drehbereichen dieser KnĂ€ufe muss das System kennen.

Der Ansatz kann relativ schnell die optimale Konfiguration des Systems finden.
Nach den Tests kann man Informationen ĂŒber die Natur der Beziehung zwischen der QualitĂ€t der Systemleistung und den Einstellungen des Systems erhalten.

Das sollte natĂŒrlich dazu beitragen, dieses tiefere VerstĂ€ndnis des Systems und seiner Funktionsweise zu fördern, zumindest unter dieser Last.

Praktisch heißt das: den Aufwandscharakter fĂŒr das VerstĂ€ndnis des anpassbaren Systems im Vergleich zu den Kosten fĂŒr die Vorbereitung solcher Systemtests zu ĂŒberdenken.

Ich möchte hervorheben, dass in diesem Ansatz die Angemessenheit der Systemtests unter den Bedingungen, unter denen es im Produktivbetrieb arbeiten wird, von entscheidender Bedeutung ist.

Danke fĂŒr Ihre Aufmerksamkeit und Ihre Zeit.

Quelle: habr.com

60GB SSD 8Gb DDR4