事务与io

 innodb引擎通过缓存技术,将常用的数据和索引缓存到内存中,这样在读取数据或者索引的时候就减少磁盘io来提高性能。

  

   innodb修改数据的时候也只是先修改下buffer pool中的数据,并不是当一个事务提交后将buffer pool中的数据刷新到磁盘中,而是将修改信息记录到事务日志中去,这样做的目的还是尽量减少磁盘的io。

   这里先解释下磁盘读写的两个概念:连续读写和随机读写。简单来说:连续读写就是将数据写入到连续的磁盘物理位置,而随机读写则相反,数据根据自身的特定位置写入到各个位置,也就是说数据被写入到并不连续的物理位置。对于磁盘来说:连续读写最大的好处就是磁头所作的寻址动作较少,而磁盘操作中开销最大的就是磁头的寻址。所以说,连续读写比随机读写有更好的性能。

  

  一般说来,我们的应该修改buffer pool中的数据都是随机的,每次所做的修改都是一个或者几个数据页,多次修改的数据也很少会连续的。如果每次修改都将buffer pool中的数据刷新到磁盘中去,这样磁盘就一直不停的忙于随机数据的读写。而事务日志在创建之初就申请了连续的磁盘空间,而且每次写入都是紧接着之前的日志数据顺序的往后写入,基本上是一个顺序的写入,所以说事务日志的写入远比同步buffer pool中的数据到磁盘中要高效些。

   事务日志是通过几个日志文件循环写入的,并且每个日志文件的大小固定,即使有再多的事务日志文件,也会有旧的日志被新的日志所覆盖,所以在旧的日志被新的日志覆盖之前,我们并且要将buffer pool中的数据刷新到磁盘中去。只不过,随着更新数据(dirty buffer)的增加,需要刷新的数据连续性就越高,所需要的随机读写也就越少,io性能也就得到了提升。

   

   而事务日志本身也有buffer(log buffer),事务日志的每次写入也并不是直接写入到文件中去的,而是暂时写入到log buffer中,然后在一定的条件下才会同步到日志文件中去,当然,为了尽可能的减少事务日志的丢失,我们可以通过innodb_log_buffer_size参数来控制log buffer 的大小。

   事务日志文件的大小与innodb的整体io性能有密切的关系,从理论上说,日志文件越大,buffer pool所需的刷新数据到磁盘的动作就越少(在buffer pool允许足够多的dirty buffer的前提下),性能也就越高。但必须注意的是,不能忽视系统crash之后的恢复。

   事务日志的主要作业有如下两个:(1)提高系统的整体io性能,(2)系统crash后的恢复。

   

   下面简单的分析下系统crash后,innodb是如何利用事务日志来进行数据的恢复:

    innodb中记录了每次对数据库所做的数据和索引的修改,并且将相关修改信息记录到日志文件中去,同时还记录了系统每次的checpointhe 和log sequence number(日志序列号)。

    假设在某一时刻,系统crash,那么所有的buffer pool中的数据丢失了,包括已经修改提交但还没来得及刷新到磁盘中的数据。所以在mysql从crash到再次启动,innodb会通过比较事务日志中所记录的checkpoint信息和各个数据文件中的checkpoint信息,找到最后一次checkpoint所对应的log sequence number,然后根据事务日志中所记录的变更记录,将从系统crash之前的checkpoint 往后的所有变更记录重新应用一次,同步所有的数据文件到一致状态,这样就找回了系统crash而造成的所有数据丢失。当然了,log buffer中未来得及同步到日志文件中的变更数据就无法找回了。系统crash的时间到最后一次checkpoint的时间越长,所需要的恢复时间也就越长。而事务日志文件越到,innodb所作的checkpoint频率也就越低,自然遇到的长时间的数据恢复可能性也就越大了。

    总体上来说,事务日志文件设置的越大,系统的整体io性能也就越好,但当遇到mysql,os或者主机crash的时候,系统所需要的恢复时间也就越长;反之,事务日志文件越小,io性能也就相对差些,但当mysql,os或者主机crash时所需要恢复数据的时间也就越小。所以,到底该将事务日志设置多大其实是一个整体平衡的问题,既要考虑到系统整体的性能,也要兼顾到crash后数据恢复所需要的时间。 事务日志文件大小设置参考方案:三组事务日志文件,每个日志文件256M。

   前面描述的只是mysql crash的场景,丢失的只是buffer pool中的数据,实际中innodb事务日志每次提交或者回滚也不一定保证同步log buffer中的数据到文件系统,并通知文件系统把数据刷新到磁盘中。所以在os crash或者主机断电后,事务日志写入文件系统log buffer中的数据还有可能丢失。这种情况下,如果事务日志没有及时同步文件系统,刷新缓存中的数据到磁盘中,就有可能会产生因日志数据的丢失而造成数据永久丢失的情况。

  mysql中控制innodb事务日志刷新方式的参数:innodb_flush_log_at_trx_commit,这个参数的主要功能就是告诉系统,什么情况下该通知文件系统刷新缓存中的数据到磁盘中,可设置的值为三个:

  *innodb_flush_log_at_trx_commit=0,innodb中的log thread每隔一秒钟将会log buffer中的数据写入文件,同时还会通知文件系统进行与文件同步的flush操作,保证数据确实已经写入磁盘。但是,每次事务的结束(commit或者rollback)并不会触发log thread将log buffer中的数据写入文件。所以当设置为0时候,在mysql crash或者oscrash或者主机断电的情况,最极端的情况是丢失一秒的数据变更。

  *innodb_flush_log_at_trx_commit=1,这也是innodb默认设置,每次事务的结束都会触发log thread将log buffer中的数据写入文件,并通知文件系统同步文件。这个设置是最安全的,能够保证不论是mysql crash,os crash还是主机断电都不会丢失任何已经提交的事务。

  *innodb_flush_log_at_trx_commit=2,log thread会在每次事务结束后将数据写入事务日志,但是仅仅是调用了文件系统的写入操作,而文件系统都是有缓存的,所以log thread的写入并不能保证将文件系统中缓存写入到物理磁盘进行永久固化。文件系统什么时候将缓存中的数据同步到物理磁盘,log thread 并不知道。所以当设置为2的时候,mysql 的吵嚷声并不会造成数据的丢失,但是os crash或者主机断电可能造成事务日志的丢失,各种文件系统对文件缓存的刷新机制各不相同。

  从以上的分析得出,当innodb_flush_log_at_trx_commit设置为1时是最安全的,但由于所作的io同步操作最多,性能也是三种当中最差的;如果设置为0,则每秒同步一次,性能相对高些,如果设置为2,性能可能是这三种中最好的,但也有可能会出现故障后丢失的数据最多的。至于具体应该如何设置,一般来说,如果不能完全接受数据的丢失,那可以通过牺牲一定的性能来换取数据的安全性,选择设置为1,如果允许丢失少量的数据(比如说1秒内),那么设置为0,当然如果操作系统够稳定,主机的硬件设备足够好的话,而且主机的供电系统也足够安全的话,那么可以将innodb_flush_log_at_trx_commit=2,保证系统的高性能。

  log buffer的大小参数innodb_log_buffer_size,log buffer所存放的数据就是事务日志写入之前在内存中一个缓冲区,所以,从理论上说,log buffer越大,系统的性能越高。但是由于触发log thread将log buffer中的数据写入文件的事件并不是仅仅log buffer空间用完的情况下,而且还与innodb_flush_log_at_trx_commit设置的参数相关。如果该参数设置为1或者2的话,那么log buffer只须保存单个事务的变更量和系统最高的并发事务的乘积,也就是说,如果系统同时进行修改的事务数是20,那么log buffer中就只需要保持20个事务所作的变更。当然,如果设置为0 的话,log buffer须要存放的只是1秒中所有的变更量。 一般来说:如果不是特别高的事务并发量或者大事务的话,8M的log buffer足够了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值