数据库作为存储系统,所有业务访问数据的操作都会转化为底层数据库系统的IO行为(缓存系统也可以当做是key-value的数据库),本文主要介绍访问MySQL数据库的IO流程以及IO相关的参数。
一、MySQL的文件
首先简单介绍一下MySQL的数据文件,MySQL 数据库包含如下几种文件类型:
1)数据文件 (datafile)
存放表中的具体数据的文件。
2)数据字典
记录数据库中所有innodb表的信息。
3)重做日志 (redolog)
记录数据库变更记录的文件,用于系统异常crash(掉电)后的恢复操作,可以配置多个(配置这个参数innodb_log_files_in_group)比如 ib_logfile0、 ib_logfile1。
4)回滚日志 (undolog)
也存在于mysql 的ibdata文件,用户记录事务的回滚操作。注在mysql5.6以上版本可以拆开出来,单独文件夹存在。
5)归档日志 (binlog)
事务提交之后,记录到归档日志中。
6)中继日志 (relaylog)
从master获取到slave的中转日志文件,sql_thread则会应用relay log并重放于从机器。
7)其他日志slowlolg, errorlog, querylog
这里慢日志也经常用。可以结合pt-query-digest工具和anemometer一起展示出来。
对于以上文件的IO访问顺序可以分为顺序访问 比如binlog ,redolog ,relay log是顺序读写,datafile,ibdata file是随机读写,这些IO访问的特点决定了在os 配置磁盘信息时候,如何考虑分区 ,比如顺序写可以的log可以放到SAS盘 ,随机读写的数据文件可以放到ssd或者fio高性能的存储。
二、数据访问流程
数据库访问分为两种类型, 一种是读操作,另外一种是写操作。读操作
create table t (
id int not null primary key ,
k1 int not null,
data varchar(50),
key ind_k1(k1)
) engine=innodb default charset=utf8;
以select * from tab where k1=1 ;为例。
读操作的IO流程
1、查看缓存中是否存在id,
2、如果有则从内存中访问,否则要访问磁盘,
3、并将索引数据存入内存,利用索引来访问数据,
4、对于数据也会检查数据是否存在于内存,
5、如果没有则访问磁盘获取数据,读入内存。
6、返回结果给用户。写操作
为了保证数据写入操作的安全性,数据库系统设置了 undo,redo 保护机制,避免因为os或者数据库系统异常导致的数据丢失或者不一致的异常情况发生。
以insert into t values(1,1,’shuiyi’);为例。
写操作的IO流程
我们假定数据在内存中,不考虑从磁盘中获取数据的情形,大致的写操作步骤:
1、先写undo log。
2、在内存更新数据,这步操作就在内存中形成了脏页,如果脏页过多,checkpoint机制进行刷新,innodb_max_dirty_pages_pct决定了刷新脏页比例。innodb_io_capacity参数可以动态调整刷新脏页的数量,innodb_lru_scan_depth这个参数决定了刷新每个innodb_buffer_pool的脏页数量。
3、记录变更到redo log,prepare这里会写事务id。innodb_flush_log_at_trx_commit决定了事务的刷盘方式。为0时,log buffer将每秒一次地写入log file中,并且log file的flush(刷到磁盘)操作同时进行。该模式下,在事务提交的时候,不会主动触发写入磁盘的操作。为1,每次事务提交时MySQL都会把log buffer的数据写入log file,并且flush(刷到磁盘)中去.为2,每次事务提交时MySQL都会把log buffer的数据写入log file.但是flush(刷到磁盘)操作并不会同时进行。该模式下,MySQL会每秒执行一次 flush(刷到磁盘)操作。
4、写入binlog这里会写入一个事务id这里有个sync_binlog参数决定多个事务进行一次性提交。
5、redo log第二阶段,这里会进行判断前2步是否成功,成功则默认commit,否则rollback。刷入磁盘操作。这里是先从脏页数据刷入到内存2M大小的doublewrite buffer,然后是一次性从内存的doublewrite buffer刷新到共享表空间的doublewrite buffer,这里产生了一次IO。然后从内存的内存的doublewrite buffer刷新2m数据到磁盘的ibd文件中,这里需要发生128次io。然后校验,如果不一致,就由共享表空间的副本进行修复。这里有个参数innodb_flush_method决定了数据刷新直接刷新到磁盘,绕过os cache。
6、返回给client。
如果有slave,第4步之后经过slave服务线程io_thread写到从库的relay log ,再由sql thread应用relay log到从库中。
关于性能?
写undo redo log ,binlog的过程中都是顺序写,都会很快的完成,随机写操作,inset_buffer功能。
对于非聚集类索引的插入和更新操作(5.5 版本及以上支持Update/Delete/Purge等操作的buffer功能),不是每一次都直接插入到索引页中,而是先插入到内存中。具体做法是:如果该索引页在缓冲池中,直接插入;否则,先将其放入插入缓冲区中,再以一定的频率和索引页合并,就可以将同一个索引页中的多个插入合并到一个IO操作中,改随机写为顺序写,大大提高写性能。
关于数据安全,这是数据库写入的重点?
1,2,3过程失败就是事务失败,因为此时还未写入磁盘,对磁盘中的数据无影响,返回事务失败给client,从库也不会受到影响。 4,5过程失败的时候或者已经将写成功返回给客户,可以根据redo log的记录来进行恢复,如果出现部分写失效请参考《double write》。
MySQL的写redo log的第一个阶段会把所有需要做的操作做完,记录数据变更,第二阶段的工作比较简单 ,只做事务提交确认。如果写入binlog成功,而第二阶段失败,MySQL启动时也会将事务进行重做,最终更新到磁盘中。MySQL 5.5+的