Skip to main content

Mysql体系结构

Mysql体系结构

MySQL体系结构图

Connectors

外部语言与SQL交互的入口

Management Serveices & Utilities

日常的Mysql管理工具。包括备份恢复、MySQL复制、集群等。

Connection Pool

管理缓冲用户连接、用户名、密码、权限校验,线程处理等需要缓存的需求。
生成线程,验证用户是否正确,保持连接会话队列。

SQL Interface

接受处理SQL语句接口。返回查询或执行结果。

Parser

SQL解析器,将SQL进行验证和解析以便Mysql优化器可以识别的数据结构或返回SQL语句的错误。

Optimizer

SQL优化器,使用查询优化器对查询进行优化,同时会验证用户是否有权限进行查询,缓存中是否有可用的最新数据。使用的是“选取-投影-
联接”策略进行查询。
选取:根据Where条件选定特定的行。
投影:提取Select查询的列。
连接:多表连接策略。根据Where筛选数据,然后取出列,最后组合。

Cache & Buffer

高速缓冲区。(Innodbbufferpool_size)。缓存数据、索引、数据字典、自适应HASH等数据。如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。通过 LRU算法将数据的冷端溢出,未来得及刷新到磁盘的数据页,叫脏页。

Engine

Mysql是数据库环境框架,其引擎采用插件方式实现。引擎是与文件打交道的子系统,它负责将数据是以某 种数据结构放到磁盘,某种算法来提取数据等(包括MVCC、锁实现,crash recovery等)。

Mysql内存结构

屏幕快照_2016-08-09_上午10.39.35

Mysql总使用的内存大小

Innodb buffer pool size = Global buffer(全局) + Thread buffer(会话级)*会话数量(max_connections) + overhead

Thread buffer

针对单个连接的会话。

parameter description
read_buffer_size 顺序读缓冲大小,提高顺序读效率(1M)
read_rnd_buffer_size 随机缓冲大小,提高随机读效率(1M)
sort_buffer_size 排序缓冲大小,提高表排序效率(几百k足够)
join_buffer_size 表连接缓冲大小,提高表连接效率(几百k足够)
binlog_cache_size 二进制日志缓冲,提高二进制日志写入效率
tmp_table_size 临时表大小,如果超过则产生基于磁盘的Myiasm表(32M)
thread_stack 线程堆栈,临时存放SQL语句、过程等(不需要配置过大,192k)
thread_cache_size 线程缓存,降低反复打开线程的开销(64)
net_buffer_length 连接池缓冲及读取结果集缓冲
bulk_insert_buffer_size MyISAM表批量写入缓冲(可选)

Global buffer

针对Mysql的全局Cache。

parameter description
innodb_buffer_pool_size innodb高速缓冲,data page、index page、lock info等(Mysql总使用内存大小,根据Threadbuffer和最大线程数来定。)
innodb_additional_mem_pool_size innodb数据字典额外内存,缓存所有字典
innodb_log_buffer_size innodb redo日志缓冲,提交redo写入效率
key_buffer_size MyISAM索引高速缓冲(建议关闭)
query_cache_size 查询高速缓冲,缓存结果、减少硬解析(建议关闭)
table_definition_cache 表定义文件描述缓存,提高表打开效率(大于4K->4096),参数和表数量有关
table_open_cache 表空间文件描述缓冲,提高表打开效率(大于4K->4096),参数和表数量有关
max_heap_table_size MEMORY、HEAP表的最大容量,如果内存不够,则不允许写入数据(Memory引擎)
tmp_table_size

Overhead

parameter description
innodb_adaptive_hash_index Adaptive index hash
System dictionary hash
Locking system
Sync_array
Os_events

Mysql文件结构

参数文件

查看mysql配置文件:

/usr/sbin/mysqld --verbose --help | grep -A 1 'Default options'

结果:

[root@cansdk ~]# /usr/sbin/mysqld --verbose --help | grep -A 1 'Default options'
Default options are read from the following files in the given order:  
/etc/my.cnf /etc/mysql/my.cnf /usr/etc/my.cnf ~/.my.cnf 
[root@cansdk ~]# 

Mysql在没有指定配置文件的时候,会按照从左到右的顺序读取配置文件。最右侧的配置文件会覆盖左侧的配置文件的配置。

错误日志文件

查看日志文件路径:

show variables like 'log_error';  

结果:

