MySQL中的幂等复制分析

来源:这里教程网 时间:2026-03-01 17:02:33 作者:

之前遇到复制异常,如果确定没有问题,都是跳过异常事务,最近看到了一个新的复制模式:幂等复制

--slave-exec-mode=mode
这个参数默认是strict模式,可以设置成IDEMPOTENT,进入幂等复制模式

官方文档中描述,这种模式能抑制duplicate-key and no-key-found errors; 下面看下代码中的实现,首先是定义了幂等模式的错误码,对普通的错误码,进行了转义

inline int idempotent_error_code(int err_code)
{
int ret= 0;
switch (err_code)
{
case 0:
ret= 1;
break;
    /*
      The following list of "idempotent" errors
      means that an error from the list might happen
      because of idempotent (more than once)
      applying of a binlog file.
      Notice, that binlog has a  ddl operation its
      second applying may cause
      case HA_ERR_TABLE_DEF_CHANGED:
      case HA_ERR_CANNOT_ADD_FOREIGN:
      which are not included into to the list.
      Note that HA_ERR_RECORD_DELETED is not in the list since
      do_exec_row() should not return that error code.
    */
case HA_ERR_RECORD_CHANGED:
case HA_ERR_KEY_NOT_FOUND:
case HA_ERR_END_OF_FILE:
case HA_ERR_FOUND_DUPP_KEY:
case HA_ERR_FOUND_DUPP_UNIQUE:
case HA_ERR_FOREIGN_DUPLICATE_KEY:
case HA_ERR_NO_REFERENCED_ROW:
case HA_ERR_ROW_IS_REFERENCED:
ret= 1;
break;
default:
ret= 0;
break;
}
return (ret);
}

处理幂等错误的函数,如果转义后的错误码是幂等的错误,那么级别就是warning,并且清理worker中的错误

int Rows_log_event::handle_idempotent_and_ignored_errors(Relay_log_info const *rli, int *err)
{
int error= *err;
if (error)
{
int actual_error= convert_handler_error(error, thd, m_table);
bool idempotent_error= (idempotent_error_code(error) &&
(rbr_exec_mode == RBR_EXEC_MODE_IDEMPOTENT));
bool ignored_error= (idempotent_error == 0 ?
ignored_error_code(actual_error) : 0);
if (idempotent_error || ignored_error)
{
loglevel ll;
if (idempotent_error)
ll= WARNING_LEVEL;
else
ll= INFORMATION_LEVEL;
slave_rows_error_report(ll, error, rli, thd, m_table,
get_type_str(),
const_cast<Relay_log_info*>(rli)->get_rpl_log_name(),
(ulong) common_header->log_pos);
thd->get_stmt_da()->reset_condition_info(thd);
clear_all_errors(thd, const_cast<Relay_log_info*>(rli));
*err= 0;
if (idempotent_error == 0)
return ignored_error;
}
}
return *err;
}

在从库的

Rows_log_event::do_scan_and_update(Relay_log_info const *rli)

执行中,查找到记录,应用的时候,报错,就调用幂等错误码处理函数,进行处理

if ((error= do_apply_row(rli)))
{
if (handle_idempotent_and_ignored_errors(rli, &error))
goto close_table;
do_post_row_operations(rli, error);
}
在找不到记录的时候也进行处理
case HA_ERR_KEY_NOT_FOUND:
        /* If the slave exec mode is idempotent or the error is
            skipped error, then don't break */
if (handle_idempotent_and_ignored_errors(rli, &error))
goto close_table;
idempotent_errors++;
continue;

我们看到在处理错误的时候,只是进行了错误日志级别的调整,和清除错误信息,没有其他操作,在找到记录后,处理了错误,然后又进行了后续的处理do_post_row_operations,这个函数中就是设置了变量m_curr_row,针对主键冲突的问题,是用master的值更新了冲突的行,这个是在哪里进行的? 在写入数据的时候,会传递一个参数,指定是否要覆盖

int
Write_rows_log_event::do_exec_row(const Relay_log_info *const rli)
{
DBUG_ASSERT(m_table != NULL);
int error= write_row(rli, rbr_exec_mode == RBR_EXEC_MODE_IDEMPOTENT);
if (error && !thd->is_error())
{
DBUG_ASSERT(0);
my_error(ER_UNKNOWN_ERROR, MYF(0));
}
return error;
}
设置了这个参数,rbr_exec_mode == RBR_EXEC_MODE_IDEMPOTENT,覆盖的参数会设置成true,这样重复的主键,
会被覆盖

幂等的设置,没有完全收敛到一个入口,还是有些分散

相关推荐