O método científico de puxão, ou como selecionar uma configuração de banco de dados usando benchmarks e um algoritmo de otimização

Olá

Decidi compartilhar minha descoberta - fruto de reflexão, tentativa e erro.
Em geral: isso não é um achado, é claro - tudo isso deveria ser conhecido há muito tempo, por aqueles que estão envolvidos no processamento de dados estatísticos aplicados e na otimização de quaisquer sistemas, não necessariamente especificamente do SGBD.
E: sim, eles sabem, escrevem artigos interessantes sobre suas pesquisas, exemplo (UPD.: nos comentários apontaram um projeto muito interessante: ottertune )
Por outro lado: de imediato não vejo qualquer menção ou divulgação generalizada desta abordagem na Internet entre especialistas em TI, DBA.

Então, direto ao ponto.

Suponhamos que temos uma tarefa: montar um determinado sistema de serviços para atender algum tipo de trabalho.

Sabe-se sobre este trabalho: o que é, como se mede a qualidade deste trabalho e qual o critério para medir essa qualidade.

Suponhamos também que seja mais ou menos conhecido e compreendido: exatamente como o trabalho é executado neste (ou com) este sistema de serviço.

“Mais ou menos” - significa que é possível preparar (ou obter de algum lugar) uma determinada ferramenta, utilidade, serviço que possa ser sintetizada e aplicada ao sistema com uma carga de testes suficientemente adequada ao que estará em produção, em condições suficientemente adequadas para trabalhar na produção.

Pois bem, vamos supor que seja conhecido um conjunto de parâmetros de ajuste para este sistema de serviço, que podem ser utilizados para configurar este sistema em termos da produtividade do seu trabalho.

E qual é o problema - não há uma compreensão suficientemente completa deste sistema de serviço, que permita definir habilmente as configurações deste sistema para carregamento futuro em uma determinada plataforma e obter a produtividade necessária do sistema.

Bem. Quase sempre é esse o caso.

O que você pode fazer aqui?

Bem, a primeira coisa que vem à mente é dar uma olhada na documentação deste sistema. Entenda quais são as faixas aceitáveis ​​para os valores dos parâmetros de ajuste. E, por exemplo, usando o método de descida por coordenadas, selecione valores para parâmetros do sistema em testes.

Aqueles. dar ao sistema algum tipo de configuração, na forma de um conjunto específico de valores para seus parâmetros de configuração.

Aplique uma carga de teste a ele, usando este gerador de carga utilitário.
E observe o valor - a resposta ou uma métrica da qualidade do sistema.

O segundo pensamento pode ser a conclusão de que se trata de um período muito longo.

Bem, isto é: se houver muitos parâmetros de configuração, se os intervalos de seus valores executados forem grandes, se cada teste de carga individual levar muito tempo para ser concluído, então: sim, tudo isso pode levar um tempo inaceitável muito tempo.

Bem, aqui está o que você pode entender e lembrar.

Você pode descobrir que no conjunto de valores dos parâmetros de configuração do sistema de serviço existe um vetor, como uma sequência de alguns valores.

Cada um desses vetores, ceteris paribus (na medida em que não é afetado por esse vetor), corresponde a um valor completamente definido da métrica - um indicador da qualidade da operação do sistema sob uma carga de teste.

Ie

Vamos denotar o vetor de configuração do sistema como O método científico de puxão, ou como selecionar uma configuração de banco de dados usando benchmarks e um algoritmo de otimizaçãoOnde O método científico de puxão, ou como selecionar uma configuração de banco de dados usando benchmarks e um algoritmo de otimização; Onde O método científico de puxão, ou como selecionar uma configuração de banco de dados usando benchmarks e um algoritmo de otimização — número de parâmetros de configuração do sistema, quantos desses parâmetros existem.

E o valor da métrica correspondente a isso O método científico de puxão, ou como selecionar uma configuração de banco de dados usando benchmarks e um algoritmo de otimização vamos denotá-lo como
O método científico de puxão, ou como selecionar uma configuração de banco de dados usando benchmarks e um algoritmo de otimização, então obtemos uma função: O método científico de puxão, ou como selecionar uma configuração de banco de dados usando benchmarks e um algoritmo de otimização

Pois bem: tudo se resume imediatamente, no meu caso: quase esquecido desde os tempos de estudante, algoritmos para busca do extremo de uma função.

Ok, mas aqui surge uma questão organizacional e aplicada: qual algoritmo usar.

  1. No sentido - para que você possa codificar menos manualmente.
  2. E para que funcione, ou seja, encontrou o extremo (se houver), bem, pelo menos mais rápido que a descida coordenada.

