InnoDB(二):InnoDB体系架构

1. 体系架构

2. 后台线程

3. 内存

4. 文件


1. 体系架构

    InnoDB的体系架构如下图所示:

InnoDB(二):InnoDB体系架构_第1张图片

     从InnoDB的体系架构图可以看出,InnoDB主要由后台线程内存池磁盘文件三个部分组成。后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存是最近的数据,此外将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常的情况下InnoDB能恢复到正常运行状态。

2. 后台线程

    InnoDB有不同的后台线程,用于处理不同的任务,主要有以下4类线程,分别是master thread、i/o thread、purge thread和page cleaner thread。

2.1 Master Thread

    master thread是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新,合并插入缓冲,undo页的回收等。其内部由多个循环组成:

  • 主循环(loop)
  • 后台循环(backgroup loop)
  • 刷新循环(flush loop)
  • 暂停循环(suspend loop)

(1)主循环(loop)

    Loop被称为主循环,因为大多数的操作是在这个循环中,其中有两大部分的操作,分别是每秒钟的操作和没10s的操作。

   每1s一次的操作包括:

  • 重做日志缓冲刷新到重做日志文件,即使这个事务还没有提交(总是)
  • 合并插入缓冲(可能)
  • 至多刷新100个InnoDB的缓冲池的脏页到磁盘(可能)
  • 如果当前没有用户活动,则切换到后台循环(可能)

   每10s一次的操作包括:

  • 刷新100个脏页到磁盘(可能的情况下)
  • 合并至多5个插入缓冲(总是)
  • 将重做日志缓冲刷新到重做日志文件(总是)
  • 删除无用的Undo页(总是)
  • 刷新100个或者10个脏页到磁盘(总是)

(2)后台循环(backgroup loop)

    若当前没有用户活动或者数据库关闭,就会切换到这个循环,后台循环会执行以下操作:

  • 删除无用的Undo页(总是)
  • 合并20个插入缓冲(总是)
  • 跳回到主循环(总是)
  • 不断刷新100个页直到符合条件(可能,跳到flush loop中完成)

(3)刷新循环(flush loop)

    刷新循环主要是刷新100个脏页到磁盘中,如果flush循环也没什么事儿可以做,则会切换到suspend_loop。

(4)暂停循环(suspend loop)

    suspend loop会将master thread挂起,等待事件的发生。

2.2 IO Thread

    InnoDB存储引擎中大量使用了异步IO(AIO)来处理IO请求,这样可以极大提高数据库的性能,而IO Thread的工作主要是负责这些IO请求的回调处理。在InnoDB 1.0 前的版本中,总共有四个IO Thread,分别是write、read、insert buffer和log IO thread,从InnoDB 1.x开始,read thread和write thread分别增大到了4个,在linux平台下,IO Thread的数量不能进行调整,在windows平台下,可以使用innodb_read_io_threadsinnodb_write_io_threads这两个参数进行设置。

2.3 Purge Thread

    事务被提交后,其所使用的undolog可能不再需要,因此需要PurgeThread来回收已经使用分配的undo页。在InnoDB 1.1版本以前,purge操作仅仅在存储引擎的master thread中完成,从1.1 版本开始,purge操作可以独立到单独的线程中进行,以此来减轻master thread的工作。1.1版本InnoDB只支持1个Purge Thread,从1.2版本开始,开始支持多个Purge Thread,这样做的目的是加快undo页的回收。

2.4 Page Cleaner Thread

    Page Cleaner Thread是在InnoDB1.2.x中引入的,其作用是为了将之前版本中的脏页的刷新操作都放入到单独的线程中来完成,其目的是为了减轻Master Thread的工作及对于用户查询线程的阻塞,进一步提高InnoDB存储引擎的性能。

3. 内存

    整个InnoDB内存的情况如下图所示:

InnoDB(二):InnoDB体系架构_第2张图片

   InnoDB的内存主要由缓冲池、重做日志缓冲和额外内存池三大部分组成,下面分别进行介绍。 

