当前位置: 代码迷 >> 综合 >> mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程
  详细解决方案

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

热度:106   发布时间:2023-09-18 18:38:47.0

启停:

 
  
  1. 重启:systemctl restart mysqld.service

  2.  
  3. 停止:systemctl stop mysqld.service

  4.  
  5. 查看状态:systemctl status mysqld.service

思考:一条mysql的执行语句,经过了哪些流程

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

1、通信协议:

   mysql服务端,暴露了一个3306端口,客户端用什么协议和他连接?
       mysql支持多种协议,一般我们都是使用TCP (mysql-connector-java-x.x.xx.jar)       

                   连接方式:可以支持同步和异步

                            同步通信一般依赖于被调用方,会被阻塞,等待被调用方返回结果

                            异步和同步相反,异步可以减少阻塞的时间,但是并不能减少sql执行的时间。存在一个问题,并发情                           况下,异步会创建大量的连接。可以使用线程池。但是对于异步结果的返回还是不可控。一般我们使用同步的方式

                   长连接还是短连接:都支持,但是在连接池中一般使用长连接。

                             show global variables like 'wait_timeout'; -- 非交互式超时时间,如 JDBC 程序  默认8小时
                             show global variables like 'interactive_timeout'; -- 交互式超时时间,如数据库工具 默认8小时

                             show variables like 'max_connections'; 最大连接数  默认151

  通信方式: 单工 半双工(mysql是半双工,客户端和服务端可以交互,但是不能同时发送) 全双工

2、缓存,mysql默认是携带缓存的,但是缓存默认是关闭的,主要由于缓存的应用场景有限,所有的sql必须完全一样,连标点空格都需要保持相同,当表里数据发生改变,缓存就失效,意义不大,可以交给缓存框架或者ORM框架处理,这部分可以看mybatis的缓存模块涉及。

3、语法解析和预处理

    我们输入一条sql,通信问题ok了,缓存不管,后面应该怎么处理呢?是解析。

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程        mysql对于词法的判断,其实是会将一个完整的sql,打散成一个个单词,从哪开始,到哪结束

        语法解析,将所有的词,组装到一个解析树内,实现解析校验

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

sql语句解析完成了,又如何知道我们的表和字段不存在的呢?

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

     对于表和字段等操作,mysql提供了解析器,来实现检查表名列名,别名等语法没办法解析的数据,封装成一个新的解析树。

4、查询优化器

   对于一个sql,mysql其实是有很多种执行方法的,但是具体最后选择了哪一种执行方法,还是由查询优化器来决定的。

  执行优化器也还是有自己的判断标准的,mysql中使用的是cost(基于开销的优化器),哪个开销小,选哪个。

  优化器可以做哪些,简单举个例子:

  1、两张表关联查询,以哪张表为基表

  2、同时可以使用多个索引,使用哪个索引。

  备注:优化器其实也是代码,还是得优先提高sql的质量。

优化器优化后,还是将解析树变化成一个查询执行计划。

查询计划的工具: EXPLAIN select name from user where id=1;

  备注: EXPLAIN 也不一定是最终执行的方式

5、存储引擎

    获得到了执行计划,那我们的执行计划在哪里运行  mysql的数据是一个什么样的格式? --> 存储引擎

   mysql每个表都可以指定不同的存储引擎,并且可以修改  show table status where name =  'user_innodb';

   查看数据的存储位置。就在如下目录中:

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

innodb有frm和ibd两个文件       frm存储表结构信息  ibd存储索引和数据信息(聚集索引和非聚集索引)

myisam 有frm MYD MYI三个文件   frm:存储表结构信息   MYD: myisam data   MYI: myisam index

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

对比一下Innodb和myisam:

myisam是5.5之前的默认的存储引擎 innodb是5.5之后的

innodb默认支持事物(undo.log redo.log 这个后续再介绍),支持行级别的锁,写不阻塞读MVCC  一般数据一致性要求高,使用

myisam只支持表级别锁 (限制了读写的能力,一般也只是用读多写少的场景), insert和select的速度更快

