MySQL体系结构概览

MySQL体系结构

一、MySQL体系结构图

1、Mysql是由SQL接口,解析器,优化器,缓存,存储引擎组成的(SQL Interface、Parser、Optimizer、Caches&Buffers、Pluggable Storage Engines)

(1) Connectors指的是不同语言中与SQL的交互 
(2)Management Serveices & Utilities: 系统管理和控制工具,例如备份恢复、Mysql复制、集群等 
(3)Connection Pool: 连接池:管理缓冲用户连接、用户名、密码、权限校验、线程处理等需要缓存的需求 
(4)SQL Interface: SQL接口:接受用户的SQL命令,并且返回用户需要查询的结果。比如select from就是调用SQL Interface 
(5)Parser: 解析器,SQL命令传递到解析器的时候会被解析器验证和解析。解析器是由Lex和YACC实现的,是一个很长的脚本, 主要功能: 
a . 将SQL语句分解成数据结构,并将这个结构传递到后续步骤,以后SQL语句的传递和处理就是基于这个结构的 
b. 如果在分解构成中遇到错误,那么就说明这个sql语句是不合理的 
(6)Optimizer: 查询优化器,SQL语句在查询之前会使用查询优化器对查询进行优化。他使用的是"选取-投影-联接"策略进行查询。 
用一个例子就可以理解: select uid,name from user where gender = 1; 
这个select 查询先根据where 语句进行选取,而不是先将表全部查询出来以后再进行gender过滤 
这个select查询先根据uid和name进行属性投影,而不是将属性全部取出以后再进行过滤 
将这两个查询条件联接起来生成最终查询结果 

(7) Cache和Buffer(高速缓存区): 查询缓存,如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。 
通过LRU算法将数据的冷端溢出,未来得及时刷新到磁盘的数据页,叫脏页。 
这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等 
(8)Engine :存储引擎。存储引擎是MySql中具体的与文件打交道的子系统。也是Mysql最具有特色的一个地方。 
Mysql的存储引擎是插件式的。它根据MySql AB公司提供的文件访问层的一个抽象接口来定制一种文件访问机制(这种访问机制就叫存储引擎) 
现在有很多种存储引擎,各个存储引擎的优势各不一样,最常用的MyISAM,InnoDB,BDB 
默认下MySql是使用MyISAM引擎,它查询速度快,有较好的索引优化和数据压缩技术。但是它不支持事务。 
InnoDB支持事务,并且提供行级的锁定,应用也相当广泛。 
Mysql也支持自己定制存储引擎,甚至一个库中不同的表使用不同的存储引擎,这些都是允许的。

二、MySQL内存结构

Mysql 内存分配规则是:用多少给多少,最高到配置的值,不是立即分配 
 
MySQL中内存大致分为:全局内存(Global buffer)、线程内存(Thread buffer)两大部分

1、全局内存(Global buffer)

 
(1)innodb_buffer_pool_size:

(2)innodb_additional_mem_pool_size:

2.1)指定InnoDB用来存储数据字典和其他内部数据结构的内存池大小。缺省值是8M8388608)。通常不用太大,只要够用就行,应该与表结构的复杂度有关系。如果不够用,MySQL会在错误日志中写入一条警告信息。

(3)innodb_log_buffer_size:

默认8M,一般设置为16 ~ 64M足够了。 
 
(4)key_buffer_size:

(5)query_cache_size :

(6)table_definition_cache:

6.1)表定义文件描述缓存,提高表打开效率。是frm文件在内存中的映射。MySQL需要打开frm文件,并将其内容初始化为Table Share 对象。这里存放与存储引擎无关的,独立的表定义相关信息。

(7)table_open_cache:

为什么MySQL会出现table_open_cahce和table_define_cache这两个概念? 
是因为:MySQL支持不同的存储引擎,每种存储引擎,数据存储的格式都是不一样的,因此需要指定一个存储引擎相关的handler。这就有了table cache的作用(table_open_cache参数)。另外表的定义也需要存放内存中,而表的定义frm文件每个存储引擎是通用的,需要另外独立开来,这就有了table definition cache。

(8)max_heap_table_size和tmp_table_size:

2、线程内存(Thread buffer)

每个连接到MySQL服务器的线程都需要有自己的缓冲。大概需要立刻分配256K,甚至在线程空闲时,它们使用默认的线程堆栈,网络缓存等。 
事务开始之后,则需要增加更多的空间。运行较小的查询可能仅给指定的线程增加少量的内存消耗。 
如果对数据表做复杂的操作例如扫描、排序或者需要临时表,则需分配大约read_buffer_size、sort_buffer_size,read_rnd_buffer_size,tmp_table_size大小的内存空间 
不过它们只是在需要的时候才分配,并且在那些操作做完之后就释放了。 
 
