Waiting for dependent transaction to commit/Waiting for preceding transaction to commit

文章目录

      • **简介**
      • **出处**
      • **图解**

简介

首先说明的是这并不是多线程复制中的问题,而是一个现象,这里来解释说明这个现象。
开启多线程回放的同学如果经常在slave示例上执行show processlist命令的话,应该对这两个信息不陌生。

出处

开启多线程回放后,回放控制线程会根据既定的规则,进行并发回放。因此,后续事务如果不可以跟正在回放的事务并发的话,就必须要进行等待。如果开启了slave_preserve_commit_order,进行并发回放的多个事务之间,也要按照和主库上提交的顺序一样,进行提交。以上所述这也是这两个信息出现的原因。其中,Waiting for dependent transaction to commit,是当前事务无法和正在回放的事务并发回放出现的等待;Waiting for preceding transaction to commit,是当前并发回放的事务在进入commit时的flush队列前,必须等到先前事务已经进入flush队列而引起的等待。

代码位置

bool Mts_submode_logical_clock::
wait_for_last_committed_trx(Relay_log_info* rli,
                            longlong last_committed_arg,
                            longlong lwm_estimate_arg)
{
   if ((!rli->info_thd->killed && !is_error) &&
      !clock_leq(last_committed_arg, get_lwm_timestamp(rli, true)))
  {
    PSI_stage_info old_stage;
    struct timespec ts[2];
    set_timespec_nsec(&ts[0], 0);

    DBUG_ASSERT(rli->gaq->len >= 2); // there's someone to wait

    thd->ENTER_COND(&rli->logical_clock_cond, &rli->mts_gaq_LOCK,
                    &stage_worker_waiting_for_commit_parent, &old_stage); //这里就是看到的Waiting for dependent transaction to commit
    do
    {
      mysql_cond_wait(&rli->logical_clock_cond, &rli->mts_gaq_LOCK);
    }
    while ((!rli->info_thd->killed && !is_error) &&
           !clock_leq(last_committed_arg, estimate_lwm_timestamp()));
    my_atomic_store64(&min_waited_timestamp, SEQ_UNINIT);  // reset waiting flag
    mysql_mutex_unlock(&rli->mts_gaq_LOCK);
    thd->EXIT_COND(&old_stage);
    set_timespec_nsec(&ts[1], 0);
    my_atomic_add64(&rli->mts_total_wait_overlap, diff_timespec(&ts[1], &ts[0]));
  }                         
                            
}

Waiting for preceding transaction to commit在如下函数中

/**
  Waits until it becomes the queue head.

  @retval false All previous threads succeeded so this thread can go
  ahead and commit.
*/
bool Commit_order_manager::wait_for_its_turn(Slave_worker *worker,
                                                  bool all)
{
  DBUG_ENTER("Commit_order_manager::wait_for_its_turn");

  /*
    When prior transaction fail, current trx should stop and wait for signal
    to rollback itself
  */
  if ((all || ending_single_stmt_trans(worker->info_thd, all) || m_rollback_trx) &&
      m_workers[worker->id].status == OCS_WAIT)
  {
    PSI_stage_info old_stage;
    mysql_cond_t *cond= &m_workers[worker->id].cond;
    THD *thd= worker->info_thd;

    DBUG_PRINT("info", ("Worker %lu is waiting for commit signal", worker->id));

    mysql_mutex_lock(&m_mutex);
    thd->ENTER_COND(cond, &m_mutex,
                    &stage_worker_waiting_for_its_turn_to_commit,
                    &old_stage); //这里就是看到的Waiting for preceding transaction to commit

    while (queue_front() != worker->id)
    {
      if (unlikely(worker->found_order_commit_deadlock()))
      {
        mysql_mutex_unlock(&m_mutex);
        thd->EXIT_COND(&old_stage);
        DBUG_RETURN(true);
      }
      mysql_cond_wait(cond, &m_mutex);
    }

    mysql_mutex_unlock(&m_mutex);
    thd->EXIT_COND(&old_stage);

    m_workers[worker->id].status= OCS_SIGNAL;

    if (m_rollback_trx)
    {
      unregister_trx(worker);

      DBUG_PRINT("info", ("thd has seen an error signal from old thread"));
      thd->get_stmt_da()->set_overwrite_status(true);
      my_error(ER_SLAVE_WORKER_STOPPED_PREVIOUS_THD_ERROR, MYF(0));
    }
  }

  DBUG_RETURN(m_rollback_trx);
}

图解

把这两种等待以图的方式展现出来,就是如下的样子
假设有三个事务如下,t1/t2可以并发回放,t3则必须等待t2回放完成之后才可以进行回放,其等待关系如下
Waiting for dependent transaction to commit/Waiting for preceding transaction to commit_第1张图片

你可能感兴趣的:(Waiting for dependent transaction to commit/Waiting for preceding transaction to commit)