3.1 缓冲池

    InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。由于CPU和磁盘间的速度差异,通常使用缓冲池技术来提高数据库的整体性能。在InnoDB存储引擎中,缓冲池中页的大小默认为16KB

  • 读取操作:首先将磁盘读到的页存放在缓冲池中,下一次读到相同的页时,首先判断是否在缓冲池中,如果在缓冲池中,直接读取该页,否则读取磁盘上的页。
  • 修改操作:首先修改在缓冲池中的页,然后再以一定的频率刷新到磁盘上。页每次从缓冲池刷新到磁盘的操作并不是每次页发生更新时触发,而是通过一种称为Checkpoint的机制刷新回磁盘。

    缓冲池中缓存的数据页类型有:索引页、数据页、undo页、插入缓冲、自适应哈希索引、锁信息、数据字典信息等。由于缓冲池的大小直接影响着数据库的整体性能,对于32位的系统而言,缓冲池的大小最多设置为3G,对于64位的系统则没有限制。对于InnoDB存储引擎而言,其缓冲池的参数可以通过innodb_buffer_pool_size来设置。

    从InnoDB 1.0.x开始,允许有多个缓冲池实例,每个页根据哈希平均分配到不同缓冲池实例中,这样可以减少数据库内部资源竞争,增加数据库并发处理能力,可以通过参数innodb_buffer_pool_instances来进行配置缓冲池的实例个数。

3.2 缓冲池的管理

    缓冲池是一个很大的内存,存放着各种类型的页,而InnoDB对缓冲池的管理主要是通过缓冲链表进行管理的,这里的缓冲链表包括LRU List、Free List、Flush List。

(1)Free List

    Free List用来管理空闲的页,当数据库刚启动时,LRU列表是空的,没有任何的页,此时所有的页都存放在Free列表中。当需要从缓冲池中分页时,首先从Free 列表中查找是否有可用的空闲页,若有则将该页从Free 列表中删除,放到LRU列表中。

(2)LRU List

    LRU列表用来管理已经读取的页,LRU算法只用来维护索引页和数据页,自适应哈希索引页、Lock信息页和Insert Buffer等页不需要LRU算法进行维护,因此不存在于LRU列表中。

    LRU即为最近最少使用算法,即最频繁使用的页在LRU列表的前端,而最少使用的页在LRU列表的尾端。当从缓冲池中分配页时,如果Free 列表中没有空闲页,那么会根据LRU算法,淘汰LRU列表末尾的页,将该内存空间分配给新的页。

    此外,InnoDB的LRU算法不是朴素的LRU算法,而是做了一些优化,这是因为如果直接将读取到的页放入到LRU的首部,那么某些SQL操作可能会使缓冲池的页被刷新出,从而影响缓冲池的效率,常见的这类操作为索引或者数据的扫描操作。

    因此,优化的LRU算法中加入了midpoint位置,当读取到新页时,不是直接放到LRU列表首部,而是放到LRU列表的midpoint这个位置,这个策略称为midpoint insertion strategy策略,默认配置下,该位置在LRU列表的5/8处,midpoint参数可以由参数innodb_old_blocks_pct控制。LRU列表midpoint位置之前的列表称为new表,midpoint之后的位置称为old表。

    那么是什么时候会吧old表中的数据加如到new表中呢,InnoDB引擎引入了另外一个参数innodb_old_blocks_time,用于表示页读取到mid位置后需要等待多久才会加入到LRU列表的热端。从old表移动到new表的操作称为page made young,而没有从old移动到new的操作称为page not made young。

(3)Flush List

    在LRU的页被修改之后,该页被称为脏页,即缓冲池中的页和磁盘中的页产生了不一致,此时该页会被复制到Flush List中,即脏页既存在于LRU列表中,也存在于Flush List中,数据库会通过CHECKPOINT机制将脏页刷新回磁盘。

3.3 重做日志缓冲

    InnoDB首先将重做日志信息放入到这个缓冲区,然后按一定的频率将其刷新到重做日志文件,重做日志缓冲不需要设置很大,因为每秒都会将重做日志缓冲刷新到日志文件,因为只要保证每秒产生的事务量在这个缓冲大小之内即可,重做日志缓冲的大小由参数innodb_log_buffer_size配置,默认为8MB,重做日志缓冲的设计是为了满足ACID特性中持久性的要求。重做日志缓冲刷新到重做日志文件的条件为:

  • Master Thread每一秒会将重做日志缓冲刷新到重做日志文件;
  • 每个事务提交时会将重做日志缓冲刷新到重做日志文件;
  • 当重做日志缓冲池的剩余空间小于1/2时,重做日志缓冲刷新到重做日志文件;

