前言
InnoDB做为一款成熟的跨平台数据库引擎,其实现了一套高效易用的IO接口,包括同步异步IO,IO合并等。本文简单介绍一下其内部实现,主要的代码集中在os0file.cc这个文件中。本文的分析默认基于MySQL 5.6,CentOS 6,gcc 4.8,其他版本的信息会另行指出。
基础知识
WAL技术 : 日志先行技术,基本所有的数据库,都使用了这个技术。简单的说,就是需要写数据块的时候,数据库前台线程把对应的日志先写(批量顺序写)到磁盘上,然后就告诉客户端操作成功,至于真正写数据块的操作(离散随机写)则放到后台IO线程中。使用了这个技术,虽然多了一个磁盘写入操作,但是由于日志是批量顺序写,效率很高,所以客户端很快就能得到相应。此外,如果在真正的数据块落盘之前,数据库奔溃,重启时候,数据库可以使用日志来做崩溃恢复,不会导致数据丢失。
数据预读 : 与数据块A“相邻”的数据块B和C在A被读取的时候,B和C也会有很大的概率被读取,所以可以在读取B的时候,提前把他们读到内存中,这就是数据预读技术。这里说的相邻有两种含义,一种是物理上的相邻,一种是逻辑上的相邻。底层数据文件中相邻,叫做物理上相邻。如果数据文件中不相邻,但是逻辑上相邻(id=1的数据和id=2的数据,逻辑上相邻,但是物理上不一定相邻,可能存在同一个文件中不同的位置),则叫逻辑相邻。
文件打开模式 : Open系统调用常见的模式主要三种:O_DIRECT,O_SYNC以及default模式。O_DIRECT模式表示后续对文件的操作不使用文件系统的缓存,用户态直接操作设备文件,绕过了内核的缓存和优化,从另外一个角度来说,使用O_DIRECT模式进行写文件,如果返回成功,数据就真的落盘了(不考虑磁盘自带的缓存),使用O_DIRECT模式进行读文件,每次读操作是真的从磁盘中读取,不会从文件系统的缓存中读取。O_SYNC表示使用操作系统缓存,对文件的读写都经过内核,但是这个模式还保证每次写数据后,数据一定落盘。default模式与O_SYNC模式类似,只是写数据后不保证数据一定落盘,数据有可能还在文件系统中,当主机宕机,数据有可能丢失。
此外,写操作不仅需要修改或者增加的数据落盘,而且还需要文件元信息落盘,只有两部分都落盘了,才能保证数据不丢。O_DIRECT模式不保证文件元信息落盘(但大部分文件系统都保证,Bug #45892),因此如果不做其他操作,用O_DIRECT写文件后,也存在丢失的风险。O_SYNC则保证数据和元信息都落盘。default模式两种数据都不保证。
调用函数fsync后,能保证数据和日志都落盘,因此使用O_DIRECT和default模式打开的文件,写完数据,需要调用fsync函数。
同步IO : 我们常用的read/write函数(Linux上)就是这类IO,特点是,在函数执行的时候,调用者会等待函数执行完成,而且没有消息通知机制,因为函数返回了,就表示操作完成了,后续直接检查返回值就可知道操作是否成功。这类IO操作,编程比较简单,在同一个线程中就能完成所有操作,但是需要调用者等待,在数据库系统中,比较适合急需某些数据的时候调用,例如WAL中日志必须在返回客户端前落盘,则进行一次同步IO操作。
异步IO : 在数据库中,后台刷数据块的IO线程,基本都使用了异步IO。数据库前台线程只需要把刷块请求提交到异步IO的队列中即可返回做其他事情,而后台线程IO线程,则定期检查这些提交的请求是否已经完成,如果完成再做一些后续处理工作。同时异步IO由于常常是一批一批的请求提交,如果不同请求访问同一个文件且偏移量连续,则可以合并成一个IO请求。例如,第一个请求读取文件1,偏移量100开始的200字节数据,第二个请求读取文件1,偏移量300开始的100字节数据,则这两个请求可以合并为读取文件1,偏移量100开始的300字节数据。数据预读中的逻辑预读也常常使用异步IO技术。
目前Linux上的异步IO库,需要文件使用O_DIRECT模式打开,且数据块存放的内存地址、文件读写的偏移量和读写的数据量必须是文件系统逻辑块大小的整数倍,文件系统逻辑块大小可以使用类似
延伸阅读
学习是年轻人改变自己的最好方式