(1)read_buffer_size:

MySQL读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySQL会为它分配一段内存缓冲区。read_buffer_size变量控制这一缓冲区的大小。如果对表的顺序扫描请求非常频繁, 并且你认为频繁扫描进行得太慢,可以通过增加该变量值以及内存缓冲区大小提高其性能。

(2)read_rnd_buffer_size:

MySQL的随机读缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySQL会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但MySQL会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。

(3)sort_buffer_size:

MySQL执行排序使用的缓冲大小。如果想要增加ORDER BY的速度,首先看是否可以让MySQL使用索引而不是额外的排序阶段。如果不能,可以尝试增加sort_buffer_size变量的大小。

(4)join_buffer_size:

应用程序经常会出现一些两表(或多表)Join的操作需求,MySQL在完成某些 Join 需求的时候(all/index join),为了减少参与Join"被驱动表"的读取次数以提高性能,需要使用到 Join Buffer 来协助完成 Join操作。当 Join Buffer 太小,MySQL 不会将该 Buffer 存入磁盘文件,而是先将Join Buffer中的结果集与需要 Join 的表进行 Join 操作,然后清空 Join Buffer 中的数据,继续将剩余的结果集写入此 Buffer 中,如此往复。这势必会造成被驱动表需要被多次读取,成倍增加 IO 访问,降低效率。

(5)binlog_cache_size:

  1. 在事务过程中容纳二进制日志SQL 语句的缓存大小。二进制日志缓存是服务器支持事务存储引擎并且服务器启用了二进制日志(—log-bin 选项)的前提下为每个客户端分配的内存,注意,是每个Client 都可以分配设置大小的binlog cache 空间。如果系统中经常会出现多语句事务的话,可以尝试增加该值的大小,以获得更好的性能。当然,我们可以通过MySQL 的以下两个状态变量来判断当前的binlog_cache_size 的状况:Binlog_cache_use Binlog_cache_disk_use"max_binlog_cache_size":和"binlog_cache_size"相对应,但是所代表的是binlog 能够使用的最大cache 内存大小。当我们执行多语句事务的时候,max_binlog_cache_size 如果不够大的话,系统可能会报出" Multi-statement transaction required more than 'max_binlog_cache_size' bytes ofstorage"的错误。

(6)tmp_table_size:

MySQL的临时表缓冲大小。所有联合在一个DML指令内完成,并且大多数联合甚至可以不用临时表即可以完成。大多数临时表是基于内存的(HEAP)表。具有大的记录长度的临时表 (所有列的长度的和)或包含BLOB列的表存储在硬盘上。如果某个内部heap(堆积)表大小超过tmp_table_sizeMySQL可以根据需要自动将内存中的heap表改为基于硬盘的MyISAM表。还可以通过设置tmp_table_size选项来增加临时表的大小。也就是说,如果调高该值,MySQL同时将增加heap表的大小,可达到提高联接查询速度的效果。

(7)thread_stack :

主要用来存放每一个线程自身的标识信息,如线程id,线程运行时基本信息等等,我们可以通过 thread_stack 参数来设置为每一个线程栈分配多大的内存。

(8)thread_cache_size:

如果我们在MySQL服务器配置文件中设置了thread_cache_size,当客户端断开之后,服务器处理此客户的线程将会缓存起来以响应下一个客户而不是销毁(前提是缓存数未达上限)。

(9)net_buffer_length:

客户发出的SQL语句期望的长度。如果语句超过这个长度,缓冲区自动地被扩大,直到max_allowed_packet个字节。

(10)bulk_insert_buffer_size:

如果进行批量插入,可以增加bulk_insert_buffer_size变量值的方法来提高速度,但是,这只能对myisam表使用。

3、overhead

(1)自适应哈希索引(Adaptive index hash)

(2)System dictionary hash 
(3)Locking system 
(4)Sync_array 
(5)Os_events

三、MySQL文件结构

1、参数文件

2、错误日志文件

