Die wissenschaftliche Poke-Methode oder wie man eine Datenbankkonfiguration mithilfe von Benchmarks und einem Optimierungsalgorithmus auswählt

Guten Tag.

Ich beschloss, meinen Fund zu teilen – das Ergebnis von Gedanken, Versuchen und Irrtümern.
Im Großen und Ganzen: Dies ist natürlich kein Fund – all dies sollte denjenigen, die sich mit der angewandten statistischen Datenverarbeitung und Optimierung von Systemen befassen, nicht unbedingt speziell des DBMS, schon lange bekannt sein.
Und: Ja, sie wissen es, sie schreiben interessante Artikel über ihre Forschung, Beispiel (UPD.: In den Kommentaren wurde auf ein sehr interessantes Projekt hingewiesen: Ottertune )
Andererseits: Ich sehe ohne weiteres keine flächendeckende Erwähnung oder Verbreitung dieses Ansatzes im Internet bei IT-Spezialisten, DBA.

Also, auf den Punkt gebracht.

Nehmen wir an, wir haben eine Aufgabe: ein bestimmtes Servicesystem einzurichten, um eine Art Arbeit zu bedienen.

Über diese Arbeit ist bekannt: Was ist sie, wie wird die Qualität dieser Arbeit gemessen und nach welchem ​​Kriterium wird diese Qualität gemessen?

Nehmen wir außerdem an, dass mehr oder weniger bekannt und verstanden ist: Wie genau in (oder mit) diesem Dienstleistungssystem gearbeitet wird.

„Mehr oder weniger“ – das bedeutet, dass es möglich ist, ein bestimmtes Werkzeug, Dienstprogramm oder einen bestimmten Dienst vorzubereiten (oder von irgendwoher zu beziehen), der synthetisiert und auf das System angewendet werden kann, mit einer Testlast, die ausreichend für das ist, was in der Produktion sein wird. unter Bedingungen, die für die Arbeit in der Produktion ausreichend sind.

Nehmen wir an, dass für dieses Servicesystem eine Reihe von Anpassungsparametern bekannt sind, mit denen dieses System im Hinblick auf die Produktivität seiner Arbeit konfiguriert werden kann.

Und was ist das Problem? Es gibt kein ausreichend umfassendes Verständnis dieses Servicesystems, das es Ihnen ermöglicht, die Einstellungen dieses Systems für die zukünftige Auslastung auf einer bestimmten Plattform fachmännisch zu konfigurieren und die erforderliche Produktivität des Systems zu erreichen.

Also. Dies ist fast immer der Fall.

Was kann man hier machen?

Nun, das erste, was mir in den Sinn kommt, ist, einen Blick auf die Dokumentation für dieses System zu werfen. Verstehen Sie, welche akzeptablen Bereiche für die Werte der Anpassungsparameter gelten. Und wählen Sie beispielsweise mithilfe der Koordinatenabstiegsmethode Werte für Systemparameter in Tests aus.

Diese. Geben Sie dem System eine Art Konfiguration in Form eines bestimmten Wertesatzes für seine Konfigurationsparameter.

Wenden Sie eine Testlast darauf an, indem Sie genau dieses Tool-Dienstprogramm, den Lastgenerator, verwenden.
Und schauen Sie sich den Wert an – die Reaktion oder ein Maß für die Qualität des Systems.

Der zweite Gedanke könnte die Schlussfolgerung sein, dass dies eine sehr lange Zeit ist.

Nun, das heißt: Wenn es viele Einstellungsparameter gibt, wenn die Bereiche ihrer Werte, die ausgeführt werden, groß sind, wenn jeder einzelne Lasttest viel Zeit in Anspruch nimmt, dann: Ja, das alles kann inakzeptabel lange dauern lange Zeit.

Nun, hier ist, was Sie verstehen und sich merken können.

Sie können herausfinden, dass es in der Wertemenge der Servicesystem-Einstellungsparameter einen Vektor als Folge einiger Werte gibt.