mysql> show variables like 'log_error';  
+---------------+---------------------------------+
| Variable_name | Value                           |
+---------------+---------------------------------+
| log_error     | /data/mysql/3306/errlog/err.log |
+---------------+---------------------------------+
1 row in set (0.00 sec)

mysql>  

二进制文件(binlog)

Mysql中任何的数据变更都先写入到redolog和binlog中。
二进制文件只记录对数据库的操作变化。目的是用于mysql复制和instance的恢复。通过show variables like ‘datadir’查看存放路径。二进制日志支持 STATEMENT、ROW、MIX三种格式,通过binlog_format参数设定。

慢查询日志(slow query)

mysql> show variables like '%long%';  
+----------------------------------------------------------+----------+
| Variable_name                                            | Value    |
+----------------------------------------------------------+----------+
| long_query_time                                          | 1.000000 |
| performance_schema_events_stages_history_long_size       | 10000    |
| performance_schema_events_statements_history_long_size   | 10000    |
| performance_schema_events_transactions_history_long_size | 10000    |
| performance_schema_events_waits_history_long_size        | 10000    |
+----------------------------------------------------------+----------+
5 rows in set (0.00 sec)

mysql>  

General Log

记录所有的SQL日志。不建议在生产环境打开。

Redo log

用于innodb engine记录物理结构的更变日志,redo log的特点循环写、顺序写。Ib_logfile*为 redo log文件。主要用于数据恢复。

*.Pid

Mysql实例的进程文件。

*.sock

Mysql实例的套接字连接句柄。

Mysql表结构文件

*.frm mysql表结构的定义文件。

Innodb数据文件

.idb innodb独立表空间文件。
.ibdata
innodb共享表空间文件(undo log会使用这一部分)。

MyIasm文件

.myi MyISAM的索引文件。
.myd MyISAM的数据文件。

Mysql存储结构

屏幕快照<em>2016-08-09</em>下午10.36.45
Mysql中表结构分为独立表空间和共享表空间。在独立表空间下,比如Innodb。idb文件为独立表空间的数据文件,一张表对应一个表空间。共享表空间始终存在,会存储锁、undo log、double write等信息。

Segment

表空间是由各个段所组成。常见段类型:数据段、索引段、回滚段。

Extend

段是由区组成。

Page

每个区由64个Page组成。每个Page的大小为16kb,即每个区大小为1Mb。 Page的大小不能更改。常见的页类型:数据页、Undo页、系 统页、事务数据页、插入缓冲位图页、插入缓冲空闲列表页、未压缩的 二进制大对象页、压缩的二进制大对象页。

Row

InnoDB存储引擎是面向行的(row-oriented),每页最多允许存放 7992行数据。Page由Row组成。每个Page最少需要容纳2行row。
常见的Row格式:

Name Description
redundant 最早的格式
compact 默认的行格式,消耗的磁盘空间和备份耗时最小
Dynamic 大字段存储在off-page中,如果不需要读取大字段时效率较高,否则效 率很低
Compress 将data index pages压缩,但在buffer pool中则不压缩,一般不推荐使用。

行溢出

Off-page相当于行溢出。每一行记录超过page一半的时候,依次将最长的列拆分到多个page中存储,直至这一行记录低于page的一半。溢出的列在一个page中无法完整存放的时候,将会放置在下一个page中。
compact模式下,溢出的列只存储768个字节,这之后的所有字节插入到新的page中。
dynamic模式下,溢出列只存储20字节的指针,指向其最新的page。所以在查询、排序、临时表的时候,系统的IO开支更高。

数据页结构

数据页采用b+树的方式存储。
屏幕快照<em>2016-08-10</em>上午9.56.46
数据页由七个部分组成:

File Header

录页的一些头信息,如页偏移量、上一页、下一页、页类型等,固定长度为38个字节

记录页的状态信息,堆中记录数、指向空闲列表的指针、已删除记录的字节数、最后插入的位置等,固定长度共56个字节

Infinum Record & Suprenum Record

在InnoDB存储引擎中,每个数据页中有两个虚拟的行记录,用来限定记录的边界。Infimun记录是比该页中任何主键都要小的值,Supermum指比任何可能大的值还要大的值。这两个值在页创建时被建立,并且在任何情况下不会被删除。在Compact行格式和Redundant行格式下,两者占用的字节数各不相同。

User records

用户记录,即行记录。实现记录的内容。Innodb的存储引擎总是B+树的索引组织表。

Free space

空闲的表空间。是一个链表数据结构,释放的资源会被加入链表。

Page Directory

