Análisis del hilo de purga InnoDB

Autor: ocho extraños (Gao) también en expertos en tecnología de bases de datos

El nivel es limitado, comprenda si hay algún error. La versión del código fuente es 8.0.21.


Cuando se trata de una falla, se sospecha que una gran cantidad de datos eliminados ha provocado que la consulta sea lenta, pero no he sido muy claro sobre el flujo de trabajo del hilo de purga. Este artículo no hace un análisis en profundidad, solo el análisis del flujo de trabajo, y espero las siguientes preguntas:

  • Si el registro de la bandera del borrado se puede limpiar a tiempo

  • ¿Por qué la longitud de la lista de historial continuamente no es 0? ¿Significa que el registro de la bandera del no se limpia?

  • ¿Cuáles son las reglas activadas por el hilo de purga?

1. Descripción general del hilo de purga

En términos generales, el hilo de purga que entendemos puede realizar el siguiente trabajo:

  • Limpiar los registros de etiquetas del flag

  • Limpiar la versión histórica de deshacer

  • Si necesita deshacer el truncamiento del espacio de tabla.

Contiene un hilo de coordinación y varios hilos de trabajo establecidos por los siguientes parámetros:

innodb_purge_threads=4

Esto representa 1 hilo de coordinación y 3 hilos de trabajo. El hilo de coordinación también actuará como hilo de trabajo.

Dos cambios de detección de bucle de hilo coordinado

如下调入:
srv_purge_coordinator_thread
 ->srv_purge_coordinator_suspend
