RedCloud Help

MYSQL的发展实例和版本分支

时间

里程碑

1996年

MySql1.0发布。他的历史可以追溯到1979年,作者Monty用BASIC设计的一个报表工具

1996年10月

3.11.1发布。Mysql没有2.x版本

2000年

ISAM升级成了MyISAM引擎。MySql开源。

2003年

Mysql4.0发布,集成InnoDB存储引擎。

2005年

MySql5.0发布,提供了视图、存储过程等功能。

2008年

MysqlAB公司被sun公司收购,进入了sun Mysql时代。

2009年

Oracle收购sun公司,进入Oracle Mysql时代。

2010年

Mysql5.5发布,InnoDB成为默认的存储引擎。

2016年

Mysql发布8.0.0版本。为什么没有6、7?5.6可以当作6.×,5.7可以当成7.×。

因为Mysql是开源(也有收费版本),所以在mysql稳定版本的基础上业发展出来很多的分支,就像linux一样,有ubuntu、redhat、centos、fedora、debian等等。

大家最熟悉的应该是MariaDB,因为centos7里面自带了一个mariaDB。它是怎么来的呢?Oracle收购了Mysql之后,Mysql创始人之一Monty担心Mysql数据库发展的未来(开发缓慢,封闭,可能会被闭源),就创建一个分支MariaDB,默认使用全新的Maria存储引擎,它是原MyISAM存储引擎的升级版本。

其他流行分支:

Percona Server是Mysql重要的分支之一,它基于InnoDB存储引擎的基础上,提升了性能和易管理性,最后形成了增强版的XtraDB引擎,可以用来更好地发挥服务器硬件上的性能。

国内也有一些mysql的分支或者自研的存储引擎,比如网易的InnoSql,极数云盘的ArkDB。

我们操作数据库有各种各样的方式,比如linux系统中的命令行,比如数据库工具Navicat,比如程序,例如java语言的jdbcApi或者Orm框架。

大家有没有思考过,当我们的工具或者程序连接到数据库之后,实际上发生了什么事情?它的内部是怎么工作的?

就像我们到餐厅去吃饭,点了菜以后,过一会菜端上来了,后出里面有哪些人?他们分别做了什么事情?这就涉及到了Mysql的整体架构和工作流程了。

以一条查询语句为例,我们来看下Mysql的工作流程是什么样的。

一条查询sql语句是如何执行的?

连接

客户端

查询缓存

解析器

预处理器

查询优化器

执行计划

执行引擎

存储引擎

我们的程序或者工具要操作数据库,第一部要做什么事情? 跟数据库连接。

通信协议

首先,mysql必须要运行一个服务,舰艇默认的3306端口。

在我们开发系统跟第三方对接的时候,必须要能清楚的有两件事。

  1. 通信协议,比如我们使用http还是webService还是TCp?

  2. 消息格式,比如我们用XML格式,还是JSON格式,还是定长歌诗?报文长度多少,包含什么内容,每个字段的详细含义。

比如我们之前跟银联对接,银联的银行卡联网规范,约定了一种比较复杂的通信协议叫做:四进四出单工异步长连接(为了保证稳定性和性能)。

mysql是支持多种通信协议的,可以使用同步/异步的方式,支持长连接/短链接。

这里我们拆分来看。第一个是通信类型。

  1. 同步通信依赖于被调用方,首先与被调用方的性能。也就是说,应用操作数据库,线程会阻塞,等待数据库的返回。

  2. 一般只能做到一对一,很难做到一对多通信。

  1. 已不可能避免应用阻塞等待,但是不能节省sql执行的时间。

  2. 如果异步存在并发,每一个sql的执行都要单独建立一个连接,避免数据混乱。但是这样会给服务端带来巨大的压力(一个连接就会创建一个线程,线程间切换会占用大量CPU资源)。另外异步通信还带来了编码的复杂度,所以一般不建议使用。如果要一步,必须使用连接池,排队从连接池获取连接而不是创建新连接。

一般来说我们连接数据库都是同步连接。

mysql即支持短连接,也支持长连接。短链接就是操作完毕以后,马上close掉。长连接可能保持打开,减少服务端创建和释放连接的消耗,后面的程序访问的时候还可以使用这个连接。一般我们会在连接池中使用长连接。

保持长连接会消耗内存。长时间不活动的连接,mysql服务器会断开。

show global variables like 'wait_timeout';--非交互式超时时间,如JDBC程序 show gloable variables like 'interactive_timeout';--交互式超时时间,如数据库连接工具

