尽量不废话直接上干货...
客户端依据通信协议请求服务端,而MySQL这个服务器执行SQL语句命令并给出反馈,整体架构如下:
可以粗略的把MySQL服务器分为两层,上面的为Server层,主要包括连接器、查询缓存、分析器【分析器+预处理器】、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。
下面的一层为存储引擎,存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL5.5版本开始成为了默认存储引擎。这两层共同构建了MySQL。
身份认证和权限相关(登录 MySQL 的时候),我们在数据库层执行SQL语句时,应用程序会连接到相应的数据库服务器,把SQL语句发送给服务器处理,实际上也就是通信。数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。
执行查询语句的时候,会先查询缓存(MySQL 8.0 版本后移除)。在接收到查询请求后,并不会直接去数据库查询,而是在数据库的查询缓存中找是否有相对应的查询数据(某条给定的查询语句在第一次执行时,服务器会缓存这条查询语句和他返回的结果)。
没有命中缓存的话,SQL 语句就会经过分析器,Mysql通过将SQL语句进行解析,并生成一棵对应的解析树。MySQL解析器将使用MySQL语法分析(语法规则验证)和解析查询,如将验证是否使用错误的关键字,或者关键字的顺序是否正确。
预处理器根据一些MySQL规则进一步检查解析树是否合法,如数据表和数据列是否存在,解析列名和别名,是否有歧义。接下来预处理器会验证用户权限(precheck)。查看用户是否有相应的操作权限
按照 MySQL 认为最优的方案去执行。例如表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序,将SQL语句转化成执行计划,一条查询可以有很多种执行方式,最后都返回相同的结果,最后找到其中最好的执行计划(Mysql使用基于成本的优化器,它将尝试预测一个查询使用某种执行计划的成本,选择其中成本最小的一个)
Mysql根据执行计划给出的指令逐步执行。开始执行的时候,要先判断一下你对这个表有没有执行查询的权限,如果没有就会返回没有权限的错误。在此过程中,有大量的操作需要通过调用存储引擎实现的接口完成,这些接口即为“handler API”接口。查询中的每一个表由一个handler的实例表示。(实际上,在优化阶段Mysql就为每一个表创建了一个handelr实例,优化器可以根据这些实例的接口获取表的相关信息,如表的所有列名、索引统计信息等)
这两层共同构建了MySQL,以上各个环节中包含如下三个注意事项:
一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。
因为创建连接比较复杂,所以建议使用长连接,但是长连接容易OOM。
查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空,8.0已经将该功能彻底移除了
总结而言,连接设置修改只影响后续连接,长连接虽好,但还是定期断开,否则容易OOM,不建议使用查询缓存。
我们知道机械硬盘是将数据写入扇区,一个扇区 512b,那么写入数据都会有一个寻址的过程(就是寻找扇区)机械硬盘写入数据时需要经过:1、定位到磁道;2、等待旋转到对应扇区;3、开始读写。步骤 1和2 机械定位,速度慢。
固态硬盘将数据写入闪存芯片,使用电子定位,所以比机械硬盘快很多。对于拷贝数据,是连续的扇区写入数据,一次性申请连续的空间,固态硬盘比机械硬盘快 3 到 5 倍。对于随机读写,固态硬盘比机械硬盘快 100-300 倍,而我们大多数的场景都是随机读写。
我们一般不会直接与硬盘打交道,都是与文件系统打交道。文件系统将硬盘划分为一个一个的 block,1block = 8扇区 = 4kb,一个block 是由多个连续的扇区组成,这样就大大减少了扇区寻址的时间,但是这样会存在 block 写不满的情况,这就是磁盘碎片。文件系统磁盘读写模式。
文件系统实际不是直接将数据读写到磁盘中,它会现将数据写入 buffer ,buffer 按照策略将数据刷新到磁盘,读取数据时先读 cache,cache读不到再去读磁盘。如果系统宕机、断电 使用 Linux 提供的 fsync 可以直接将数据刷新到磁盘。
Innodb 以页为单位进行磁盘交互,一页大小 16 KB,一页中至少保存两条数据,也就是说当你更新某个页中的一条数据中的一个字段,它都需要整页更新,而且如果事务修改了很多页,页之间的block不是连续的,磁盘还要不停的寻址。
我们知道InnoDB的记录结构、数据页结构、数据目录、表空间很复杂,所以本文抛砖引玉,先入门基础,之后的文章按重点和大家感兴趣点知识点来续写。
InnoDB是一个健壮的事务型存储引擎,InnoDB就是作为默认的存储引擎。
当执行 select count(*) from t 时,会先把数据读出来,一行一行的累加,最后返回总数量,需要注意的是,当count(*) 语句包含 where 条件时,两种表的操作是一样的,当count(*)语句包含where条件时MyISAM也需要扫描整个表。
一般来说,如果需要事务支持,并且有较高的并发读取频率,InnoDB是不错的选择。大数据量下用innodb,因为支持事务,行级锁。对于InnoDB来说,最大的优势在于支持事务,当然这是以牺牲效率为代价的
Buffer Pool 主要分为以下几个部分:Buffer Pool,change buffer,Adaptive hash Index,Redo log buffer。
缓存的是页面信息,包括数据也和索引页。默认大小是128M,可以调整。如果写满了,则使用LRU算法来替换。
Change buffer是Buffer Pool的一部分。作用是减少随机磁盘访问,提升更新性能,如果这个数据也不是唯一索引,不存在数据重复的情况(更新记录的数据页不在内存中),就不需要从磁盘加载索引页判断数据是否重复(唯一性检查)。这种情况下先把修改记录在内存的缓冲区中,从而提升更新语句的执行速度。这块区域就是Change Buffer。
最后把Change Buffer记录到数据页的操作叫做merge。什么时候发生merge?有几种情况:
如果数据库大部分索引都是非唯一索引,并且业务是写多读少,不会再写数据后立即读取,就可以使用CB。可以调大这个值来支持写多读少的情况。也就是CB占BP的的比例,默认是25%。
索引应放在磁盘的,为什么要专门把一种hash的索引放到内存?在索引讲解再讨论。
Redo log也不是每一次都直接写入磁盘,在BP里面有一块内存区域(Log Buffer)专门用来保存即将要写入日志文件的数据,默认16M。
redo log的内容主要用于崩溃恢复。磁盘的数据文件,数据来自BP。redo log写入到系统,而不写入数据文件。
表空间可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都存在表空间里。InnoDB的表空间分为5类:
在默认情况下InnoDB存储引擎有一个共享表空间,也叫系统表空间,包含InnoDB数据字典和双写缓冲区,change Buffer和Undo log。
单独介绍。
由内部系统表组成,存储表和索引的元数据
InnoDb的页和OS的页大小不一致,InnoDB的是16K,OS的是4K,因此InnodB的页写入时,一个页需要分4次写。如果存储引擎正在写时发生宕机,可能出现页只写了一部分的情况,这叫做部分写失效。由于此时某个页已经破坏了,无法通过redo log来修复,此时只能通过一个页的副本来进行,这就是双写技术。
跟redo log一样,double wire 由两部分组成,一部分是内存的double write,一部分是磁盘的double write。因为double write是顺序写入的,不会带来太大的开销。DWB由128个页构成,容量只有2M。
具体执行过程为:
步骤2和步骤3要写2次磁盘,这就是“Double Write”的由来。
能够通过DWB保证页数据的完整性,但毕竟DWB要写两次磁盘,会不会导致数据库性能急剧降低呢?
分析DWB执行的三个步骤:
128页(每页16K)2M的DWB,会分两次刷入磁盘,每次最多64页,即1M的数据,执行也是非常之快的。
根据第三方测评,该方式损失大约10%的性能。
【扩展】
DW :data warehouse数据仓,DW数据分层,由下到上为 DWD,DWB,DWS
开启后,为每个表单独占一个表空间。但是其他的数据,例如回滚信息,插冲索引页,系统事务信息,双写信息等都还是放在原来的共享表空间里的。
通用表空间也是一种共享的表空间,看相关说明,功能存储不同数据库的表,还能自定义数据路径和文件,其他没看出来有啥用。
存储临时表的数据,包括用户创建的临时表,服务器重启时会被删除重建。
undo log默认在系统表空间ibdata1文件中,因为共享表空间不会自动收缩,也可以单独创建一个undo表空间。
后台线程的主要作用是负责刷新内存池和把修改的数据页刷新到磁盘上,后台线程有master thread,IO thread,purge thread,page cleaner thread几种。
DML数据操作语句(更新、删除、插入)这些在执行的时候肯定要记录日志,MySQL 自带的日志模块 binlog(归档日志) ,所有的存储引擎都可以使用,常用的 InnoDB 引擎还自带了一个日志模块 redo log(重做日志),假如我们要更新ID为2的这条数据当前值自增1,其中ID为主键,加了索引:
update T set c=c+1 where ID=2;
我们就以 InnoDB 模式下来探讨这个语句的执行流程。流程如下:
图中浅色框表示是在 InnoDB 内部执行的,深色框表示是在执行器中执行的
以上就是在更新数据时的一些操作。
是为了让两份日志之间的逻辑一致。
由于 redo log 和 binlog 是两个独立的逻辑,如果不用两阶段提交,要么就是先写完 redo log 再写 binlog,或者采用反过来的顺序。看看这两种方式会有什么问题。
仍然用前面的 update 语句来做例子。假设当前 ID=2 的行,字段 c 的值是 0,再假设执行 update 语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了 crash,会出现什么情况呢?
可以看到,如果不使用“两阶段提交”,那么数据库的状态就有可能和用它的日志恢复出来的库的状态不一致。
binlog 会记录所有的逻辑操作,并且是采用“追加写”的形式。如果 DBA 承诺说半个月内可以恢复,那么备份系统中一定会保存最近半个月的所有 binlog,同时系统会定期做整库备份。这里的“定期”取决于系统的重要性,可以是一天一备,也可以是一周一备。
当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,可以这么做:
当然不只是误操作后需要用这个过程来恢复数据。当需要扩容的时候,也就是需要再多搭建一些备库来增加系统的读能力的时候,常见的做法也是用全量备份加上应用 binlog 来实现的,这个两个日志的“不一致”就会导致线上出现主从数据库不一致的情况。一定程度上,redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。
因为InnoDB的Buffer Pool即是重点又是难点,所以下篇文章“详细”分析…
#程序员##学习#