El método científico de empuje, o cómo seleccionar una configuración de base de datos utilizando puntos de referencia y un algoritmo de optimización

¡Hola

Decidí compartir mi hallazgo, fruto del pensamiento, prueba y error.
En general: esto no es un hallazgo, por supuesto; todo esto debería ser conocido desde hace mucho tiempo por aquellos que participan en el procesamiento de datos estadísticos aplicados y la optimización de cualquier sistema, no necesariamente específicamente el DBMS.
Y sí, lo saben, escriben artículos interesantes sobre sus investigaciones, ejemplo (UPD.: en los comentarios señalaron un proyecto muy interesante: sintonizar )
Por otro lado, no veo ninguna mención o difusión generalizada de este enfoque en Internet entre los especialistas en TI, DBA.

Entonces, al grano.

Supongamos que tenemos una tarea: configurar un determinado sistema de servicios para realizar algún tipo de trabajo.

Se sabe sobre este trabajo: qué es, cómo se mide la calidad de este trabajo y cuál es el criterio para medir esta calidad.

Supongamos también que se conoce y comprende más o menos: exactamente cómo se realiza el trabajo en (o con) este sistema de servicios.

"Más o menos": esto significa que es posible preparar (o obtenerlo de algún lugar) una determinada herramienta, utilidad o servicio que se puede sintetizar y aplicar al sistema con una carga de prueba suficientemente adecuada a lo que estará en producción. en condiciones suficientemente adecuadas para trabajar en la producción.

Bueno, supongamos que se conoce un conjunto de parámetros de ajuste para este sistema de servicio, que pueden usarse para configurar este sistema en términos de la productividad de su trabajo.

¿Y cuál es el problema? No existe una comprensión suficientemente completa de este sistema de servicio, que le permita configurar de manera experta los ajustes de este sistema para la carga futura en una plataforma determinada y obtener la productividad requerida del sistema.

Bien. Este es casi siempre el caso.

¿Qué puedes hacer aquí?

Bueno, lo primero que me viene a la mente es mirar la documentación de este sistema. Comprender cuáles son los rangos aceptables para los valores de los parámetros de ajuste. Y, por ejemplo, utilizando el método de descenso de coordenadas, seleccione valores para los parámetros del sistema en las pruebas.

Aquellos. Dar al sistema algún tipo de configuración, en forma de un conjunto específico de valores para sus parámetros de configuración.

Aplíquele una carga de prueba, utilizando esta misma herramienta-utilidad, generador de carga.
Y observe el valor: la respuesta o una métrica de la calidad del sistema.

El segundo pensamiento puede ser la conclusión de que esto es mucho tiempo.

Bueno, es decir: si hay muchos parámetros de configuración, si los rangos de sus valores que se ejecutan son grandes, si cada prueba de carga individual requiere mucho tiempo para completarse, entonces: sí, todo esto puede tomar un tiempo inaceptable. largo tiempo.

Bueno, esto es lo que puedes entender y recordar.

Puede descubrir que en el conjunto de valores de los parámetros de configuración del sistema de servicio hay un vector, como una secuencia de algunos valores.

Cada uno de estos vectores, en igualdad de condiciones (en el sentido de que no se ve afectado por este vector), corresponde a un valor completamente definido de la métrica, un indicador de la calidad del funcionamiento del sistema bajo una carga de prueba.

es decir,

Denotaremos el vector de configuración del sistema como El método científico de empuje, o cómo seleccionar una configuración de base de datos utilizando puntos de referencia y un algoritmo de optimizaciónDonde El método científico de empuje, o cómo seleccionar una configuración de base de datos utilizando puntos de referencia y un algoritmo de optimización; Dónde El método científico de empuje, o cómo seleccionar una configuración de base de datos utilizando puntos de referencia y un algoritmo de optimización — número de parámetros de configuración del sistema, cuántos de estos parámetros hay.

Y el valor de la métrica correspondiente a este El método científico de empuje, o cómo seleccionar una configuración de base de datos utilizando puntos de referencia y un algoritmo de optimización denotémoslo como
El método científico de empuje, o cómo seleccionar una configuración de base de datos utilizando puntos de referencia y un algoritmo de optimización, entonces obtenemos una función: El método científico de empuje, o cómo seleccionar una configuración de base de datos utilizando puntos de referencia y un algoritmo de optimización

Bueno, entonces: todo se reduce inmediatamente a, en mi caso: casi olvidados de mi época de estudiante, algoritmos para buscar el extremo de una función.