Jeder dieser Vektoren entspricht bei sonst gleichen Bedingungen (da er von diesem Vektor nicht beeinflusst wird) einem völlig bestimmten Wert der Metrik – einem Indikator für die Qualität des Systembetriebs unter Testlast.

Das heißt,

Bezeichnen wir den Systemkonfigurationsvektor als Die wissenschaftliche Poke-Methode oder wie man eine Datenbankkonfiguration mithilfe von Benchmarks und einem Optimierungsalgorithmus auswähltWo Die wissenschaftliche Poke-Methode oder wie man eine Datenbankkonfiguration mithilfe von Benchmarks und einem Optimierungsalgorithmus auswählt; Wo Die wissenschaftliche Poke-Methode oder wie man eine Datenbankkonfiguration mithilfe von Benchmarks und einem Optimierungsalgorithmus auswählt — Anzahl der Systemkonfigurationsparameter, wie viele dieser Parameter es gibt.

Und der Wert der entsprechenden Metrik Die wissenschaftliche Poke-Methode oder wie man eine Datenbankkonfiguration mithilfe von Benchmarks und einem Optimierungsalgorithmus auswählt bezeichnen wir es als
Die wissenschaftliche Poke-Methode oder wie man eine Datenbankkonfiguration mithilfe von Benchmarks und einem Optimierungsalgorithmus auswählt, dann erhalten wir eine Funktion: Die wissenschaftliche Poke-Methode oder wie man eine Datenbankkonfiguration mithilfe von Benchmarks und einem Optimierungsalgorithmus auswählt

Nun ja: Alles läuft in meinem Fall sofort auf: aus meiner Studienzeit fast vergessene Algorithmen zur Suche nach dem Extremum einer Funktion hinaus.

Okay, aber hier stellt sich eine organisatorische und angewandte Frage: Welcher Algorithmus soll verwendet werden?

  1. In dem Sinne – damit Sie weniger mit Ihren Händen programmieren müssen.
  2. Und damit es funktioniert, d.h. Habe das Extremum gefunden (falls es eines gibt), zumindest schneller als der Koordinatenabstieg.

Der erste Punkt weist darauf hin, dass wir uns nach einigen Umgebungen umsehen müssen, in denen solche Algorithmen bereits implementiert wurden und in irgendeiner Form für die Verwendung im Code bereit sind.
Nun Ich weiss python и cran-r

Der zweite Punkt bedeutet, dass Sie sich über die Algorithmen selbst informieren müssen, was sie sind, welche Anforderungen sie haben und welche Merkmale sie haben.

Und was sie liefern, können nützliche Nebenwirkungen sein – Ergebnisse oder direkt vom Algorithmus selbst.

Oder sie können aus den Ergebnissen des Algorithmus gewonnen werden.

Viel hängt von den Eingabebedingungen ab.

Wenn Sie beispielsweise aus irgendeinem Grund schneller zu einem Ergebnis gelangen müssen, müssen Sie einen Blick auf Gradientenabstiegsalgorithmen werfen und einen davon auswählen.

Oder wenn die Zeit keine so große Rolle spielt, können Sie beispielsweise stochastische Optimierungsverfahren wie einen genetischen Algorithmus verwenden.

Ich schlage vor, die Arbeit dieses Ansatzes, die Auswahl der Systemkonfiguration unter Verwendung eines genetischen Algorithmus, sozusagen im nächsten Schritt zu betrachten: Laborarbeit.

Quelle:

  1. Als Dienstleistungssystem soll es sein: oracle xe 18c
  2. Lassen Sie es der Transaktionsaktivität und dem Ziel dienen: den höchstmöglichen Durchsatz der Unterdatenbank in Transaktionen/Sekunde zu erreichen.
  3. Transaktionen können in der Art der Arbeit mit Daten und im Arbeitskontext sehr unterschiedlich sein.
    Lassen Sie uns zustimmen, dass es sich hierbei um Transaktionen handelt, die keine große Menge tabellarischer Daten verarbeiten.
    In dem Sinne, dass sie nicht mehr Undo-Daten als Redo-Daten generieren und keine großen Prozentsätze von Zeilen und großen Tabellen verarbeiten.

