时间:2021-07-01 10:21:17 帮助过:9人阅读
MySQL版本 | 版本 | 功能 |
MySQL5.1 | 老版本的InnoDB | 支持ACID(原子性、一致性、隔离性、持久性)、行锁设计、MVCC(多版本并发控制技术) |
InnoDB 1.0.x | 继承上述版本所有功能,增加了compress(压缩)和dynamic(动态)页格式 | |
MySQL5.5 | InnoDB 1.1.x | 继承上述版本所有功能,增加了Linux AIO、多回滚段 |
MySQL5.6 | InnoDB 1.2.x | 继承上述版本所有功能,增加了全文索引支持、在线索引添加 |
mysql在创建表的时候定义表的性质(也叫表的类型),共有三种:静态表,动态表,压缩表。默认是静态表,如果存在varchar、blob、text字段,表类型就是动态了。
1.静态表:
字段有固定长度,例如:char(20)。如果使用gbk字符集存储中文username,将占用40byte,如果username的实际内容没有达到 40byte,将会填充空格,以达到40byte。速度很快,因为mysql知道username总是从第41个字节开始,容易缓存,出现问题后也容易恢 复(mysql知道记录的确切位置),需要更多的硬盘空间(如果有三个上面的字段,一条记录就会占120字节,即使实际只用了其中一部分)
2.动态表:
字段不定长(变长),这种表格式比较节省空间,但是复杂度更高,每条记录都有一个header,作用就是表明该记录有多长,所有的字符串列都是动态的(除非 小于4个字节,这种情况下,节省的空间可以忽略不计,增加的复杂度会反而会导致性能丢失),通常占用比静态表少的多地空间,但是必须经常维护(避免碎 片),例如:更新了用户名somebody为somebodyt,t不能立刻就出现在y的后面,因为空间被其他记录占用,对于出现碎片的列,每个新连接会 损失6个字节。而且出现问题后不容易重建(前面我说的静态表正好相反),如果碎片严重,有可能出现库爆炸(^_^).
不包括连接的动态记录的空间消耗可以用下面的公式计算:
3+(列数+7)/8+(字符列数)+数字列的打包尺寸+字符串长度+(空列的数量+7)/8
每条记录的header可以表明那个字符串列是空的,那个数字列包含0(非空),在那种情况下不向磁盘存储,非空字符串包含一个长度字节加上字符串内容。
压缩表:
只读,使用很少的空间,用myisampack工具创建,表要少得多,每条记录分开压缩,所以不能同时访问,可以压缩静态表和动态表。
创建方法:myisampack [options] filename
Master Thread是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲(INSERT BUFFER)、UNDO页的回收等。
在InnoDB存储引擎中大量使用了AIO(Async IO)来处理写请求,这样可以极大提高数据库的性能。而IO Thread的工作主要是负责这些IO请求的回调处理。
可以通过命令show engine innodb status来观察InnoDB中的IO Thread。
事务提交后,其所使用的undolog可能不在需要,因此需要Purge Thread来回收已经使用并分配的undo页。在InnoDB1.1版本之前,purge操作仅在Master Thread中完成。而从InnoDB1.1开始,purge操作可以独立到单独的线程中完成,以此来减少Master Thread的工作,从而提高CPU的使用率以及提升存储引擎的性能。
从InnoDB1.2开始,InnoDB支持多个Purge Thread,这样做的目的是为了进一步加快undo页的回收。
Undolog:在操作任何数据之前,首先将数据备份到一个地方(undolog),然后再修改;这样就算更改到一半的时候,出现意外更改没有完成,则还可以恢复原来数据。
Redolog:每当操作数据前,将数据真正更改的时候,先将相关操作写入重做日志。这样当断电或者发生意外,导致后续操作未能完成,系统恢复后,还可以继续操作。
Page Cleaner Thread是在InnoDB1.2.x版本中引入的。其作用是将之前的版本中脏页的刷新操作都放入单独的线程中完成。其目的是减轻原Master Thread的工作及对于用户查询线程的阻塞,进一步提高InnoDB存储引擎的性能。
InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式的方式进行管理。
缓冲池简单来说就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响。常见的在数据库中操作页有两种情况:首先是读取页操作,会先将从磁盘读到的页放在缓冲池中,下一次再读相同的页时,会首先判断该页是否再缓冲池中;然后还有修改操作,当修改了缓冲池中的页,会以一定频率刷新到磁盘上。注:刷新回磁盘不是再每次页发生更改的时候触发,而是通过一种称为Checkpoint的机制刷新回磁盘。
缓冲池的大小直接影响着数据库的整体性能。
下图缓冲池中还有undo页。
从InnoDB 1.0.x版本开始,允许有多个缓冲池实例。每个页根据哈希值平均分配到不同缓冲池实例中。可以减少数据库内部资源的竞争,增加数据库的并发处理能力。
数据库中的缓冲池是通过LRU(最近最少使用)算法来进行管理的。即最频繁使用的页在LRU列表的前端,而最少使用的页在lRU列表的尾端。当缓冲池不能存放新读取到的页时,将首先释放LRU列表中尾端的页。
InnoDB存储引擎对传统的LRU算法做了一些优化,LRU列表中还加入了midpoint位置。即新读入的页不是先放在LRU列表的首部,而是放在midpoint位置。原因是,直接放入列表首端的话会,那么某些sql操作可能会使缓冲池中的页被刷新出,从而影响缓冲池的效率。
LRU列表用来管理已经读取的页,但当数据库刚启动时,LRU列表时空的,即没有任何的页。这时页都存放在free列表中,当需要从缓冲池中分页时,首先从free列表中查找是否有可用的空闲页,如有则将该页从free列表中删除,放入到LRU列表中。否则,根据LRU算法,淘汰LRU列表末尾的页,将该内存空间分配给新的页。
Innodb存储引擎从1.0.x版本开始只是压缩页的功能。原来LRU列表中的页 默认为16k,而对于非16k的页,是通过unzip_LRU列表进行管理的。
在LRU列表中的页被修改后,称该页为脏页,即缓冲池中的页和磁盘上的页的数据产生了不一致。这时数据库会通过checkpoint机制将脏页刷新回磁盘,而flush列表中的页即为脏页列表,需要注意的是,脏页既存在与LRU列表中,也存在与flush列表中。LRU列表用来管理缓冲池中的页的可用性,flush列表用来管理将页刷新回磁盘,二者互不影响。
InnoDB存储引擎会首先将重做日志信息先放入到这个缓冲区,然后按一定频率将其刷新到重做日志文件。重做日志缓冲一般不需要设置很大。
通常情况下,8MB的重做日志缓冲池足以满足绝大部分的应用,因为重做日志在以下三种情况会将重做日志缓冲中的内容刷新到外部磁盘的重做日志文件中。
1. master Thread每一秒将重做日志缓冲刷新到重做日志文件;
2. 每个事物提交时会将重做日志缓冲刷新到重做日志文件;
3. 当重做日志缓冲池剩余空间小于1/2时,重做日志缓冲刷新到重做日志文件。
在innodb存储引擎中,对内存的管理是通过一种称为内存堆的方式进行的。在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域的内存不够时,会从缓冲池中进行申请。
为了协调CPU和磁盘速度的鸿沟,因此页的操作首先都是在缓冲池中完成的。当一个页在缓冲池中被DML语句修改,就会成为脏页,这时就需要将其刷新回磁盘。但是如果每次修改都将其刷新会严重影响性能。
同时,如果在从缓冲池将页的数据刷新到磁盘时发生宕机,那么数据就不能恢复了。为了避免发生数据丢失的问题,当事务提交的时候,先写重做日志,再修改页。当由于发生宕机导致数据丢失的时候,通过重做日志来完成数据的恢复。
但是重做日志不能无限增大
因此checkpoint技术的目的是解决以下几个问题:
01.缩短数据库恢复的时间
当数据库发生宕机 不需要重做所有日志,只需要重做checkpoint之后的操作。
02.当缓冲池不可用时,将数据刷新回磁盘
当缓冲池溢出页,那么也会强制执行checkpoint操作。
03.重做日志不可用时,刷新脏页
重做日志可以被重用的部分是指这些重做日志已经不再需要,当数据库发生宕机时,数据库恢复操作不需要这部分的重做日志,因此这部分可以被覆盖重用。若此时重做日志还需要使用,那么必须强制产生checkpoint,将缓冲池中的页至少刷新到当前重做日志的位置。
发生在数据库关闭时将所有的脏页都刷新回磁盘。
若在数据库运行时也使用sharp checkpoint,那么数据库的可用性就会受到很大的影响。所以用fuzzy checkpoint只刷新部分脏页。
1. Master Thread Checkpoint
Master Thread中会发生Checkpoint,差不多以每秒或者每十秒的速度从缓冲池的脏页列表中刷新一定比例的页回磁盘。这个过程是异步的,不影响MySQL的其他操作,用户查询线程不会影响。
2. Flush_lru_list Checkpoint
Flush_lru_list Checkpoint是因为innodb存储引擎需要保证lru列表中需要有差不多100个空闲页可供使用。
3. Async/Sync Flush Checkpoint
这个指重做日志文件不可用的情况,这时强制将一些页刷新回磁盘,而此时是从脏页列表中选取的。
4. Dirty Page too much Checkpoint
当脏页数量太多,导致innodb存储引擎强制进行checkpoint。其目的总的来说还是为了保证缓冲池中有足够可用的页。
Master thread具有最高的线程优先级别。其内部由多个循环组成:主循环(loop)、后台循环(backgroud loop)、刷新循环(flush loop)、暂停循环(suspend loop)。
其中有两大部分操作——每秒的操作和每10秒操作。
每秒的操作:
l 日志缓冲刷新到磁盘,即使这个事务还没有提交(总是);
即使某个事务还没有提交,InnoDB 存储引擎仍然每秒会将重做日志缓冲中的内容刷新到重做日志文件。这一点是必须要知道的,因为这可以很好地解释为什么再大的事务提交(commit)的时间也是很短的。
l 合并插入缓冲(可能);
合并插入缓冲(Insert Buffer)并不是每秒都会发生的。InnoDB 存储引擎会判断当前一秒内发生的IO 次数是否小于5 次,如果小于5 次,InnoDB 认为当前的IO 压力很小,可以执行合并插入缓冲的操作。
l 至少刷新100个innodb的缓冲池中的脏页到磁盘(可能);
同样,刷新100 个脏页也不是每秒都会发生的。InnoDB 存储引擎通过判断当前缓冲池中脏页的比例(buf_get_modified_ratio_pct)是否超过了配置文件中innodb_max_dirty_pages_pct 这个参数(默认为90,代表90%),如果超过了这个阈值,InnoDB 存储引擎认为需要做磁盘同步的操作,将100 个脏页写入磁盘中。
l 如果当前没有用户活动,则切换到background loop(可能)。
每10秒的操作:
l 刷新100个脏页到磁盘(可能的情况下);
l 合并至多5个插入缓冲(总是);
l 将日志缓冲刷新到磁盘(总是);
l 删除无用的undo页(总是);
l 刷新100个或者10个脏页到磁盘(总是)。
在以上的过程中,InnoDB 存储引擎会先判断过去10 秒之内磁盘的IO 操作是否小于200 次,如果是,InnoDB 存储引擎认为当前有足够的磁盘IO 操作能力,因此将100个脏页刷新到磁盘。接着,InnoDB 存储引擎会合并插入缓冲。不同于每秒一次操作时可能发生的合并插入缓冲操作,这次的合并插入缓冲操作总会在这个阶段进行。之后,InnoDB 存储引擎会再进行一次将日志缓冲刷新到磁盘的操作。这和每秒一次时发生的操作是一样的。
接着InnoDB 存储引擎会进行一步执行full purge 操作, 即删除无用的Undo页。对表进行update、delete 这类操作时,原先的行被标记为删除,但是因为一致性读(consistent read)的关系,需要保留这些行版本的信息。但是在full purge 过程中,InnoDB 存储引擎会判断当前事务系统中已被删除的行是否可以删除,比如有时候可能还有查询操作需要读取之前版本的undo 信息,如果可以删除,InnoDB 会立即将其删除。
从源代码中可以发现,InnoDB 存储引擎在执行full purge 操作时,每次最多尝试回收20个undo 页。
然后,InnoDB 存储引擎会判断缓冲池中脏页的比例(buf_get_modified_ratio_pct),如果有超过70% 的脏页,则刷新100 个脏页到磁盘,如果脏页的比例小于70%,则只需刷新10% 的脏页到磁盘。
若当前没有用户活动(数据库空闲时)或者数据库关闭(shutdown),就会切换到这个循环。
background loop 会执行以下操作:
u 删除无用的Undo 页(总是);
u 合并20 个插入缓冲(总是);
u 跳回到主循环(总是);
u 不断刷新100 个页直到符合条件