默认都是28800秒,8小时。

我们怎么查看mysql当前有多少个连接?

可以用show status命令:

show global status like 'Thread%';

Thread_cached:缓存中的线程连接数。

Thread_connected:当前打开的连接数。

Thread_created:未处理连接创建的线程数。

Thread_running:非睡眠状态的连接数,通常指并发连接数。

没产生一个连接或者一个会话,在服务器端就会创建一个线程来处理。反过来,如果要杀死回话,就要kill线程。

有了连接数,怎么知道当前连接的状态?

也可以使用show processlist;(root用户)查看sql的执行状态。

一些常见的状态:

状态

含义

sleep

线程正在等待客户端,以向它发送一个新语句

query

线程正在执行查询或往客户端发送数据

locked

该查询被其他查询锁定

copying to tmp table on disk

临时结果集合大于tmp_table_size。线程把临时表从存储器内部格式改变磁盘模式,以节约存储器

sending data

线程正在为select语句处理行,同时正在向客户端发送数据

sorting for group

线程正在进行分类,以满足group by要求

sorting for order

线程正在进行分类,以满足order by要求

mysql服务器允许的最大连接数是多少?

在5.7版本中默认是151个,最大可以设置成16384(2^14)。

show variables like 'max_connections';
  1. 级别:会话session级别(默认);全局global级别。

  2. 动态修改:set,重启后失效;永久生效,修改配置文件/etc/my.cnf.

set global max_connections=1000;