Hierbei handelt es sich um Transaktionen, die eine Zeile in einer mehr oder weniger großen Tabelle mit einer kleinen Anzahl von Indizes in dieser Tabelle ändern.

In dieser Situation: Die Produktivität der Subdatenbank zur Verarbeitung von Transaktionen wird unter Vorbehalt durch die Qualität der Verarbeitung durch die Redox-Datenbank bestimmt.

Haftungsausschluss – wenn wir speziell über die Subdb-Einstellungen sprechen.

Denn im Allgemeinen kann es aufgrund der Gestaltung der Benutzerarbeit mit tabellarischen Daten und/oder des tabellarischen Modells beispielsweise zu Transaktionssperren zwischen SQL-Sitzungen kommen.

Was sich natürlich deprimierend auf die TPS-Metrik auswirkt und im Verhältnis zur Unterdatenbank ein exogener Faktor ist: Nun ja, so wurde das tabellarische Modell entworfen und die Arbeit mit den darin enthaltenen Daten führt dazu, dass Blockaden auftreten.

Aus Gründen der Reinheit des Experiments werden wir diesen Faktor daher ausschließen, und im Folgenden werde ich genau erläutern, wie.

  1. Nehmen wir der Sicherheit halber an, dass 100 % der an die Datenbank übermittelten SQL-Befehle DML-Befehle sind.
    Lassen Sie die Eigenschaften des Benutzers, der mit der Unterdatenbank arbeitet, in Tests gleich sein.
    Nämlich: die Anzahl der SKL-Sitzungen, tabellarische Daten und wie SKL-Sitzungen damit funktionieren.
  2. Subd funktioniert FORCE LOGGING, ARCHIVELOG Modifikationen. Der Flashback-Datenbankmodus ist auf der Subd-Ebene deaktiviert.
  3. Redo-Protokolle: befinden sich in einem separaten Dateisystem, auf einer separaten „Festplatte“;
    Der Rest der physischen Komponente der Datenbank: in einem anderen, separaten Dateisystem, auf einer separaten „Festplatte“:

Weitere Details zum physischen Gerät. Komponenten der Labordatenbank

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

Unter diesen Lastbedingungen wollte ich zunächst die Transaktion subd verwenden SLOB-Dienstprogramm
Es hat so eine wunderbare Funktion, ich zitiere den Autor:

Das Herzstück von SLOB ist die „SLOB-Methode“. Die SLOB-Methode zielt darauf ab, Plattformen zu testen
ohne Anwendungskonflikt. Man kann nicht die maximale Hardwareleistung erreichen
Verwendung von Anwendungscode, der beispielsweise durch Anwendungssperren oder sogar gebunden ist
Teilen von Oracle-Datenbankblöcken. Das ist richtig – beim Datenaustausch entsteht ein Mehraufwand
in Datenblöcken! Aber SLOB ist – in seiner Standardbereitstellung – gegen solche Auseinandersetzungen immun.

Diese Erklärung: entspricht, es ist.
Es ist praktisch, den Grad der Parallelität von CL-Sitzungen zu regulieren, das ist der Schlüssel -t Starten Sie das Dienstprogramm runit.sh von SLOB
Der Prozentsatz der DML-Befehle wird durch die Anzahl der Textnachrichten, die an den Subd gesendet werden, pro Textsitzung, Parameter reguliert UPDATE_PCT
Separat und ganz bequem: SLOB selbst, vor und nach der Ladesitzung – bereitet ein Statistikpaket oder AWR-Snapshots vor (was zur Vorbereitung eingestellt ist).

Es stellte sich jedoch heraus, dass SLOB unterstützt keine SQL-Sitzungen mit einer Dauer von weniger als 30 Sekunden.
Deshalb habe ich zunächst meine eigene Arbeiter-Bauern-Version des Laders programmiert, und dann blieb er in Betrieb.