3.4 额外的内存池

    在InnoDB引擎中,对一些数据结构本身的内存进行分配时,需要从额外的内存池进行申请,当该区域的内存不够时,会从缓冲池中进行申请。这些数据结构包括每个缓冲池中的帧缓冲(frame buffer)、对应缓冲控制对象(buffer control block),这些对象记录了一些诸如LRU、锁、等待等信息,而这个对象的内存需要从额外的内存池申请。

3.5 Checkpoint技术

    在数据库中,倘若缓存中每一次页发生变化,就将新页刷新到磁盘,那这个开销是非常大的,同时,如果缓冲池中新页刷新到磁盘时发生宕机,那么数据就不能恢复了。为了避免数据丢失问题,当前事务数据库普遍都采取礼物Write Ahead Log策略,即将事务提交时,先写重做日志,再修改页。当发生宕机而丢失数据时,可以通过重做日志来完成数据的恢复。

    然而,由于缓冲池不可能缓存所有的数据,而且重做日志文件也不可能无限大,因此出现了CheckPoint技术,用于将缓冲池的脏页刷新到磁盘中,Checkpoint技术的目的是解决以下几个问题:

  • 缩短数据库的恢复时间:Checkpoint之前的脏页都已经刷新回磁盘,只需对Checkpoint之后的重做日志进行恢复,大大缩小恢复时间。
  • 缓冲池不够用时,将脏页刷新到磁盘:缓冲池不够用时,根据LRU算法会溢出最近最少使用的页,如果这个页为脏页,那么需要强制执行Checkpoint,将脏页刷新到磁盘
  • 重做日志不可用时,刷新脏页:由于重做日志文件可能无限大,当重做日志文件达到一定的大小时,必须强制Checkpoint,当缓冲池中的页刷新到当前重做日志的位置。

    InnoDB存储引擎中,Checkpoint发生的时间、条件及脏页的选择等都非常复杂,而Checkpoint所做的事情无外乎是将缓冲池中的脏页刷回到磁盘,不同之处在于每次刷新多少页到磁盘,每次从哪里取脏页,以及什么时候触发Checkpoint。InnoDB有两种类型的Checkpoint,分别是Sharp Checkpoint和Fuzzy Checkpoint,前者发生在数据库关闭时将所有的脏页刷新回磁盘,后者在数据库运行时使用,即刷新时只刷新一部分脏页,而不是所有的脏页回磁盘。在InnoDB可能发生如下几种的Fuzzy Checkpoint:

(1)Master Thread Checkpoint

    Master Thread会以每秒或每十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘,这个过程是异步的,此时InnoDB存储引擎可以进行其他操作,用户查询线程不会阻塞。

(2)FLUSH_LRU_LIST Checkpoint

    InnoDB需要保证LRU中有差不多100个空闲页可供使用。在InnoDB1.1.x版本之前,这个检查操作会阻塞用户的查询操作,在1.2.x版本之后,这个检查被放在单独的一个Page Cleaner线程中,并且可以通过参数innodb_lru_scan_depth控制LRU列表中可用页的数量,该值默认为1024.

(3)Async/Sync Flush Checkpoint

    重做日志不可用的情况下,需要强制将一些页刷新回磁盘,此时的脏页是从脏页列表中选取的。1.2.x版本之前,这个操作会阻塞用户的查询线程,1.2.x版本之后,这部分刷新操作同样放到了单独的Page Cleaner线程中,不会阻塞用户的查询线程。

(4)Dirty Page too much Checkpoint

    脏页过多,导致InnoDB存储引擎强制进行Checkpoint,其目的总的来说还是为了保证缓冲池中有足够可用的页,可由参数innodb_max_dirty_pages_pct控制,这个值默认为75,即当缓冲池中的脏页数量占据75%时,强制进行Checkpoint,刷新一部分脏页到磁盘。