页目录。页目录存放了记录的相对位置,并不是偏移量,有些时候这些记录称为Slots(槽); 槽是一个稀疏目录,即一个槽中可能属于多个记录,最少属于4条记录,最多属于8条记录。需要牢记的是,B+树索引本身并不能找到具体的一条记录,B+树索引能找到只是该记录所在的页。数据库把页载入内存,然后通过Page Directory再进行二叉查找。只不过二叉查找的时间复杂度低,同时内存中的查找很快,因此通过忽 略了这部分查找所用的时间。

File Trailer

文件结尾信息。为了保证页完整地写入磁盘(如写过程的磁盘损坏、机器宕机等),固定长8个字节。

Innodb体系结构

Mysql标配的文件系统为XFS。
屏幕快照<em>2016-08-10</em>上午10.16.09

后台进程

Innodb由15个后台进程组成:
通过 show engine innodb status \G 可以查看引擎状态。

mysql> show engine innodb status \G  
*************************** 1. row ***************************
  Type: InnoDB
  Name: 
Status:  
=====================================
2016-08-09 19:27:03 0x700000d31000 INNODB MONITOR OUTPUT  
=====================================
Per second averages calculated from the last 44 seconds  
-----------------
BACKGROUND THREAD  
-----------------
srv_master_thread loops: 31 srv_active, 0 srv_shutdown, 111768 srv_idle  
srv_master_thread log flush and writes: 111797  
----------
SEMAPHORES  
----------
OS WAIT ARRAY INFO: reservation count 91  
OS WAIT ARRAY INFO: signal count 85  
RW-shared spins 0, rounds 125, OS waits 61  
RW-excl spins 0, rounds 9, OS waits 0  
RW-sx spins 3, rounds 37, OS waits 1  
Spin rounds per wait: 125.00 RW-shared, 9.00 RW-excl, 12.33 RW-sx  
------------
TRANSACTIONS  
------------
Trx id counter 1496  
Purge done for trx's n:o < 1494 undo n:o < 0 state: running but idle  
History list length 50  
LIST OF TRANSACTIONS FOR EACH SESSION:  
---TRANSACTION 281479827471032, not started
0 lock struct(s), heap size 1136, 0 row lock(s)  
---TRANSACTION 281479827470128, not started
0 lock struct(s), heap size 1136, 0 row lock(s)  
--------
FILE I/O  
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)  
I/O thread 1 state: waiting for i/o request (log thread)  
I/O thread 2 state: waiting for i/o request (read thread)  
I/O thread 3 state: waiting for i/o request (read thread)  
I/O thread 4 state: waiting for i/o request (read thread)  
I/O thread 5 state: waiting for i/o request (read thread)  
I/O thread 6 state: waiting for i/o request (write thread)  
I/O thread 7 state: waiting for i/o request (write thread)  
I/O thread 8 state: waiting for i/o request (write thread)  
I/O thread 9 state: waiting for i/o request (write thread)  
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,  
 ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0  
319 OS file reads, 801 OS file writes, 284 OS fsyncs  
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s  
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX  
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges  
merged operations:  
 insert 0, delete mark 0, delete 0
discarded operations:  
 insert 0, delete mark 0, delete 0
Hash table size 69239, node heap has 0 buffer(s)  
Hash table size 69239, node heap has 0 buffer(s)  
Hash table size 69239, node heap has 0 buffer(s)  
Hash table size 69239, node heap has 0 buffer(s)  
Hash table size 69239, node heap has 0 buffer(s)  
Hash table size 69239, node heap has 0 buffer(s)  
Hash table size 69239, node heap has 0 buffer(s)  
Hash table size 69239, node heap has 0 buffer(s)  
0.00 hash searches/s, 0.00 non-hash searches/s  
---
LOG  
---
Log sequence number 2780946  
Log flushed up to   2780946  
Pages flushed up to 2780946  
Last checkpoint at  2780937  
0 pending log flushes, 0 pending chkp writes  
126 log i/o's done, 0.00 log i/o's/second  
----------------------
BUFFER POOL AND MEMORY  
----------------------
Total large memory allocated 274857984  
Dictionary memory allocated 639272  
Buffer pool size   16382  
Free buffers       15892  
Database pages     490  
Old database pages 0  
Modified db pages  0  
Pending reads 0  
Pending writes: LRU 0, flush list 0, single page 0  
Pages made young 0, not young 0  
0.00 youngs/s, 0.00 non-youngs/s  
Pages read 286, created 204, written 586  
0.00 reads/s, 0.00 creates/s, 0.00 writes/s  
No buffer pool page gets since the last printout  
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s  
LRU len: 490, unzip_LRU len: 0  
I/O sum[0]:cur[0], unzip sum[0]:cur[0]  
--------------
ROW OPERATIONS  
--------------
0 queries inside InnoDB, 0 queries in queue  
0 read views open inside InnoDB  
Process ID=2855, Main thread ID=123145310887936, state: sleeping  
Number of rows inserted 1845, updated 7, deleted 4, read 5267  
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s  
----------------------------
END OF INNODB MONITOR OUTPUT  
============================

