数据库在系统应用中处于核心位置。个人对数据库的定义是对数据进行中转或存储的组件都可以称之为数据库;熟悉的非关系型数据库nosql:mongdb、redis、hbase等,关系型数据库:mysql,oracle等。甚至我们自己定义的存储的数据文件都可以称之为数据库只不过它的功能相对简陋。今天主要介绍的使用相对广泛的关系型数据库mysql。
以java语言为例:我们在使用mysql时需要引入mysql驱动包,以及Druid、C3P0、DBCP数据库连接池。那么我们有没有想过为什么需要引入这些以及引入它们的作用是什么?
首先jdk并没有提供访问数据库的能力,试想下市面上这么多不同的数据库种类,如果jdk都对其实现那么jdk的体量会惊人的增长,变得臃肿。所以jdk它提供了一套数据库访问的标准接口,具体的实现由对应的数据厂商进行实现。这也是设计原则中的单一职责。
Class.forName("com.mysql.jdbc.Driver")
在介绍数据库连接池前,我们需要知道mysql驱动和mysql建立连接是通过TCP/IP协议。所以对于这种请求就会涉及到连接的建立和销毁,这必然会造成性能的浪费。所以数据库连接池的出现也就顺理成章了,在这里不会详细介绍数据库连接池的细节。我们可以把它想象成一个类似高配版的线程池。池化技术在越来越多针对需要高性能高并发的场景出现,其中著名的Netty buffer池化技术、jdk并发包的线程池、kakfa缓存池等。
-
mysql本身也存在一个连接池来处理
sql执行过程
sql解析器:mysql是不能直接执行我们可读的sql语句,需要转换机器认识的语言。这也就是mysql的一个步骤 解析器。这里不会详细介绍,主要是mysql的解析规则:词法分析和语法分析两部分
-
mysql优化器:将解析之后的sql生成对应的执行计划,并根据mysql的最小成本原则选择最优的执行计划。IO成本+CPU成本之和(这也是有时候为什么会出现实际执行和预期执行不一样的原因)
- IO成本:磁盘数据加载到内存,mysql中数据的读取单元为页。mysql利用局部性原理,查询的当前的数据,那么它周围的数据被查到到概率会增加。innodb中默认页大小是16K,也可以通过修改mysql源文件来修改页的大小
storage/innobase/include/univ.i UNIV_PAGE_SIZE属性
不推荐修改 - CPU成本:将数据加载到内存,我们需要对数据进行排序或者过滤。这里需要消耗CPU的操作
- IO成本:磁盘数据加载到内存,mysql中数据的读取单元为页。mysql利用局部性原理,查询的当前的数据,那么它周围的数据被查到到概率会增加。innodb中默认页大小是16K,也可以通过修改mysql源文件来修改页的大小
执行器流程:调用innodb引擎接口取满足条件的第一行,之后循环取满足条件的下一行
存储引擎:因为这一块个人认为是比较重要的一个,所以会详细介绍
存储引擎
Buffer Pool (缓冲池)是 InnoDB 存储引擎中非常重要的内存结构
-
sql语句的执行步骤:
- innodb存储引擎首先会在缓冲池查询有没有对应的数据(如果存在就直接返回)
- 如果不存在,则去磁盘进行加载,并加入缓冲池
- 同时该条记录会被加上独占锁(防止多人修改,出现数据不一致)
从它的执行过程,其实我们可以发现设置 Buffer Pool 的大小,可以加快sql的查询速度。修改
my.cnf
配置中的innodb_buffer_pool_size
属性。需要注意并不是越大越好,过大会造成系统的swap空间被占用,导致操作系统变慢,降低sql查询性能。
Mysql几个对应的日志文件分析
- undo 日志文件:记录数据被修改前的样子
- redo 日志文件:记录数据被修改后的样子
- bin log 日志文件:记录整个操作过程
undo日志文件
undo 顾名思义就是没有发生的,或者说发生之前的。主要体现在事务回滚的时候。
在执行一条涉及到数据变更的sql时,在数据加载到buffer pool时,同时会在undo log中写入原来的记录。
redo 日志文件
我们需要了解mysql对于数据的更新操作实际上都是在buffer pool中进行,也就是在内存中操作,这大大提高了处理速度。但是在内存操作就会面临一个问题:对于断电这种情况内存中的数据会全部丢失。这时redo日志的作用就体现出来了。redo 记录的是数据修改之后的值,不管事务是否提交都会记录下来。
- 准备更新一条 SQL 语句
- MySQL(innodb)会先去缓冲池(BufferPool)中去查找这条数据,没找到就会去磁盘中查找,如果查找到就会将这条数据加载到缓冲池(BufferPool)中
- 在加载到 Buffer Pool 的同时,会将这条数据的原始记录保存到 undo 日志文件中
- innodb 会在 Buffer Pool 中执行更新操作
- 更新后的数据会记录在 redo log buffer 中(引入redo log buffer 提高处理速度)
关于redo log buffer出现异常的情况mysql如何处理
- 在redo log buffer中的数据未能及时写入时,系统就宕机了。那么mysql默认这个是一个失败的操作。重启之后数据将会滚
- redo log buffer刷入磁盘成功之后宕机,那么重启之后mysql会加载redo 内容恢复到buffer pool中。(redo log 由commit标记才算成功)
bin log日志文件
bin log是记录整个操作过程。主要应用场景主从数据的同步。(不详细介绍了)
最后盗了一张画的不错的图
重要知识点
- Buffer Pool 是 MySQL 的一个非常重要的组件,因为针对数据库的增删改操作都是在 Buffer Pool 中完成的
事务提交流程
- 首先执行器根据 MySQL 的执行计划来查询数据,先是从缓存池中查询数据,如果没有就会去数据库中查询,如果查询到了就将其放到缓存池中
- 在数据被缓存到缓存池的同时,会写入 undo log 日志文件
- 更新的动作是在 BufferPool 中完成的,同时会将更新后的数据添加到 redo log buffer 中
- 将redo log buffer中的数据刷入到 redo log 文件中
- 将本次操作记录写入到 bin log文件中
- 将 bin log 文件名字和更新内容在 bin log 中的位置记录到redo log中,同时在 redo log 最后添加 commit 标记