Zur Verdeutlichung möchte ich klarstellen, was der Loader tut und wie er es tut.
Im Wesentlichen sieht der Loader so aus:

Arbeitercode

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

Arbeiter werden auf diese Weise gestartet:

Laufende Arbeiter

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

Und Tische für Arbeiter 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<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"

Diese. Für jeden Worker (praktisch: eine eigene SQL-Sitzung in der DB) wird eine eigene Tabelle erstellt, mit der der Worker arbeitet.

Dadurch wird sichergestellt, dass es zwischen Worker-Sitzungen keine Transaktionssperren gibt.
Jeder Arbeiter: macht das Gleiche, mit seinem eigenen Tisch, die Tische sind alle gleich.
Alle Arbeitnehmer verrichten ihre Arbeit gleich lange.
Darüber hinaus so lange, dass beispielsweise ein Protokollwechsel auf jeden Fall stattfinden würde, und zwar mehr als einmal.
Nun, dementsprechend entstanden auch damit verbundene Kosten und Auswirkungen.
In meinem Fall habe ich die Arbeitsdauer der Arbeiter auf 8 Minuten konfiguriert.

Ein Teil eines Statspack-Berichts, der den Betrieb des Subds unter Last beschreibt

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

Rückkehr zur Laborarbeit.
Unter sonst gleichen Bedingungen werden wir die Werte der folgenden Parameter der Labor-Subdatenbank variieren:

  1. Größe der Datenbankprotokollgruppen. Wertebereich: [32, 1024] MB;
  2. Anzahl der Journalgruppen in der Datenbank. Wertebereich: [2,32];
  3. log_archive_max_processes Wertebereich: [1,8];
  4. commit_logging zwei Werte sind erlaubt: batch|immediate;
  5. commit_wait zwei Werte sind 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 zwei Werte sind 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;

Eine Person mit Erfahrung in der Pflege von Oracle-Datenbanken kann sicherlich bereits sagen, was und welche Werte aus den angegebenen Parametern und ihren akzeptablen Werten gesetzt werden sollten, um eine höhere Produktivität der Datenbank für die Arbeit mit den angegebenen Daten zu erzielen den Anwendungscode, hier oben.

Aber.

Ziel der Laborarbeit ist es zu zeigen, dass der Optimierungsalgorithmus selbst dies relativ schnell für uns klären wird.

Für uns bleibt nur noch ein Blick in das Dokument über das anpassbare System, gerade genug, um herauszufinden, welche Parameter in welchen Bereichen geändert werden müssen.
Und außerdem: Codieren Sie den Code, der für die Arbeit mit dem benutzerdefinierten System des ausgewählten Optimierungsalgorithmus verwendet wird.

So, nun zum Code.
Ich habe oben darüber gesprochen cran-r, d.h.: alle Manipulationen mit dem angepassten System werden in Form eines R-Skripts orchestriert.

Die eigentliche Aufgabe, Analyse, Auswahl nach Metrikwert, Systemzustandsvektoren: Das ist ein Paket GA (Dokumentation)
Das Paket ist in diesem Fall nicht sehr geeignet, da es erwartet, dass Vektoren (Chromosomen, wenn es um das Paket geht) in Form von Zahlenfolgen mit einem Bruchteil angegeben werden.

Und mein Vektor aus den Werten der Einstellparameter: Das sind 14 Größen – Ganzzahlen und Stringwerte.

Das Problem lässt sich natürlich leicht vermeiden, indem man den String-Werten bestimmte Zahlen zuweist.

Somit sieht der Hauptteil des R-Skripts am Ende so aus:

Rufen Sie GA::ga auf

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

Hier, mit der Hilfe lower и upper Unterprogrammattribute ga Im Wesentlichen wird ein Bereich des Suchraums angegeben, innerhalb dessen nach einem solchen Vektor (oder Vektoren) gesucht wird, für den der Maximalwert der Fitnessfunktion ermittelt wird.