* 大文件导入时,是不是可以先设置为myisam 然后再转换成innodb,黑操作!

6、执行引擎(执行器)

谁使用执行计划去操作存储引擎数据

mysql其实 实现可以替换执行引擎,其本质思想就是多态,调用指定的规范(接口),具体的存储引擎(实现)都可以使用。

 

mysql的内部模块:

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

上面是查询的逻辑,一条更新语句是如何查询的

     更新语句的基本流程和查询前面是一致的,区别在于,更新语句,拿到了符合条件数据之后的操作是怎么进行处理的?

下面介绍一下基本概念:

1、缓冲池 buffer pool

 Innodb的数据上面我们也介绍了,其本质是存在磁盘文件中的,那么交互时速度必然是比较慢的。一般性思维,加载到内存中。

innodb逻辑上的最小单位是页,那么每次加载的页到内存的区域(预读),就是buffer pool。下一次取数据(数据页或者索引页)时,会优先判断在不在buffer页中。修改时,会修改内存中的数据,当buffer pool中数据和磁盘不一致时,这时就叫脏页。会有工作线程会定时同步,这个操作就叫刷脏

满了怎么办?默认大小是128M  LRU的淘汰策略(简单来说就是链表,有头和尾,并且划分年轻代和老年代,以解决预读和扫表产生的问题)

mysql的LRU算法:

 普通的LRU:移出链表尾部的数据,从链表头插入

 mysql改进的LRU:

       1、buffer pool 分为了young(5/8)和old(5/3)两个部分

        2、数据会在 用户的sql用到了页中的数据,或者mysql猜测你很可能会用到的数据-预读,这两种情况加载到buffer pool

        3、数据优先进入old,old满了就会被移出。young区呢? 用户sql使用的页,会被移入young。而预读的数据,没被使用则会一直在old区带着,被清除

问题: 当一次大表扫描,会导致大量数进入young? 

解决:mysql有参数控制 innodb_old_blocks_time,需要在old区待满多少秒,且再被用户sql调用时,才会进入young

下面是找的内存结构和磁盘结构图:

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

内存:

我们可以看到 buffer pool内部主要包含: buffer pool、  change buffer、 Log Buffer 、  adaptive Hash Index

buffer pool 缓存的主要是页信息(数据页 索引页) 

当数据不存在buffer pool时,是不是所有的数据,都需要加载进来呢,这样会多一次 io,有没有优化的?

change Buffer(老版本叫insert buffer)当一个数据没有唯一索引,不存在重复数据的情况,更新操作时就不需要加载出来校验唯一性,可以直接在内存中记录操作,不需要这次io。将数据同步到数据页的操作叫merge。什么时候去merge,一般是在访问这个数据页时、后台线程处理、shoutdown 、redo.log 写满时

Log Buffer  用来记录redo log

作用: 主要用来做崩溃恢复,用来实现事务持久性

内部机制:数据存在缓冲区,没有进行刷盘操作时,如果数据库宕机或者重启,会导致数据丢失。 redo log的作用就是,数据写入缓存后,写一下redo log ,记录的不是最终的结果,而是要进行的操作。崩溃恢复时,将redo log数据重新加载到缓冲区。

策略: 0(延时写)  1  2(实时写,延时刷)  默认是1 事务操作实时记录并刷新数据页

疑问:写到日志文件还是一次磁盘io,为什么不直接写到数据页 ?    顺序IO和随机IO  写日志是顺序io,效率高(可以参考Kakfa)

内存和磁盘中间其实还有一层osCache,用来提高效率。 

磁盘:

磁盘中主要组成部分是5类表空间

系统表空间(ibdata1):

   组成:数据字典(表和索引的元信息) 、双写缓冲区 、undo log、Change buffer

  双写缓冲: Innodb和操作系统的页大小不一致 前者16K后者4K ,会存在分批写的情况,如果写的途中出现崩溃的情                           况,  这个时候页出现损坏,这个时候redo log的崩溃恢复没法生效,发生数据丢失。innodb 为我们提供双写                       的  机制,先拷贝一份数据的副本,如果崩溃,就将副本恢复,再使用redo log。

