以前没有深究过数据库底层是怎么运作的,只是停留在了概念层次,但是后来发现写程序的时候不是很舒服,总感觉有些点不通,于是决定看看mysql innodb引擎的原理,这里只是一个大致的介绍,不会太深入。
如果我们在mysql的客户端执行了一些个操作,我们不了解任何底层,最简单的概念模型就是认为数据库的表里面的数据被修改了。也就是我这里执行一次update,数据库就会执行一个修改的io操作,很直观的理解吧?
数据库当然不会这么简单,如果这样做会有一个很明显的性能问题,因为io操作是很慢的,而且数据库都是cs架构,还要搭上网络层的延迟,如果每一次操作都对应了一次真正的磁盘io,那么数据库的性能会大打折扣。那么怎么做呢?缓存。
mysql innodb引擎有自己的内存管理,管理的重点就是日志和数据块,先抛开日志的东西,单说数据块。mysql的逻辑是,我们任何通过客户端提交的操作,最终都只在内存中进行,不会进行磁盘io,当然如果内存没有还是需要io一次的,但是针对此数据的后续的操作就不需要io了。这样,我们每一次才做就成了内存级别的操作,相比磁盘操作,肯定会快不少。但是这样也有问题,如果我们改了内存中的缓存数据,就造成了数据不一致,因为此时同时存在磁盘的数据和内存的数据两个版本,怎么办?mysql innodb的做法是,使用一些后台的线程来周期性的刷缓存的数据到磁盘里。所以说,我们执行一次更新操作,只会改变内存的值,磁盘的值肯定会有延迟。总之,使用缓存提高了mysql的处理效率。
那么刚才说的日志是什么回事?日志主要是与事务相关的,我们知道事务是原子的,要么不做,要么全做,这样才能保持一致性。那如果现在事务做了一半,宕机了,不是违反事务的特性了吗?这是数据库产品必须解决的问题。日志正是干这个事情的。
每当要做一次操作时,在更新数据前,必须先记录日志,只有记录了日志,才能更新,因为如果先更新,一旦宕机,那么这次更新就无法追溯了,我们根本无法得知做了一次更新,如果这次更新只是事务的中间一部分,那么显然它需要回滚,我们根本不知道该如何回滚,所以在更新数据前一定要先记录日志,这样才能保证事务的追溯。
同样的,日志也有缓存,我们每当做一次更新,先把日志记录到日志的缓存中,日志缓存也会被周期性的刷回磁盘,不过频率要更高,innodb是每一秒一次。并且,当执行到commit语句时,会强制把该事务的日志刷回磁盘,只有事务的commit操作的日志记录被刷回磁盘,我们才认为这个事务的commit了的。因为只要我们有日志了,那么数据肯定可以重做,不会丢失。那么有一个问题,既然commit是一个关键点,为何不等到commit了再把数据和日志刷回磁盘而非要再起后台的线程周期性的刷呢?因为如果事务很长,那么最后commit时要刷回的东西就会很大,这样commit的压力太大,不如提前就刷一部分回去,commit的时候刷的内容就少了,提高了效率。
如果我们正在提交一个事务t1,然后宕机了,当数据库再次起起来的时候,数据库会检查日志,看到之前t1事务的日志,假设没有commit点,就会认为该事务只执行了一办,那么所有操作都会被回滚,如果看到了commit,就会认为该事务提交,只需要重做一次日志即可。
所以,借助了缓存技术和日志,可以实现高效率和事务的功能,虽然这只是一个概念级别或者宏观的理解,但是对于理解数据库底层的运作还是至关重要的。后面我会深入到细节取研究这些,比如redo undo,mvcc,线程的功能等等