mysql中binlog与存储引擎的2PC

本文详细解析了MySQL中通过二阶段提交(2PC)协议确保binlog与存储引擎间数据一致性的机制。介绍了核心函数ha_commit_trans及其内部调用流程,展示了如何利用MYSQL_BIN_LOG类实现prepare和commit阶段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mysql内部的2PC

mysql开启binlog后实际上可以认为其数据有两份,binlog中一份,引擎中一份(这里先把存储引擎中数据看成整体的单独一份,另外也可以把binlog看成是一个引擎)。既然出现了副本,那么就不可避免的牵涉到了一致性问题,mysql在内核内部使用了经典的2PC协议实现数据一致性。

2PC协议需要一个协调者,而在binlog与引擎的2PC协议实现中,由binlog充当这一角色。

mysql事务的提交函数为ha_commit_trans

//sql/handler.cc
int ha_commit_trans(THD *thd, bool all, bool ignore_global_read_lock)
{
  ······  
}

这个函数为mysql的提交函数,这个函数在事务提交时被调用,在内部实现了2PC的事务提交。众所周知,2PC分为两个阶段,prepare与commit,而在这个函数的代码中,实际上也可以看到以这两个阶段为名的调用 tc_log->prepare(thd, all)与tc_log->commit:

int ha_commit_trans(THD *thd, bool all, bool ignore_global_read_lock)
{
  ······
  if (!trn_ctx->no_2pc(trx_scope) && (trn_ctx->rw_ha_count(trx_scope) > 1))
      error= tc_log->prepare(thd, all);
  ······
  if (error || (error= tc_log->commit(thd, all)))
  {
    ha_rollback_trans(thd, all);
    error= 1;
    goto end;
  }
  ······
}

tc_log是一个全局指针:

//sql/tc_log.cc
TC_LOG *tc_log;

查看tc_log的定义:

/**
  Transaction Coordinator Log.

  A base abstract class for three different implementations of the
  transaction coordinator.

  The server uses the transaction coordinator to order transactions
  correctly and there are three different implementations: one using
  an in-memory structure, one dummy that does not do anything, and one
  using the binary log for transaction coordination.
*/
class TC_LOG
{
public:
  /**
    Perform heuristic recovery, if --tc-heuristic-recover was used.

    @note no matter whether heuristic recovery was successful or not
    mysqld must exit. So, return value is the same in both cases.

    @retval false  no heuristic recovery was requested
    @retval true   heuristic recovery was performed
  */
  bool using_heuristic_recover();

  TC_LOG() {}
  virtual ~TC_LOG() {}

  enum enum_result {
    RESULT_SUCCESS,
    RESULT_ABORTED,
    RESULT_INCONSISTENT
  };

  /**
    Initialize and open the coordinator log.
    Do recovery if necessary. Called during server startup.

    @param opt_name  Name of logfile.

    @retval 0  sucess
    @retval 1  failed
  */
  virtual int open(const char *opt_name)=0;

  /**
    Close the transaction coordinator log and free any resources.
    Called during server shutdown.
  */
  virtual void close()=0;

  /**
     Log a commit record of the transaction to the transaction
     coordinator log.

     When the function returns, the transaction commit is properly
     logged to the transaction coordinator log and can be committed in
     the storage engines.

     @param thd Session to log transaction for.
     @param all @c True if this is a "real" commit, @c false if it is a "statement" commit.

     @return Error code on failure, zero on success.
   */
  virtual enum_result commit(THD *thd, bool all) = 0;

  /**
     Log a rollback record of the transaction to the transaction
     coordinator log.

     When the function returns, the transaction have been aborted in
     the transaction coordinator log.

     @param thd Session to log transaction record for.

     @param all @c true if an explicit commit or an implicit commit
     for a statement, @c false if an internal commit of the statement.

     @return Error code on failure, zero on success.
   */
  virtual int rollback(THD *thd, bool all) = 0;

  /**
     Log a prepare record of the transaction to the storage engines.

     @param thd Session to log transaction record for.

     @param all @c true if an explicit commit or an implicit commit
     for a statement, @c false if an internal commit of the statement.

     @return Error code on failure, zero on success.
   */
  virtual int prepare(THD *thd, bool all) = 0;
};

从源码和注释中可以看出,这是一个虚基类,作为事务提交的协调器,实现了事务的prepare、commit、rollback等接口。那么既然是一个虚基类,那么实际上tc_log指针在ha_commit_trans中应该是指向继承TC_LOG类的一个子类的对象。查看这个指针在何处初始化可以发现在init_server_components中,该指针被赋值:

//sql/mysqld.cc
static int init_server_components()
{
······
  if (total_ha_2pc > 1 || (1 == total_ha_2pc && opt_bin_log))
  {
    if (opt_bin_log)
      tc_log= &mysql_bin_log;
    else
      tc_log= &tc_log_mmap;
  }
······
}