Vale, pero aquí surge una cuestión organizativa y aplicada: qué algoritmo utilizar.

  1. En el sentido de que puedas codificar menos a mano.
  2. Y para que funcione, es decir. Encontré el extremo (si lo hay), bueno, al menos más rápido que el descenso coordinado.

El primer punto sugiere que debemos mirar hacia algunos entornos en los que dichos algoritmos ya se hayan implementado y, de alguna forma, estén listos para su uso en el código.
Bueno, yo sé python и cran-r

El segundo punto significa que es necesario leer sobre los algoritmos en sí, qué son, cuáles son sus requisitos y las características de su trabajo.

Y lo que aportan pueden ser efectos secundarios útiles: resultados o directamente del propio algoritmo.

O pueden obtenerse a partir de los resultados del algoritmo.

Mucho depende de las condiciones de entrada.

Por ejemplo, si, por alguna razón, necesita obtener un resultado más rápido, debe buscar algoritmos de descenso de gradiente y elegir uno de ellos.

O, si el tiempo no es tan importante, se pueden utilizar, por ejemplo, métodos de optimización estocástica, como un algoritmo genético.

Propongo considerar el trabajo de este enfoque, seleccionando la configuración del sistema, utilizando un algoritmo genético, en el siguiente, por así decirlo: trabajo de laboratorio.

Fuente:

  1. Que haya, como sistema de servicio: oracle xe 18c
  2. Que sirva para la actividad transaccional y el objetivo: obtener el mayor rendimiento posible de la subbase de datos, en transacciones/seg.
  3. Las transacciones pueden ser muy diferentes en la naturaleza del trabajo con datos y el contexto del trabajo.
    Aceptemos que se trata de transacciones que no procesan una gran cantidad de datos tabulares.
    En el sentido de que no generan más datos de deshacer que de rehacer y no procesan grandes porcentajes de filas y tablas grandes.

Son transacciones que cambian una fila en una tabla más o menos grande, con una pequeña cantidad de índices en esta tabla.

En esta situación: la productividad de la subbase de datos para procesar transacciones estará determinada, con reserva, por la calidad del procesamiento por parte de la base de datos redox.

Descargo de responsabilidad: si hablamos específicamente de la configuración de subdb.

Porque, en el caso general, puede haber, por ejemplo, bloqueos transaccionales entre sesiones SQL, debido al diseño del trabajo del usuario con datos tabulares y/o al modelo tabular.

Lo que, por supuesto, tendrá un efecto deprimente en la métrica TPS y será un factor exógeno, en relación con la subbase de datos: bueno, así es como se diseñó el modelo tabular y el trabajo con los datos que contiene, por lo que se producen bloqueos.

Por lo tanto, para garantizar la pureza del experimento, excluiremos este factor y a continuación aclararé exactamente cómo.

  1. Supongamos, para ser más precisos, que el 100% de los comandos SQL enviados a la base de datos son comandos DML.
    Deje que las características del usuario que trabaja con la subbase de datos sean las mismas en las pruebas.
    A saber: el número de sesiones de skl, datos tabulares, cómo funcionan las sesiones de skl con ellos.
  2. Subd trabaja en FORCE LOGGING, ARCHIVELOG mods. El modo de base de datos Flashback está desactivado, en el nivel subd.
  3. Registros de rehacer: ubicados en un sistema de archivos separado, en un “disco” separado;
    El resto del componente físico de la base de datos: en otro sistema de archivos separado, en un “disco” separado:

Más detalles sobre el dispositivo físico. componentes de la base de datos de laboratorio

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, bajo estas condiciones de carga, quería usar subd de transacción Utilidad SLOB
Tiene una característica tan maravillosa que citaré al autor:

En el corazón de SLOB se encuentra el “método SLOB”. El Método SLOB tiene como objetivo probar plataformas
sin contienda de solicitud. No se puede lograr el máximo rendimiento del hardware
utilizando código de aplicación que está, por ejemplo, vinculado por el bloqueo de la aplicación o incluso
compartir bloques de bases de datos Oracle. Así es, hay gastos generales al compartir datos.
en bloques de datos! Pero SLOB—en su implementación predeterminada—es inmune a tal disputa.

Esta declaración: corresponde, lo es.
Conviene regular el grado de paralelismo de las sesiones cl, esta es la clave -t iniciar la utilidad runit.sh de SLOB
Se regula el porcentaje de comandos DML, en la cantidad de mensajes de texto que se envían al subd, cada sesión de texto, parámetro UPDATE_PCT
Por separado y muy cómodamente: SLOB en sí, antes y después de la sesión de carga: prepara un paquete de estadísticas o awr-snapshots (lo que está configurado para prepararse).

