MySQL高手第一章

天天写CRUDH,你知道系统是如何与MySQL打交道的吗?

在程序员眼中数据库是什么?

大多数Java工程师对MySQL的了解和掌握程度,大致停留在对MySQL可以建数据库索引,然后就是去执行增删改查去更新和查询里面的数据。

但是在实际的使用过程中,我们会遇到这样的一些问题,死锁异常,SQL性能太差,异常报错等等。

MySQL驱动是什么?

在我们的Maven中,如果要引入MySQL驱动,这里mysql-connector-java就是面向Java语言的MySQL驱动

复制代码
<dependency>
    <groupId>MySQL</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

而这个MySQL驱动就是和数据库建立的一个网络连接,将请求发送给数据库服务器

建立好连接后,Java代码才能基于这个连接去执行各种各样的SQL语句

数据库连接池是什么?

现在我们要思考一下,一个Java程序难度只会和数据库建立一个连接吗?

这肯定是不行的,假如我们用Java开发了一个web系统,部署在Tomact中,而Tomact本身是多线程来并发处理同时接收到的多个请求。

如果多个线程并发处理多个请求,去抢夺一个数据库资源的化,效率肯定很低下。

那么我们在每个线程去访问数据库都会时候,都去创建一个新的MySQL驱动,执行完毕后再销毁,这样可以吗?

这样也十分不好,因为7每次建立一个数据库连接都很耗时,这样效率很低下。

所以我们一般需要使用一个数据库连接池,让多个线程使用里面不同的数据库连接去执行SQL,执行完毕后不要摧毁这个数据库连接,放回池子里面后面继续使用。

数据库连接池是用来干啥的?

我们日常开发的系统中很多要与MySQL数据库建立多个 连接,所以MySQL架构体系中的第一个环节就是 数据库连接池

MySQL连接池维护了与系统之间的多个数据库连接,此外还会进行账号密码验证,库表权限都会验证。

为了执行SQL语句,MySQL用了什么样的架构设计?

大部分程序员,只知道insert语句执行后表中会多出一条数据,执行update后会对表里面的数据进行更改,执行了delete之后会删除表中的一条数据,执行select之后会查询出一条数据。

在我们的日常使用中完全是将其当成了一个黑盒去使用他,我们要更好的去了解数据库就需要深入底层去探索数据库原理以及生产问题的优化手段。

网络连接必须让线程来处理

假设数据库的连接池接收到网络请求后,有没有想过这样的问题:谁负责从这个连接中去监听网络请求?谁负责嘲弄网络连接里面把请求数据读出来?

从计算机基础知识我们可以知道网络连接必须分出一个线程去处理请求,由一个线程来监听请求以及读取请求数据。

SQL接口:负责处理接收到的SQL语句

MySQL内部提供了一个组件,SQL接口,他是一套执行SQL语句的接口,专门用于执行我们发送给MySQL的那些增删改查语句。

查询解析器:让MySQL可以看懂SQL语句

我们来看一下这个MySQL语句

复制代码
select id,name from users where id=1;

我们自己可以看懂这个数据库语句,但是电脑不可以,这时候查询 解析器就派上了用处。

这个查询解析器会将其拆下一下几个部分:

  • 从"user"表查询数据
  • 查询"id"字段等于1的那行数据
  • 查询"id,name"两个字段

查询优化器:选择最优的查询路径

当解析器理解SQL要干啥后,接下来会查询优化器来选择最优的查询路径。

查询优化器会针对你编写的几十行,百行复杂SQL语句 生成查询路径树,从里面查询最优的路径出来。

调用存储引擎,执行真正的SQL语句

真正执行SQL语句的时候,数据应该放到哪里?

对于数据库而言,我们的数据要么放到内存中,要么放到磁盘中

那么问题来了,在我们一个SQL语句要执行,我们怎么知道哪些数据放到内存,哪些数据放到磁盘中?

是应该先更新内存中的数据,还是磁盘中的数据?

如果先更新磁盘数据,先查询那个磁盘文件?再更新那个磁盘文件?

所以这个时候就需要存储引擎,它会按照一定步骤去查询内存缓存数据,更新磁盘数据,查询磁盘数据等。

场景的存储引擎由:MyISAM,InnoDB

执行器:根据执行计划调用存储引擎接口

执行器就是会根据我们的优化器去生成一套执行计划,然后不停的调用存储引擎的各种接口去完成SQL语句都会执行计划。

初步了解InnoDB存储引擎的架构设计

现在我们有一条更新语句

复制代码
update users set name='guslegend' where id=1;

接下来我们就依据这个更新语句来探索一下存储引擎里面的架构设计,以及如何基于存储引擎完成一条更新语句