Die GA-Unterroutine führt eine Suche durch, um die Fitnessfunktion zu maximieren.

Nun stellt sich heraus, dass es in diesem Fall notwendig ist, dass die Fitnessfunktion, die den Vektor als eine Reihe von Werten für bestimmte Parameter des Subd versteht, eine Metrik vom Subd erhält.

Das heißt: wie viele, bei einem bestimmten Subd-Setup und einer bestimmten Auslastung des Subds: Der Subd verarbeitet Transaktionen pro Sekunde.

Das heißt, beim Entfalten muss innerhalb der Fitnessfunktion folgender Mehrschritt durchgeführt werden:

  1. Verarbeiten des Eingabevektors von Zahlen – Konvertieren in Werte für die Unterdatenparameter.
  2. Ein Versuch, eine bestimmte Anzahl von Redo-Gruppen einer bestimmten Größe zu erstellen. Darüber hinaus kann der Versuch erfolglos sein.
    Zeitschriftengruppen, die im Subd bereits in gewisser Menge und Größe existierten, für die Reinheit des Experiments – d.b. gelöscht.
  3. Wenn der vorherige Punkt erfolgreich ist: Geben Sie die Werte der Konfigurationsparameter an die Datenbank an (erneut: Möglicherweise liegt ein Fehler vor)
  4. Wenn der vorherige Schritt erfolgreich ist: Stoppen des Subd, Starten des Subd, damit die neu angegebenen Parameterwerte wirksam werden. (nochmals: Möglicherweise liegt ein Fehler vor)
  5. Wenn der vorherige Schritt erfolgreich ist: Führen Sie einen Belastungstest durch. Holen Sie sich Metriken von subd.
  6. Bringen Sie den Subd in seinen ursprünglichen Zustand zurück, d. h. Löschen Sie zusätzliche Protokollgruppen und stellen Sie sicher, dass die ursprüngliche Unterdatenbankkonfiguration wieder funktioniert.

Fitness-Funktionscode

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

Das. Alle Arbeiten: werden in der Fitnessfunktion ausgeführt.

Die ga-Subroutine verarbeitet Vektoren, oder genauer gesagt, Chromosomen.
Dabei ist für uns die Auswahl von Chromosomen mit Genen, für die die Fitnessfunktion große Werte liefert, am wichtigsten.

Dies ist im Wesentlichen der Prozess der Suche nach dem optimalen Chromosomensatz mithilfe eines Vektors in einem N-dimensionalen Suchraum.

Sehr klar, detailliert eine Erklärung, mit Beispielen für R-Code, die Arbeit eines genetischen Algorithmus.

Zwei technische Punkte möchte ich gesondert erwähnen.

Hilfsaufrufe der Funktion evaluate, zum Beispiel Stop-Start, Festlegen des Werts des Subd-Parameters, werden basierend auf ausgeführt cran-r Funktionen system2

Mit dessen Hilfe wird ein Bash-Skript oder ein Bash-Befehl aufgerufen.

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 Linie, evaluate Funktionen, mit dem Speichern eines bestimmten Metrikwerts und seines entsprechenden Optimierungsvektors in einer Protokolldatei:

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

Dies ist wichtig, da aus diesem Datenarray zusätzliche Informationen darüber gewonnen werden können, welche der Komponenten des Abstimmungsvektors einen größeren oder geringeren Einfluss auf den metrischen Wert haben.

Das heißt: Es wird möglich sein, eine Attribut-Wichtigkeitsanalyse durchzuführen.

Was kann also passieren?

Wenn Sie die Tests in Diagrammform in aufsteigender metrischer Reihenfolge anordnen, sieht das Bild wie folgt aus:

Die wissenschaftliche Poke-Methode oder wie man eine Datenbankkonfiguration mithilfe von Benchmarks und einem Optimierungsalgorithmus auswählt