Sin embargo, resultó que SLOB no admite sesiones SQL con una duración inferior a 30 segundos.
Por lo tanto, primero codifiqué mi propia versión trabajador-campesino del cargador y luego permaneció en funcionamiento.

Permítanme aclarar qué hace el cargador y cómo lo hace, para mayor claridad.
Básicamente, el cargador se ve así:

codigo de trabajador

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

Los trabajadores se lanzan de esta manera:

trabajadores corriendo

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

Y las mesas para los trabajadores se preparan así:

Creación de la tabla

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"

Aquellos. Para cada trabajador (prácticamente: una sesión SQL separada en la base de datos) se crea una tabla separada con la que trabaja el trabajador.

Esto garantiza la ausencia de bloqueos transaccionales entre sesiones de trabajadores.
Cada trabajador: hace lo mismo, con su propia mesa, las mesas son todas iguales.
Todos los trabajadores realizan su trabajo durante la misma cantidad de tiempo.
Además, durante un tiempo suficientemente largo como para que, por ejemplo, se produzca definitivamente un cambio de registro, y más de una vez.
Bueno, en consecuencia, surgieron costos y efectos asociados.
En mi caso configuré la duración del trabajo de los trabajadores en 8 minutos.

Una parte de un informe del paquete de estadísticas que describe el funcionamiento del subd bajo 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

Volviendo al trabajo de laboratorio.
En igualdad de condiciones, variaremos los valores de los siguientes parámetros de la subbase de datos del laboratorio:

  1. Tamaño de los grupos de registros de bases de datos. rango de valores: [32, 1024] MB;
  2. Número de grupos de revistas en la base de datos. rango de valores: [2,32];
  3. log_archive_max_processes rango de valores: [1,8];
  4. commit_logging Se permiten dos valores: batch|immediate;
  5. commit_wait Se permiten dos valores: wait|nowait;
  6. log_buffer rango de valores: [2,128] MB.
  7. log_checkpoint_timeout rango de valores: [60,1200] segundos
  8. db_writer_processes rango de valores: [1,4]
  9. undo_retention rango de valores: [30;300] segundos
  10. transactions_per_rollback_segment rango de valores: [1,8]
  11. disk_asynch_io Se permiten dos valores: true|false;
  12. filesystemio_options Se permiten los siguientes valores: none|setall|directIO|asynch;
  13. db_block_checking Se permiten los siguientes valores: OFF|LOW|MEDIUM|FULL;
  14. db_block_checksum Se permiten los siguientes valores: OFF|TYPICAL|FULL;

Una persona con experiencia en el mantenimiento de bases de datos Oracle seguramente ya puede decir qué y hasta qué valores se deben establecer, a partir de los parámetros especificados y sus valores aceptables, para obtener una mayor productividad de la base de datos para el trabajo con los datos indicados por el código de la aplicación, aquí arriba.

Pero.

El objetivo del trabajo de laboratorio es demostrar que el propio algoritmo de optimización nos aclarará esto con relativa rapidez.

A nosotros sólo nos queda mirar el documento, a través del sistema personalizable, lo suficiente para saber qué parámetros cambiar y en qué rangos.
Y también: codificar el código que se utilizará para trabajar con el sistema personalizado del algoritmo de optimización seleccionado.

Entonces, ahora sobre el código.
hablé arriba sobre cran-r, es decir: todas las manipulaciones con el sistema personalizado se organizan en forma de script R.

La tarea real, análisis, selección por valor métrico, vectores de estado del sistema: este es un paquete GA (la documentación)
El paquete, en este caso, no es muy adecuado, en el sentido de que espera que los vectores (cromosomas, si están en términos del paquete) se especifiquen en forma de cadenas de números con una parte fraccionaria.

Y mi vector, a partir de los valores de los parámetros de configuración: estas son 14 cantidades: números enteros y valores de cadena.

El problema, por supuesto, se evita fácilmente asignando algunos números específicos a los valores de las cadenas.

Así, al final, la parte principal del script R se ve así:

Llamar a 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

Aquí, con la ayuda lower и upper atributos de subrutina ga esencialmente, se especifica un área del espacio de búsqueda, dentro de la cual se realizará una búsqueda de dicho vector (o vectores) para los cuales se obtendrá el valor máximo de la función de aptitud.

La subrutina ga realiza una búsqueda maximizando la función de aptitud.

Pues bien, resulta que, en este caso, es necesario que la función fitness, entendiendo el vector como un conjunto de valores para ciertos parámetros del subd, reciba una métrica del subd.