Mysql 支持呢些通信协议呢?

  1. Unix Socket:比如我们在linux服务器上,如果没有指定-h参数,他就用socket方式登录(省略了-S /var/lib/mysql/mysql.sock

    1. 他不是通过通信协议,也可以连接到mysql的服务器,它需要用到服务器上的一个物理文件(/var/lib/mysql/mysql.sock)。

    select @@socket;

    如果执行-h参数,就会用第二种方式,TCP/IP协议。

    mysql -h 192.168.8.211 -uroot -p123456

    我们的编程语言的连接模块都是用tcp协议连接到mysql服务器的,比如mysql-connector-java-x.x.xx.jar。

  2. 命名管道(named pipes)和内存共享(share memory)的方式,这两种通行方式只能在windows上面使用,一般用的比较少。

通信方式

第二个是通信方式。

img_1.png

要么是客户端向服务端发送数据,要么是服务端想客户端发送数据,这两个动作不能同时发生。所以客户端发送sql语句给服务端的时候,(再一次连接里面)数据是不能分成小块发送的,不关你的sql语句有多大,都是一次性发送。

比如我们用mybatis动态sql生成了一个批量插入的语句,插入10万条数据,values后面跟了一长串的内容,或者where条件in里面的值太多,会出现问题。

这个时候我们必须调整mysql服务器配置max_allowed_packet参数的值(默认是4M),把它调大,否则会报错。

另外一方面,对于服务端来说,也是一次发送所有的数据,不能因为你已经到了想要的数据就中断操作,这个时候会对网络和内存产生大量消耗。

所以,我们一定要在程序里面避免不带limit的这种操作,比如一次所有满足条件的数据全部查询出来,一定要先count一下。如果数据量大的话,可以分批查询。

执行一条查询语句,客户端跟服务端建立连接之后呢?下一步要做什么?

查询缓存

mysql内部自带了一个缓存模块。

缓存的作用我们应该很清楚了,把数据以kv的形式放到内存里面,可以加快数据的读取数据,也可以减少服务器处理的时间。但是mysql的缓存我们好像比较陌生,从来没有去配置过,也不知道它什么时候生效?

比如user_innodb有500万行数据,没有索引。我们在没有索引的字段上执行同样的查询,大家觉得第二次会快吗?

select * from user_innodb where name='青山'

缓存没有生效,为什么?Mysql的缓存默认是关闭的。

show variables like 'query_cache%'

默认关闭的意思就是不推荐使用,为什么mysql不推荐使用它自带的缓存呢?

  1. 第一个是他要求sql语句必须一模一样,中间多一个空格,字母大小写不同都这认为是不同的sql。

  2. 第二个表里面任何一个数据发生变化的时候,这张表所有的缓存都会失效,所以对于有大量数据更新的应用,也不适合。

索引缓存这一块,我们还是交给ORM框架(比如MyBatis默认开启了一级缓存),或者独立的缓存服务,比如redis来处理更合适。

语法解析和预处理(Parser & Preprocessor)

我们没有使用缓存的话,就会跳过缓存的模块,下一步我们要做什么呢?

ok,这里我会有一个疑问,为什么我的一条sql语句能够被识别呢?假如我随便执行一个字符串penyuyan,服务器报错了一个1064的错:

这个就是mysql的parser解析器和preprocessor预处理模块。

这一步主要做的事情是对于语句基于sql语法进行词法和语法分析和语义的解析。

词法解析

词法解析就是把一个完整的sql语句打碎成一个个的单词。

比如一个简单的sql语句:

select name from user where id =1;

它会打碎成8个符号,每个符号是什么类型,从呢里开始到呢里结束。

语法解析

第二步就是语法解析,语法分析会对sql做一些语法检查,比如单引号有没有闭合,然后根据mysql定义的语法规则,根据sql语法生成了一个数据结构。这个语法结构我们把它叫做解析树(select_lex)。

1

20

=

Unsupported markdown: blockquote

select语句

select

Fields

from

tables

where

conditions

and

age

id

select name from user_info where id = 1 and age >20

任何数据库的中间件,比如mycat、sharding-jdbc(用到了DruidParser),都必须有词法和语法分析功能,在市面上也有很多的开源的词法解析的工具(比如LEX,Yacc)。

预处理器

问题:如果我写了一个词法和语法都是正确的sql,但是表明或者字段不存在,会在呢里报错?是在数据库的执行层还是解析器?比如:

select * from penyuyan;

解析器可以分析语法,但是他怎么知道数据库里面有什么表,表里面有什么字段呢?

实际上还是在解析的时候报错,解析sql的环节里面有个预处理器。

它会检查生成的解析树,解析sql的环节里面有个预处理器。

他会检查生成的解析树,解决解析器无法解析的语义。比如,他会检查和列名是否存在,检查名字和别名,保证没有歧义。

预处理之后得到一个新的解析树。

查询优化(Query Optimizer)与查询执行计划

什么是优化器?

得到解析树之后,是不是执行sql语句了呢?

这里我们有一个问题,一个sql语句是不是只有一个执行方式?或者说数据库最终执行的sql是不是就是我们发送的sql?

这个答案是否定的。一条sql语句是可以有很多执行方式的,最终返回相同的结果,他们是等价的。但是如果有这么多执行方式,这些执行方式怎么得到的?最终选择呢一种执行?根据什么判断标准去选择?

这个就是mysql的查询优化器的模块(Optimizer)。

查询优化器的目的就是根据解析树生成不同的执行计划(execution plan),然后选择一种最优的执行计划,mysql里面使用的是基于开销(cost)的优化器,呢中执行计划开销最小,就用哪种。

可以使用这个命令查看查询的开销:

show status like 'Last_query_cost'

优化器可以做什么?

mysql的优化器处理呢些优化类型呢?

  1. 当我们对多张表进行关联查询的时候,以哪个表的数据作为基准表。

  2. 有多个索引可以使用的时候,选择哪个索引。 实际上,对于每一种数据库来数,优化器的模块都是必不可少的,他们通过复杂的算法实现最可能优化查询效率的目标。

如果对于优化器的细节感兴趣,可以看看《数据库查询优化器的艺术-原理解析与sql性能优化》。

img_1.png

但是优化器也不是万能的,并不是在垃圾的sql语句都能自动优化,也不是每次都能选择到最优的执行计划,大家在编写sql语句的时候还是要注意。

如果我们想知道优化器是怎么工作的,它生成了几种执行计划,每种执行计划的cost是多少,应该怎么做?

优化器是怎么得到执行计划的?

首先我们要启用优化器的追踪(默认是关闭的):

show variables like 'optimizer_trace'; set optimizer_trace='enabled=on"

注意开启着开关是会消耗性能的,因为它要把优化分析的结果写到表里面,所以不要轻易开启,或者查看完之后关闭它(改成off)。

注意:参数分为session和global级别。

接着我们执行一个sql语句,优化器会生成执行计划:

select t.tcid from teacher t,teacher_contact tc where t.tcid = tc.tcid;

这个时候优化器分析的过程已经记录到系统表里面了,我们可以查询:

select * from information_schema.optimizer_trace;

它是一个json类型的数据,主要分为三部分,准备阶段、优化阶段和执行阶段。

setps

join_perparation-SQL准备阶段

select

steps

join_perparation-SQL优化阶段

join_perparation-SQL执行阶段

set optimizer_trace='enabled=off'; show variables like 'optimizer_trace';

优化器得到的结果

优化器最终会把解析树变成一个查询执行计划,查询执行计划是一个数据结构。

不一定,因为mysql也有可能覆盖不到所有的执行计划。

mysql提供了一个执行计划的工具。我们在sql语句前面加上explain,就可以看到执行计划的信息。

explain select name from user where id = 1;

注意explain的结果也不一定最终执行的方式。

存储引擎

得到执行计划以后,sql语句是不是终于可以执行了?

问题又来了:

  1. 从逻辑的角度来说,我们的数据是放在那里的,或者说放在一个什么结构里面?

  2. 执行计划在那里执行?是谁去执行?

存储引擎基本介绍

我们先回答第一个问题:在管理型数据库里面,数据是放在什么结构里面的?

放在表table里面的,我们可以把这个表理解成excel电子表格的形式。所以我们的表在存储数据的同时,还要组织数据的存储结构,这个存储结构就是由我们的存储引擎决定的,所以我们可以把存储引擎叫做表类型。

在mysql里面,支持多种存储引擎,他们是可以替换的,所以叫做插件式的存储引擎。为什么要搞这么多存储引擎呢?一种还不够用吗?

这个问题先留着。

查看存储引擎

比如我们数据库里面已经存在的表,我们怎么查看他们的存储引擎呢?

show table status from 'gupao';

或者通过ddl见表语句来查看。

在mysql里面,我们创建的每一张表都可以指定它的存储引擎,而不是一个数据库只能使用一个存储引擎。存储引擎的使用时仪表为单位的。而且,建造表之后还可以修改存储引擎。

我们说一张表使用的存储引擎决定我们存储数据的结构,呢在数据库上他们是怎么存储的呢?我们先要找到数据库存储数据的路径:

show variables like 'datadir';

默认情况下,每一个数据库有一个自己文件夹,以gupao数据库为例。

任何一个存储引擎都有一个frm文件,这个是表结构定义文件。

user_innodb.frm user_innodb.ibd user_memory.frm user_myisam.frm user_myisam_MYD user_myisam.MYI

不同的存储引擎存放数据的方式不一样,产生的文件也是不一样,innodb是一个,memory没有,myisam是两个。

这些存储引擎的差别在哪里?

存储引擎比较

MyISAM和InnoDB是我们用的最多的两个存储引擎,在mysql5.5版本之前,默认的存储引擎是MyISAM,它是Mysql自带的。我们建造表的时候不指定存储引擎,它就会默认使用MyISAM作为存储引擎。

MyISAM的前身是ISAM(Indexed Sequential Access Method:利用索引,顺序存取数据的方法)。

5.5版本之后默认的存储引擎改为InnoDB,它是第三方公司为mysql开发的。为什么要改?主要的原因还是Innodb支持事务,执行行级别的锁,对于业务一致性要求高的场景来说更合适。

这个里面又有Oracle和Mysql公司的一段恩怨情仇。

InnoDB本来是InnobaseOy公司开发的,它和MysqlAB公司合作开源了InnoDB的代码。但是没想到MYSQL的竞争对手Oracle把InnobaseOy收购了。

后来08年sun公司(开发java语言的sun)收购了MysqlAB,09年sun公司又被oracle收购了,所以mysql,InnoDB又是一家公司了。有人觉得Mysql越来越像Oracle,其实也是这个原因。

哪么除了这两个我们最熟悉的存储引擎,数据库还支持其他呢些常用的存储引擎呢?

我们可以用这个命令查看数据库对存储引擎的支持情况:

show engines;

其中有存储引擎的描述和对事务、XA协议和savepoints的支持。

  • XA协议用来实现分布式事务(分为本地资源管理器,事务管理器)。

  • Savepoints用来实现子事务(嵌套事务)。创建了一个Savepoints之后,事物就可以回滚到这一点,不会影响到创建Savepoints之前的操作。

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 table

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

csv storage engine

no

no

no

performance_schema

yes

performance schema

no

no

no

federated

no

federated mysql storage engine

null

null

null

这些数据库支持的存储引擎,分别有什么特性呢?

应用范围小。标记锁定限制了读/写性能,因此在web和数据存储配置中,它通常用于只读或以读为主的工作。

  • 特点:

    • 支持表级别的锁(插入和更新会锁表)。不支持事务。

    • 拥有较高的插入(insert)和查询(select)速度。

    • 存储了表的行数(count速度更快)。

    • 怎么快速向

  • 适合

    • 只读之类的数据分析的项目。

mysql5.7中的默认存储引擎。InnoDB是一个事务安全(与ACID兼容)的MySQL存储引擎,它具有提交、回滚和崩溃恢复功能来保护用户数据。InnoDB行级锁(不升级为更粗粒度的锁)和 Oracle风格的一致非锁读提高了多用户并发性和性能。InnoDB将用户数据存储在聚集索引中,以减少基于主键的常见查询的I/O。为了保持数据完整性,InnoDB还支持外键引用完整性约束。

  • 特点

    • 支持事务,支持外键,因此数据的完整性、一致性更高。

    • 支持行级锁和表级锁。

    • 支持读写并发,写不阻塞读(MVCC)。

    • 特殊的索引存放方式,可以减少I/O,提升查询效率。

  • 适合

    • 经常更新的表,存在并发读写或者有事务处理的业务系统。

将所有数据存储在RAM中,以便在需要快速查找非关键数据的环境中快速访问。这个引擎以前被称为堆内存。其使用案例正在减少;InnoDB及其缓冲池内存区提供了一种通用、持久的方法来将大部分或所有数据保存在内存中,而ndbcluster为大型分布式数据集提供了快速的键值查找。

  • 特点

    • 把数据放在内存里面,读写的速度很快,但是数据库重启或者崩溃,数据会全部消失。只适合做临时表。

它的表实际上是带有逗号分隔值的文本文件。csv表允许以csv格式导入或转存数据,以便于读写相同格式的脚本和应用程序交换数据。因为csv表没有索引,所以通常在正常操作期间将数据保存在innodb表中,并且只在导出或导入阶段使用csv表。

  • 特点

    • 不允许空行 ,不支持索引。格式通用,可以直接编辑,适合在不同数据库之间导入导出。

这些紧凑的未索引的表用于存储和检索大量很少引用的历史、存档和安全审计信息。

  • 特点

    • 不支持索引,不支持update delete。

这是mysql里面常见的一些存储引擎,我们看到,不同的存储引擎提供的特性都不一样,他们有不同的存储机制、索引方式、锁定水平等功能。

我们在不同的业务场景中对数据操作的要求不同,就可以选择不同的存储引擎来满足我们的需求,这个就是mysql支持这么多存储引擎的原因。

如何选择存储引擎?

  • 如果对数据一致性要求比较高,需要事务支持,可以选择InnoDB。

  • 如果数据查询多更新少,对查询性能要求比较高,可以选择MyISAM。

  • 如果需要一个用于查询的临时表,可以选择Memory。

  • 如果所有的存储引擎都不满足你的需求,并且技术能力足够,可以根据官网内部手册用c语言开发一个存储引擎:https://dev.mysql.com/doc/internals/en/custom-engine.html

执行引擎(query execution engine),返回结果

ok,存储引擎分析完了,它是我们存储数据的形式,继续第二个问题,是谁使用执行计划去操作存储引擎呢?

这就是我们的执行引擎,它利用存储引擎提供的相应的api来操作。

为什么我们修改了表的存储引擎,操作方式不需要做什么改变?ybwwbutsgsngdecyiuybqkuixmdeAPI是相同。

最后把数据返回给客户端,即使没有结果也要返回。

Mysql体系结构总结

基于上面分析的流程,我们一起来梳理一下mysql的内部模块。

模块详解

img_1.png
  1. Connector:用于支持各种语言与sql的交互,比如php、python、java的jdbc;

  2. Management Services & Utilities:系统管理和控制工具,包括备份恢复、Mysql复制,集群等等。

  3. Connection Pool:连接池,管理需要缓冲的资源,包括用户密码权限县城等等。

  4. Sql interface:用来接收用户的sql命令,返回用户需要的查询结果。

  5. Parser:用于解析sql语句;

  6. Optimizer:查询优化器;

  7. Cache and Buffer:查询缓存,除了行记录的缓存之外,还有表缓存,key缓存,权限缓存等等;

  8. Pluggable Storage Engines:插件是存储引擎,它提供API给服务层使用,跟具体的文件打交道。

架构分层

总体上,我们可以把Mysql分为三层,跟客户端对接的连接层,真正执行操作的服务层,和硬件打交道的存储引擎层(参考MyBtis:接口、核心、基础)。

13 February 2026