潦草白纸

MySQL学习笔记-cache 与 buffer

Cache和Buffer是两个不同的概念,简单的说,Cache是加速“读”,而 buffer是缓冲“写”,前者解决读的问题,保存从磁盘上读出的数据,后者是解决写的问题,保存即将要写入到磁盘上的数据。在很多情况下,这两个名词并没有严格区分,常常把读写混合类型称为buffer cache,在Oracle Instance里同样有一块区域作为数据库缓冲区&&高速缓存。

 

Oracle Overview


MySQL Memory Overview

数据库设计普遍都会有插入缓冲、双写(double wirte)的特性,这些特性为数据库带来了更好的性能和更高的可靠性能。


双写


Oracle中的redo log buffer和MySQL Innodb引擎中的日志缓冲是解决redo写入的问题,而database buffer cache和插入缓冲则解决data block的读写问题。

对于Oracle来说,如果IO没有在SGA中命中,都会发生物理IO,Oracle并不关心底层存储的类型,可能是一套存储系 统,可能是本地磁盘,可能是RAID 10,也可能是RAID 5,可能是文件系统,也可能是裸设备,或是ASM。总之,Oracle把底层的存储系统称为存储子系统。


存储系统

在存储系统中,buffer几乎无处不在,文件系统有buffer,存储有buffer,RAID控制器上有 buffer,磁盘上也有buffer。为了提高性能,Oracle的一个写操作,很有可能写在存储的buffer上就返回了,如果这时存储系统发生问题,Oracle如何来保证数据一致性的问题。

Oracle数据库最重要的特性是:Write ahead logging,在data block在写入前,必须保证首先写入redo log,在事务commit时,同时必须保证redo log被写入。Oracle为了保证数据的一致性,对于redo log采用了direct IO,Direct IO会跳过了OS上文件系统的cache这一层。但是,OS管不了存储这一层,虽然跳过了文件系统的cache,但是依然可能写在存储的cache上。

对于MySQL来说,redolog在每次事务commit的时候,就立刻将事务更改操作记录到redolog。所以即使bufferpool中的dirtypage在断电时丢失,InnoDB在启动时,仍然会根据redolog中的记录完成数据恢复。redolog的另一个作用是,通过延迟dirtypage的flush最小化磁盘的随机写入。(redolog会合并一段时间内TRX对某个page的修改)

一般的存储都有buffer,为了提高性能,写操作在buffer上完成就返回给OS了,我们称这种写操作为write back,为了保证掉电时cache中的内容不会丢失,存储都有电池保护,这些电池可以供存储在掉电后工作一定时间,保证cache中的数据被刷入磁盘, 不会丢失。不同于UPS,电池能够支撑的时间很短,一般都在30分钟以内,只要保证buffer中的数据被写入就可以了。存储可以关闭写cache,这时所 有的写操作必须写入到磁盘才返回,我们称这种写操作为write throuogh,当存储发现某些部件不正常时,存储会自动关闭写buffer,这时写性能会下降。

RAID

RAID卡上也有buffer,一般是256M,同样是通过电池来保护的,不同于存储的是,这个电池并不保证数据可以被写入到磁盘上,而是为 buffer供电以保护数据不丢失,一般可以支撑几天的时间。还有些RAID卡上有flash buffer,掉电后可以将cache中的内容写入到flash buffer中,保证数据不丢失。如果你的数据库没有存储,而是放在普通PC机的本地硬盘之上的,一定要确认主机中的RAID卡是否有电池,很多硬件提供商 默认是不配置电池的。当然,RAID卡上的buffer同样可以选择关闭。

磁盘

磁盘上的buffer,一般是16M-64M,很多存储厂商都明确表示,存储中磁盘的buffer是禁用的,这也是可以理解的,为了保证数据可靠性,而 存储本身又提供了非常大的buffer,相比较而言,磁盘上的buffer就不再那么重要。SCSI指令中有一个FUA(Force Unit Access)的参数,设置这个参数时,写操作必须在磁盘上完成才可以返回,相当于禁用了磁盘的写buffer。虽然没有查证到资料,但是我个人认为一旦磁 盘被接入到RAID控制器中,写buffer就会被禁用,这也是为了数据可靠性的考虑,我相信存储厂商应该会考虑这个问题。