Es decir: cuántos, con una configuración de subd determinada y una carga determinada en el subd: el subd procesa transacciones por segundo.

Es decir, al desplegarse se deben realizar los siguientes multipasos dentro de la función fitness:

  1. Procesar el vector de entrada de números: convertirlo en valores para los parámetros de subdatos.
  2. Un intento de crear un número determinado de grupos de rehacer de un tamaño determinado. Además, el intento puede fracasar.
    Grupos de revistas que ya existían en el subd, en cierta cantidad y de cierto tamaño, para la pureza del experimento - d.b. eliminado.
  3. Si el punto anterior tiene éxito: especificar los valores de los parámetros de configuración a la base de datos (nuevamente: puede haber un fallo)
  4. Si el paso anterior es exitoso: detener el subd, iniciar el subd para que los valores de los parámetros recién especificados surtan efecto. (nuevamente: puede haber un problema técnico)
  5. Si el paso anterior tiene éxito: realice una prueba de carga. obtener métricas de subd.
  6. Devuelve el subd a su estado original, es decir. elimine grupos de registros adicionales y vuelva a funcionar la configuración de la subbase de datos original.

Código de función de 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 ) {
 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)
}

Eso. todo el trabajo: realizado en la función fitness.

La subrutina gas procesa vectores o, más correctamente, cromosomas.
En lo cual, lo más importante para nosotros es la selección de cromosomas con genes para los cuales la función de aptitud produce valores grandes.

Este, en esencia, es el proceso de buscar el conjunto óptimo de cromosomas utilizando un vector en un espacio de búsqueda N-dimensional.

Muy claro, detallado explicación, con ejemplos de código R, obra de un algoritmo genético.

Me gustaría señalar por separado dos puntos técnicos.

Llamadas auxiliares desde la función. evaluate, por ejemplo, detener-iniciar, establecer el valor del parámetro subd, se realizan en función de cran-r funciones system2

Con la ayuda del cual: se llama algún script o comando bash.

Por ejemplo:

set_db_parametro

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

El segundo punto es la línea, evaluate funciones, guardando un valor métrico específico y su correspondiente vector de ajuste en un archivo de registro:

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

Esto es importante porque a partir de esta matriz de datos será posible obtener información adicional sobre cuál de los componentes del vector de sintonización tiene un mayor o menor efecto sobre el valor de la métrica.

Es decir: será posible realizar un análisis de importancia de los atributos.

Entonces, ¿qué puede pasar?

En forma de gráfico, si ordena las pruebas en orden métrico ascendente, la imagen es la siguiente:

El método científico de empuje, o cómo seleccionar una configuración de base de datos utilizando puntos de referencia y un algoritmo de optimización

Algunos datos correspondientes a los valores extremos de la métrica:
El método científico de empuje, o cómo seleccionar una configuración de base de datos utilizando puntos de referencia y un algoritmo de optimización
Aquí, en la captura de pantalla con los resultados, lo aclararé: los valores del vector de ajuste se dan en términos del código de la función de aptitud, no en términos de la lista numérica de parámetros/rangos de valores de parámetros, que se formuló arriba en el texto.

Bien. ¿Es mucho o poco? ~8 mil tps: una pregunta aparte.
En el marco del trabajo de laboratorio esta cifra no es importante, lo importante es la dinámica, cómo cambia este valor.

La dinámica aquí es buena.
Es obvio que al menos un factor influye significativamente en el valor de la métrica, el algoritmo ga, que clasifica los vectores cromosómicos: cubiertos.
A juzgar por la dinámica bastante vigorosa de los valores de la curva, hay al menos un factor más que, aunque significativamente menor, influye.

Aquí es donde lo necesitas attribute-importance análisis para comprender qué atributos (bueno, en este caso, componentes del vector de ajuste) y cuánto influyen en el valor de la métrica.
Y a partir de esta información: comprender qué factores se vieron afectados por cambios en atributos significativos.

corrida attribute-importance posible de diferentes maneras.

Para estos propósitos, me gusta el algoritmo. randomForest Paquete R del mismo nombre (la documentación)
randomForest, según tengo entendido su trabajo en general y su enfoque para evaluar la importancia de los atributos en particular, construye un cierto modelo de dependencia de la variable de respuesta de los atributos.

En nuestro caso, la variable respuesta es una métrica obtenida de la base de datos en pruebas de carga: tps;
Y los atributos son componentes del vector de sintonización.

Entonces randomForest evalúa la importancia de cada atributo del modelo con dos números: %IncMSE — cómo la presencia/ausencia de este atributo en un modelo cambia la calidad MSE de este modelo (error cuadrático medio);