独占表空间: 可以为每个表设置为独占表空间

临时表空间:

通用表空间:

redo log:记录数据到磁盘,用于崩溃恢复,保证事务的持久性

undo log:撤销回滚日志 记录事务发生之前的状态,如果发生异常,可以使用undo log回滚。

 

bin log ,server层的日志。主要是用来主从复制(原理就是读取主库的bin log,执行一遍sql),数据恢复。记录所有的sql语句,有点aof的味道,可以将操作重复实现数据恢复。和redo log不一样,他的内容可以追加,没有大小限制

 

更新的流程:

1、事务开始,从内存或磁盘读取这条数据,返回给server执行器

2、记录数据到 undo log

3、调用存储引擎api,将结果修改内存(buffer pool)

4、记录数据到redo log(二阶段提交 prepare commit),将这一行数据记录为prepare

5、写入bin log

6、commit 提交事务

7、将redo log事务状态设置为commit

8、等待工作线程刷脏

 

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

 

上面是mysql的一些底层存储情况,下面介绍一下索引

索引是什么?

     是数据库中一种排序的数据结构,用来协助于快速查询和更新数据表。

     索引记录的是索引字段信息+对应的磁盘地址,以便于快速查找

     mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

索引类型:

普通索引nomol  、 唯一索引unique、全文索引 fulltext

普通索引,没有任何限制 

唯一索引,唯一性约束,主键是特殊的唯一索引

全文索引,针对比较大的数据,查询部分内容,使用like性能低,我们可以再文本类型上建全文索引

mysql索引存储模型的演进推倒:

1、我们想用什么数据结构,来存储,实现快速定位?

      数组或者链表,但是存在修改的情况,使用链表  但存在问题,链表从上往下查询,数据量大时,效率低O(n)

2、链表效率低,2分增加效率 使用二叉查找数

     提高了效率,但是二叉查找树极端情况会变成链表,依旧时O(n)

3、平衡二叉查找数 可以在数据分布不均匀的时候,利用左旋和右旋,实现平衡

     平衡二叉树解决了分布问题,但是查询效率受树的度所影响,会出现瘦长的树,效率不高

4、B Tree 可以使用多路的特性(路数比关键字数多1),将二叉演进成多叉,将瘦长的树,转换为矮胖的那种,符合条件

     还能不能再进一步了,优化一波

5、B+Tree (加强版) 

     特点:路数和关键字数相等 

               根节点和枝节点不存数据,只有叶子节点存数据

               叶子节点维护了一个指针,指向相邻的叶子节点,形成一个有序列表

               根据左闭右开的区间来检索数据

      优势: 1、存储的数据量大,一个三层的B+Tree就可以存储千万级数据,查询效率高

                       数据量大的原因:

                                     路数和关键字相等

                                     根节点和枝节点不存数据,所消耗的空间就更少,所存储的子节点的路数就更多

                  2、所有的数据都存在叶子节点,查询效率稳定

                  3、叶子节点维护了一个类似于链表的指针,范围查询时快,不需要每次都从上往下查一遍

                  4、扫库扫表能力强,所有数据都在叶子节点上

  了解:

  为什么不用红黑树?

         红黑树的特点:节点分为红色和黑色 

                                   根节点必须是黑色

                                   叶子节点都是黑色的null节点

                                   红色节点的两个子节点都是黑色(不允许相邻两个红节点)   

                                  从任意节点,到每个叶子节点路径上包含相同的黑色节点

        原因:红黑树是二叉,只有两路   不够平衡             

MYISAM和INNODB的索引存储形式:

show VARIABLES LIKE 'datadir';查看数据存储路径
 

MYISAM:

生成的文件:

user_myisam.frm
user_myisam.MYD
user_myisam.MYI

.frm存储的是表结构定义信息等数据。

.MYD D代表data 存储的数据文件,存放的是所有的数据信息

.MYI  I代表index 存储的是索引信息

也就是说,在MYISAM内部,其实B+Tree中维护的叶子节点,也全部都是数据文件的地址信息,根据MYI中的index的地址,然后查询.MYD。主键索引和辅助索引都是这种形式

