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, (UPD.: In den Kommentaren wurde auf ein sehr interessantes Projekt hingewiesen: )
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 
; Wo
â die Anzahl der Konfigurationsparameter des Systems ist, wie viele es gibt, diese Parameter.
Und der Metrikwert, der diesem
entspricht, wird bezeichnet als
, also ergibt sich fĂŒr uns eine Funktion: 
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?
- Im Sinne von â um selbst weniger hĂ€ndisch zu codieren.
- 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:
- Lass es eine Servicelösung geben:
oracle xe 18c - Lass sie die TransaktionsaktivitÀt verwalten und das Ziel: eine möglichst hohe DurchsatzkapazitÀt der Datenbank pro Transaktionen/Sekunde zu erreichen.
- 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.
- 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. - Die Datenbank arbeitet in
FORCE LOGGING,ARCHIVELOGModi. Der Flashback-Datenbankmodus ist auf Ebene der Datenbank deaktiviert. - 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 0UrsprĂŒnglich wollte ich unter diesen Lastbedingungen eine Transaktionsdatenbank verwenden.
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 dotxWorker 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"
waitDie 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.1Kehren wir zur Formulierung der Laborarbeit zurĂŒck.
Wir werden, bei gleichen Bedingungen, die Werte solcher Parameter der Labor-DB variieren:
- GröĂe der Datenbank-Protokollgruppen. Wertebereich: [32, 1024] MB;
- Anzahl der Datenbank-Protokollgruppen. Wertebereich: [2, 32];
log_archive_max_processesWertebereich: [1, 8];commit_logginges sind zwei Werte erlaubt:batch|immediate;commit_waites sind zwei Werte erlaubt:wait|nowait;log_bufferWertebereich: [2, 128] MB.log_checkpoint_timeoutWertebereich: [60, 1200] Sekundendb_writer_processesWertebereich: [1, 4]undo_retentionWertebereich: [30, 300] Sekundentransactions_per_rollback_segmentWertebereich: [1, 8]disk_asynch_ioes sind zwei Werte erlaubt:true|false;filesystemio_optionsFolgende Werte sind erlaubt:none|setall|directIO|asynch;db_block_checkingFolgende Werte sind erlaubt:OFF|LOW|MEDIUM|FULL;db_block_checksumFolgende 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 ()
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@solutionHiermit 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:
- Verarbeitung des Eingangsvektors von Zahlen - Umwandlung in Werte fĂŒr die Parameter der Subdatenbank.
- 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. - Bei Erfolg des vorherigen Punktes: Festlegung der Datenbankwerte fĂŒr die Konfigurationsparameter (erneut: es kann zu einem Fehler kommen)
- 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)
- Bei Erfolg des vorherigen Punktes: DurchfĂŒhrung eines Lasttests. Metriken von der Datenbank erhalten.
- 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 , 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:

Einige Daten, die den extremen Werten der Metrik entsprechen:

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 ()
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. ).
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$importanceMan 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:

Nun, somit kann man zu globalen Ăberlegungen ĂŒbergehen:
- 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.
Wertnowait, 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. - 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. - 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