1 row in set (0.03 sec)

mysql>  
  • 1x Master Thread
  • IO Thread:
    • 4x Read Thread(innodbreadiothreads)
    • 4x Write Thread(innodbwriteiothreads)
    • 1x Insert Buffer Thread(Change Buffer Thread)
    • 1x Log Thread
  • 1x Lock Monitor Thread
  • 1x Error Monitor Thread
  • 1x Purge Thread
  • 1x Page Cleaner Thread

Master Thread

拥有最高的优先级,内部由主循环(loop)、后台循环(background loop)、刷新循环(flush loop)、暂停循环( suspend loop)组成。
每1s或10s便会触发一次循环工作。大多数时候都在进行loop的循环。而他通过sleep的方式来实现定时的操作。在高负载的情况下就会出现延时的问题。

主循环(loop)

每1s:
刷新10个脏页到磁盘。如果超过innodbmaxdirtypagespct,即超过脏页所占的缓冲池比例,则刷新100个脏页到磁盘(默认90%)。
执行insert buffer merge合并查询缓冲(最多100个)。
刷新redo log buffer到磁盘。
建立checkpoint。
检查dictionary table cache ,并且清理陈旧的 dict table cache对象。 如果没有活跃用户的话,就向background loop切换。
每10s:
刷新dirty page到磁盘。
执行insert buffer merge。
刷新redo log buffer到磁盘。
Undo purge(清理过期的undo page)。
建立checkpoint。

后台循环(Background loop)

当前用户活动或者数据库空闲、关闭时,会切换到此循环。
Undo purge(清理过期的undo page)。
Insert buffer purge(合并插入缓冲)。
跳回主循环。

刷新循环(flush loop)

暂停循环( suspend loop)

挂起Master Thread,等待事件发生。

Insert Buffer Thread

随机IO:通过扫描磁盘查询到所需数据。需要花费寻道时间。
顺序IO:不需要扫描磁盘,所需要的数据是连续的。不需要花费寻道时间。
因为基于主键索引的insert(change)是顺序的,不会造成数据库的随机读取,而对于非聚集索引(即辅助索引),叶子节点的insert(change)不再是顺序的了,这时需要离散地访问非聚集索引,insert(change)的性能就变低了。
为解决该问题,Innodb引入insert (change) buffer。判断非聚集索引页是否在缓冲池中,如果在则直接插入;不在,则先放在插入缓冲区中。此外,辅助索引不能是唯一的,因为插入到插入缓冲时,并不去查找索引页的情况,否则仍然会造成随机读,失去插入缓冲的意义了。最后通过 master thread 定时将insert(change) buffer flush至磁盘。

  • 将非唯一辅助索引上的UPDATE INESRT DELETE随机IO变为顺序IO。
  • 判断插入的非聚集索引页是否在缓冲池中,若在,则直接插入;若不在, 则先放入到insert buffer中

插入缓冲可能会占缓冲池中内存,默认也能会占到1/2,所以可以将这个值调小点。
当更新/插入的非聚集索引的数据所对应的页不在内存中时(对非聚集索引的更新操作通常会带来随机IO),会将其放到一个insert buffer中,当随后页面被读到内存中时,会将这些变化的记录merge到页中。当服务器比较空闲时,后台线程也会做merge操作。

配置项:

Innodbchangebuffermaxsize 设置最大的Change buffer在buffer pool占据的最大比例(%),默认值是25,最大可以设置到50。
Innodbchangebuffering insert buffer会占用buffer pool,并且在非聚集索引很少时,并不总是必要的,反而会降低buffer pool做data cache的能力,Mysql提供了参数innodbchangebuffering来对其进行控制

Value Descrpition
all The default value: buffer inserts, delete-marking operations, and purges.
none Do not buffer any operations.
inserts Buffer insert operations.
deletes Buffer delete-marking operations.
changes Buffer both inserts and delete-marking operations.
purges Buffer the physical deletion operations that happen in the background.