从代码中可以看出当有超过一个支持2PC的存储引擎或者只有一个但是开启了binlog的话,tc_log就会被设置,如果开启了binlog就会以binlog作为事务的协调器。因此,ha_commit_trans中调用的实际上就是MYSQL_BIN_LOG中实现的prepare与commit.查看MYSQL_BIN_LOG中实现的prepare:

int MYSQL_BIN_LOG::prepare(THD *thd, bool all)
{
  DBUG_ENTER("MYSQL_BIN_LOG::prepare");

  DBUG_ASSERT(opt_bin_log);
  /*
    The applier thread explicitly overrides the value of sql_log_bin
    with the value of log_slave_updates.
  */
  DBUG_ASSERT(thd->slave_thread ?
              opt_log_slave_updates : thd->variables.sql_log_bin);

  /*
    Set HA_IGNORE_DURABILITY to not flush the prepared record of the
    transaction to the log of storage engine (for example, InnoDB
    redo log) during the prepare phase. So that we can flush prepared
    records of transactions to the log of storage engine in a group
    right before flushing them to binary log during binlog group
    commit flush stage. Reset to HA_REGULAR_DURABILITY at the
    beginning of parsing next command.
  */
  thd->durability_property= HA_IGNORE_DURABILITY;

  int error= ha_prepare_low(thd, all);

  DBUG_RETURN(error);
}

可以看出,这个函数实际上就只是调用了存储引擎接口的ha_prepare_low接口,使得引擎处于prepare状态。对于binlog而言,实际上只需要写入文件即可,因此可以认为本身就处于prepare状态。事务处于prepare状态后,调用MYSQL_BIN_LOG的commit函数进行提交。

TC_LOG::enum_result MYSQL_BIN_LOG::commit(THD *thd, bool all)
{
······
    if (ordered_commit(thd, all, skip_commit))
      DBUG_RETURN(RESULT_INCONSISTENT);
······
}

这个函数比较长,先省略其他部分,只看进行的提交的调用,实际上提交操作是在ordered_commit中实现。ordered_commit入口是mysql组提交的入口,暂时不深入,在这个函数中会将binlog写入,并且调用ha_commit_low在引擎进行提交。

将几个主要的函数调用栈总结如下:

ha_commit_trans
     |
     |_ _ _ _ MYSQL_BIN_LOG::prepare
     |                   |
     |                   |_ _ _ _ ha_prepare_low
     |
     |_ _ _ _ MYSQL_BIN_LOG::commit
                        |
                        | _ _ _ _MYSQL_BIN_LOG::ordered_commit
                                            |
                                            |_ _ _ _ha_commit_low
                        

转载于:https://www.cnblogs.com/FateTHarlaown/p/8674536.html