3、二进制日志文件

  1. 3.1 登录到mysql查看binlog

    1. show binlog events;

    2. show binlog events in 'mysql-bin.000002';

    3. show master status\G;

    4. show binary logs;

    3.2 用mysqlbinlog工具查看

    注意:

    3.3 本地查看

    1. mysqlbinlog --start-datetime='2013-09-10 00:00:00' --stop-datetime='2013-09-10 01:01:01' -d 库名 二进制文件

    2. mysqlbinlog --start-postion=107 --stop-position=1000 -d 库名 二进制文件

    查看row模式日志的方法: 
    mysqlbinlog –base64-output=decode-rows -vv mysql-bin.000001 ,其中–base64-output=decode-rows是将原编码过的日志转码;而-vv则时以注释形式显示做过的SQL操作,可以指定mysql-bin.000001

       

    4、慢查询日志

    5、Genaral日志

    启动general log和慢日志类似,需要同时设置两个参数:general_log=ON开启general日志;general_log_file=/path to/general.log设置general日志的文件路径。

    6、Redo log

    7、Pid文件

    mysql实例的进程ID文件

    8、Socket文件

    当用unix套接字方式进行连接时需要的文件

    9、MySQL表结构文件

    .frm后缀命名的文件都是表结构文件,和存储引擎类型无关。所有的表都会生成一个.frm文件;

    10、innodb数据文件

     
    (1)共享表空间:共享表空间文件以.ibdata*来命名; 共享表空间下,innodb所有数据保存在一个单独的表空间里面,而这个表空间可以由很多个文件组成,一个表可以跨多个文件存在,所以其大小限制不再是文件大小的限制,而是其自身的限制。从Innodb的官方文档中可以看到,其表空间的最大限制为64TB,也就是说,Innodb的单表限制基本上也在64TB左右了,当然这个大小是包括这个表的所有索引等其他相关数据。 
    共享表空间主要存放double write、undo log(undo log没有独立的表空间,需要存放在共享表空间) 
    (2)独立表空间:每个表拥有自己独立的表空间用来存储数据和索引。 
    (3)查看数据库是否启用独立表空间: 
    show variables like 'innodb_file_per_table';查看,innodb_file_per_table=ON,表示启用了独立表空间; 
    (4)使用独立表空间的优点: 
    如果使用软链接将大表分配到不同的分区上,易于管理数据文件 
    易于监控解决IO资源使用的问题; 
    易于修复和恢复损坏的数据; 
    相互独立的,不会影响其他innodb表; 
    导出导入只针对单个表,而不是整个共享表空间; 
    解决单个文件大小的限制; 
    对于大量的delete操作,更易于回收磁盘空间; 
    碎片较少,易于整理optimize table; 
    易于安全审计; 
    易于备份 
    如果在innodb表已创建后设置innodb_file_per_table,那么数据将不会迁移到单独的表空间上,而是续集使用之前的共享表空间。只有新创建的表才会分离到自己的表空间文件。 
    (5)共享表空间的数据文件配置: 
    innodb_data_file_path参数:设置innoDB共享表空间数据文件的名字和大小,例如innodb_data_file_path=ibdata1:12M:autoextend(初始大小12M,不足自增) 
    innodb_data_home_dir参数:innodb引擎的共享表空间数据文件的存放目录 
    目前主要是使用独立表空间,但是共享表空间也是需要的,共享表空间主要存放double write、undo log等

    11、MYISAM文件

    四、MySQL存储结构(Innodb存储结构)

     

    2、行模式类型: 
     
    3、行溢出 

    五、innodb体系结构

     
    上图中,没有画出purge thread和page cleaner thread,mysql5.6版本后,这2个线程从master thread里独立出来,缓解master thread的压力

    1、主要的后台线程

    (1)master thread

    master thread是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括:脏页(dirty page)的刷新、合并插入缓冲(insert buffer merge)、回滚页回收(undo purge)等。 
     
    1、Master thread线程的优先级最高,内部主要是4个循环loop组成:主循环、后台循环、刷新循环、暂停循环。 
    2、在master thread线程里,每1秒或每10秒会触发1oop(循环体)工作,loop为主循环,大多数情况下都运行在这个循环体。loop通过sleep()来实现定时的操作,所以操作时间不精准。负载高的情况下可能会有延迟; 
    3、dirty page:当事务(Transaction)需要修改某条记录(row)时,InnoDB需要将该数据所在的page从disk读到buffer pool中,事务提交后,InnoDB修改page中的记录(row)。这时buffer pool中的page就已经和disk中的不一样了,我们称buffer pool中的被修改过的page为dirty page。Dirty page等待flush到disk上。 
    4、insert buffer merge: 
    innodb使用insert buffer"欺骗"数据库:对于为非唯一索引,辅助索引的修改操作并非实时更新索引的叶子页,而是把若干对同一页面的更新缓存起来做合并(merge)为一次性更新操作,转化随机IO 为顺序IO,这样可以避免随机IO带来性能损耗,提高数据库的写性能。 
    (1)Insert Buffer是Innodb处理非唯一索引更新操作时的一个优化。最早的Insert Buffer,仅仅实现Insert操作的Buffer,这也是Insert Buffer名称的由来。在后续版本中,Innodb多次对Insert Buffer进行增强,到Innodb 5.5版本,Insert Buffer除了支持Insert,还新增了包括Update/Delete/Purge等操作的buffer功能,Insert Buffer也随之更名为Change Buffer。 
    (2)insert buffer merge分为主动给merge和被动merge。 
    (2.1)master thread线程里的insert buffer merge是主动merge,原理是:a、若过去1秒内发生的IO小于系统IO能力的5%,则主动进行一次insert buffer merge(merge的页面数为系统IO能力的5%且读取page采用async io模式)。 b、每10秒,必须触发一次insert buffer merge(merge的页面数仍旧为系统IO能力的5%) 
    (2.2)被动Merge,则主要是指在用户线程执行的过程中,由于种种原因,需要将insert buffer的修改merge到page之中。被动Merge由用户线程完成,因此用户能够感知到merge操作带来的性能影响。例如:a、Insert操作,导致页面空间不足,需要分裂。由于insert buffer只能针对单页面,不能buffer page split,因此引起页面的被动Merge; 
    b、insert操作,由于其他各种原因,insert buffer优化返回失败,需要真正读取page时,也需要进行被动Merge;c、在进行insert buffer操作时,发现insert buffer已经太大,需要压缩insert buffer。 
    5、check point:

    (2)IO thread

    在innodb存储引擎中大量使用AIO来处理IO请求,这样可以极大提高数据库的性能,而IO thread的工作就是负责这些IO请求的回调处理(call back); 
     
    小知识 
    1、聚集索引: 
    聚集索引不是一种单独的索引类型,而是一种存储数据方式。其具体细节依赖于实现方式,但是InnoDB的聚集索引实际上在同样的结构中保存了B+Tree索引和数据行。 
    InnoDB的索引属于聚集索引,就是说表数据文件和索引文件都是同一个,表数据的分布按照主键排序,以B+TREE数据格式存储; 
    MyISAM引擎的索引属于非聚集索引,索引文件跟数据文件是分开的。而索引文件的所指向的是对应数据的物理地址。 
    2、辅助索引:非聚集索引: 
    3、innodb_change_buffer_max_size:如果是日志类服务,可以考虑把这把这个增值调到50 
    4、innodb_change_buffering:默认即可

    (3)lock monitor thread

    (4)error monitor thread

    (5)purge thread

    1、事务被提交后,其所使用的undo log可能将不再需要,因此需要purge thread来回收已经使用并分配的undo页; 
    2、从mysql5.5开始,purge操作不再做主线程的一部分,而作为独立线程。 
    3、开启这个功能:innodb_purge_threads=1。调整innodb_purge_batch_size来优化purge操作,batch size指一次处理多少undo log pages, 调大这个参数可以加块undo log清理(类似oracle的undo_retention)。 
    从mysql5.6开始,innodb_purge_threads调整范围从0–1到0–32,支持多线程purge,innodb-purge-batch-size会被多线程purge共享 

    (6)page cleaner thread

    page cleaner thread是在innodb1.2.x中引用的,作用是将之前版本中脏页的刷新操作都放入到单独的线程中来完成,其目的是为了减轻master thread的工作及对于用户查询线程的阻塞,进一步提高innodb存储引擎的性能。 
    1、将dirty page刷新到磁盘。 
    2、两种算法:

    3、innodb_adaptive_flushing=1,该值影响每秒刷新脏页的操作,开启此配置后,刷新脏页会通过判断产生重做日志的速度来判断最合适的刷新脏页的数量; 
    4、innodb_flush_neighbors=1,InnoDB存储引挚还提供了flush Neighbor page(刷新邻接页)的特性。InnoDB存储引挚从1.2.x版本开始提供了参数innodb_flush_neighbors,用来控制是否开启这个特性。 
    对于传统的机械硬盘建议开启该特性; 
    对于固态硬盘有着超高的IOPS性能,则建议将该参数设置为0,即关闭此特性,因为使用顺序IO没有任何性能收益. 在使用RAID的某些硬件上也应该禁用此设置,因为逻辑上连续的块在物理磁盘上并不能保证也是连续的。 
    其工作原理为:当刷新一个脏页时,InnoDB存储引挚会检测该页所在区(extent)的所有页,如果是脏页,那么一起进行刷新。这样做的好处显而易见,通过AIO可以将多个IO写入操作合并为一个IO操作,故该工作机制在传统的机械硬盘下有着显著的优势。

    其他注意小点

    (1)adaptive hash index(AHI):

    提高buffer pool遍历page的效率O(1) VS O(B+Tree高度)AHI会自动对buffer pool热点数据创建AHI(非持久化);只支持等值查询;加入AHI的条件是:索引是否被访问17次以上+索引中某个页已经被访问至少100次。

    (2)double write:

     

    (3)缓冲池:

    ====================================================================================================

    补充

    undo redo

    1、undo

    Undo Log 是为了实现事务的原子性(事物里的操作要么都完成,要么都不完成),在MySQL数据库InnoDB存储引擎中,还用Undo Log来实现多版本并发控制(简称:MVCC)。

    (1)undo的原理

    为了满足事务的原子性,在操作任何数据之前,首先将数据备份到一个地方(也就是Undo Log,undo日志存放在共享表空间里),然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。 
    除了可以保证事务的原子性,Undo Log也可以用来辅助完成事务的持久化(事务一旦完成,该事务对数据库所做的所有修改都会持久的保存到数据库中)

    (2)用Undo Log实现原子性和持久化的事务的简化过程

    之所以能同时保证原子性和持久化,是因为以下特点:

    2、redo

    如果能够将数据缓存一段时间,就能减少IO提高性能。但是这样就会丧失事务的持久性。因此引入了另外一种机制来实现持久化,即Redo Log.

    (1)redo原理

    和Undo Log相反,Redo Log记录的是新数据的备份,在事务提交前,只要将Redo Log持久化即可,不需要将数据持久化。当系统崩溃时,虽然数据没有持久化,但是Redo Log已经持久化。系统可以根据Redo Log的内容,将所有数据恢复到最新的状态。

    (2)Undo + Redo事务的简化过程

