登录
首页 >  数据库 >  MySQL

什么是Binlog?浅析MySQL中的Binlog

来源:SegmentFault

时间:2023-02-24 13:41:38 475浏览 收藏

小伙伴们有没有觉得学习数据库很有意思?有意思就对了!今天就给大家带来《什么是Binlog?浅析MySQL中的Binlog》,以下内容将会涉及到MySQL、binlog,若是在学习中对其中部分知识点有疑问,或许看了本文就能帮到你!

原文地址:https://xcoder.in/2015/08/10/mysql-binlog-try/

  花瓣网的搜索架构需要重构,尤其是在索引建立或者更新层面。

  目前的一个架构导致的结果就是时间越久,数据本体与搜索引擎索引中的数据越不同步,相差甚大。

  新的一个架构打算从 MySQL 的 Binlog 中读取数据更新、删除、新增等历史记录,并把相应信息提取出来丢到队列中慢慢去同步。

  所以我就在这里小小去了解一下 Binlog。

准备工作

什么是 Binlog

  MySQL Server 有四种类型的日志——Error Log、General Query Log、Binary Log 和 Slow Query Log。

  第一个是错误日志,记录 mysqld 的一些错误。第二个是一般查询日志,记录 mysqld 正在做的事情,比如客户端的连接和断开、来自客户端每条 Sql Statement 记录信息;如果你想准确知道客户端到底传了什么瞎 [哔哔] 玩意儿给服务端,这个日志就非常管用了,不过它非常影响性能。第四个是慢查询日志,记录一些查询比较慢的 SQL 语句——这种日志非常常用,主要是给开发者调优用的。

  剩下的第三种就是 Binlog 了,包含了一些事件,这些事件描述了数据库的改动,如建表、数据改动等,也包括一些潜在改动,比如

  • v1,用于 MySQL 3.2.3

  • v3,用于 MySQL 4.0.2 以及 4.1.0

  • v4,用于 MySQL 5.0 以及更高版本

  •   实际上还有一个 v2 版本,不过只在早期 4.0.x 的 MySQL 版本中使用过,但是 v2 已经过于陈旧并且不再被 MySQL 官方支持了。

    通常我们现在用的 MySQL 都是在 5.0 以上的了,所以就略过 v1 ~ v3 版本的 Binlog,如果需要了解 v1 ~ v3 版本的 Binlog 可以自行前往上述的《High-level...》文章查看。

    事件头

      一个事件头有 19 字节,依次排列为四字节的时间戳、一字节的当前事件类型、四字节的服务端 ID、四字节的当前事件长度描述、四字节的下个事件位置(方便跳转)以及两字节的标识。

      用 ASCII Diagram 表示如下:

    +---------+---------+---------+------------+-------------+-------+
    |timestamp|type code|server_id|event_length|next_position|flags  |
    |4 bytes  |1 byte   |4 bytes  |4 bytes     |4 bytes      |2 bytes|
    +---------+---------+---------+------------+-------------+-------+

      也可以字节编造一个结构体来解读这个头:

    c

    struct BinlogEventHeader
    {
        int   timestamp;
        char  type_code;
        int   server_id;
        int   event_length;
        int   next_position;
        char  flags[2];
    };

    如果你要直接用这个结构体来读取数据的话,需要加点手脚。

    因为默认情况下 GCC 或者 G++ 编译器会对结构体进行字节对齐,这样读进来的数据就不对了,因为 Binlog 并不是对齐的。为了统一我们需要取消这个结构体的字节对齐,一个方法是使用

    #pragma pack(n)
    ,一个方法是使用
    __attribute__((__packed__))
    ,还有一种情况是在编译器编译的时候强制把所有的结构体对其取消,即在编译的时候使用
    fpack-struct
    参数,如:

    ```sh
     $ g++ temp.cpp -o a -fpack-struct=1


      根据上述的结构我们可以明确得到各变量在结构体里面的偏移量,所以在 MySQL 源码里面([libbinlogevents/include/binlog_event.h](https://github.com/mysql/mysql-server/blob/5.7/libbinlogevents/include/binlog_event.h#L353))有下面几个常量以快速标记偏移: ```c #define EVENT_TYPE_OFFSET    4 #define SERVER_ID_OFFSET     5 #define EVENT_LEN_OFFSET     9 #define LOG_POS_OFFSET       13 #define FLAGS_OFFSET         17

      而具体有哪些事件则在 libbinlogevents/include/binlog_event.h#L245 里面被定义。如有个

    FORMAT_DESCRIPTION_EVENT
    事件的
    type_code
    是 15、
    UPDATE_ROWS_EVENT
    type_code
    是 31。

      还有那个

    next_position
    ,在 v4 版本中代表从 Binlog 一开始到下一个事件开始的偏移量,比如到第一个事件的
    next_position
    就是 4,因为文件头有一个字节的长度。然后接下去对于事件 n 和事件 n + 1 来说,他们有这样的关系:

    next_position(n + 1) = next_position(n) + event_length(n)

      关于 flags 暂时不需要了解太多,如果真的想了解的话可以看看 MySQL 的相关官方文档

    事件体

      事实上在 Binlog 事件中应该是有三个部分组成,

    header
    post-header
    payload
    ,不过通常情况下我们把
    post-header
    payload
    都归结为事件体,实际上这个
    post-header
    里面放的是一些定长的数据,只不过有时候我们不需要特别地关心。想要深入了解可以去查看 MySQL 的官方文档。

      所以实际上一个真正的事件体由两部分组成,用 ASCII Diagram 表示就像这样:

    +=====================================+
    | event  | fixed part (post-header)   |
    | data   +----------------------------+
    |        | variable part (payload)    |
    +=====================================+

      而这个

    post-header
    对于不同类型的事件来说长度是不一样的,同种类型来说是一样的,而这个长度的预先规定将会在一个“格式描述事件”中定好。
    格式描述事件

      在上文我们有提到过,在 Magic Number 之后跟着的是一个格式描述事件(Format Description Event),其实这只是在 v4 版本中的称呼,在以前的版本里面叫起始事件(Start Event)。

      在 v4 版本中这个事件的结构如下面的 ASCII Diagram 所示。

    +=====================================+
    | event  | timestamp         0 : 4    |
    | header +----------------------------+
    |        | type_code         4 : 1    | = FORMAT_DESCRIPTION_EVENT = 15
    |        +----------------------------+
    |        | server_id         5 : 4    |
    |        +----------------------------+
    |        | event_length      9 : 4    | >= 91
    |        +----------------------------+
    |        | next_position    13 : 4    |
    |        +----------------------------+
    |        | flags            17 : 2    |
    +=====================================+
    | event  | binlog_version   19 : 2    | = 4
    | data   +----------------------------+
    |        | server_version   21 : 50   |
    |        +----------------------------+
    |        | create_timestamp 71 : 4    |
    |        +----------------------------+
    |        | header_length    75 : 1    |
    |        +----------------------------+
    |        | post-header      76 : n    | = array of n bytes, one byte per event
    |        | lengths for all            |   type that the server knows about
    |        | event types                |
    +=====================================+

      这个事件的

    type_code
    是 15,然后
    event_length
    是大于等于 91 的值的,这个主要取决于所有事件类型数。

      因为从第 76 字节开始后面的二进制就代表一个字节类型的数组了,一个字节代表一个事件类型的

    post-header
    长度,即每个事件类型固定数据的长度。

      那么按照上述的一些线索来看,我们能非常快地写出一个简单的解读 Binlog 格式描述事件的代码。

    如上文所述,如果需要正常解读 Binlog 文件的话,下面的代码编译时候需要加上

    -fpack-struct=1
    这个参数。

    cpp
    #include 
    #include 
    
    struct BinlogEventHeader
    {
        int  timestamp;
        unsigned char type_code;
        int  server_id;
        int  event_length;
        int  next_position;
        short flags;
    };
    
    int main()
    {
        FILE* fp = fopen("/usr/local/var/mysql/master-bin.000001", "rb");
        int magic_number;
        fread(&magic_number, 4, 1, fp);
    
        printf("%d - %sn", magic_number, (char*)(&magic_number));
    
        struct BinlogEventHeader format_description_event_header;
        fread(&format_description_event_header, 19, 1, fp);
    
        printf("BinlogEventHeadern{n");
        printf("    timestamp: %dn", format_description_event_header.timestamp);
        printf("    type_code: %dn", format_description_event_header.type_code);
        printf("    server_id: %dn", format_description_event_header.server_id);
        printf("    event_length: %dn", format_description_event_header.event_length);
        printf("    next_position: %dn", format_description_event_header.next_position);
        printf("    flags[]: %dn}n", format_description_event_header.flags);
    
        short binlog_version;
        fread(&binlog_version, 2, 1, fp);
        printf("binlog_version: %dn", binlog_version);
    
        char server_version[51];
        fread(server_version, 50, 1, fp);
        server_version[50] = '
    声明:本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除
    相关阅读
    更多>
    最新阅读
    更多>
    课程推荐
    更多>
    评论列表