zoukankan      html  css  js  c++  java
  • 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;
  • 相关阅读:
    English 2
    速算24点
    心理学1
    从微服务到函数式编程
    034 01 Android 零基础入门 01 Java基础语法 04 Java流程控制之选择结构 01 流程控制概述
    033 01 Android 零基础入门 01 Java基础语法 03 Java运算符 13 运算符和表达式知识点总结
    032 01 Android 零基础入门 01 Java基础语法 03 Java运算符 12 运算符和if-else条件语句的综合案例——闰年问题
    031 01 Android 零基础入门 01 Java基础语法 03 Java运算符 11 运算符的优先级
    030 01 Android 零基础入门 01 Java基础语法 03 Java运算符 10 条件运算符
    029 01 Android 零基础入门 01 Java基础语法 03 Java运算符 09 逻辑“非”运算符
  • 原文地址:https://www.cnblogs.com/moss_tan_jun/p/6027283.html
Copyright © 2011-2022 走看看