为了保证Redo Log能够有比较好的IO性能,InnoDB 的 Redo Log的设计有以下几个特点:

  1. 10 E. Redo Log上只进行顺序追加的操作,当一个事务需要回滚时,它的Redo Log记录也不会从Redo Log中删除掉。

(3)、恢复(Recovery)

(3.1)恢复策略 
未提交的事务和回滚了的事务也会记录Redo Log,因此在进行恢复时,这些事务要进行特殊的处理。有2中不同的恢复策略: 
A. 进行恢复时,只重做已经提交了的事务。 
B. 进行恢复时,重做所有事务包括未提交的事务和回滚了的事务。然后通过Undo Log回滚那些未提交的事务。

(3.2)InnoDB存储引擎的恢复机制 
MySQL数据库InnoDB存储引擎使用了B策略(进行恢复时,重做所有事务包括未提交的事务和回滚了的事务。然后通过Undo Log回滚那些未提交的事务), InnoDB存储引擎中的恢复机制有几个特点: 
A. 在重做Redo Log时,并不关心事务性。 恢复时,没有BEGIN,也没有COMMIT,ROLLBACK的行为。也不关心每个日志是哪个事务的。尽管事务ID等事务相关的内容会记入Redo Log,这些内容只是被当作要操作的数据的一部分。 
B. 使用B策略就必须要将Undo Log持久化,而且必须要在写Redo Log之前将对应的Undo Log写入磁盘。Undo和Redo Log的这种关联,使得持久化变得复杂起来。为了降低复杂度,InnoDB将Undo Log看作数据,因此记录Undo Log的操作也会记录到redo log中。这样undo log就可以象数据一样缓存起来,而不用在redo log之前写入磁盘了。 
包含Undo Log操作的Redo Log,看起来是这样的: 
记录1: <trx1, Undo log insert <undo_insert>> 
记录2: 
记录3: <trx2, Undo log insert <undo_update>> 
记录4: 
记录5: <trx3, Undo log insert <undo_delete>> 
记录6: 
C. 到这里,还有一个问题没有弄清楚。既然Redo没有事务性,那岂不是会重新执行被回滚了的事务?确实是这样。同时Innodb也会将事务回滚时的操作也记录到redo log中。回滚操作本质上也是对数据进行修改,因此回滚时对数据的操作也会记录到Redo Log中。 
一个回滚了的事务的Redo Log,看起来是这样的: 
记录1: <trx1, Undo log insert <undo_insert>> 
记录2: 
记录3: <trx1, Undo log insert <undo_update>> 
记录4: 
记录5: <trx1, Undo log insert <undo_delete>> 
记录6: 
记录7: 
记录8: 
记录9: 
一个被回滚了的事务在恢复时的操作就是先redo再undo,因此不会破坏数据的一致性.

======================================================================================

转载:https://www.cnblogs.com/zhoubaojian/category/1117963.html

posted @ 2020-08-12 10:45  别看窗外的世界  阅读(1250)  评论(0编辑  收藏  举报