Einige Daten, die den Extremwerten der Metrik entsprechen:
Die wissenschaftliche Poke-Methode oder wie man eine Datenbankkonfiguration mithilfe von Benchmarks und einem Optimierungsalgorithmus auswählt
Hier, im Screenshot mit den Ergebnissen, möchte ich klarstellen: Die Werte des Tuning-Vektors werden in Form des Fitness-Funktionscodes angegeben, nicht in Form der Zahlenliste von Parametern/Bereichen von Parameterwerten, die formuliert wurde oben im Text.

Also. Ist es viel oder wenig, etwa 8 TPS: eine separate Frage.
Im Rahmen der Laborarbeit ist diese Zahl nicht wichtig, wichtig ist die Dynamik, wie sich dieser Wert verändert.

Die Dynamik hier ist gut.
Es ist offensichtlich, dass mindestens ein Faktor den Wert der Metrik, des Ga-Algorithmus, der die Chromosomenvektoren sortiert, maßgeblich beeinflusst: abgedeckt.
Gemessen an der recht starken Dynamik der Kurvenwerte gibt es noch mindestens einen weiteren Faktor, der zwar deutlich kleiner ist, aber einen Einfluss hat.

Hier brauchen Sie es attribute-importance Analyse, um zu verstehen, welche Attribute (in diesem Fall Komponenten des Abstimmungsvektors) und wie stark sie den metrischen Wert beeinflussen.
Und anhand dieser Informationen: Verstehen Sie, welche Faktoren von Änderungen wichtiger Attribute betroffen waren.

Run attribute-importance auf unterschiedliche Weise möglich.

Für diese Zwecke gefällt mir der Algorithmus randomForest R-Paket mit dem gleichen Namen (Dokumentation)
randomForestSo wie ich seine Arbeit im Allgemeinen und seinen Ansatz zur Bewertung der Wichtigkeit von Attributen im Besonderen verstehe, baut er ein bestimmtes Modell der Abhängigkeit der Antwortvariablen von den Attributen auf.

In unserem Fall ist die Antwortvariable eine Metrik, die in Lasttests aus der Datenbank gewonnen wird: tps;
Und Attribute sind Komponenten des Tuning-Vektors.

Also randomForest bewertet die Wichtigkeit jedes Modellattributs mit zwei Zahlen: %IncMSE – wie das Vorhandensein/Fehlen dieses Attributs in einem Modell die MSE-Qualität dieses Modells verändert (mittlerer quadratischer Fehler);

Und IncNodePurity ist eine Zahl, die angibt, wie gut ein Datensatz mit Beobachtungen basierend auf den Werten dieses Attributs unterteilt werden kann, sodass in einem Teil Daten mit einem Wert der zu erklärenden Metrik vorhanden sind und im anderen Teil mit ein anderer Wert der Metrik.
Nun, das heißt: Inwieweit ist dies ein klassifizierendes Attribut (die klarste Erklärung in russischer Sprache habe ich auf RandomForest gesehen). hier).

Arbeiter-Bauern-R-Code zur Verarbeitung eines Datensatzes mit den Ergebnissen von Belastungstests:

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

Sie können die Hyperparameter des Algorithmus direkt mit Ihren Händen auswählen und unter Berücksichtigung der Qualität des Modells ein Modell auswählen, das die Vorhersagen des Validierungsdatensatzes genauer erfüllt.
Sie können für diese Arbeit eine Art Funktion schreiben (übrigens wiederum unter Verwendung einer Art Optimierungsalgorithmus).

Sie können das R-Paket verwenden caret, nicht der Punkt ist wichtig.

Als Ergebnis erhält man in diesem Fall folgendes Ergebnis zur Beurteilung des Wichtigkeitsgrades der Attribute:

Die wissenschaftliche Poke-Methode oder wie man eine Datenbankkonfiguration mithilfe von Benchmarks und einem Optimierungsalgorithmus auswählt