判断如下:
(rseg_history_len <= trx_sys->rseg_history_len) { 
//如果当前history_len大于等于上一次循环的的history_len
      ret =os_event_wait_time_low(slot->event, SRV_PURGE_MAX_TIMEOUT, sig_count); 
//等待10毫秒后进行处理或者等待被唤醒

La condición para despertar es la confirmación o reversión de la transacción.

    /* Tell server some activity has happened, since the trx
    does changes something. Background utility threads like
    master thread, purge thread or page_cleaner thread might
    have some work to do. */
    srv_active_wake_master_thread();

Pero debe tenerse en cuenta que si no hay una nueva transacción para confirmar durante mucho tiempo, puede entrar en un estado de bloqueo permanente en lugar de despertarse cada 10 milisegundos hasta que se despierte.

if (ret == OS_SYNC_TIME_EXCEEDED) { //如果是等待超时
      if (rseg_history_len == trx_sys->rseg_history_len &&
          trx_sys->rseg_history_len < 5000) { //如果上次的history_len和本次history_len相同且小于5000那么需要等待唤醒
        stop = true; //设置为true,进行无限期等待,直到唤醒
      }

Tres, clona la vista de lectura más antigua

No hay nada que decir sobre este paso, porque el deshacer debe limpiarse de acuerdo con la vista de lectura más antigua actual; de lo contrario, puede limpiarse hasta el deshacer que se está leyendo.

如下调入:
srv_purge_coordinator_thread
 ->srv_do_purge
  ->trx_purge
操作如下:
trx_sys->mvcc->clone_oldest_view(&purge_sys->view); //克隆老的 read view srv_do_purge

En cuarto lugar, elimine el segmento de deshacer de purge_queue que puede necesitar ser limpiado (entendido simplemente como una transacción)

调入如下:
srv_purge_coordinator_thread
 ->srv_do_purge
  ->trx_purge
   ->trx_purge_attach_undo_recs
    ->trx_purge_fetch_next_rec
     ->TrxUndoRsegsIterator::set_next
操作如下:
const page_size_t &page_size = purge_sys->rseg_iter->set_next();

Tenga en cuenta que este es un iterador, la iteración es purge_sys-> purge_queue, que es la cola de prioridad implementada por std :: priority_queue. El código de iteración específico es el siguiente:

while (!m_purge_sys->purge_queue->empty()) { //如果有事务需要清理
      if (m_trx_undo_rsegs.get_trx_no() == UINT64_UNDEFINED) {
        m_trx_undo_rsegs = purge_sys->purge_queue->top();
      } else if (purge_sys->purge_queue->top().get_trx_no() ==
                 m_trx_undo_rsegs.get_trx_no()) {
        m_trx_undo_rsegs.append(purge_sys->purge_queue->top()); //弹出一个
      } else {
        break;
      }

La transacción entra en purge_queue llamando a trx_serialisation_number_get cuando la transacción se confirma

purge_sys->purge_queue->push(elem);

Entonces, aquí sabemos que cuando se confirma la transacción, el subproceso de coordinación de purga puede activarse para que funcione y se agregará a la cola de transacciones purge_queue que puede requerir purga.

Cinco, juzga si cumple con las reglas de limpieza

调入如下:
srv_purge_coordinator_thread
 ->srv_do_purge
  ->trx_purge
   ->trx_purge_attach_undo_recs
    ->trx_purge_fetch_next_rec
判断如下:
  if (purge_sys->iter.trx_no >= purge_sys->view.low_limit_no()) {
    return (nullptr);
  }

Aquí se determina si el número de trx de la transacción que debe limpiarse es mayor que el número de límite bajo de la vista de lectura más antigua. Si no se cumple, devuelve nullptr. Si lo hace, devuelve el número de páginas que deben limpiarse y apunta al siguiente segmento de deshacer que debe limpiarse.

6. El valor predeterminado de cada limpieza es de 300 páginas.

Este valor está controlado por el parámetro innodb_purge_batch_size, el valor predeterminado es 300

调入如下:
srv_purge_coordinator_thread
 ->srv_do_purge
  ->trx_purge
   ->trx_purge_attach_undo_recs
生效如下:
for (ulint i = 0; n_pages_handled < batch_size; ++i)

El proceso de limpieza continuará hasta que no queden páginas que limpiar.

调入如下:
srv_purge_coordinator_thread
 ->srv_do_purge
判断如下:
 (!srv_purge_should_exit(n_pages_purged) && n_pages_purged > 0 &&
           purge_sys->state == PURGE_STATE_RUN); 
//清理完成后n_pages_purged > 0 将不会满足
return (rseg_history_len); //返回 rseg_history_len

Siete, procesamiento de subprocesos de trabajo

Después de ser distribuido al hilo de trabajo, ingresa la siguiente llamada para limpiar la bandera del. Sin mirar esta parte con cuidado, la llamada es más complicada. Pero es cierto que su proceso de construcción (row_purge_parse_undo_rec) y eliminación puede requerir muchos bucles y operaciones de posicionamiento de datos (btr_cur_search_to_nth_level).

srv_worker_thread
 ->srv_task_execute
  ->que_run_threads
   ->que_run_threads_low
    ->que_thr_step
     ->row_purge_step
      ->row_purge
       ->row_purge_record_func

8. De forma predeterminada, el historial de deshacer se limpiará cada 128 lotes de limpieza de deshacer.

Esto está relacionado con la configuración del parámetro innodb_purge_rseg_truncate_frequency, el valor predeterminado es 128, si la carga completa se calcula como:

  • 300 (deshacer páginas de registro) * 128 (frecuencia truncada) = 38,400

Después de que se procesen 38400 páginas de registro de deshacer, el historial de deshacer se limpiará una vez.

根据参数赋值
set_rseg_truncate_frequency(
        static_cast<ulint>(srv_purge_rseg_truncate_frequency));

参数判断
    ulint rseg_truncate_frequency = ut_min(
        static_cast<ulint>(srv_purge_rseg_truncate_frequency), undo_trunc_freq); //128

    n_pages_purged = trx_purge(n_use_threads, srv_purge_batch_size,
                               (++count % rseg_truncate_frequency) == 0);//每128次进行一次清理

判断是否进入truncate流程
  if (truncate || srv_upgrade_old_undo_found) { //truncate就是根据(++count % rseg_truncate_frequency)计算而来
    trx_purge_truncate();
  }

Pero debe tenerse en cuenta que contar es una variable local estática, por lo que cada vez que se llama a la función, el último valor seguirá contando. Si la presión es baja, es posible que deshacer no se limpie a tiempo:

  • Si las transacciones pequeñas son transacciones pequeñas, es posible que la cantidad de páginas de deshacer modificadas por cada transacción no llegue a 300, por lo que debe esperar a que las 128 transacciones se limpien una vez.

  • Transacción grande Si la transacción es relativamente grande y hay muchas páginas para deshacer, se limpiará si supera los 300 * 128.

Esto no quiere decir que los registros de la bandera del no se limpien, pero que la lista enlazada del historial de deshacer no se limpia. Por lo tanto, a menudo vemos que la longitud de la lista de Historial no es 0.

Nueve, limpia el historial de deshacer y deshace el espacio

Aquí hay un registro simple de su proceso de trabajo. Sin descripción detallada de la función (capacidad limitada)

Kiyori deshacer la historia
调入如下:
srv_purge_coordinator_thread
 ->srv_do_purge
  ->trx_purge
   ->trx_purge_truncate
    ->trx_purge_truncate_history
     ->trx_purge_truncate_rseg_history

El método de limpieza es el siguiente:

清理的起点:
hdr_addr = trx_purge_get_log_from_hist(
      flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
向上扫描:
 hdr_addr = prev_hdr_addr;
结束条件:
  if (undo_trx_no >= limit->trx_no) { //这里代表结束了
    /* limit space_id should match the rollback segment
    space id to avoid freeing if the page belongs to a
    different rollback segment for the same trx_no. */
    if (undo_trx_no == limit->trx_no &&
        rseg->space_id == limit->undo_rseg_space) {
      trx_undo_truncate_start(rseg, hdr_addr.page, hdr_addr.boffset,
                              limit->undo_no);
    }

    rseg->unlatch();
    mtr_commit(&mtr);

    return;
  }

Vale la pena señalar que este proceso de limpieza no puede ser mayor que trx no de la vista de lectura más antigua; de lo contrario, la limpieza finaliza.

truncar el proceso de deshacer
调入如下:
srv_purge_coordinator_thread
 ->srv_do_purge
  ->trx_purge
   ->trx_purge_truncate
    ->trx_purge_truncate_history
     ->trx_purge_truncate_marked_undo

Antes de esto, hay un proceso para determinar si limpiar

trx_purge_mark_undo_for_truncate
 ->Tablespace::needs_truncation

Tablespace :: need_truncation determinará si realizar deshacer truncar, aquí hay dos parámetros involucrados

  • El rol del parámetro innodb_undo_log_truncate

  if (!srv_undo_log_truncate || m_rsegs == nullptr || m_rsegs->is_empty() ||
      m_rsegs->is_init()) {
    m_rsegs->s_unlock();
    return (false); //如果没有开启undo truncate则不进行清理
  }
  • El rol del parámetro innodb_max_undo_log_size

page_no_t trunc_size = ut_max(
      static_cast<page_no_t>(srv_max_undo_tablespace_size / srv_page_size),
      static_cast<page_no_t>(SRV_UNDO_TABLESPACE_SIZE_IN_PAGES)); //10MB

  if (fil_space_get_size(id()) > trunc_size) { //如果undo tablespace大小大于了innodb_max_undo_log_size
    return (true); //则进行清理
  }

10. Resumen

Básicamente, hemos entendido los problemas al principio de aquí, de la siguiente manera:

  • Una vez que se envía la transacción, el subproceso de coordinación determina si se puede limpiar la marca del. Si se puede limpiar, se distribuirá al subproceso de trabajo para su limpieza. Este es un proceso asíncrono. Si hay más cambios de datos, este proceso puede ser más lento y puede ver El hilo relacionado con la purga está bajo presión, pero aún es oportuno.

  • El hilo de purga siempre se acumulará durante un período de tiempo para limpiar la longitud de la lista del historial. Si se trata de una transacción pequeña (la página que se modifica cada vez es menor que la configuración de innodb_purge_batch_size), entonces se necesitan 128 transacciones pequeñas para limpiar una vez, si es una transacción grande, entonces modifique Si la configuración excede (innodb_purge_batch_size * innodb_purge_rseg_truncate_frequency), se limpiará una vez, pero no importa cómo este indicador siga siendo distinto de cero, es normal. Si es más grande, puede significar que hay consultas grandes o que cada subproceso de purga está funcionando a plena capacidad. De la siguiente manera, 9281 es un hilo de trabajo de purga:

Y el estado del hilo de purga está en estado de ejecución

  • El hilo de coordinación de purga se activa cada vez que se confirma una transacción para determinar si hay una transacción que debe limpiarse. Si no hay ninguna transacción durante mucho tiempo, esperará los primeros 10 ms y entrará en un estado de espera de bloqueo a largo plazo después del tiempo de espera.

Se acabó el texto completo.

Disfruta MySQL :)

Escanee el código para agregar el autor WeChat

La clase "MySQL Core Optimization" de Teacher Ye se ha actualizado a MySQL 8.0, escanee el código para comenzar el viaje de la práctica de MySQL 8.0

Supongo que te gusta

Origin blog.csdn.net/n88Lpo/article/details/110507415
Recomendado
Clasificación