缓冲池

InnoDB里面有一个非常重要的组件,就是缓冲池,里面会放缓存很多数据,以便我们去查询的时候,可以从内存缓存池中查询数据,不用走磁盘。

当我们去更新这个数据的时候,看到是不可以运行别人同时去更新的,这时候就需要加锁

undo日志文件:让你更新的数据可以回滚

在我们执行一个更新语句 ,要是他在一个事务中,那么事务提交之前我们都可以对数据进行回滚,这时我们把更新前的值写到undo日志里面

更新buffer pool的缓存数据

当我们需要把更新的那行记录嘲弄磁盘文件中加载到缓冲池,同时对其加锁之后,还把更新前都会旧值写入undo日志文件,在我们更新的时候,先是会更新缓冲池中的记录,此时这个数据就是脏数据。

Redo Log Buffer:系统宕机避免数据丢失

在我们内存里面的数据更改后,但是磁盘上的数据没有更新,但是此时MySQL机器宕机了,导致内存中修改的数据丢失,该怎么办?

这时候,就需要将对内存的修改写入Redo Log Buffer中,这也是内存里的一个缓冲区,用来存放redo日志。

如果MySQL还没有提交事务就宕机怎么办?

其实这个没有关系,当一个更新语句,没有提交事务,就代表没有成功,此时内存中的数据丢失了,但是磁盘上的数据还是没有改变。

提交事务,将redo日志写入磁盘中

当我们想要提交一个事务的时候,此时会根据一定策略把redo日志从redo log buffer里刷入磁盘文件中

这个策略是通过innodb_flush_log_at_trx_commit来配置的。

当这个值设置为0的时候,提交事务都会时候,不好把redo log buffer里的数据刷入磁盘文件的,此时可能你都提交事务,结果mysql宕机,然后此时内存里的数据全部丢失。

相当于你提交事务成功了,但是由于MySQL突然宕机,导致内存中的数据和redo日志都丢失了

当这个值设置为1的时候,你提交事务的时候,就必须要把redo log从内存刷入到磁盘文件里面新,只要提交成功,那么redo log就必然在磁盘里了。

如果出现了以下的情况,提交事务后处于这样的状态,但是MySQL突然宕机了,此时会如何?会丢失数据吗?

不会,虽然内存中的数据会丢失,但是redo日志里面说明了,对某某数据修改了。此时MySQL重启后,可以根据redo日志区修复之前做过的修改

最后当这个值设置为2的时候呢?

这个意思是,提交事务的时候,把redo日志写入磁盘文件对应的os cache缓存里面去,而不是进入磁盘文件,可能1s后才会把os cache里的数据写入到磁盘文件里去。

万一在redo log仅仅停留在os cache内存缓存里,没实际进入磁盘文件,此时机器宕机,那么os cache里的redo log就会丢失,同样会让你感觉事务提交,结果数据丢失了。

BinLog是什么?

前面我们说了redo log 他是一种偏向物理性质对话重做日志,是属于InnoDB存储引擎特有的一个东西

而binlog叫做归档日志,里面记录的是偏向于逻辑性的日志,是属于mysql server自己的日志文件

BinLog是怎么写入的?

前面我们说到,在提交事务的时候,会把redo log写入磁盘文件中去,其实接下来我们还会同时将binlog日志也写入磁盘文件中。

这里面我们引入了一个非常重要的东西,执行器,负责和存储引擎配合完成一个SQL语句在磁盘于内存层面的全部数据更新操作。

BinLog的刷盘策略

binlog里面有一个sync_binlog参数可以控制其刷盘策略,默认值是0,此时将binlog写入磁盘的时候,不是直接将其写入磁盘文件,而是写入os cache缓存中,如果此时系统宕机,binlog日志会丢失。

如果把sync_binlog设置为1,此时会强行在提交事务的时候将binlog直接写入到磁盘文件中,哪怕系统宕机了也不会丢失binlog文件。

事务提交完毕是怎么样的?

当我们将binlog写入到磁盘后,最终会完成事务的提交,把此次更新对应的binlog文件名称和更新的binlog日志在文件里的位置,都写入redo log日志文件中,同时在redo log日志文件写入一个commit标记,这样才最终提交了事务。

commit标记是干啥的?

现在我们肯定有疑问这个commit标记的意义是什么?

它其实是来保证redo log和bin log一致性的。

比如说,我们事务提交要满足5,6,7三个步骤都执行完毕才算提交事务,但是如果刚刚完成步骤6的时候,mysql就宕机了怎么办?

这时候没有最终的事务commit标记在redo日志中,所以此处事务判断失效,不会出现事务不一致的情况发送。

