• MySQL Binlog 【ROW】和【STATEMENT】选择(转)


    前言:
           二进制日记录了数据库执行更改的操作,如Insert,Update,Delete等。不包括Select等不影响数据库记录的操作,因为没有对数据进行修改。二进制主要的功能有:复制(Replication)和恢复(Recovery)。具体的二进制里面的格式表示的意思请见这篇文章
           MySQL记录的日志有三种模式:STATEMENT、ROW、MIXED,这3个到底有什么区别呢?对Replication有什么区别呢?本文开始进行一些说明,如有遗漏请大家补充。
    一,大小:日志产生量。
    Client1:

    View Code

    Client2:

    View Code

    Client3:

    View Code

    除了binlog_format不一样之外,其他都是一样的。先看下事务操作的日志大小(物理)。删除数据:

    delete from me_info where id < 2153269;

    查看他们日志的大小:发现ROW 和 其他2个大小不一致,而MIXED和STATEMENT一致。通过mysqlbinlog 发现他们记录的格式ROW不同于STATEMENT和MIXED。

    -rw-rw---- 1 mysql adm 3.7M 2012-12-31 16:19 mysql-bin.000001
    -rw-rw---- 1 mysql adm  207 2012-12-31 16:19 mysql-bin2.000001
    -rw-rw---- 1 mysql adm  207 2012-12-31 16:19 mysql-bin3.000001

    小结1:
           通过上面的说明得出一点是ROW格式比MIX和STATEMENT要大,原因是ROW记录的是记录更新后的值(不需要记录上下文信息),而其他2个模式记录的只是一个逻辑的SQL语句(需要记录上下文信息),具体格式可以看这里的ROW日志信息。因为上面的表删除了3W的记录,ROW模式会记录每一条删除语句,所以日志会很大。这也说明将格式设置成ROW,对于磁盘空间的要求增加了,而复制采用传输二进制日志方式实现的,所以复制的网络开销也有增加。所以最后的结果是:ROW>STATEMENT=MIXED

    二,复制:对复制产生的影响
    表:

    复制代码
    root@127.0.0.1 : rep_test 05:38:06>desc user;
    +---------------+--------------+------+-----+---------------------+----------------+
    | Field         | Type         | Null | Key | Default             | Extra          |
    +---------------+--------------+------+-----+---------------------+----------------+
    | id            | int(11)      | NO   | PRI | NULL                | auto_increment |
    | username      | varchar(20)  | NO   | UNI |                     |                |
    | status        | int(4)       | YES  | MUL | NULL                |                |
    …………………………………………
    …………………………………………
    …………………………………………
    +---------------+--------------+------+-----+---------------------+----------------+
    42 rows in set (0.03 sec)
    复制代码

    1,磁盘IOPS,网卡流量,cpu:

    一个更新脚本,更新一个字符串字段。

    复制代码
    import MySQLdb
    from random import choice
    from random import randint
    def get_str(n):
        A=''    
        for i in range(n):
            A=A+chr(97+randint(0,25))
        return A
    
    if __name__ =='__main__':
        pwd = get_str(16)
        conn = MySQLdb.connect(host='localhost',user='root',passwd='123456',charset='utf8',db='rep_test')
        for i in xrange(1000000):
            query ="update user set password = '%s' where id =%d" %(pwd,i)
            cursor = conn.cursor()
            cursor.execute(query)
            print query
        print 'OK'
    复制代码

    执行脚本,查看网卡流量{iftop、ifstat、dstat -N eth0 }
    STATEMENT下主从的情况:

    复制代码
    STATEMENT
    -rw-rw---- 1 mysql adm 128M 2013-01-15 09:29 mysql-bin.000001
    主:
    ----total-cpu-usage---- -dsk/total- --net/eth0- ---paging-- ---system--
    usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
     50  16  31   0   0   2|   0     0 | 362k 1184k|   0     0 |  12k   48k
     45  15  37   2   0   2|   0  6892k| 196k 1041k|   0     0 |  11k   45k
     58  20  21   0   0   2|   0     0 | 332k 1088k|   0     0 |  10k   46k
     52  17  31   0   0   2|   0     0 | 353k 1122k|   0     0 |  12k   45k
     52  19  28   0   0   2|   0    84k| 308k 1032k|   0     0 |  10k   42k
     50  17  31   0   0   2|   0     0 | 360k 1171k|   0     0 |  12k   47k
     44  17  33   4   0   1|   0  9560k| 350k 1092k|   0     0 |  11k   44k
     49  18  31   0   0   2|   0     0 | 355k 1111k|   0     0 |  11k   46k
     47  24  27   0   0   3|   0     0 | 357k 1144k|   0     0 |  11k   48k
     64  18  17   0   0   2|   0    76k| 264k  985k|   0     0 |7621    43k
     56  16  26   0   0   1|   0     0 | 365k 1146k|   0     0 |  11k   47k
     49  16  31   3   0   2|4096B 6820k| 341k 1071k|   0     0 |  10k   44k
     51  23  25   0   0   2|   0     0 | 357k 1170k|   0     0 |  11k   48k
     56  17  25   0   0   1|   0     0 | 334k 1068k|   0     0 |  11k   44k
     50  15  32   1   0   3|   0  2132k| 375k 1170k|   0     0 |  12k   47k
     46  18  33   0   0   2|   0  2048k| 374k 1180k|   0     0 |  12k   47k
     59  16  23   0   0   2|   0     0 | 362k 1168k|   0     0 |  10k   49k
     48  13  29   9   0   1|4096B   12M| 271k  889k|   0     0 |8581    37k
     48  19  32   0   0   2|   0     0 | 385k 1209k|   0     0 |  12k   49k
     59  18  21   0   0   2|   0    92k| 311k 1022k|   0     0 |8799    44k
     47  20  31   0   0   2|   0     0 | 375k 1190k|   0     0 |  12k   48k
     50  18  30   0   0   3|   0     0 | 313k 1086k|   0     0 |  10k   44k
     59  19  20   0   0   3|   0    16k| 360k 1134k|   0     0 |9620    48k
    
    从:
    ----total-cpu-usage---- -dsk/total- --net/eth0- ---paging-- ---system--
    usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
     64  12  18   0   0   6|   0   512B|1204k  381k|   0     0 |  19k   16k
     48  14  33   0   0   6|   0     0 |1134k  357k|   0     0 |  17k   16k
     30  18  46   0   0   6|   0     0 |1070k  319k|   0     0 |  13k   14k
     45  18  34   0   0   4|   0     0 |1069k  326k|   0     0 |  13k   15k
     70  11  15   0   0   3|   0    33k|1156k  363k|   0     0 |  12k   15k
     42  12  43   0   0   3|   0   512B|1092k  338k|   0     0 |  14k   17k
     34  16  46   0   0   4|   0     0 |1195k  349k|   0     0 |  15k   17k
     56  11  14  15   0   5|   0    52M| 973k  177k|   0     0 |7848  7420 
     69  10  16   0   0   5|   0     0 |1107k  352k|   0     0 |  12k   15k
     31  14  49   0   0   5|   0    41k|1066k  316k|   0     0 |  13k   16k
     39  18  39   0   0   4|   0   512B|1117k  340k|   0     0 |  13k   15k
     60  13  20   0   0   7|   0     0 |1105k  349k|   0     0 |  17k   14k
     61  14  18   1   0   7|   0  9216B|1189k  377k|   0     0 |  18k   15k
     32  12  52   0   0   5|   0     0 |1109k  344k|   0     0 |  15k   17k
     34  20  42   0   0   4|   0    33k|1068k  319k|   0     0 |  13k   14k
     63  14  17   0   0   6|   0   512B|1063k  304k|   0     0 |  15k   12k
     61  10  22   0   0   7|   0     0 |1071k  340k|   0     0 |  17k   14k
     36  19  42   0   0   4|   0     0 |1141k  354k|   0     0 |  15k   18k
     26  17  53   0   0   4|   0     0 |1125k  347k|   0     0 |  15k   18k
     58  15  18   1   0   8|   0   141k|1119k  347k|   0     0 |  17k   14k
     62  12  18   0   0   7|   0   512B|1173k  374k|   0     0 |  18k   15k
     33  17  43   0   0   6|   0     0 |1182k  367k|   0     0 |  17k   19k
     27  15  55   0   0   3|   0     0 | 889k  273k|   0     0 |  12k   14k
    复制代码

    从上面信息可以看出:产生了128M的二进制日志,在复制期间,Master网卡出去(send)流量平均1M左右,Slave网卡接收(recv)流量平均1M左右,Master的CPU空闲30左右,Slave的CPU空闲30~40,磁盘读写都比较小。

    ROW下主从的情况:

    复制代码
    ROW:
    -rw-rw---- 1 mysql adm 706M 2013-01-15 09:37 mysql-bin.000002
    主:
    ----total-cpu-usage---- -dsk/total- --net/eth0- ---paging-- ---system--
    usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
     53  20  25   0   0   2|   0  4096k| 189k 4376k|   0     0 |  11k   43k
     55  16  29   0   0   1|   0  8176k| 232k 4644k|   0     0 |  12k   45k
     64  19  13   1   0   2|   0  5548k| 234k 3379k|   0     0 |7497    36k
     50  19  26   3   0   3|   0    26M| 322k 4692k|   0     0 |  12k   47k
     52  19  27   0   0   2|   0     0 | 311k 4638k|   0     0 |  12k   46k
     52  18  28   0   0   1|   0     0 | 276k 4157k|   0     0 |  10k   42k
     55  20  24   0   0   2|   0     0 | 308k 4353k|   0     0 |  11k   44k
     57  16  23   2   0   1|4096B 4624k| 262k 3772k|   0     0 |9495    39k
     47  16  29   7   0   2|   0    21M| 229k 3982k|   0     0 |  10k   40k
     54  14  31   1   0   1|   0    16k| 296k 4572k|   0     0 |  12k   45k
     50  20  29   0   0   2|   0     0 | 322k 4595k|   0     0 |  12k   44k
     54  15  31   0   0   1|   0     0 | 308k 4461k|   0     0 |  11k   45k
     58  20  20   0   0   1|   0     0 | 154k 4133k|   0     0 |8725    43k
     58  16  23   1   0   2|   0    60k| 293k 4133k|   0     0 |9969    42k
     59  16  24   0   0   2|   0  4096k| 283k 4388k|   0     0 |  11k   44k
     50  18  30   0   0   3|   0  4096k| 184k 4681k|   0     0 |  12k   46k
     50  19  28   1   0   1|   0  4072k| 306k 4716k|   0     0 |  12k   46k
     53  19  27   0   0   2|   0    31M| 329k 4701k|   0     0 |  12k   47k
     51  19  28   1   0   2|   0   100k| 306k 4560k|   0     0 |  12k   45k
     50  17  31   0   0   2|   0     0 | 307k 4587k|   0     0 |  12k   45k
     53  19  26   1   0   2|   0    28k| 279k 4120k|   0     0 |  10k   42k
     52  16  30   0   0   2|   0     0 | 323k 4628k|   0     0 |  12k   46k
     47  17  28   9   0   1|4096B   26M| 106k 3751k|   0     0 |8305    39k
    
    从:
    ----total-cpu-usage---- -dsk/total- --net/eth0- ---paging-- ---system--
    usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
     71  19   7   0   0   4|   0     0 |4635k  319k|   0     0 |  12k   13k
     53  18  24   0   0   4|   0     0 |3844k  264k|   0     0 |  15k   21k
     47  31  12   0   0  10|   0   178k|4710k  173k|   0     0 |  16k   16k
     58  27   8   0   0   7|   0     0 |4332k  180k|   0     0 |  14k   11k
     60  12  25   0   0   3|   0    25k|2918k  206k|   0     0 |  11k 9933 
     49  19  28   0   0   4|   0     0 |4200k  292k|   0     0 |  22k   29k
     33  28  35   0   1   3|   0     0 |4434k  297k|   0     0 |  26k   37k
     48  25  21   0   0   6|   0    33k|4238k  283k|   0     0 |  18k   24k
     67  14  16   0   0   3|   0     0 |3682k  254k|   0     0 |9446    12k
     43  24  30   0   0   4|   0     0 |4461k  305k|   0     0 |  23k   33k
     32  29  35   0   0   4|   0     0 |4273k  293k|   0     0 |  27k   40k
     58  21  17   0   0   4|   0     0 |4012k  275k|   0     0 |  14k   20k
     67  16  14   0   0   3|4096B  161k|4135k  285k|   0     0 |  11k   13k
     39  34  21   0   0   6|   0     0 |4255k  185k|   0     0 |  17k   20k
     40  32  13   8   0   7|   0    26M|4198k  164k|   0     0 |  15k   14k
     77  18   0   0   0   5|   0     0 |4560k  277k|   0     0 |  11k 9888 
     57  20  16   0   0   7|   0     0 |4707k  328k|   0     0 |  17k   22k
     35  29  31   0   1   4|   0    33k|4571k  298k|   0     0 |  26k   36k
     40  18  38   0   0   4|   0     0 |3493k  203k|   0     0 |  15k   18k
     56   5   1  36   0   2|   0    96M|1607k   44k|   0     0 |5375  7012 
     57  23  16   0   0   4|   0    11M|6747k  149k|   0     0 |  18k   20k
     31  31  34   0   0   4|   0     0 |4691k  314k|   0     0 |  27k   39k
     48  24  22   0   0   5|   0    45k|4471k  286k|   0     0 |  19k   22k
     64  16  12   0   0   8|   0     0 |4607k  319k|   0     0 |  17k   16k
     47  22  27   0   0   4|   0  5120B|4295k  293k|   0     0 |  21k   27k
     47  32  12   0   0   8|   0     0 |4645k  194k|   0     0 |  17k   17k
    复制代码

    从上面信息可以看出:产生了706M的二进制日志,在复制期间,Master网卡出去(send)流量4M~5M,Slave网卡接收(recv)流量4M~5M,Master的CPU空闲20~30,Slave的CPU空闲20左右,磁盘读写也不算大。

    对比Row和Statement:R比S产生的日志量大5.5倍,网卡流量高4~5倍,cpu稍微忙了10个百分点。在复制过程中,从均没有延迟。因为SQL过滤条件WHERE 后面的字段利用好索引,ROW和STATEMENT模式下效果一样。要是没有利用好索引,则:
    STATEMENT下:在主上执行(3~5s)一条,从上也是需要这个时间,并且出现延迟。(Seconds_Behind_Master)。本来就单线程的,导致从的可用性更差。
    ROW下:在主上执行(3~5s)一条,正常情况下每张表都有主键,所以按照ROW的记录的SQL格式,不会出现对这类sql的延迟。除非极端情况下更新一张没有主键甚至没有任何索引的表。

    范围内的批量更新结果怎么样?【update user set password = 'serqrnncavfyozeu' where id > 0 and id < 1000000】

    STATEMENT下主从的情况:

    View Code

     ROW下主从的情况:

    View Code

    对比发现:在执行此类sql的时候,在STATEMENT下面,(利用好索引)主和从的各个开销都很小,网络流量都不大。而在ROW下面:因为日志产生量就很大,导致在复制期间网卡流量就很大:12M。网卡流量:【1:10000】,日志大小:【1:2000000】,CPU空闲:【80:20】。这个只限于这个例子,看范围大小和表字段的大小。总之在网络和磁盘开销上面比较,他们差距了好几个数量级。

    小结2:
        对于更新单条的sql语句,在STATEMENT和ROW下
    1,CPU消耗差距不大,都需要执行这么sql。消耗 R=S
    2,磁盘写和网络传输上,因为ROW记录的格式的原因。消耗 R>S
    3,SQL效率来看,合理利用索引的更新,效率差距不大,不合理利用索引的更新,效率 R>S
    4,日志文件大小上,因为都需要记录这么多SQL,但是由于R和S的记录格式不一样,大小 R>S

        对于执行一个大范围的sql语句,在STATEMENT和ROW下
    1,CPU上,主上只要执行一条SQL,而从上需要执行N条,消耗 R>S
    2,磁盘写和网络传输上,因为ROW记录的格式的原因。消耗R>S,看范围条件,大的话,差距巨大。
    3,日志文件大小上,主记录一条,从记录N条,并且还由于R和S的记录格式不一样,R>S,差距巨大。
    从上面的分析得出,STATEMENT要比ROW划算。要是使用STATEMENT没有任何问题的话,就推荐使用STATEMENT/MIXED格式记录二进制日志。

    2,数据的一致性:

    其实ROW有很多一些好处。特别对数据的一致性有了很严的要求。
    情况1:

    View Code

    更新主上有的数据,但从上没有:在STATEMENT/MIXED下,复制正常,没有报错。而在ROW下,复制终止。
    情况2:和ROW记录的格式有关

    View Code

    主从上的字段属性不一样,在STATEMENT/MIXED下,不受影响,复制正常,而在ROW下,复制报错。varcar <=> char

    主从上的字段长度不一样,在STATEMENT/MIXED下,不受影响,复制正常,而在ROW下,复制报错。varchar(10) <=> varchar(20)

    对于情况2,在5.1里面没有办法自动处理复制的错误,但是在5.5版本中增加了一个参数控制:slave_type_conversions

    ALL_LOSSY:仅支持有损转换,比如一个值本来是bigint存储为9999999999999,现在转换为int类型势必会要截断从而导致数据不一致。
    ALL_NON_LOSSY:仅支持无损转换,只能在无损的情况下才能进行转换
    ALL_LOSSY,ALL_NON_LOSSY:有损/无算转换都支持
    空,即不设置这个参数:必须主从的字段类型一模一样。

    表示允许相同类型字段、长度不同,否则默认为空,会导致主从停止

    复制代码
    zjy@localhost : test 01:52:45>show variables like 'slave_type%';
    +------------------------+-------+
    | Variable_name          | Value |
    +------------------------+-------+
    | slave_type_conversions |       |
    +------------------------+-------+
    
    zjy@localhost : test 01:53:00>set global  slave_type_conversions ='ALL_LOSSY,ALL_NON_LOSSY';
    Query OK, 0 rows affected (0.00 sec)
    
    zjy@localhost : test 01:53:30>show variables like 'slave_type%';
    +------------------------+-------------------------+
    | Variable_name          | Value                   |
    +------------------------+-------------------------+
    | slave_type_conversions | ALL_LOSSY,ALL_NON_LOSSY |
    +------------------------+-------------------------+
    复制代码

    在从上修改了之后,情况2的复制报错不会再出现。

    目前只发现这2个,后期发现再补充进来。

    小结3:
    对于ROW和STATEMENT的复制,ROW在数据的一致性上面要求更好,从库要是提供服务,最好把复制模式改成ROW。

    3,复制下的各种情况:可以参考这篇文章

    对于ROW和STATEMENT下的存储过程,函数,触发器,事件的记录方式有什么区别呢?结果:

    复制代码
    触发器(TRIGGER):
    ROW
    主上有,从上没有,复制正常,数据正常。
    主上有,从上也有,复制正常,数据正常。
    
    STATEMENT/MIXED
    主上有,从上没有,复制正常,数据不正常,触发器里面的sql语句没有执行。
    主上有,从上也有,复制正常,数据正常。
    
    函数(FUNCTION):
    ROW
    主上有,从上没有,复制正常,数据正常。日志里记录的是UDF转换过的结果。
    主上有,从上也有,复制正常,数据正常。
    
    STATEMENT/MIXED
    主上有,从上没有,复制报错。从不识别UDF函数。
    主上有,从上也有,复制正常,数据正常。
    
    存储过程(STORED PROCEDURES)
    ROW
    主上有,从上没有,复制正常,数据正常。记录的不是call sp,而是SP里面的sql。
    主上有,从上也有,复制正常,数据正常。
    
    STATEMENT/MIXED
    主上有,从上没有,复制正常,数据正常。记录的不是call sp,而是SP里面的sql。
    主上有,从上也有,复制正常,数据正常。
    
    事件(event):
    ROW
    主上有,从上没有,复制正常,数据正常。记录的不是计划,而是EVENT里面的sql。
    主上有,从上也有,复制正常,数据正常。(默认,DISABLE ON SLAVE),ALTER EVENT event_name ENABLE/DISABLE
    
    STATEMENT/MIXED
    主上有,从上没有,复制正常,数据正常。记录的不是计划,而是EVENT里面的sql。
    主上有,从上也有,复制正常,数据正常。(默认,DISABLE ON SLAVE), ALTER EVENT event_name ENABLE/DISABLE
    复制代码

     小结4:
    Event最好在主上,其他的都可以在主从上同时存在,要是人为的操作数据库而修改模式(R-S)也不会出现问题,更能确保数据的一致性。
    对于【小结2】和【小结3】,说明了ROW和STATEMENT的各自优势,下面这个功能更能体现出ROW的优势。

    三,数据回滚:误删除、更新的回退
    请见:这里这里

    总结:
    经过上面的分析,到底是使用ROW好还是STATEMENT好?这个需要权衡, 在【小结2】中:

    更新一个大范围的SQL,针对STATEMENT没有什么疑问。对比ROW,其磁盘写和网卡流量以及CPU消耗都比较大,特别是一大个范围的sql语句,差距很大,这时候用STATMENT相对来说更好,因为在利用好索引的前提下,STATEMENT更划算。如上面的例子。

    更新小数据,比如每次sql更新一条或则几条,ROW和STATMENT差距不是很大。虽然有几倍的差距,但是这些影响对目前的设备来讲也没任何压力。而且利用ROW之后,可以使没有利用好索引的sql,在从上能更好的执行,并且更能保证主从数据的一致性,更诱人的是ROW下可以实现误操作回退的功能。

    所以权衡下,有大范围的更新(一般线上很少),人为的去执行,在执行前,把当前session设置成STATEMENT,其余的都用ROW。这样就避免了上面所说的情况。要是线上有这类操作的话,可以让程序先执行 :

    set session binlog_format=mixed;
  • 相关阅读:
    第二次冲刺-个人总结01
    构建之法阅读笔记03
    第一次冲刺-个人总结07
    第十四周总结
    第一次冲刺-个人总结07
    第一次冲刺-个人总结06
    第一次冲刺-个人总结05
    mysql优化
    springmvc常用注解标签详解
    弄懂JDK、JRE和JVM到底是什么
  • 原文地址:https://www.cnblogs.com/moss_tan_jun/p/6027283.html
Copyright © 2020-2023  润新知