O primeiro ponto sugere que precisamos olhar para alguns ambientes nos quais tais algoritmos já foram implementados e estão, de alguma forma, prontos para uso em código.
Bem, eu sei python и cran-r

O segundo ponto significa que você precisa ler sobre os próprios algoritmos, o que são, quais são seus requisitos e as características de seu trabalho.

E o que eles fornecem podem ser efeitos colaterais úteis - resultados ou diretamente do próprio algoritmo.

Ou podem ser obtidos a partir dos resultados do algoritmo.

Depende muito das condições de entrada.

Por exemplo, se, por algum motivo, você precisa obter um resultado mais rápido, bem, você precisa olhar para os algoritmos de gradiente descendente e escolher um deles.

Ou, se o tempo não for tão importante, você pode, por exemplo, usar métodos de otimização estocástica, como um algoritmo genético.

Proponho considerar o trabalho desta abordagem, selecionando a configuração do sistema, utilizando um algoritmo genético, no próximo, por assim dizer: trabalho de laboratório.

Fonte:

  1. Que haja, como sistema de serviço: oracle xe 18c
  2. Deixe-o servir à atividade transacional e ao objetivo: obter o maior rendimento possível do subbanco de dados, em transações/s.
  3. As transações podem ser muito diferentes na natureza do trabalho com dados e no contexto do trabalho.
    Vamos combinar que são transações que não processam uma grande quantidade de dados tabulares.
    No sentido de que não geram mais dados de desfazer do que de refazer e não processam grandes porcentagens de linhas e tabelas grandes.

São transações que alteram uma linha em uma tabela mais ou menos grande, com um pequeno número de índices nesta tabela.

Nesta situação: a produtividade da subbase de dados para processamento de transações será, com ressalva, determinada pela qualidade do processamento da base de dados redox.

Isenção de responsabilidade - se falarmos especificamente sobre as configurações do subdb.

Porque, no caso geral, pode haver, por exemplo, bloqueios transacionais entre sessões SQL, devido ao desenho do trabalho do usuário com dados tabulares e/ou modelo tabular.

O que, claro, terá um efeito deprimente na métrica TPS e este será um fator exógeno, em relação ao subbanco de dados: bem, é assim que o modelo tabular foi desenhado e o trabalho com os dados nele contém que ocorrem bloqueios.

Portanto, para a pureza do experimento, excluiremos esse fator, e a seguir esclarecerei exatamente como.

  1. Vamos supor, para maior certeza, que 100% dos comandos SQL enviados ao banco de dados sejam comandos DML.
    Deixe que as características do usuário que trabalha com o subbanco de dados sejam as mesmas nos testes.
    A saber: o número de sessões skl, dados tabulares, como as sessões skl funcionam com eles.
  2. Subd trabalha em FORCE LOGGING, ARCHIVELOG modificações. O modo de banco de dados flashback está desativado, no nível subd.
  3. Redo logs: localizados em um sistema de arquivos separado, em um “disco” separado;
    O restante do componente físico do banco de dados: em outro sistema de arquivos separado, em um “disco” separado:

Mais detalhes sobre o dispositivo físico. componentes de banco de dados de laboratório

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

Inicialmente, sob essas condições de carregamento, eu queria usar a transação subd Utilitário SLOB
Tem um recurso tão maravilhoso que citarei o autor:

No coração do SLOB está o “método SLOB”. O Método SLOB visa testar plataformas
sem contenção de aplicativos. Não é possível obter o desempenho máximo do hardware
usando o código do aplicativo que está, por exemplo, vinculado ao bloqueio do aplicativo ou até mesmo
compartilhando blocos do Oracle Database. É isso mesmo: há sobrecarga ao compartilhar dados
em blocos de dados! Mas o SLOB – em sua implantação padrão – é imune a tal contenção.

Esta declaração: corresponde, é.
É conveniente regular o grau de paralelismo das sessões cl, esta é a chave -t inicie o utilitário runit.sh de SLOB
A porcentagem de comandos DML é regulada, na quantidade de mensagens de texto que são enviadas ao subd, em cada sessão de texto, parâmetro UPDATE_PCT
Separadamente e muito convenientemente: SLOB em si, antes e depois da sessão de carregamento - prepara um statspack, ou awr-snapshots (o que está definido para ser preparado).

No entanto, descobriu-se que SLOB não oferece suporte a sessões SQL com duração inferior a 30 segundos.
Portanto, primeiro codifiquei minha própria versão operária e camponesa do carregador, e então ele permaneceu em operação.