3.6 插入缓冲

    对于聚集索引和非聚集索引的区别参考这篇博客。在InnoDB存储引擎中,主键的索引是聚集索引,即InnoDB表文件本身就是数据文件。非主键的索引是辅助索引,辅助索引是非聚集索引,辅助索引的数据域存放的值是相应的主键的值,所以辅助索引查找时,会根据辅助索引查找到主键,再根据主键索引找到实际的数据。

    InnoDB引擎中,通常应用程序中行记录的插入顺序是按照主键递增的顺序进行插入的,因此,插入聚集索引一般是顺序的,不需要磁盘的随机读取,比如:

create table t
{
    a int auto_increment,
    b varchar(30),
    primary key(a)
};

    其中a是自增长的,对a列插入NULL值,则由于其具有auto_increment属性,值会自动增长,同时页中的值按a的值进行顺序存放,在一般情况下,不需要随机读取另一个页的记录,因此对这类情况的插入,速度是非常快的。

    但是不可能每张表上只有一个聚集索引,更多情况下,一张表有多个非聚集的辅助索引,比如用户需要按b这个字段查找,并且b这个字段不是唯一的:

create table t
{
    a int auto increment,
    b varchar(30),
    primary key(a),
    key(b)
};

    进行插入时,数据页的存放还是按主键a进行顺序存放的,但是对于非聚集索引叶子节点的插入不再是顺序的了,这时就需要离散的访问非聚集索引页,由于随机读取的存在导致插入性能的下降。 对于非聚集索引的插入或者更新来说,B+树的特性决定了该操作是离散的。

    为了提升这种情况下的性能,InnoDB存储引擎开创性地设计了Insert Buffer,对于非聚集索引的插入或者更新操作,不是每一次直接插入到索引页中,而是先判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入,若不在,则先放到一个Insert Buffer对象中。数据库的这个非聚集的索引已经插到叶子节点,而实际并没有,只是存放在另一个位置,然后再以一定的频率和情况进行Insert Buffer和辅助索引叶子节点的merge操作,这时通常能将多个插入合并到一个操作中,这就大大提升了非聚集索引的插入性能。

    Insert Buffer的使用需要同时满足以下两个条件:

  • 索引是辅助索引
  • 索引不是唯一的

    当满足以上两个条件时,InnoDB存储引擎会使用Insert Buffer,这样就能提高插入操作的性能了。目前Insert Buffer 存在的一个问题是,在写密集的情况下,插入缓冲会占用过多的缓冲池内存,默认可以占到1/2缓冲池内存,不过通过修改IBUF_POOL_SIZE_PER_MAX_SIZE就可以对插入缓冲的大小进行控制,比如将这个参数设置为3,则最大只能使用1/3的缓冲池内存。

3.7 两次写

    在数据库发生宕机时,可能存储引擎正在写入某个脏页到表中,而这个页只写了一部分,比如16KB的页,只写了前4KB,之后就发生了宕机,这种情况被称为部分写失效。这种情况可以通过重做日志进行恢复吗 ? 必须认识到,innodb为了节约日志量及其它一些原因,设计为逻辑处理的方式,即日志只记录了在一个页面的基础上如何把一条记录插入。在日志记录中记录的内容为表空间号、页面号、记录的各个列的值等等,在重做日志进行恢复时,先在内部转换为物理操作,然后再进行恢复。但这里的一个问题是,如果那个页面本身是错误的,根据日志进行恢复的操作就没办法完成了,因为它的前提是这个页面还是正确的。

    这就是说,在使用重做日志进行恢复时,用户需要一个页的副本,当写入失效发生时,先通过页的副本来还原该页,再进行重做,这就是double write,Innodb存储引擎中double write的体系架构如下图所示:

InnoDB(二):InnoDB体系架构_第3张图片

    doublewrite由两部分构成,一部分是内存中的doublewrite buffer,大小为2MB,另外一部分是物理磁盘上的共享表空间中连续的128个页,大小同样为2MB。在对缓冲池的脏页进行刷新时,并不直接写磁盘,而是会将脏页先复制到doublewrite buffer中,之后通过doublewrite buffer分两次,每次1MB的顺序地写入共享表空间的物理磁盘上,然后马上调用fsync函数,同步磁盘。再这个过程中,因为doublewrite页是连续的,因此这个过程是顺序写,开销并不大。完成doublewrite的页之后,再将doublewrite buffer中的页写入各个表空间文件中,此时的写入则是离散的。

    如果操作系统将页写入磁盘的过程中发生了宕机,在恢复过程中,InnoDB存储引擎可以从共享表空间中的doublewrite中找到该页的一个副本,将其复制到表空间文件,再应用重做日志。参数skip_innodb_doublewrite用来控制是否使用doublewrite功能。 

3.8 自适应哈希

  InnoDB存储引擎会监控对表上各索引页的查询,如果观察到建立哈希索引可以带来速度提升,则建立哈希索引,称之为自适应哈希索引(Adaptive Hash Index,AHI),AHI是通过缓冲池的B+树页构造而来的,因此建立速度非常快,不需要对整张表构建哈希索引,InnoDB存储引擎会自动根据访问频率和模式来自动为某些热点页建立哈希索引。

    值得注意的是,哈希索引只能用来搜索等值的查询,而对于其他查询,比如范围查询,是不能使用哈希索引的,可以用过参数innodb_adaptive_hash_index来控制禁用或者启用该特性,默认AHI为开启状态。

3.9 异步I/O

    同步IO意味着每进行一次IO操作,需要等待此次操作结束后才能继续接下来的操作。而异步IO则意味着用户可以在发出一个IO请求后立即再发送另外一个IO请求,当全部IO请求发送完毕后,等待所有的IO操作的完成,这就是异步IO。为了提高磁盘性能,当前的数据库系统都采用异步IO的方式来处理磁盘操作,InnoDB存储引擎也是如此。

    异步IO的另外一个优势是可以进行IO Merge操作,也就是将多个IO合并为1个IO,这个可以提高IOPS的性能。在1.1.x版本之前,异步IO通过InnoDB存储引擎的代码来模拟实现,而1.1.x开始后,提供了内核级别的AIO的支持,即Native AIO,因此在编译或者运行该版本的Mysql时,需要libaio库的支持。参数innodb_use_native_aio用来控制是否启用Native AIO,linux操作系统下,默认为ON。    

3.10 刷新邻接页

    其工作原理是:当刷新一个脏页时,InnoDB存储引擎会检测该页所在的区(extent)的所有页,如果是脏页,那么一起进行刷新,通过AIO可以将多个IO写入操作合并为一个IO操作,故该工作机制在传统的机械磁盘下有着显著的优势。1.2.x版本开始,InnoDB提供了参数innodb_flush_neighbors用来控制该特性是否启用,对于机械硬盘可以开启该特性,但是对于固态硬盘,建议关闭该特性。    

4. 文件

4.1 参数文件

    Mysql实例启动时,数据库会去读一个配置参数文件,用来寻找数据库的各种文件所在位置以及指定某些初始化参数。用户可以根据命令来寻找参数文件的位置:

mysql --help | grep my.cnf

    但是Mysql实例也可以不需要参数文件,这时所有的参数值取决于编译MySQL时指定的默认值和源代码中指定参数的默认值。

    Mysql的参数可以分成两类:

  • 动态参数
  • 静态参数

    动态参数意味着可以在MySQL实例运行中进行更改,静态参数说明在整个实例生命周期内都不得进行更改。可以通过set命令对动态参数进行修改,set命令的语法如下:

set [global | session] system_var_name= expr

    global和session关键字表明该参数的修改是基于当前会话还是整个实例的生命周期。有些参数只能在会话中进行修改,有些参数则在修改完后,在整个生命周期中都会生效,而有些参数既可以在会话中又可以在整个实例的生命周期内生效。 

4.2 日志文件

    常见的日志文件有错误日志、慢查询日志、查询日志和二进制日志。

(1)错误日志

    错误日志文件对Mysql的启动、运行、关闭过程进行了记录,mysql在遇到问题时应该首先查看该文件以便定位问题,该文件不仅记录了所有的错误信息,也记录了一些警告信息或正确的信息。用户可以通过以下命令来定位该文件:

SHOW VARIABLES LIKE ‘log_error’

    默认情况下错误文件的文件名为服务器的主机名.err,如果主机名为stargazer,那么错误文件名为startgazer.err。 

(2)慢查询日志

    可以在MySQL启动时设定一个阈值,将运行时间超过该值的所有SQL语句都记录到慢查询日志文件中,DBA每天或每过一段时间对其进行检查,确认是否有SQL语句需要进行优化。该阈值可以通过参数long_query_time来设置,默认值为10,代表10s。默认情况下,MYSQL数据库并不启动慢查询日志,用户需要手工将这个参数设置为ON。

    另一个和慢查询有关的参数是log_queries_not_using_indexes,如果运行的SQL语句没有使用索引,则MYSQL数据库同样会将这条SQL语句记录到慢查询日志文件中。DBA可以通过慢查询日志来找出有问题的SQL语句,对其进行优化。然而随着MySQL服务器的运行时间增加,可能会有越来越多的SQL查询被记录到慢查询日志文件中,此时要进行分析就不那么直观了。这时MYSQL提供了mysqldumpslow命令,可以很好地帮助DBA解决该问题。 

    MySQL 5.1开始可以将慢查询的日志放入一张表中,慢查询表在mysql的架构下,名为show_log,参数log_output指定了慢查询输出的格式,默认为FILE,可以将它设定为TABLE,然后就可以查询mysql架构下的slow_log表了。

    MYSQL的show log通过运行时间来对SQL语句进行捕获,这是一个非常有用的优化技巧。InnoSQL版本加强了对SQL语句的捕获方式,在原版MySQL的基础上在show_log中增加了对逻辑读取和物理读取的统计,物理读取指从磁盘进行IO读取的次数,逻辑读取包含所有的读取,不管是磁盘还是缓冲池。用户可以通过额外的参数long_query_io将超过指定逻辑IO次数的SQL语句记录到show log中。为了兼容原MYSQL数据库的运行方式,还添加了参数slow_query_type,用来表示启动slow log的方式,可选值为:

  • 0表示不将SQL语句记录到slow log;
  • 1表示根据运行时间将SQL 语句记录到slow log;
  • 2表示根据逻辑IO次数将SQL语句记录到slow log;
  • 3表示根据运行时间及逻辑IO次数将SQL语句记录到slow log; 

(3)查询日志

    查询日志记录了所有对MySQL数据库请求的信息,无论这些请求是否得到了正确的执行,默认文件名为:主机名.log。对于未能正确执行的SQL语句,查询日志也会进行记录,从MYSQL5.1开始,可以将查询日志的记录放入mysql架构下的general_log表中,这个表和slow_log的使用方式一样。查询日志只记录SELECT和SHOW等SQL语句,如果想查看更改的SQL语句,那么得查看二进制日志。