Also. So können wir mit der globalen Reflexion beginnen:

  1. Es stellte sich heraus, dass unter diesen Testbedingungen der Parameter der wichtigste war commit_wait
    Technisch gesehen gibt es den Ausführungsmodus des IO-Vorgangs zum Schreiben von Redo-Daten aus dem Subdb-Protokollpuffer in die aktuelle Protokollgruppe an: synchron oder asynchron.
    Wert nowait was zu einem nahezu vertikalen, mehrfachen Anstieg des Wertes der TPS-Metrik führt: Dies ist die Einbeziehung des asynchronen IO-Modus in Redo-Gruppen.
    Eine andere Frage ist, ob Sie dies in einer Lebensmitteldatenbank tun sollten oder nicht. Hier beschränke ich mich auf die Feststellung: Das ist ein wesentlicher Faktor.
  2. Es ist logisch, dass die Größe des Protokollpuffers des Subd: ein wesentlicher Faktor ist.
    Je kleiner der Protokollpuffer ist, desto geringer ist seine Pufferkapazität, desto häufiger kommt es zu Überläufen und/oder desto unfähiger ist es, darin einen freien Bereich für einen Teil neuer Redoxdaten zuzuweisen.
    Dies bedeutet: Verzögerungen im Zusammenhang mit der Zuweisung von Speicherplatz im Protokollpuffer und/oder dem Ablegen von Redo-Daten daraus in Redo-Gruppen.
    Diese Verzögerungen sollten sich natürlich auf den Durchsatz der Datenbank für Transaktionen auswirken und tun dies auch.
  3. Parameter db_block_checksum: Nun, im Allgemeinen ist es auch klar: Die Transaktionsverarbeitung führt zur Bildung von Darty-Blöcken im Puffercache der Unterdatenbank.
    Was die Datenbank verarbeiten muss, wenn die Überprüfung von Prüfsummen von Datenblöcken aktiviert ist: Berechnen Sie diese Prüfsummen aus dem Hauptteil des Datenblocks und überprüfen Sie sie mit dem, was im Datenblock-Header steht: stimmt überein/stimmt nicht überein.
    Auch diese Arbeit kann die Datenverarbeitung nur verzögern, und dementsprechend erweisen sich der Parameter und der Mechanismus, der diesen Parameter festlegt, als bedeutsam.
    Aus diesem Grund bietet der Anbieter in der Dokumentation für diesen Parameter unterschiedliche Werte dafür (den Parameter) an und weist darauf hin, dass es zwar Auswirkungen geben wird, Sie aber unterschiedliche Werte wählen können, bis hin zu „aus“ und unterschiedliche Auswirkungen.

Nun, eine globale Schlussfolgerung.

Der Ansatz erweist sich im Allgemeinen als recht erfolgreich.

Er erlaubt sich durchaus, in den frühen Phasen des Lasttests eines bestimmten Dienstsystems, um seine (system-)optimale Konfiguration für die Last auszuwählen, nicht zu sehr auf die Besonderheiten der Einrichtung des Systems für die Last einzugehen.

Aber es schließt es nicht völlig aus – zumindest auf der Ebene des Verständnisses: Das System muss über die „Verstellknöpfe“ und die zulässigen Drehbereiche dieser Knöpfe Bescheid wissen.

Der Ansatz kann dann relativ schnell die optimale Systemkonfiguration finden.
Und basierend auf den Testergebnissen ist es möglich, Informationen über die Art der Beziehung zwischen den Systemleistungsmetriken und den Werten der Systemeinstellungsparameter zu erhalten.

Was natürlich dazu beitragen sollte, dass dieses sehr tiefe Verständnis des Systems und seiner Funktionsweise entsteht, zumindest unter einer bestimmten Last.

In der Praxis handelt es sich dabei um einen Austausch der Kosten für das Verständnis des angepassten Systems gegen die Kosten für die Vorbereitung solcher Tests des Systems.

Ich möchte gesondert darauf hinweisen: Bei diesem Ansatz ist der Grad der Eignung der Systemtests für die Betriebsbedingungen, die im kommerziellen Betrieb herrschen werden, von entscheidender Bedeutung.

Vielen Dank für Ihre Aufmerksamkeit und Zeit.

Source: habr.com

Kommentar hinzufügen