Deixe-me esclarecer o que o carregador faz e como faz, para maior clareza.
Essencialmente, o carregador se parece com isto:

Código do trabalhador

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

Os trabalhadores são lançados desta forma:

Trabalhadores em execução

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

E as mesas para os trabalhadores são preparadas assim:

Criando tabelas

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"

Aqueles. Para cada trabalhador (praticamente: uma sessão SQL separada no banco de dados) é criada uma tabela separada, com a qual o trabalhador trabalha.

Isso garante a ausência de bloqueios transacionais entre sessões de trabalho.
Cada trabalhador: faz a mesma coisa, com a sua mesa, as mesas são todas iguais.
Todos os trabalhadores realizam trabalho pelo mesmo período de tempo.
Além disso, por um tempo suficiente para que, por exemplo, uma troca de log ocorresse definitivamente, e mais de uma vez.
Bem, consequentemente, surgiram custos e efeitos associados.
No meu caso, configurei a duração do trabalho dos trabalhadores em 8 minutos.

Um pedaço de relatório do statspack descrevendo a operação do subd sob carga

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

Voltando ao trabalho de laboratório.
Iremos, em igualdade de circunstâncias, variar os valores dos seguintes parâmetros da subbase de dados do laboratório:

  1. Tamanho dos grupos de logs do banco de dados. faixa de valores: [32, 1024] MB;
  2. Número de grupos de diários no banco de dados. faixa de valores: [2,32];
  3. log_archive_max_processes faixa de valores: [1,8];
  4. commit_logging dois valores são permitidos: batch|immediate;
  5. commit_wait dois valores são permitidos: wait|nowait;
  6. log_buffer faixa de valores: [2,128] MB.
  7. log_checkpoint_timeout faixa de valores: [60,1200] segundos
  8. db_writer_processes faixa de valores: [1,4]
  9. undo_retention intervalo de valores: [30;300] segundos
  10. transactions_per_rollback_segment faixa de valores: [1,8]
  11. disk_asynch_io dois valores são permitidos: true|false;
  12. filesystemio_options os seguintes valores são permitidos: none|setall|directIO|asynch;
  13. db_block_checking os seguintes valores são permitidos: OFF|LOW|MEDIUM|FULL;
  14. db_block_checksum os seguintes valores são permitidos: OFF|TYPICAL|FULL;

Uma pessoa com experiência em manutenção de bancos de dados Oracle certamente já pode dizer o que e para quais valores devem ser definidos, a partir dos parâmetros especificados e seus valores aceitáveis, a fim de obter maior produtividade do banco de dados para o trabalho com os dados que são indicados por o código do aplicativo, aqui acima.

Mas.

O objetivo do trabalho de laboratório é mostrar que o próprio algoritmo de otimização esclarecerá isso para nós de forma relativamente rápida.

Para nós, basta olhar para o documento, através do sistema customizável, apenas o suficiente para saber quais parâmetros alterar e em quais faixas.
E também: codifique o código que será usado para trabalhar com o sistema customizado do algoritmo de otimização selecionado.

Então, agora sobre o código.
falei acima sobre cran-r, ou seja: todas as manipulações com o sistema customizado são orquestradas na forma de um script R.

A tarefa real, análise, seleção por valor métrico, vetores de estado do sistema: este é um pacote GA (a documentação)
O pacote, neste caso, não é muito adequado, no sentido de que espera que os vetores (cromossomos, se em termos do pacote) sejam especificados na forma de sequências de números com parte fracionária.

E meu vetor, a partir dos valores dos parâmetros de configuração: são 14 quantidades - inteiros e valores de string.

O problema, é claro, é facilmente evitado atribuindo alguns números específicos aos valores das strings.

Assim, no final, a parte principal do script R fica assim:

Ligue para GA::ga

cat( "", file=v_logfile, sep="n", append=F)

pSize = 10
elitism_value=1
pmutation_coef=0.8
pcrossover_coef=0.1
iterations=50

gam=GA::ga(type="real-valued", fitness=evaluate,
lower=c(32,2, 1,1,1,2,60,1,30,1,0,0, 0,0), upper=c(1024,32, 8,10,10,128,800,4,300,8,10,40, 40,30),
popSize=pSize,
pcrossover = pcrossover_coef,
pmutation = pmutation_coef,
maxiter=iterations,
run=4,
keepBest=T)
cat( "GA-session is done" , file=v_logfile, sep="n", append=T)
gam@solution

Aqui, com a ajuda lower и upper atributos de sub-rotina ga essencialmente, é especificada uma área do espaço de busca, dentro da qual será realizada uma busca por tal vetor (ou vetores) para o qual será obtido o valor máximo da função de aptidão.