(4)二进制日志

    二进制日志记录了对MySQL数据库执行更改的所有操作,但是不包括select和show这类操作,因为这类操作对数据本身并没有修改,SELECT和SHOW这类操作的语句主要记录在查询日志中。

    二进制日志主要有以下几种作用:

  • 恢复:某些数据的恢复需要二进制文件,例如,在一个数据库全备文件恢复后,用户可以通过二进制进行point-in-time的恢复。
  • 主从复制:其原理和恢复类似,通过复制和执行二进制日志使一台远程的MYSQL数据库(一般称为slave)与一台MySQL数据库(一般称为master)进行实时同步。
  • 审计:用户可以通过二进制日志的信息来进行审计,判断是否有对数据库进行注入的攻击。

    通过配置参数log-bin [=name]可以启动二进制日志,如果不指定name,则默认二进制日志文件名为主机名,后缀名为二进制日志的序列号,所在路径为数据库所在目录,例如bin_log.00001。二进制文件在默认情况下并没有启动,需要手动指定参数来启动。以下配置文件的参数影响着二进制日志记录的信息和行为:

  • max_binlog_size:

    该参数指定了单个二进制日志文件的最大值,如果超过该值,则产生新的二进制文件,后缀名+1,并记录到.index。从MYSQL5.0开始,这个参数的默认值是1GB。

  • binlog_cache_size:

    使用事务的存储引擎时,所有未提交的二进制日志会被记录到一个缓存中去,等该事务提交时直接将缓冲中的二进制日志写入二进制文件,而该缓冲的大小由binlog_cache_size决定,默认大小为32K。此外,binlog_cache_size是基于会话的,即每开启一个事务时,MYSQL会自动分配一个大小为binlog_cache_size的缓存,因此该值的设置不能过大也不能过小,如果设置过大,事务量多时内存容易爆,如果设置过小,当一个事务的记录大于该值时,MYSQL会把缓冲中的日志写入一个临时文件中。

  • sync_binlog

    默认情况下,二进制日志并不是每次写的时候会同步到磁盘,因此,当数据库发生宕机时,可能会有一部分数据没有写入二进制日志文件中。参数sync_binlog= [N]表示每写缓冲多少次就同步到磁盘,如果将N设置为1,表示采用同步写磁盘的方式来写二进制文件,此时可以得到最大的高可用性,不过会对数据库的IO系统带来一定的影响。

  • binlog-do-db:

    表示需要写入哪些库的日志,默认为空,表示需要同步所有库的日志到二进制日志。

  • binlog-ignore-db:

    表示需要忽略写入哪些库的日志,默认为空,表示需要同步所有库的日志到二进制日志。

  • log-slave-update:

    如果当前数据库时复制的slave角色,它表示是否从maser取得并执行二进制日志写入自己的二进制日志文件中去,如果需要写入,则设置该参数。    

  • binlog-format:

    该参数十分重要,它影响了记录二进制日志的格式。 该参数可以设置成STATEMENTROWMIXED

    1)statement和之前的mysql版本一样,二进制日志文件记录的是日志的逻辑SQL语句。这种格式下,对于主从复制是有一定的要求的,如在主服务器运行rand、uuid等函数,又或者使用触发器等操作,这些都可能导致主从服务器上表中数据的不一致。另一个影响是,如果InnoDB的隔离级别设置成read commited,会出现丢失更新的现象,从而出现主从数据库上的数据不一致。

    2)在row格式下,二进制日志记录的不再是简单的SQL语句了,而是记录表的行更改情况,对于设置为statement格式时出现的问题基本解决,此外,可以将事务的隔离级别设置为read commited,以获得更好的并发性。

    3)mixed格式下,Mysql默认采用statement格式进行二进制文件的记录,但是在一些情况下会使用row格式,可能的情况有:

  • 表的存储引擎为NDB,这时对表的DML操作都会以ROW格式记录
  • 使用了UUID()、USER()、CURRENT_USER()、FOUND_ROWS()、ROW_COUNT()等不确定函数
  • 使用了INSERT DELA Y函数
  • 使用了用户定义函数(UDF)
  • 使用了临时表

    binlog_format是动态参数,因此可以在数据库运行环境下更改,通常情况下,我们将该参数设置为row,这可以为数据库的恢复和复制带来更好的性能,但是不能忽略一点是,这会带来二进制文件大小的增加。由于复制是采用传输二进制日志方式实现的,因此复制的网络开销也有所增加。

    二进制日志文件格式是二进制的,不像错误日志和慢查询日志那样用cat、head、tail等命令查看,要查看二进制文件的内容,必须通过MySQL提供的工具mysqlbinlog来查看。

4.3 socket文件

      unix系统下本地连接Mysql可以采用UNIX域套接字的方式,这种方式需要一个套接字(socket)文件,套接字文件可由参数socket控制,一般在/tmp目录下,名为mysql.sock

4.4 pid文件

    mysql启动时,会将自己的进程ID写入一个文件中,该文件即为pid文件,该文件可由pid_file控制,默认位于数据库目录下,文件名为主机名.pid

4.5 MySQL表结构文件

    mysql数据的存储是根据表进行的,每个表都会有与之对应的文件,但不论采用何种存储引擎,mysql都有一个以frm为后缀的文件,这个文件记录了该表的表结构定义。

    frm还用来存放视图的定义,如用户创建了一个v_a视图,那么对应地会产生一个v_a.frm文件,用来记录视图的定义,该文件是文本文件。    

