引言
作为一个后端程序员,我们几乎每天都要和数据库打交道,市面上的数据库有很多,比如:mysql,oracle,sqlserver等等,那么我们的写的程序是怎么和数据库连接起来的呢?那就是数据库驱动,不同的数据库对应了不同的数据库驱动。在我们连接数据库的时候,首先将数据库驱动进行注册,然后基于数据库地址,用户名,密码等信息与数据库建立连接。如果用maven来管理项目的话,一般会看到如下配置:
1
2
3
4
5
|
<dependency>
<groupid>mysql</groupid>
<artifactid>mysql-connector-java</artifactid>
<version>8.0.24</version>
</dependency>
|
如上,通过maven导mysql的驱动jar包,接着就可以在项目中通过sql语句操作数据库了。那数据库在接收到请求后,是怎么执行的呢?接下来我将通过mysql数据库进行详细阐述。
1、mysql数据库整体架构
一般我们知道,web项目开发完以后,可以将项目文件打成一个war包,然后通过tomcat容器进行发布,最后用户就可以访问我们的系统了。我们都知道,tomcat是支持并发访问的,当多个请求需要同时操作数据库的时候,难道是多个请求去抢占一个数据库连接吗?那肯定不是的,不然效率得多低下,那难道是给每个请求都建立一个连接,请求结束后再销毁连接吗?那肯定也不是的,频繁建立、销毁连接肯定也是影响性能的。那tomcat是如何解决这个问题的呢? 还记得我们在讲线程池的时候提到的“池化”思想吗?是的,tomcat中有一个数据库连接池,那么同样的数据库服务器中也有一个对应的数据库连接池,大致结构如下图所示
sql接口
当请求到达数据库以后,会被监听的线程发现,继而将请求转交给sql接口来处理,sql接口专门用于执行增删改查这样的sql语句。
解析器
虽然sql语句我们比较容易理解,但是对于mysql系统来说是没法直接理解的,所以sql接口会把sql语句转交给解析器,查询解析器负责将sql语句进行解析,也就是按照既定的sql语法,对sql语句进行解析,理解这个sql要完成的操作。
优化器
当解析器理解了sql语句需要完成的操作后,接着通过优化器选择一条它认为的最优路径。一般情况下,要达到某种结果并不是只有一条路径,比如,要查询在表t里,符合条件c的两个字段f1,f2的值,至少可以有以下两种路径:
- 先去表t中筛选出符合条件c的所有数据行,再选出字段f1,f2的值作为结果集;
- 先选出所有f1,f2的值,再根据条件c筛选出符合条件的数据行组成结果集。
优化器会根据不同的策略得到它认为最优的查询路径。
执行器
当优化器选出最优的查询路径后,并不能得到我们最终希望得到的结果,所以还需要用执行器。执行器的作用就是根据优化器选出的最优查询路径生成一套执行计划,然后不停的去调用数据库存储引擎提供的接口去完成sql语句的执行计划。
存储引擎
数据库一般将数据无非存储在两个地方:内存或磁盘。那么假如我们查询数据时,执行器需要到去磁盘还是内存中查询呢?内存中是如何查询的?磁盘中是如何查询的,内存的容量是有限的,当内存中没有多余的空间怎么办?等等一系列问题的解决方案就是存储引擎,mysql提供了多种存储引擎:innodb,myisam,memory等等,比较常见的是innodb和myisam,可以通过show engines命令查看当前mysql数据库的存储引擎。本系列将主要分析innodb存储引擎。
综上,一套完整的sql语句执行流程如下图所示
2、innodb存储引擎架构
假如现在一条sql语句通过上述的流程,到了执行器调用innodb存储引擎的接口,那么innodb存储引擎是怎么工作的呢?
内存缓冲池
首先介绍innodb存储引擎中第一个重要组件—内存缓冲池,即buffer pool,这是内存中的一块区域,存储了大量数据,便于执行查询、更新等操作。这样做的目的就是提高sql语句的执行效率,所以要明确一个概念,我们的查询、更新等操作都是在buffer pool中完成(无论数据是否存在于buffer pool中,存在的话直接操作,不存在的话先从磁盘中加载到buffer pool中再操作)。
undo log日志文件
熟悉数据库的同学都知道,在我们更新数据的时候一般是放在一个事务中进行操作。事务有4大特性:acid,其中a就代表了原子性,即这次操作要么全部成功要么全部失败,成功的话就提交(commit)事务,失败就回滚(rollback),其中回滚就是通过undo log来实现的。(有一次被问到了,一时紧张没想起来,过了一会才反应过来...)。
一般mysql数据库会默认开启事务自动提交,所以不需要我们做额外的操作,我们可以通过set autocommit = 0 来关闭自动提交事务和set autocommit来打开自动提交事务。有兴趣可以试试去感受感受。
redolog日志文件
前面我们已经介绍了,更新操作是在buffer pool中完成的,也就是在内存中完成的,万一操作完以后mysql宕机了,那么必然会使内存中修改过的数据丢失。为了解决这个问题innodb架构中设计了redo log,用来记录你对什么数据进行了修改。如果出现mysql宕机,重启之后可以通过redo log来进行数据恢复。但是redo log也是先将redo log写到内存中的redo log buffer中,并没有持久化到磁盘,所以数据丢失的风险依然存在。所以innodb提供了几种redo log刷盘策略,通过innodb_flush_log_at_trx_commit来进行设置刷盘策略,比如innodb_flush_log_at_trx_commit=1表示事务提交日志马上刷入磁盘,这样就不会存在数据丢失的风险,但是性能肯定会受到影响。一般可以根据业务需求进行设置策略。
binlog日志文件
binlog也叫归档日志,与redo log不同,这是mysql server的,而不是innodb所特有的,一般用户恢复某个时间点的数据,主从同步等,而redo log用户故障恢复。一般提交事务的时候也会提交归档日志。同样的归档日志也有几种刷盘策略,通过sync_binlog来控制几次事务提交后会刷盘。特别的sync_binlog=0表示由操作系统控制刷盘时机,而不是mysql。
innodb执行流程
介绍完innodb存储引擎的几个组件后,假设现在需要更新一条数据,那么在innodb中的执行流程应该是怎么样的呢?如下:
- 如果数据不存在于buffer pool中,则随机i/o从磁盘读取数据,放入buffer pool;
- 写undo log用于回滚数据;
- 更新buffer pool中的数据;
- 写redo log到redo log buffer用于故障恢复数据;
- 准备提交事务,redo log日志基于策略准备刷入磁盘;
- 准备提交事务,binlog日志基于策略准备刷入磁盘;
- 写入binlog文件与commit标记到redo log日志文件;
- 提交事务;
- 后台io线程将buffer pool中脏数据输入磁盘。(因为前期只修改了buffer pool中日志,磁盘中数据并未修改,所以对于磁盘数据来说,buffer pool中的数据是脏数据)
流程如下图所示:
以上就是mysql innodb架构的相关总结的详细内容,更多关于mysql innodb架构的资料请关注服务器之家其它相关文章!