A sub-rotina ga realiza uma busca maximizando a função de aptidão.

Pois bem, verifica-se que, neste caso, é necessário que a função de aptidão, entendendo o vetor como um conjunto de valores para determinados parâmetros do subd, receba uma métrica do subd.

Ou seja: quantos, com uma determinada configuração de subd e uma determinada carga no subd: o subd processa transações por segundo.

Ou seja, ao desdobrar, as seguintes etapas múltiplas devem ser executadas dentro da função de fitness:

  1. Processando o vetor de entrada de números - convertendo-o em valores para os parâmetros de subdados.
  2. Uma tentativa de criar um determinado número de grupos de refazer de um determinado tamanho. Além disso, a tentativa pode não ter sucesso.
    Grupos de revistas que já existiam no subd, em alguma quantidade e de algum tamanho, para a pureza do experimento - d.b. excluído.
  3. Se o ponto anterior for bem sucedido: especificando os valores dos parâmetros de configuração para o banco de dados (novamente: pode haver uma falha)
  4. Se a etapa anterior for bem-sucedida: parar o subd, iniciar o subd para que os valores dos parâmetros recém-especificados tenham efeito. (novamente: pode haver uma falha)
  5. Se a etapa anterior for bem-sucedida: execute um teste de carga. obtenha métricas do subd.
  6. Retorne o subd ao seu estado original, ou seja, exclua grupos de log adicionais, retorne a configuração original do subbanco de dados para funcionar.

Código de função de condicionamento físico

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

Que. todo trabalho: realizado na função fitness.

A sub-rotina ga processa vetores ou, mais corretamente, cromossomos.
Nesse caso, o que é mais importante para nós é a seleção de cromossomos com genes para os quais a função de aptidão produz grandes valores.

Este é, em essência, o processo de busca pelo conjunto ideal de cromossomos usando um vetor em um espaço de busca N-dimensional.

Muito claro, detalhado explicação, com exemplos de código R, trabalho de um algoritmo genético.

Gostaria de observar separadamente dois pontos técnicos.

Chamadas auxiliares da função evaluate, por exemplo, stop-start, definindo o valor do parâmetro subd, são executados com base em cran-r funções system2

Com a ajuda de: algum script ou comando bash é chamado.

Por exemplo:

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

O segundo ponto é a linha, evaluate funções, salvando um valor de métrica específico e seu vetor de ajuste correspondente em um arquivo de log:

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

Isso é importante, pois a partir dessa matriz de dados será possível obter informações adicionais sobre qual dos componentes do vetor de sintonia tem maior ou menor efeito no valor da métrica.

Ou seja: será possível realizar análises de importância de atributos.

Então, o que pode acontecer?

Em forma de gráfico, se você ordenar os testes em ordem métrica crescente, a imagem será a seguinte:

O método científico de puxão, ou como selecionar uma configuração de banco de dados usando benchmarks e um algoritmo de otimização

Alguns dados correspondentes aos valores extremos da métrica:
O método científico de puxão, ou como selecionar uma configuração de banco de dados usando benchmarks e um algoritmo de otimização
Aqui, na captura de tela com os resultados, vou esclarecer: os valores do vetor de ajuste são dados em termos do código da função de fitness, não em termos da lista numérica de parâmetros/intervalos de valores de parâmetros, que foi formulada acima no texto.

Bem. É muito ou pouco, ~8 mil tps: uma questão à parte.
No âmbito do trabalho de laboratório este valor não é importante, o que importa é a dinâmica, como este valor muda.

A dinâmica aqui é boa.
É óbvio que pelo menos um fator influencia significativamente o valor da métrica, o algoritmo ga, classificando os vetores cromossômicos: coberto.
A julgar pela dinâmica bastante vigorosa dos valores da curva, há pelo menos mais um fator que, embora significativamente menor, tem influência.

É aqui que você precisa attribute-importance análise para entender quais atributos (bem, neste caso, componentes do vetor de ajuste) e o quanto eles influenciam o valor da métrica.
E a partir dessas informações: entenda quais fatores foram afetados pelas mudanças em atributos significativos.

corrida attribute-importance possível de diferentes maneiras.

Para esses fins, gosto do algoritmo randomForest Pacote R com o mesmo nome (a documentação)
randomForest, pelo que entendo seu trabalho em geral e sua abordagem para avaliar a importância dos atributos em particular, constrói um certo modelo de dependência da variável resposta dos atributos.

