来源:https://mp.weixin.qq.com/s/nV4lBgIkzwEbBStK02JL_A
作者:Java知音
MySQL的基本体系和架构介绍
相信在大部分的程序员在工作中都有接触过MySQL这款数据库,在MySQL的官网上边,你会看到这样的一段介绍内容:
![1934aa7f53259fbb9d6fb528b66d2633.png](https://i-blog.csdnimg.cn/blog_migrate/45c6ed78de20c481939c11d932abfdc6.jpeg)
大致翻译过来的意思就是说:
MySQL是世界上最受欢迎的开源数据库。无论您是快速增长的Web资产,技术ISV还是大型企业,MySQL都能经济高效地帮助您交付高性能,可扩展的数据库应用程序。
这款开源的数据库,其源码在github上边的地址为:https://github.com/mysql/mysql-server star还是挺多的。
那么既然是一款公认好用的开源数据库,我们是否应该对它进行一个深入的了解呢?
下边我画了一个大致的MySQL基本架构图:
![69dafef62a42f03fbef285f8dd36647e.png](https://i-blog.csdnimg.cn/blog_migrate/a562fe9ad6539341e1c441238dfac812.jpeg)
客户端
MySQL的整体结构可以分成2个大模块,分别是客户端和服务端。这里头说的客户端是指提供连接MySQL的工具集合,常见的客户端工具有mysql client,mysqladmin,mysqldump,mysqlcheck,mysqlimport,mysqlshow等等。
MySQL数据库本身提供的连接方式包含有多种,其中最常见的就是基于TCP/IP协议进行连接了。基础的命令内容为:
mysql -h [地址] -u [用户名] -p [密码]
在安装完毕了MySQL数据库之后,我们会发现数据库自身已经帮我们预先设置了一个名字叫做mysql的数据库,每次获取链接的时候就会到该库里面的User表检索账号信息以及相关的权限。
在Linux系统中,有很多进程间通信方式,套接字(Socket)就是其中的一种。但传统的套接字的用法都是基于TCP/IP协议栈的,需要指定IP地址。如果不同主机上的两个进程进行通信,当然这样做没什么问题。但是,如果只需要在一台机器上的两个不同进程间通信,还要用到IP地址就有点大材小用了。
MySQL数据库自身是支持UNIX域套接字进行通信的,因此在MySQL的文件目录下边会有这么一份文件:mysql.sock。
这份文件主要是通过Unix域套接字供我们将本地连接数据库服务器使用。
以前笔者就遇到过sock文件被修改,导致连接数据库异常的情况,例如mysql的log中出现这种报错记录:
Could not create unix socket lock file
如果sock文件丢失的话,可以重新创建一份sock文件进行通信。
MySQL的链接分析
关于mysql的链接部分,可以先思考一下下边的这个问题:
MySQL的链接分类包含有哪些?
其实MySQL的链接包含有很多种类型,常见的类型有:
- Unix Sockets
- TCP/IP
- TLE/SSL
在MySQL的链接中,主要的分类有长链接类型和短连接类型。
短连接
每次客户端和服务端进行通信的时候,都需要创建链接—>进行通信—>关闭连接。这种链接的我们将其定义为短连接,从它的操作来看这种连接的行为对于资源的浪费和网络的消耗是最高的。所以通常我们在工作中较少使用。
长链接
长链接是我们工作中比较常用的类型,客户端第一次构建连接之后就不会轻易断开,这样可以给后边多次连接的程序应用所使用,这种设计的好处在于能够减少资源的损耗,相对于短连接来说要高效些。
长链接通常都会被设计存储在连接池里面,从而减少连接创建的开销。如果在工作中遇到了一些并发请求比较高的业务场景,光是单单通过配置连接池的方式还是不够的,还需要有经验的开发人员在业务层进行相应的隔离。
对于一些访问量比较低的应用其实可以不使用到连接池这种较重的组件,有可能大量的长链接建立了之后都处于sleep状态,这样对于内存资源的占用会比较高。
服务端
每次客户端想要链接到服务端都需要从服务端的连接池获取相应的连接。假设我们每次连接都需要完成 构建链接,当我们获取到了链接之后,在mysql的server端可以通过这样一个命令来对每个链接的状态进行查看:
mysql> show processList;
结果:
+----+-------------+-----------+------+---------+--------+--------------------------------------------------------+------------------+ | Id | User | Host | db | Command | Time | State | Info |+----+-------------+-----------+------+---------+--------+--------------------------------------------------------+------------------+ | 2 | system user | | NULL | Connect | 195290 | Waiting for master to send event | NULL | | 3 | system user | | NULL | Connect | 257 | Slave has read all relay log; waiting for more updates | NULL | | 19 | root | localhost | NULL | Query | 0 | starting | show processList |+----+-------------+-----------+------+---------+--------+--------------------------------------------------------+------------------+ 3 rows in set (0.01 sec)
除了上边的这种情况之外,如果当连接数较多的时候,希望能够清晰查看到连接的信息可以通过下边这条指令来操作:
show status like 'Threads%';
结果内容:
+-------------------+-------+ | Variable_name | Value |+-------------------+-------+ | Threads_cached | 213 | 当前被缓存的空闲线程的数量 |Threads_connected | 191 | 正在使用(处于连接状态)的线程 | Threads_created | 409 | 服务启动以来,创建了多少个线程 | Threads_running | 5 | 正在忙的线程(正在查询数据,传输数据等等操作)+-------------------+-------+
查询缓存
对于一些数据修改变动比较少的表,MySQL内部提供了一套叫做Query Cache的缓存池来进行存储。MySQL可以通过以下命令来查看内部的查询缓存参数值:
mysql> show variables like '%query_cache%';+------------------------------+---------+ | Variable_name | Value |+------------------------------+---------+ | have_query_cache | YES | | query_cache_limit | 1048576 | | query_cache_min_res_unit | 4096 | | query_cache_size | 1048576 | | query_cache_type | OFF | | query_cache_wlock_invalidate | OFF | +------------------------------+---------+ 6 rows in set (0.00 sec)
关于查询缓存的功能,在MySQL中是根据sql语句来做缓存检索的,默认情况下该缓存属性是关闭的,对于更新频率较高的表而言,设置查询缓存其实本身的命中率并不高,因此该功能MySQL也并不提倡使用,在MySQL8.0的时候该功能也被移除了。
MySQL服务器团队有一篇关于此的详细文章有所提及到移除的原因,具体地址如下:https://mysqlserverteam.com/mysql-8-0-retiring-support-for-the-query-cache/
语法分析器
接下来便是语法分析环节了,该模块主要是对传入的SQL语句内容做一些语法内容的校验,例如判断该语句是属于DQL还是DML等类型。
其实我个人觉得MySQL里面最为复杂的部分就是SQL的语法树生产的部分,对于这部分的生成需要对编译器的原理有一定的了解,我个人的功力不是很足,所以这里借用了美团一朋友之前分享给我的图片来大概演示:
![ad7b02c4501239c542d3ee5755cd857c.png](https://i-blog.csdnimg.cn/blog_migrate/2079e8efc280f10d40f82f3acbf6646d.jpeg)
优化器
有时候你会发现,执行的一条SQL可能并不会按照预期的方式来走索引,这是因为MySQL内部有优化器这么一个角色的存在。当然索引的选择只是优化器的其中一项特点,优化器的目的就是通过一定的逻辑判断,将sql执行的效率发挥到最高效化。
执行器
当Sql准备好了,优化结束了,执行器便会发起sql请求引擎层的接口,这个环节中,执行器还会去判断当前请求的会话是否有权限执行相关的SQL语句。
存储引擎
可以说整个数据库的核心部分就是存储引擎环节了,不同的存储引擎对数据的存储结构和算法设计方便都有着巨大的差别,而且不同的存储引擎专门为不同的应用场景所设计。
目前我们主流的存储引擎有Innodb,MyISAM,Memory等
通过下边的这条命令我们可以看到MySQL数据库所支持的存储引擎有哪些:
mysql> show engines;+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | Engine | Support | Comment | Transactions | XA | Savepoints |+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ | InnoDB | DEFAULT | Supports transactions, row-level locking, and foreign keys | YES | YES | YES | | MRG_MYISAM | YES | Collection of identical MyISAM tables | NO | NO | NO | | MEMORY | YES | Hash based, stored in memory, useful for temporary tables | NO | NO | NO | | BLACKHOLE | YES | /dev/null storage engine (anything you write to it disappears) | NO | NO | NO | | MyISAM | YES | MyISAM storage engine | NO | NO | NO | | CSV | YES | CSV storage engine | NO | NO | NO | | ARCHIVE | YES | Archive storage engine | NO | NO | NO | | PERFORMANCE_SCHEMA | YES | Performance Schema | NO | NO | NO | | FEDERATED | NO | Federated MySQL storage engine | NULL | NULL | NULL | +--------------------+---------+----------------------------------------------------------------+--------------+------+------------+ 9 rows in set (0.00 sec)
关于MySQL的整体架构介绍大概到这里就先暂时告一段落了,下次有空会讲解一些关于存储引擎的比对问题。