INNODB内部结构:

user_innodb.frm  存储表结构定义信息
user_innodb.ibd  存储索引文件和数据(B+Tree内部)

主键索引的所有数据,都存在叶子节点上

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

辅助索引,通过主键索引来实现查找(回表)

辅助索引存储的是主键的值,然后通过这个值去主键索引中查找

为什么存主键的值,不存地址?因为会存在分裂合并的情况,地址会变,不利于维护

 

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

 

如果没有主键怎么办?

1、定义了主键,会使用主键

2、没有主键,会选择第一个不包含null的一个唯一索引

3、没有唯一索引,会使用内置的ROWID,并随着记录写入,递增

 

索引使用的原则:

   肯定不是越多越好,占空间,插入需要写入更多的,耗性能

列的离散度  count(distinct(column_name)) : count(*)  离散度越高,数据区分度越大,重复值越少,适合

联合索引的最左匹配:(name,phone)

mysql的架构、数据结构(内存+磁盘)以及一条sql的执行过程

联合索引是复合数据结构,按照从左到右的顺序来创建索引树

查询时是先根据name去匹配,匹配到了,再根据phone匹配。匹配不到,就不知道往哪去查了

所以建联合索引的时候,一定要把最常用的列放在最左边。

思考下哪个用不到索引:

SELECT * FROM user_innodb WHERE name= '权亮' AND phone='15204661800';
SELECT * FROM user_innodb WHERE name= '权亮';
SELECT * FROM user_innodb WHERE phone='15204661800';

创建的话,按照最左匹配原则,(a,b,c)其实本质已经有了(a)(a,b) (a,b,c)无需重复创建

 

覆盖索引:

上面也介绍过,辅助索引有回表的情况,多了一次io,但是如果只查询索引包含的数据,就不用回表,也提醒我们,不要select *,只查询自己需要的数据

索引的创建和使用原则:

1、在where和order join使用的字段上创建索引

2、索引个数不要太多,按需(浪费空间,更新变慢)

3、区分度低的数据,不要建索引(大量数据一致,扫描行数过高)

4、频繁更新的值,不要作为主键或者索引(页分裂)

5、组合索引 区分度高的放在左边,最左原则

6、适当使用复合索引

 

什么时候用不到索引:

1、索引列上使用函数、计算  SELECT*FROM`t2`whereid+1=4;

2、字符串不加引号,出现隐式转换

3、like条件中前面使用%

 

     

索引介绍完了,讲一下事物

事物的定义:

       数据库的逻辑单元(最小的工作单位),由一个有限的数据库操作序列构成(一个或多个DML)。

事物的特性ACID:

1、原子性Atomicity:事物是数据库的最小的逻辑单元,内部所有操作,要么全部成功,要么全部失败。(使用undo log来实现)

2、一致性consistent:  主要是两个方面,一个是数据库层面(完整性约束保持一致,不会被破坏) 一个是业务层面,业务约束满足(举例:A转账B A1000 转账成功  B 收到500 也成功,满足原子性都成功了,但是不满足一致性,账目不对)

3、隔离性 Isolation: 数据库有事物存在,就会同时有许多事物共同运行,隔离性主要就是为了让事物之间操作透明,互补干扰

4、持久性 Durable :持久性的意思就是,一个事物操作,只要事物提交了,结果就是持久性的,不会因为服务器宕机重启丢失,实现方式其实主要就是redo log + dubbo write 来实现。

***:原子性 隔离性 持久性 是手段,目的都是为了实现一致性

 

什么时候出现事物:

1、自动提交,默认打开 show variables like 'autocommit';

2、手动: begin; / start transaction;    commit;  rollback;

 

事物并发会带来什么问题?

1、脏读 (读到另外一个事物里面未提交的数据)

2、不可重复读(读到另外一个事物已提交的事物-修改,数据前后不一致情况)

3、幻读  (读到另外一个事物,新增的数据)

这三个统一是读一致性问题,必须由数据库厂家的事物隔离界别

  相关解决方案