4.6 存储引擎文件

    以上文件都是MYSQL数据库本身的文件,和存储引擎无关,除了这些文件之外,每个存储引擎还有自己的文件。

(1)表空间文件

    InnoDB采用将存储的数据按表空间进行存放的设计,默认情况下,会有一个初始大小为10MB,名为ibdata1的文件,该文件就是默认的表空间文件,用户可以通过参数innodb_data_file_path对其进行设置,格式如下:

innodb_data_file_path=datafile_spec1[;datafile_spec2]...

    用户可以通过多个文件组成一个表空间,同时制定文件属性,如:

[mysqld]
innodb_data_file_path=/db/bidata1:2000M:/dr2/db/ibdata2:2000M;autoextend

    这里将两个磁盘上的不同文件组成表空间,磁盘的负载可能被平均,因此可以提高数据库的整体性能。设置了innodb_data_file_path之后,所有基于InnoDB存储引擎的表的数据都会记录到该共享表空间中。

    如果设置了参数innodb_file_per_table,则用户可以将每个基于I你弄DB存储引擎的表产生一个独立表空间,独立表空间命名规则为:表名.ibd。 

(2)重做日志文件

    默认情况下,在InnoDB存粗引擎的数据目录下会有两个名为ib_logfile0和ib_logfile1的文件,这两个文件为重做日志文件。每个InnoDB至少有一个重做日志文件组,每个文件组下至少有2个重做日志文件,如默认的ib_logfile0和ib_logfile1。为了得到更高的可用性,可以设置多个镜像的日志组,将不同的文件组放在不同的磁盘上,以此提高重做日志的高可用性。在日志组中,每个重做日志的大小一致,并以循环写入的方式运行。如ib_logfile0 --> ib_logfile1 --> ib_logfile2 --> ib_logfile0。以下参数影响着重做日志文件的属性:

  • innodb_log_file_size:

    这个参数指定了每个重做日志文件的大小,InnoDB1.2.x之后,大小扩大为不得超过512GB。重做日志文件不能设置的太大,太大了在恢复时可能需要很长时间,重做日志也不能设置得太小,否则可能导致一个事务的日志需要多次切换重做日志文件。

  • innodb_log_files_in_group:

    这个参数指定了日志文件组中重做日志文件的数量,默认为2。

  • innodb_mirrored_log_groups:

    这个参数指定了日志镜像文件组的数量,默认为1,表示只有一个日志文件组,没有镜像。

  • innodb_log_group_home_dir: 

    这个参数指定了日志文件组所在的路径,默认为./,表示在MYSQL数据库的数据目录下。

    写重做日志文件时不是直接写,而是先写入一个重做日志缓冲,然后按一定的条件顺序地写入日志文件。重做日志缓冲写入重做日志文件需要满足一定的条件:第一个条件是主线程每秒会将重做日志缓冲写入磁盘的重做日志文件中,不论事务是否已经提交;第二个条件是由参数innodb_flush_log_at_trx_commit参数控制的,该参数可以设置为0,1,2。

  • 0表示事务提交时不写入磁盘,等着主线程每秒刷行;
  • 1表示事务提交时日志同步写入磁盘,同时调用fsync;
  • 2表示事务提交时日志异步写入磁盘,不调用fsync;

    因此,为了保证事务ACID中的持久性,必须将这个参数设置为1。

(3)重做日志文件和二进制文件的区别

  •  二进制日志会记录所有MYSQL数据库有关的日志记录,包括各种引擎的的日志,而重做日志只记录该存储引擎本身的事务日志。
  • 记录的内容不同,二进制日志记录的是事务的具体操作内容,即该日志是逻辑日志,而重做日志记录的是关于每个页的更改的物理情况。
  • 写入时间也不同,二进制日志只在事务提交前进行提交,即只写磁盘一次,不论这时事务有多大。而在事务进行过程中,却不断有重做日志条目被写入到重做日志文件中。

你可能感兴趣的:(InnoDB,数据库,memcached,mysql,1024程序员节)