Y IncNodePurity es un número que refleja qué tan bien, en base a los valores de este atributo, se puede dividir un conjunto de datos con observaciones, de modo que en una parte haya datos con un valor de la métrica que se está explicando, y en la otra con otro valor de la métrica.
Bueno, es decir: ¿hasta qué punto es este un atributo de clasificación (vi la explicación más clara en ruso en RandomForest aquí).

Código R trabajador-campesino para procesar un conjunto de datos con los resultados de las pruebas 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

Puede seleccionar directamente los hiperparámetros del algoritmo con sus manos y, centrándose en la calidad del modelo, seleccionar un modelo que cumpla con mayor precisión las predicciones en el conjunto de datos de validación.
Puedes escribir algún tipo de función para este trabajo (por cierto, nuevamente, usando algún tipo de algoritmo de optimización).

Puedes usar el paquete R. caret, no el punto es importante.

Como resultado, en este caso, se obtiene el siguiente resultado para evaluar el grado de importancia de los atributos:

El método científico de empuje, o cómo seleccionar una configuración de base de datos utilizando puntos de referencia y un algoritmo de optimización

Bien. Así, podemos iniciar una reflexión global:

  1. Resulta que el más significativo, en estas condiciones de prueba, fue el parámetro commit_wait
    Técnicamente, especifica el modo de ejecución de la operación io de escribir datos de rehacer desde el búfer de registro subdb al grupo de registro actual: sincrónico o asincrónico.
    Valor nowait lo que da como resultado un aumento múltiple casi vertical en el valor de la métrica tps: esta es la inclusión del modo io asíncrono en los grupos de rehacer.
    Una pregunta aparte es si debería o no hacer esto en una base de datos de alimentos. Aquí me limito a decir: este es un factor significativo.
  2. Es lógico que el tamaño del búfer de registro del subd: resulte ser un factor importante.
    Cuanto menor es el tamaño del búfer de registro, menor es su capacidad de almacenamiento en búfer, más a menudo se desborda y/o la imposibilidad de asignar un área libre en él para una parte de los nuevos datos redox.
    Esto significa: retrasos asociados con la asignación de espacio en el búfer de registro y/o el volcado de datos de rehacer en grupos de rehacer.
    Estos retrasos, por supuesto, deberían afectar y afectan el rendimiento de la base de datos para las transacciones.
  3. Parámetro db_block_checksum: bueno, también, en general, está claro: el procesamiento de transacciones conduce a la formación de bloques dardos en el caché del búfer de la subbase de datos.
    Lo cual, cuando la verificación de las sumas de verificación de los bloques de datos está habilitada, la base de datos debe procesar: calcular estas sumas de verificación a partir del cuerpo del bloque de datos, verificarlas con lo que está escrito en el encabezado del bloque de datos: coincide/no coincide.
    Este trabajo, nuevamente, no puede dejar de retrasar el procesamiento de datos y, en consecuencia, el parámetro y el mecanismo que establece este parámetro resultan significativos.
    Es por eso que el proveedor ofrece, en la documentación para este parámetro, diferentes valores para él (el parámetro) y señala que sí, habrá un impacto, pero, bueno, puedes elegir diferentes valores, hasta “off” y diferentes impactos.

Bueno, una conclusión global.

El enfoque, en general, resulta bastante funcional.

Se permite bastante, en las primeras etapas de las pruebas de carga de un determinado sistema de servicio, seleccionar su configuración óptima (del sistema) para la carga, sin profundizar demasiado en los detalles de la configuración del sistema para la carga.

Pero esto no lo excluye por completo, al menos a nivel de comprensión: el sistema debe conocer los “perillas de ajuste” y los rangos de rotación permitidos de estas perillas.

El enfoque puede entonces encontrar con relativa rapidez la configuración óptima del sistema.
Y según los resultados de las pruebas, es posible obtener información sobre la naturaleza de la relación entre las métricas de rendimiento del sistema y los valores de los parámetros de configuración del sistema.

Lo que, por supuesto, debería contribuir al surgimiento de esta comprensión muy profunda del sistema, de su funcionamiento, al menos bajo una carga determinada.

En la práctica, se trata de un intercambio de los costes de comprensión del sistema personalizado por los costes de preparación de dichas pruebas del sistema.

Me gustaría señalar por separado: en este enfoque, el grado de adecuación de las pruebas del sistema a las condiciones de funcionamiento que tendrá en funcionamiento comercial es de vital importancia.

Gracias por su atención y tiempo.

Fuente: habr.com

Añadir un comentario