<think>嗯,我现在要了解一下MySQLbinlog,也就是二进制日志。首先,我大概知道binlogMySQL用来记录数据库更改的日志文件,但具体细节可能不太清楚。让我先理清楚几个问题:binlog是什么,它的作用是什么,怎么配置和使用,以及它在实际应用中的常见场景。 首先,binlog是二进制日志,应该是以二进制的形式记录所有对数据库的修改操作,比如INSERT、UPDATE、DELETE这些,可能还包括DDL语句,比如CREATE TABLE或者ALTER TABLE。这样的话,主从复制(replication)应该就是基于binlog的,主库把binlog传给从库,从库重放这些操作来保持数据一致。那binlog的格式是不是有不同的类型呢?比如statement-based、row-based或者mixed?这可能影响记录的内容和复制的效率。 接下来,binlog的作用可能包括数据恢复,比如通过回放binlog来恢复某个时间点的数据。还有主从复制,以及审计,可以查看数据库的历史操作。不过,审计可能需要更详细的信息,binlog是否足够呢? 然后配置方面,MySQL默认可能没有开启binlog,所以需要修改配置文件,比如my.cnf或者my.ini,设置log_bin参数,指定日志的路径和名称。另外,可能还需要配置一些其他参数,比如expire_logs_days来控制日志保留时间,max_binlog_size设置单个日志文件的大小,还有binlog_format来指定格式。 binlog的格式有三种,statement-based记录的是SQL语句,row-based记录的是每一行的变化,mixed则是混合两种方式。不同的格式有不同的优缺点。比如,statement-based可能节省空间,但在某些情况下可能不安全,比如使用非确定性函数时,主从数据可能不一致。而row-based更安全,但日志量更大。mixed模式会根据情况自动选择。 使用binlog进行数据恢复的话,可能需要用到mysqlbinlog工具,把binlog文件转换成SQL语句,然后执行。比如误删了一个表,可以找到删除之前的binlog位置,导出相关操作进行恢复。不过具体步骤可能需要详细学习。 另外,binlog和事务的关系,比如InnoDB引擎支持事务,binlog在事务提交时记录。这涉及到两阶段提交(2PC),确保redo log和binlog的一致性。这也是MySQL保证数据一致性的重要机制。 还有一些问题,比如binlog的写入时机,是同步还是异步的?可能影响性能和数据一致性。还有binlog的索引文件,记录所有binlog文件的列表,方便管理和查找。 还有binlog的清除策略,比如手动执行PURGE BINARY LOGS命令,或者自动根据过期时间删除。需要合理管理,避免磁盘空间被占满。 实际应用中,主从复制是常见用途,比如读写分离,灾备等。数据恢复也是关键用途,特别是在没有备份的情况下,binlog可以用于恢复到某个时间点。此外,像Canal这样的中间件可以解析binlog,用于实时数据同步到其他系统,比如Elasticsearch或者大数据平台。 可能需要注意的还有权限问题,查看binlog需要哪些权限,比如REPLICATION SLAVE和REPLICATION CLIENT权限。另外,binlog_row_image参数在row-based模式下控制记录哪些行的信息,全镜像还是最小化变化的部分。 还有一些工具,比如mysqlbinlog的使用方法,如何指定开始和结束的时间或位置,如何过滤特定的数据库或表,以及如何将binlog输出重定向到SQL文件进行恢复。 总结一下,binlogMySQL的重要功能,用于数据复制、恢复和审计。需要了解如何配置、不同格式的区别,以及如何使用工具进行恢复和解析。同时,理解其事务和存储引擎的交互,以及在实际应用中的最佳实践。</think>MySQL的二进制日志(binlog)是记录数据库所有更改操作的核心组件。以下从作用、配置、格式、应用场景等方面逐步解析: --- ### **一、binlog的作用** 1. **主从复制** - 主库将binlog发送给从库,从库重放日志实现数据同步。 2. **数据恢复** - 通过重放binlog,可将数据库恢复到特定时间点(如误删数据后)。 3. **审计分析** - 解析binlog可追踪历史操作(需结合工具)。 --- ### **二、binlog的配置** 1. **启用binlog** 修改MySQL配置文件(`my.cnf`或`my.ini`): ```ini [mysqld] log_bin = /var/lib/mysql/mysql-bin # 日志路径及前缀 server_id = 1 # 主从环境需唯一 expire_logs_days = 7 # 日志保留7天 max_binlog_size = 100M # 单个日志文件最大100MB binlog_format = ROW # 可选STATEMENT、ROW、MIXED ``` 2. **关键参数说明** - `binlog_format`: - **STATEMENT**:记录SQL语句(节省空间,但非确定性操作可能主从不一致)。 - **ROW**:记录行数据变化(安全,但日志量大)。 - **MIXED**:自动选择模式。 --- ### **三、binlog的格式内容** 1. **文件结构** - 文件名如`mysql-bin.000001`,索引文件`mysql-bin.index`记录所有binlog列表。 2. **事件类型** - 记录的事件包括:`Query`(DDL/DML)、`Write_rows`(INSERT)、`Update_rows`(UPDATE)、`Delete_rows`(DELETE)等。 --- ### **四、binlog操作示例** 1. **查看binlog列表** ```sql SHOW BINARY LOGS; ``` 输出: ``` +------------------+-----------+ | Log_name | File_size | +------------------+-----------+ | mysql-bin.000001 | 177 | | mysql-bin.000002 | 154 | +------------------+-----------+ ``` 2. **解析binlog内容** 使用`mysqlbinlog`工具: ```bash mysqlbinlog --start-datetime="2023-10-01 00:00:00" \ --stop-datetime="2023-10-02 00:00:00" \ mysql-bin.000001 > binlog.sql ``` --- ### **五、数据恢复实践** **场景**:误删表`users`后恢复。 1. 定位误操作时间点(如`2023-10-01 14:00:00`)。 2. 导出binlog到SQL文件: ```bash mysqlbinlog --start-datetime="2023-10-01 13:55:00" \ --stop-datetime="2023-10-01 14:05:00" \ mysql-bin.000002 > recovery.sql ``` 3. 手动编辑`recovery.sql`,删除误操作的`DELETE`语句。 4. 重放日志: ```bash mysql -u root -p < recovery.sql ``` --- ### **六、常见应用场景** 1. **主从复制** - 主库写,从库读,实现读写分离。 2. **实时数据同步** - 使用Canal、Debezium等工具解析binlog,同步到Elasticsearch/Kafka。 3. **审计合规** - 分析binlog记录的操作日志。 --- ### **七、注意事项** 1. **性能影响** - ROW格式日志量大,可能增加I/O压力。 2. **事务一致性** - InnoDB通过两阶段提交(2PC)确保binlogredo log的一致性。 3. **权限控制** - 用户需`REPLICATION SLAVE`权限读取binlog。 --- 通过合理配置和使用binlog,可有效保障数据安全性系统可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值