后台IO线程随机将内存更新后的脏读数据刷回磁盘

当IO线程把buffer pool里面的修改后的脏数据刷回磁盘后,磁盘上的数据才会和内存里的一样。

在IO线程把脏数据刷回磁盘前宕机也没有关系,因为重启之后,redo日志恢复之前提交事务做过的修改到内存区,再等待适当时机,IO线程还是会将这个修改后的数据刷回磁盘上的数据文件中。

真实生产环境下数据库机器如何规划

生产数据库一般用什么配置?

首先我们需要明确,如果你的系统没有什么并发量,什么配置的机器去部署影响都没很多

我们考虑的是互联网类的系统,对数据库可能每秒几千上万的并发请求,这类的机器该怎么部署我们的数据库?

普通的Java应用程序部署在机器上能抗住多少并发?

Java应用程序一般的配置是2核4G,4核8G较多 ,数据库最低配置都是8核16G以上,正常是16核32G。

4核8G机器,每秒可以抗住500左右并发访问,当然这个也不是一定的,如果一个请求1s完成,那么每台机器也只可以处理100个请求,但是如果一个请求100ms完成,每秒可以处理几百个请求。

所以一台机器能抗下多少并发和每个请求耗时时机是关联的。

4核8G系统,每秒大致就是几百请求并发访问。

高并发下数据库应该选择什么机器?

我们常说一个Java系统压力很大,负载很高,主要是你这个Java系统压力和负载都集中在你的MySQL上。

因为数据库需要大量执行磁盘IO操作,所以每个请求耗时较长一些,机器配置要求更高。

一般来说一个8核16G数据库,每秒可以一两千并发请求;一个16核32G数据库,每秒两三千请求。

对于数据库而言,如果可以选择SSD固态云盘更好,因为数据库最大复杂就是在于大量磁盘IO,选择这个磁盘可以每秒抗住并发请求更高。

互联网公司是如何进行数据库性能测试的?

机器交给DBA,让其部署MySQL

一个数据库机器申请下来,会有专门的DBA去安装部署,其会根据自己的MySQL调优模板,放到MySQL中,对linux机器OS内存参数进行调整,接下来才交给你。

有了数据库先进行压测

在公司里面有了一个数据库,要先进行基本的基准测试,才开发Java系统。

比如根据工具每秒发送1000个请求,去观察CPU负载,磁盘IO负载,网络IO负载,内存负载,看数据库是否可以每秒处理这些请求,或者只是可以处理更少的请求。

那为什么不是等我们Java程序开发完毕再去连上数据库压测呢?

这是因为数据库压力和Java压力是两个事情,要先对数据库压测做个底。

QPS和TPS

QPS:每秒可以处理多少个请求,也可以理解为一次请求是一个SQL,每秒可以处理多少个SQL

TPS:每秒可以处理多少个事务,也就是说数据库每秒会处理多少次事务或者提交或者回滚

IO相关指标

  • IOPS:这个机器随机IO并发处理能力,如果IOPS指标太差,会导致内存里的脏数据刷回磁盘效率不高
  • 吞吐量:机器磁盘存储每秒可以读写多少字节的数据量,决定了每秒可以把多少redo log之类的日志写入到磁盘里面去,一般磁盘顺序写入吞吐量可以每秒200MB左右
  • latency:往磁盘里写入一条数据的延迟,写一条日志到磁盘文件中,到底是1ms还是100us,对数据库SQL语句执行性能有影响。

压测需要关注的其他指标

CPU负载:如果CPU负载高,说明数据库不能继续往下压测更高QPS,否则CPU吃不消

网络负载

内存负载

相关推荐
计算机学姐2 小时前
基于SpringBoot的校园二手书籍交易系统【个性化推荐+数据可视化统计+我买到的+我卖出的】
vue.js·spring boot·后端·mysql·信息可视化·intellij-idea·mybatis
闭关苦炼内功2 小时前
Win10 安装 MySQL5.7.36 数据库记录
数据库·windows·mysql
栀椩2 小时前
MySQL数据库自动备份方法
数据库·mysql
2401_891655813 小时前
MySQL安全加固十大硬核操作技术大纲
数据库·mysql·安全
dovens3 小时前
从MySQL迁移到PostgreSQL的完整指南
数据库·mysql·postgresql
Irissgwe3 小时前
Mysql数据库基础
数据库·c++·mysql·mysql数据库基础
轩情吖4 小时前
MySQL Connect
数据库·mysql·adb·select·连接·远程访问数据库
Seven974 小时前
InnoDB存储结构全解析:行页区段与单表2000W行的关系
mysql
恋红尘4 小时前
K8S 高级调度-叩丁狼
adb·容器·kubernetes