在写《高性能Java持久化培训》一书时,我逐渐意识到让读者了解关系型数据库的工作原理将是理解如何构建高性能Java持久化的重要基石。性能Java持久存储。但是,关系型数据库中与事务相关的重要概念:原子性、持久性、检查点等也比较混乱。在这篇文章中,我希望能比较高深地解释一下关系型数据库的内部工作原理,同时也会涉及到一些数据库的实现细节。数据页通常以较慢的速度访问磁盘上的数据。也就是说,数据在内存中的访问速度还是比SSD快很多。基于这样的考虑,基本上所有的数据库引擎都尽量避免访问磁盘数据。并且数据库表和数据库索引都被划分为固定大小的数据页(例如,8KB)。当我们需要读取表或索引中的数据时,关系数据库会将磁盘中的数据页映射到存储缓冲区中。当我们需要修改数据时,关系型数据库会先修改内存页中的数据,然后使用fsync等同步工具将修改同步回磁盘。由于Undolog可能同时被多个事务并发修改内存中的数据,所以关系型数据库往往需要依赖并发控制机制(2PL或MVCC)来保证数据的一致性。因此,当事务需要更改数据表中的一行时,未提交的更改将被写入内存数据,而之前的数据将追加到undolog文件中。在Oracle或MySQL中使用了所谓的undolog数据结构,而在SQLServer中使用事务日志来完成这项工作。PostgreSQL没有undolog,但是内置支持所谓的多版本表数据,即同一行数据可能同时存在多个版本。总而言之,任何关系数据库都使用类似的数据结构来允许数据的回滚和原子性。如果当前运行的事务被回滚,undolog将用于重建事务开始时的内存页。RedoLog中的事务提交后,需要将内存中的变化同步到磁盘中。但是并不是所有的事务提交都会立即触发同步,过于频繁的同步会损害应用性能。但是根据ACID原则,提交后的事务必须是持久的,即即使此时数据库引擎宕机,提交后的变化也要持久化存储。这里关系型数据库是依靠重做日志来实现的,重做日志是一种基于磁盘的数据结构,只允许追加写入,它记录了所有尚未同步的事务操作。与一次将固定数量的数据页写入磁盘相比,顺序写入重做日志比随机访问要快得多。因此在事务的ACID属性的保证和应用程序的性能之间有一个更好的平衡。这种数据结构在Oracle和MySQL中被称为redolog,而在SQLServer中它是由事务日志执行的,而在PostgreSQL中它使用的是Write-AheadLog(WAL)。我们回到上面的问题,内存中的数据应该什么时候写入磁盘。关系数据库系统通常使用检查点将内存中的脏数据页与磁盘上的脏数据页同步。为了避免IO阻塞,同步过程往往需要等待很长时间才能完成。因此,关系数据库需要保证在所有内存脏页同步到磁盘之前,即使引擎崩溃也不会丢失数据。同样,每次重启数据库时,数据库引擎都会根据redolog重建自上次checkpoint成功后的所有内存数据页。总结我们上面简要讨论的原则和注意事项都是关于在保证数据一致性的同时确保基于磁盘的存储的高吞吐量。其中undolo主要用来提供原子性(允许回滚),而redolog是为了保证磁盘页的不变性。【本文为专栏作家“张子雄”原创文章,如需转载,请通过联系作者】点击此处阅读该作者更多好文