Purge Thread

删除辅助索引中不存在的记录
删除已经被打了delete-marked标记的记录
删除不再需要的undo log
配置项:

Innodbpurgethreads = 1
Innodbpurgebatch_size = 300

Page cleaner

将脏数据写入到磁盘。

Page Cleaner算法

LRU 算法:基于lru list(最后访问的时间排序)的刷新顺序
Adaptive 算法,基于flush list(最后修改时间的排序)的刷新顺序
配置项:

Innodbadaptiveflushing = 1 开启Adaptive算法。
Innodbflushneighbors = 1 扫描列表,和相邻的Page一起刷新。

Adaptive hash index(AHI)

自适应hash。提高buffer pool遍历page的效率 O(1) vs O(B+Tree高度)。AHI会自动对buffer pool 热点数据创建AHI(非持久化),只支持等值查询。所以zishiyinghash只针对等值操作,in操作等。
自适应hash不需要人工干预,并且只对热数据有效。
自适应hash被加入的条件:
* 索引是否被访问了17次以上 * 索引中某个页已经被访问了至少100次

checkpoint

触发redo log buffer或者脏页的写入。

Sharp checkpoint

强制将脏页和redo log buffer刷新入磁盘。会有短暂性的挂起操作

Fuzzy checkpoint

延迟检查点。持续将脏页和redo log buffer更新到磁盘。但是会有延迟。
配置项:

innodbmaxdirtypagespct = 90 最大脏页百分比

在Innodb内部使用Fuzzy checkpoint进行刷新,以下情况会触发延时刷新: * Master thread checkpoint * Flush lru list checkpoint * async | sync flush checkpoint * Dirty page too much checkpoint

Double Write

屏幕快照<em>2016-08-11</em>上午9.38.34

InnoDB的PageSize一般是16KB,系统写入是以4k为一个原子,在极端情况下(比如断电)往往并不能保证这一操作的原子性,16K的数据,写入4K时,发生了系统断电 /os crash,只有一部分写是成功的,这种情况下就是partial page write

为解决以上问题,当mysql将脏数据flush到data file的时候,先使用memcopy将脏数据复制到内存中的double write buffer,之后通过double write buffer再分2次,每次写入1MB到共享表空间,然后马上调用fsync函数,同步到磁盘上,避免缓冲带来的问题,在这个过程中,doublewrite是顺序写,开销并不大,在完成doublewrite写入后,在将double write buffer写入各表空间文件,这时是离散写入。

Innodb在Pagesize写入失败的时候,通过binlog和redolog无法恢复。
Mysql中任何的数据变更都先写入到redolog和binlog中。

  • 过程
    1. 将操作写入binlog和redolog日志。
    2. Memcpy将dirty page拷贝到大小2MB的double write buffer中
    3. Double write buffer 以每次1M写入到共享表空间(ibdata)中的double write
    4. 离散IO的方式写入到数据文件(ibd file)中

开启Doublewrite(默认开启)

InnoDBdoublewrite=1表示启动double write
show status like 'InnoDB
dblwr%'可以查询double write的使用情况;

mysql> show status like  "%InnoDB_dblwr%";  
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Innodb_dblwr_pages_written | 392   |刷新到double write buffer数量
| Innodb_dblwr_writes        | 21    |写入文件次数
+----------------------------+-------+
2 rows in set (0.00 sec)

mysql>  

缓冲池

Buffer pool

参数:

innodbbufferpool_size

Innodb参数优化最重要的参数,也是使用内存最大的区域。用来存放各种数据的缓存包括有索引页、数据页、undo页、插入缓冲、自适应哈希索引、innodb存储的锁信息、数据字典信息等。
Innodb将数据库文件按页(每页16k)读取到缓冲池,然后按最近最少使用(lru)的算法来保留在缓冲池中的缓存数据。如果数据库文件需要修改,总是首先修改在缓存池中的页(发生修改后即为脏页),然后再按照一定的频率 将缓冲池的脏页刷新到文件。

Redo log Buffer

将重做日志信息先放入这个缓冲区,然后按一定频率将其刷新到重做日志文件,刷入磁盘的算法由innodbflushlogattrx_commit参数控制
参数:

innodblogbuffersize
innodb
flushlogattrxcommit

Additional memory pool

innodb_additional_mem_pool_size 是InnoDB用来保存数据字典信息和其他内部数据结构的内存池的大小,单位是 byte,参数默认值为8M
参数:

innodbadditionalmempoolsize