Cześć.
Postanowiłem podzielić się swoim odkryciem — owocem przemyśleń, prób i błędów.
Jak na to spojrzeć: to wcale nie jest odkrycie, oczywiście — wszystko to powinno być od dawna znane tym, którzy zajmują się aplikacyjną obróbką danych i optymalizacją różnych systemów, niekoniecznie baz danych.
I: tak, wiedzą, piszą ciekawe artykuły na temat swoich badań, (UPD.: w komentarzach wskazano na bardzo interesujący projekt: )
Z drugiej strony: na szybko nie dostrzegam szerokiego wspomnienia ani rozpowszechnienia tego podejścia w Internecie wśród specjalistów IT i DBA.
I tak, przejdźmy do sedna.
Załóżmy, że mamy zadanie: skonfigurować pewien system usługowy do obsługi jakiejś pracy.
O tej pracy wiadomo: jaka ona jest, w jaki sposób mierzy się jakość tej pracy oraz jakie są kryteria pomiaru tej jakości.
Załóżmy również, że mniej więcej wiadomo, jak dokładnie wykonywana jest praca w (lub z) tym systemem usługowym.
"Mniej więcej" — oznacza to, że istnieje możliwość przygotowania (lub skądś wzięcia) pewnego narzędzia, które można by wykorzystać do wygenerowania i dostarczenia na system testowego obciążenia, które jest w miarę adekwatne do tego, co będzie w produkcji, w warunkach dostatecznie realistycznych dla pracy w produkcji.
I, powiedzmy, że znany jest zestaw parametrów regulacyjnych tego systemu usługowego, które mogą być używane do dostosowywania tego systemu pod kątem jego wydajności.
I, w czym tkwi problem — brakuje wystarczającego zrozumienia tego systemu usługowego, takiego, które pozwala na eksperckie ustawienie tej konfiguracji pod przyszłe obciążenie na danej platformie, aby uzyskać wymaganą wydajność systemu.
Cóż, tak to już zazwyczaj bywa.
Co można z tym zrobić.
Pierwsze, co przychodzi mi do głowy: zajrzeć do dokumentacji tego systemu. Zrozumieć, jakie są dopuszczalne zakresy wartości parametrów regulacyjnych. I na przykład, metodą spadku współrzędnych, dobierać wartości dla parametrów systemu w testach.
Tzn. ustawić system w pewnej konfiguracji jako konkretny zestaw wartości jego parametrów regulacyjnych.
Poddać go testowemu obciążeniu, tą właśnie narzędziem-utility, generatorem obciążenia.
I obserwować wartość — odpowiedź, lub metrykę jakości pracy systemu.
Drugą myślą może być takie zakończenie, że — to naprawdę bardzo czasochłonne.
To znaczy: jeśli jest wiele parametrów konfiguracyjnych, jeśli zakresy ich wartości są duże, jeśli każdy pojedynczy test obciążeniowy trwa długo, to: tak, to wszystko może zająć nieakceptowalnie dużo czasu.
No i co można z tego zrozumieć i przypomnieć sobie.
Można ustalić, że w zbiorze wartości parametrów konfiguracyjnych systemu usługowego - wektor, jako sekwencja pewnych wartości.
Każdemu takiemu wektorowi, przy równych innych warunkach (w tym, że ten wektor nie jest dotknięty) odpowiada dość określona wartość metryki - wskaźnika jakości pracy systemu pod testowym obciążeniem.
To znaczy.
Oznaczmy wektor konfiguracji systemu jako
, gdzie
; gdzie
— liczba parametrów konfiguracji systemu, ile ich jest, tych parametrów.
A wartość metryki, odpowiadająca temu
oznaczmy jako
, więc mamy funkcję: 
No a zatem: wszystko sprowadza się do, w moim przypadku: prawie zapomnianych z czasów studenckich algorytmów znajdowania ekstremum funkcji.
Dobrze, ale pojawia się tu pytanie organizacyjno-aplikacyjne: który algorytm użyć.
- W sensie - aby samemu jak najmniej pisać kod.
- I żeby działało, tzn. znajdowało ekstremum (jeśli istnieje), przynajmniej - szybciej niż spadek współrzędnych.
Pierwszy moment wskazuje, że warto spojrzeć w stronę jakichś środowisk, w których takie algorytmy już są wdrożone i są, w jakiejś formie, gotowe do użycia w kodzie.
No, znane mi są python i cran-r
Drugi moment oznacza, że trzeba przeczytać o samych algorytmach, jakie istnieją, jakie mają wymagania, szczególne cechy w pracy.
I co one dają, mogą być użyteczne efekty uboczne-wyniki, albo bezpośrednio, od samego algorytmu.
Lub można je uzyskać na podstawie wyników działania algorytmu.
Tu wiele zależy od warunków początkowych.
Na przykład, jeśli z jakichś powodów, trzeba szybciej uzyskać wynik, to warto spojrzeć w stronę algorytmów spadku gradientu, wybrać któryś z nich.
Albo, jeśli czas nie jest tak ważny, można na przykład skorzystać z metod optymalizacji stochastycznej, na przykład algorytmu genetycznego.
Proponuję rozważyć działanie takiego podejścia, przy doborze konfiguracji systemu, z wykorzystaniem algorytmu genetycznego, na następnej, że tak powiem: pracy laboratoryjnej.
Źródła:
- Niech będzie, jako system serwisowy:
oracle xe 18c - Niech on — obsługuje aktywność transakcyjną z celem: uzyskać jak największą przepustowość bazy danych, na transakcjach/sek.
- Transakcje — są bardzo różne, w swoim charakterze pracy z danymi i kontekście pracy.
Umówmy się, że to transakcje, które nie przetwarzają dużej ilości danych tabelarycznych.
W tym sensie, że nie generują danych undo więcej niż redo i nie przetwarzają dużych procentów wierszy z dużych tabel.
To transakcje, które zmieniają jeden wiersz w mniej więcej dużej tabeli, z niewielką ilością indeksów nad tą tabelą.
W takim układzie: wydajność bazy danych przy przetwarzaniu transakcji będzie, z zastrzeżeniem, określana jakością przetwarzania bazy danych redo.
Zastrzeżenie — jeśli mówimy konkretnie o ustawieniach bazy danych.
Bo, w ogólnym przypadku, mogą występować, na przykład, blokady transakcyjne, między sesjami SQL, z powodu projektowania użytkowników pracy z danymi tabelarycznymi i/lub modelu tabelarycznego.
Które, oczywiście, będą negatywnie wpływać na metrykę tps i będzie to egzogeniczny, w odniesieniu do bazy danych, czynnik: tak zaprojektowano model tabelaryczny i pracę z danymi w nim, że pojawiają się blokady.
Dlatego, dla czystości eksperymentu, wykluczmy ten czynnik, poniżej wyjaśnię jak dokładnie.
- Załóżmy, dla pewności, że 100% przesyłanych do bazy danych komend SQL: to komendy dml.
Niech charakterystyki pracy użytkowników z bazą danych: będą takie same w testach.
A dokładnie: liczba sesji SQL, dane tabelaryczne, sposób w jaki sesje SQL z nimi pracują. - Baza danych działa w
FORCE LOGGING,ARCHIVELOGtrybach. Tryb flashback bazy danych jest wyłączony, na poziomie bazy danych. - Dzienniki redo: umieszczone w osobnym systemie plików, na osobnym "dysku";
Cała reszta fizycznych komponentów bazy danych: w innym, osobnym systemie plików, na osobnym "dysku":
Szczegóły dotyczące budowy fizycznych komponentów laboratoryjnej bazy danych.
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 0Początkowo chciałem użyć systemu baz danych do obciążeń transakcyjnych w tych warunkach.
Ma taką wspaniałą cechę, zacytuję autora:
W centrum SLOB znajduje się "metoda SLOB". Metoda SLOB ma na celu testowanie platform
bez rywalizacji aplikacji. Nie można osiągnąć maksymalnej wydajności sprzętu
używając kodu aplikacji, który na przykład jest ograniczony przez blokowanie aplikacji lub nawet
dzielenie bloków bazy danych Oracle. Dokładnie tak — przy dzieleniu danych
w blokach danych występuje nadmiar! Ale SLOB — w swoim domyślnym wdrożeniu — jest odporny na tego typu rywalizację.
Ta deklaracja: jest zgodna, rzeczywiście tak jest.
Wygodnie regulować poziom równoległości sesji SLOB, to klucz -t uruchomienia narzędzia runit.sh w składzie SLOB-a
Regulowany jest procent poleceń DML, w tym liczba sesji SLOB, które wysyła do systemu baz danych, każda sesja SLOB, parametr UPDATE_PCT
Oddzielnie i bardzo wygodne: SLOB sam, przed i po sesji obciążenia — przygotowuje statspak lub zrzuty awr (co ma być przygotowane).
Jednak okazało się, że SLOB nie wspiera pracy sesji SLOB dłużej niż 30 sekund.
Dlatego najpierw napisałem swoją, prosto-robotniczą wersję narzędzia obciążającego, a potem tak już pozostała w użyciu.
Wyjaśnię, co robi narzędzie obciążające, dla jasności.
Zasadniczo narzędzie obciążające wygląda tak:
Kod pracownika
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 dotxWykonujemy pracowników w ten sposób:
Uruchamianie pracowników
echo "rozpoczęcie testu, czas trwania: ${TEST_DURATION}" >> "$v_logfile"
for((i=1;i> "$v_logfile"
dotx "$i" "${TEST_DURATION}" &
done
echo "oczekiwanie..." >> "$v_logfile"
waitA tabele dla pracowników przygotowuje się tak:
Tworzenie tabel
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"Tzn. dla każdego pracownika (praktycznie: osobna sesja SQL w DB) tworzona jest osobna tabela, z którą pracuje pracownik.
Osiąga się to brakiem blokad transakcyjnych między sesjami SQL pracowników.
Każdy pracownik: wykonuje to samo na swojej tabeli, wszystkie tabele są identyczne.
Wszyscy pracownicy wykonują pracę przez ten sam czas.
Przy czym - wystarczająco długi czas, aby na przykład dokładnie wystąpił i nie raz przełączanie logów.
No i odpowiednio pojawiały się z tym związane koszty i efekty.
W moim przypadku - czas trwania pracy pracowników skonfigurowałem na 8 minut.
Kawałek raportu statspak, z opisem pracy DB pod obciążeniem
Baza danych Id DB Instancja Nr inst Czas uruchomienia Wydanie RAC
~~~~~~~~ ----------- ------------ -------- --------------- ----------- ---
2929910313 XE 1 07-Wrz-20 23:12 18.0.0.0.0 NIE
Nazwa hosta Platforma Procesory Rdzeni Gniazda Pamięć (G)
~~~~ ---------------- ---------------------- ----- ----- ------- ------------
billing.izhevsk1 Linux x86 64-bit 2 2 1 15.6
Zrzut Id zrzutu Czas zrzutu Sesje Curs/Sess Komentarz
~~~~~~~~ ---------- ------------------ -------- --------- ------------------
Początek zrzutu: 1630 07-Wrz-20 23:12:27 55 .7
Koniec zrzutu: 1631 07-Wrz-20 23:20:29 62 .6
Czas trwania: 8.03 (min) Średnia aktywna sesja: 8.4
Czas DB: 67.31 (min) Czas CPU DB: 15.01 (min)
Rozmiary pamięci podręcznej Początek Koniec
~~~~~~~~~~~ ---------- ----------
Pamięć podręczna: 1,392M Rozmiar bloku Std: 8K
Współdzielona pula: 288M Pamięć bufora logów: 103,424K
Profil obciążenia Na sekundę Na transakcję Na wykonanie Na wywołanie
~~~~~~~~~~~~ ------------------ ----------------- ----------- -----------
Czas DB(s): 8.4 0.0 0.00 0.20
Czas CPU DB(s): 1.9 0.0 0.00 0.04
Rozmiar redo: 7,685,765.6 978.4
Logiczne odczyty: 60,447.0 7.7
Zmiany bloków: 47,167.3 6.0
Fizyczne odczyty: 8.3 0.0
Fizyczne zapisy: 253.4 0.0
Wywołania użytkownika: 42.6 0.0
Parsowania: 23.2 0.0
Trudne parsowania: 1.2 0.0
W/A MB przetworzono: 1.0 0.0
Logowania: 0.5 0.0
Wykonania: 15,756.5 2.0
Wycofania: 0.0 0.0
Transakcje: 7,855.1Wracając do postawienia pracy laboratoryjnej.
Będziemy, przy tych samych warunkach, zmieniać wartości takich parametrów laboratorium bazy danych:
- Rozmiar grup dzienników bazy danych. Zakres wartości: [32, 1024] MB;
- Liczba grup dzienników bazy danych. Zakres wartości: [2,32];
log_archive_max_processeszakres wartości: [1,8];commit_loggingdopuszczalne są dwie wartości:batch|immediate;commit_waitdopuszczalne są dwie wartości:wait|nowait;log_bufferzakres wartości: [2,128] MB.log_checkpoint_timeoutzakres wartości: [60,1200] sekunddb_writer_processeszakres wartości: [1,4]undo_retentionzakres wartości: [30;300] sekundtransactions_per_rollback_segmentzakres wartości: [1,8]disk_asynch_iodopuszczalne są dwie wartości:true|false;filesystemio_optionsdopuszczalne są takie wartości:none|setall|directIO|asynch;db_block_checkingdopuszczalne są takie wartości:OFF|LOW|MEDIUM|FULL;db_block_checksumdopuszczalne są takie wartości:OFF|TYPICAL|FULL;
Człowiek z doświadczeniem w zarządzaniu bazami danych Oracle może bez wątpliwości powiedzieć — jakie wartości powinny być ustawione wśród podanych parametrów i ich dopuszczalnych wartości, aby uzyskać większą wydajność bazy danych do pracy z danymi zdefiniowanymi w aplikacyjnym kodzie powyżej.
Ale.
Celem laboratorium jest pokazanie, że algorytm optymalizacyjny działa samodzielnie i stosunkowo szybko.
Musimy jedynie zajrzeć do dokumentacji dotyczącej konfigurowanego systemu, aby ustalić, które parametry i w jakich zakresach zmieniać.
Należy również napisać kod, który będzie realizował współpracę z konfigurowanym systemem wybranego algorytmu optymalizacji.
Zatem, przechodząc do kodu.
Wcześniej mówiłem o cran-r, tj.: wszystkie operacje na konfigurowanym systemie są orkiestrane w postaci skryptu R.
Właściwe zadanie, analiza, dobór według wartości metryki oraz wektorów stanu systemu: to pakiet GA ()
Pakiet, w tym przypadku, niezbyt pasuje, ponieważ oczekuje on zadania wektorów (chromosomów, jeśli stosować terminologię pakietu) w postaci liczb rzeczywistych.
A mój wektor z wartości parametrów konfiguracyjnych to 14 wartości — liczby całkowite i wartości tekstowe.
Problem jest łatwy do obejścia, przypisując wartości tekstowe do określonych liczb.
Zatem, ostatecznie, główna część skryptu R wygląda tak:
Wywołanie GA::ga
cat( "", file=v_logfile, sep="n", append=F)
pSize = 10
elitism_value=1
pmutation_coef=0.8
pcrossover_coef=0.1
iterations=50
gam=GA::ga(type="real-valued", fitness=evaluate,
lower=c(32,2, 1,1,1,2,60,1,30,1,0,0, 0,0), upper=c(1024,32, 8,10,10,128,800,4,300,8,10,40, 40,30),
popSize=pSize,
pcrossover = pcrossover_coef,
pmutation = pmutation_coef,
maxiter=iterations,
run=4,
keepBest=T)
cat( "GA-session is done" , file=v_logfile, sep="n", append=T)
gam@solutionTutaj, przy użyciu lower i upper atrybutów podprogramu ga określa się praktycznie obszar przestrzeni poszukiwań, w którym będzie poszukiwany taki wektor (lub wektory), dla którego uzyskane będzie maksymalne wartość funkcji dopasowania.
Podprogram ga wykonuje poszukiwanie, maksymalizując funkcję dopasowania.
Zatem, w tym przypadku, należy dopilnować, aby funkcja dopasowania, interpretując wektor jako zestaw wartości dla określonych parametrów bazy danych, wygenerowała metrykę z bazy danych.
Tj.: ile, przy danej konfiguracji bazy danych i danym obciążeniu bazy danych, baza przetwarza transakcji na sekundę.
Innymi słowy, należy zadbać, aby wewnątrz funkcji dopasowania realizowana była taka wieloetapowa operacja:
- Przetwarzanie wejściowego wektora liczb — przekształcenie go w wartości dla parametrów bazy danych.
- Próba utworzenia określonej liczby grup redo o określonym rozmiarze. Przy czym próba może zakończyć się niepowodzeniem.
Już istniejące w subd grupy dziennikowe, w jakiejś ilości i jakiegoś rozmiaru, dla czystości eksperymentu — powinny zostać usunięte. - W przypadku sukcesu poprzedniego punktu: zadanie bazie wartości parametrów konfiguracyjnych (wciąż możliwe są błędy)
- W przypadku sukcesu poprzedniego punktu: zatrzymanie subd, uruchomienie subd, aby ponownie zadane wartości parametrów — weszły w życie. (wciąż możliwe są błędy)
- W przypadku sukcesu poprzedniego punktu: przeprowadzenie testu obciążeniowego. uzyskanie metryki od subd.
- Przywrócenie subd do stanu początkowego, tzn. usunięcie dodatkowych grup dziennikowych, przywrócenie pierwotnej konfiguracji subd.
Kod funkcji fitness
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," 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)
}Tak więc, cała praca: odbywa się w funkcji dopasowania.
Podprogram ga, wykonuje przetwarzanie wektorów, lub, dokładniej mówiąc — chromosomów.
W której najbardziej interesuje nas: selekcja chromosomów z takimi genami, przy których funkcja dopasowania wydaje dużą wartość.
To tak naprawdę proces poszukiwania optymalnego zestawu chromosomów wektorem w N-wymiarowej przestrzeni poszukiwania.
Bardzo czytelne, szczegółowe , z przykładami kodu R, działania algorytmu genetycznego.
Osobno zaznaczę dwa techniczne punkty.
Pomocnicze wywołania, z funkcji evaluate, na przykład zatrzymanie-uruchomienie, ustawienie wartości parametru bazy danych, są realizowane na podstawie cran-r funkcji system2
Dzięki której: wywoływana jest już jakakolwiek skrypt bash lub polecenie.
Na przykład:
set_db_parameter
set_db_parameter=function(p1, p2) {
v_module="set_db_parameter"
v_cmd="/home/oracle/testingredotrace/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)
}
}Drugi punkt — linia, evaluate funkcji, z zachowaniem konkretnej wartości metryki oraz odpowiadającego jej wektora ustawień, do logu:
cat( paste("result: ",v_metric," ",v_vector,sep="") , file=v_logfile, sep="n", append=T)To ważne, ponieważ z tej tablicy danych można uzyskać dodatkowe informacje na temat tego, który z komponentów wektora ustawień ma większy lub mniejszy wpływ na wartość metryki.
Czyli: można przeprowadzić analizę istotności atrybutów.
Zatem, co może się zdarzyć.
W postaci wykresu, jeśli uporządkujesz testy według rosnącej wartości metryki, obraz będzie taki:

Niektóre dane, odpowiadające skrajnym wartościom metryki:

Tutaj, na zrzucie ekranu z wynikami, doprecyzuję: wartości wektora ustawień — podane są w terminach kodu funkcji dopasowania, a nie w terminach listy parametrów/zasięgów wartości parametrów, którą przedstawiłem powyżej w tekście.
Cóż. Dużo to, czy mało, ~8 tys. tps: osobny temat.
W ramach pracy laboratoryjnej — ta liczba nie ma znaczenia, ważna jest dynamika, jak ta wartość się zmienia.
Dynamika tutaj jest dobra.
Jasne, że co najmniej jeden czynnik istotnie wpływa na wartość metryki, algorytm ga, przeszukując wektory-chromosomy: został pokryty.
Sądząc po dość dynamicznej wartości krzywej — istnieje jeszcze co najmniej jeden czynnik, który, choć znacznie mniejszy, wpływa.
Tutaj potrzebna jest attribute-importance analiza, aby zrozumieć: jakie atrybuty (w tym przypadku — komponenty wektora konfiguracyjnego) i jak mocno wpływają na wartość metryki.
A na podstawie tych informacji: zrozumieć — jakie czynniki były poruszane przez zmiany znaczących atrybutów.
Wykonaj attribute-importance można to zrobić na różne sposoby.
Dla tych celów, podoba mi się algorytm randomForest tej samej nazwy pakietu R ()
randomForest, jak rozumiem jego działanie w ogóle oraz jego podejście do oceny ważności atrybutów w szczególności, buduje pewien model zależności zmiennej odpowiedzi od atrybutów.
W naszym przypadku zmienną odpowiedzi — jest metryka uzyskiwana z systemów baz danych w testach obciążeniowych: tps;
A atrybuty to — komponenty wektora konfiguracyjnego.
Otóż to randomForest ocenia ważność każdego atrybutu modelu dwoma liczbami: %IncMSE — jak obecność/nieobecność tego atrybutu w modelu zmienia jakość modelu MSE (średni błąd kwadratowy);
A IncNodePurity — to liczba, która pokazuje, jak dobrze, według wartości danego atrybutu, można podzielić zbiór danych z obserwacjami, tak aby w jednej części znalazły się dane z jednym wartością wyjaśnianej metryki, a w drugiej z inną wartością metryki.
Czyli: na ile jest to atrybut klasyfikujący (najlepsze wyjaśnienie w języku rosyjskim, które widziałem dotyczące random forest). ).
Roboczy kod R do przetwarzania zbioru danych z wynikami testów obciążeniowych:
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$importanceMożna ręcznie dobrać hiperparametry algorytmu i, kierując się jakością modelu, wybrać model, który dokładniej wykonuje prognozy na zbiorze danych walidacyjnych.
Można napisać jakąś funkcję do tej pracy (przy okazji — znów, na jakimś algorytmie optymalizacyjnym).
Można skorzystać z pakietu R caret, nie ma to większego znaczenia.
W rezultacie, w tym przypadku, otrzymujemy taki wynik, który pozwala ocenić znaczenie atrybutów:

Cóż, w ten sposób można przejść do globalnych rozważań:
- Okazuje się, że najbardziej znaczącym parametrem w tych warunkach testowania był
commit_wait
Technicznie określa on tryb wykonywania operacji I/O zapisu danych redo z log-buforu bazy danych do aktualnej grupy journal: synchroniczny lub asynchroniczny.
Wartośćnowaitw którym osiągany jest praktycznie pionowy, wielokrotny wzrost wartości metryki TPS: to włączenie trybu asynchronicznego I/O w grupach redo.
Osobne pytanie — warto, czy nie warto tak robić w produkcyjnej bazie danych. W tej kwestii ograniczam się jedynie do stwierdzenia: to znaczący czynnik. - Logiczne jest, że rozmiar log-buforu bazy danych: okazuje się znaczącym czynnikiem.
Im mniejszy rozmiar log-buforu, tym mniejsza jego zdolność buforowania, tym częściej zdarzają się jego przepełnienia i/lub brak możliwości przydzielenia w nim wolnej przestrzeni na porcję nowych danych redo.
A to oznacza: opóźnienia związane z alokowaniem przestrzeni w log-buforze i/lub zrzucaniem danych redo z niego do grup redo.
Te opóźnienia oczywiście powinny wpływać i wpływają na przepustowość bazy danych w kwestii transakcji. - Parametr
db_block_checksum: no cóż, to również w zasadzie jasne — przetwarzanie transakcji prowadzi do powstawania bloków dirty w buforze podrzędnym bazy danych.
Które, przy aktywowanej kontroli sum kontrolnych bloków danych, baza musi przetwarzać — obliczając te sumy kontrolne dla treści bloku danych, porównując je z tym, co jest zapisane w nagłówku bloku danych: zgadza się/nie zgadza się.
Taka praca, znów, nie może nie opóźnić przetwarzania danych, a więc parametry i mechanizm, który ten parametr definiuje — okazują się istotne.
Dlatego też dostawca w dokumentacji tego parametru proponuje różne jego wartości i zauważa, że – tak, wpływ będzie, ale, oto, różne wartości, aż do 'wyłączone' i różny wpływ, można wybrać.
No i ogólny wniosek.
Podejście, generalnie rzecz biorąc, okazuje się całkiem skuteczne.
Pozwala ono, na wczesnych etapach testowania obciążeniowego pewnego systemu serwisowego, na dobór jego (systemu) optymalnej konfiguracji pod obciążenie bez wnikania zbytnio w szczegóły dostosowania systemu do obciążenia.
Ale nie wyklucza tego całkowicie — przynajmniej na poziomie rozumienia: "regulacyjnych pokręteł" i dopuszczalnych zakresów obrotu tych pokręteł system znać trzeba.
Podejście to może dość szybko znaleźć optymalną konfigurację systemu.
I w wyniku testowania można uzyskać informacje o naturze zależności między metryką jakości działania systemu a wartościami parametrów konfiguracyjnych systemu.
Co oczywiście powinno sprzyjać powstawaniu głębokiego zrozumienia systemu, jego działania, przynajmniej — w ramach określonego obciążenia.
Praktycznie oznacza to: rozłożenie kosztów na zrozumienie konfigurowanego systemu oraz wydatków związanych z przygotowaniem takiego testowania działania systemu.
Osobno zauważę: w tym podejściu kluczowa jest adekwatność testowania systemu do warunków jego pracy, które będą miały miejsce w eksploatacji produkcyjnej.
Dziękuję za uwagę, czas.
Źródło: habr.com