至此,我们可以看到Oracle的一个物理IO是经历了一系列的buffer之后,最终被写入到磁盘上。buffer虽然可以提高性能,但是也要考虑掉电保护的问题。关于数据的一致性,是由Oracle数据库,操作系统和存储子系统共同来保证的。


插入缓冲


插入缓冲是InnoDB存储引擎的关键特性。它并不仅仅是缓冲池的一部分,InnoDB缓冲池中有Insert Buffer信息固然不错,但是Insert Buffer和数据页一样,也是物理页的一个组成部分。

通常来说主键作为行唯一标识符,在应用程序中记录的插入顺序是按照主键递增顺序进行插入的。因为插入聚集索引一般是顺序的,所以不需要磁盘的随机读取。
但是不可能每张表上只有一个聚集索引,在更多的情况下,一张表上有多个非聚集的辅助索引(secondary index)。比如,name这一字段,对于它进行查询,并且namel字段不是唯一的。即,表是按照如下的SQL语句定义的:
  1. mysql> create table t ( id int auto_increment, name varchar(30), primary key (id), key(name) );
id列是自增长的,这意味着当插入操作时,id列会自动增长,页中的行记录按id执行顺序存放。
一般情况下,不需要随机读取另一夜执行记录的存放。因此,在这样的情况下,插入操作一般很快就能完成。但是,不可能每张表上只有一个聚集索引,在更多的情况下,一张表上有个多个非聚集的辅助索引(secondary index)。
比如,我们还需要按照name这个字段进行查找,这样的情况下产生了一个非聚集并且不是唯一的索引。在进行插入操作时,数据页的存放还是按主键id的执行顺序存放,但是对于非聚集索引,叶子节点的插入不再是顺序的了。这时就需要离散地方问非聚集索引页,插入性能在这里变低了。因为B+树的特性决定了非聚集索引插入的离散性。


当事务(Transaction)需要修改某条记录(row)时,InnoDB需要将该数据所在的page从disk读到bufferpool中,事务提交后,InnoDB修改page中的记录(row)。这时bufferpool中的page就已经和disk中的不一样了,我们称bufferpool中的page为dirtypage。Dirtypage等待flush到disk上。这大大提高了对非聚集索引执行插入和修改操作的性能。


脏页

因为硬盘的读写速度远赶不上内存的速度,系统就把读写比较频繁的数据事先放到内存中,以提高读写速度,这就叫高速缓存。
linux是以页作为高速缓存的单位,当进程修改了高速缓存里的数据时,该页就被内核标记为脏页,内核将会在合适的时间把脏页的数据写到磁盘中去,以保持高速缓存中的数据和磁盘中的数据是一致的。

正常情况下,dirtypage什么时候flush到disk上?

  1. redolog是一个环(ring)结构,当redo空间占满时,将会将部分dirtypageflush到disk上,然后释放部分redolog。这种情况可以通过Innodb_log_wait(SHOWGLOBALSTATUS)观察,情况发生该计数器会自增一次。
  2. 当需要在Bufferpool分配一个page,但是已经满了,并且所有的page都是dirty的(否则可以释放不dirty的page),通常是不会发生的。这时候必须flushdirtypagestodisk。这种情况将会记录到Innodb_buffer_pool_wait_free中。一般地,可以可以通过启动参数innodb_max_dirty_pages_pct控制这种情况,当bufferpool中的dirtypage到达这个比例的时候,将会强制设定一个checkpoint,并把dirtypageflush到disk中。
  3. 检测到系统空闲的时候,会flush,每次64pages。
涉及的InnoDB配置参数:innodb_flush_log_at_trx_commit、innodb_max_dirty_pages_pct;
状态参数:Innodb_log_wait、Innodb_buffer_pool_wait_free。

参考文献:

浅谈数据库系统中的cache
《MySQL高性能书籍
Oracle9i Database Administration Student Guide I


来自为知笔记(Wiz)


posted on 2015-08-16 14:54  潦草白纸  阅读(6827)  评论(2编辑  收藏  举报

导航