No nosso caso, a variável resposta é uma métrica obtida do banco de dados em testes de carga: tps;
E os atributos são componentes do vetor de ajuste.

Então aqui randomForest avalia a importância de cada atributo do modelo com dois números: %IncMSE — como a presença/ausência deste atributo num modelo altera a qualidade do MSE deste modelo (Erro Quadrático Médio);

E IncNodePurity é um número que reflete o quão bem, com base nos valores deste atributo, um conjunto de dados com observações pode ser dividido, de modo que em uma parte haja dados com um valor da métrica que está sendo explicada, e na outra com outro valor da métrica.
Bem, isto é: até que ponto isso é um atributo de classificação (vi a explicação mais clara em russo no RandomForest aqui).

Código R do trabalhador-camponês para processar um conjunto de dados com os resultados dos testes de carga:

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

Você pode selecionar diretamente os hiperparâmetros do algoritmo com suas mãos e, focando na qualidade do modelo, selecionar um modelo que atenda com mais precisão às previsões no conjunto de dados de validação.
Você pode escrever algum tipo de função para este trabalho (aliás, novamente, usando algum tipo de algoritmo de otimização).

Você pode usar o pacote R caret, não o ponto é importante.

Como resultado, neste caso, obtém-se o seguinte resultado para avaliar o grau de importância dos atributos:

O método científico de puxão, ou como selecionar uma configuração de banco de dados usando benchmarks e um algoritmo de otimização

Bem. Assim, podemos iniciar a reflexão global:

  1. Acontece que o mais significativo, sob estas condições de teste, foi o parâmetro commit_wait
    Tecnicamente, ele especifica o modo de execução da operação io de gravação de dados redo do buffer de log subdb para o grupo de log atual: síncrono ou assíncrono.
    Valor nowait o que resulta em um aumento múltiplo quase vertical no valor da métrica tps: esta é a inclusão do modo io assíncrono em grupos de redo.
    Uma outra questão é se você deve ou não fazer isso em um banco de dados de alimentos. Aqui me limito a apenas afirmar: este é um fator significativo.
  2. É lógico que o tamanho do buffer de log do subd: seja um fator significativo.
    Quanto menor o tamanho do buffer de log, menor sua capacidade de buffer, mais frequentemente ele transborda e/ou a incapacidade de alocar uma área livre nele para uma porção de novos dados redox.
    Isso significa: atrasos associados à alocação de espaço no buffer de log e/ou ao despejo de dados de redo dele em grupos de redo.
    Esses atrasos, é claro, deveriam afetar e afetam o rendimento do banco de dados para transações.
  3. Parâmetro db_block_checksum: bem, também, em geral, é claro - o processamento de transações leva à formação de blocos darty no cache do buffer do subbanco de dados.
    Que, quando a verificação de somas de verificação de blocos de dados está habilitada, o banco de dados deve processar - calcular essas somas de verificação a partir do corpo do bloco de dados, verificá-las com o que está escrito no cabeçalho do bloco de dados: corresponde/não corresponde.
    Tal trabalho, novamente, não pode deixar de atrasar o processamento de dados e, consequentemente, o parâmetro e o mecanismo que define esse parâmetro revelam-se significativos.
    É por isso que o fornecedor oferece, na documentação deste parâmetro, valores diferentes e observa que sim, haverá impacto, mas você pode escolher valores diferentes, até mesmo “off” e impactos diferentes.

Bem, uma conclusão global.

A abordagem, em geral, acaba funcionando bastante.

Ele se permite, nos estágios iniciais do teste de carga de um determinado sistema de serviço, selecionar sua configuração ideal (do sistema) para a carga, sem se aprofundar muito nas especificidades da configuração do sistema para a carga.

Mas não o exclui completamente - pelo menos no nível de compreensão: o sistema deve conhecer os “botões de ajuste” e as faixas de rotação permitidas desses botões.

A abordagem pode então encontrar com relativa rapidez a configuração ideal do sistema.
E com base nos resultados dos testes, é possível obter informações sobre a natureza da relação entre as métricas de desempenho do sistema e os valores dos parâmetros de configuração do sistema.

O que, claro, deve contribuir para o surgimento dessa compreensão muito profunda do sistema, do seu funcionamento, pelo menos sob uma determinada carga.

Na prática, trata-se de uma troca dos custos de compreensão do sistema customizado pelos custos de preparação de tais testes do sistema.

Gostaria de observar separadamente: nesta abordagem, o grau de adequação dos testes do sistema às condições operacionais que terá na operação comercial é extremamente importante.

Obrigado pela sua atenção e tempo.

Fonte: habr.com

Adicionar um comentário