引言

MySQL数据库从1995年诞生至今,已经过去了二十多个年头了,到2022.04.26日为止,MySQL8.0.29正式发行了GA版本,在此之前版本也发生了多次迭代,发行了大大小小N多个版本,其中每个版本中都有各自的新特性,所有版本的特性加起来,用一本书的篇幅也无法完全阐述清楚,因此本章主要会挑重点特性来讲,具体各版本的特性可参考MySQL官网的开发手册:

  • 《MySQL官网-5.6版手册》

  • 《MySQL官网-5.7版手册》

  • 《MySQL官网-8.0版手册》

这里主要讲一下5.6、5.7、8.0这三个版本,因为这三个版本属于MySQL里程碑式的发行版,其实除开这三个版本外,5.1版本也是早期最主流的版本,但迄今为止基本上都并不用该版本了,因此不再对其做过多描述。

一、MySQL5.6支持的新技术

MySQL5.6是Oracle的得意之作,该版本是让MySQL真正走向高性能数据库的里程碑版本,在此之前的MySQL版本中,虽然功能也较为丰富,但性能相较于同期的Oracle数据库而言,其实表现并不佳。直到Oracle公司接手MySQL后,首先就对其进行了大刀阔斧的改进,从Oracle中移植了很多特性过来,同时废弃一些原有的鸡肋功能,也优化了很多内部实现,最终打造出了MySQL5.6这个版本。

但这里对于MySQL5.6中不重要的特性不会罗列,重点讲解一些较为重要的新特性。

1.1、支持只读事务(Read-Only)

在此之前的版本中,MySQL默认会为每个事务都分配事务ID,所有事务都一视同仁,但在5.6版本中开始支持只读事务,MySQL内部会有两个事务链表,一个是只读事务链表,一个是正常事务链表。

当一个事务中只有读操作时,MySQL并不会为这些事务分配ID,默认全部为0(但是会分配查询ID),然后将其标记为一个只读事务,并加入只读事务链表中,直到当这个事务中出现变更数据的操作时,才会正式为其分配事务ID,以及将其挪动到正常事务链表中。

这样做的好处在于:其他事务利用MVCC机制读取数据时,生成的ReadView读视图中的活跃事务链表会小很多很多,因此遍历的速度更快,同时也无需为其分配回滚段,从而进一步提升了MySQL整体的查询性能。

1.2、InnoDB存储引擎增强

主要就说一下InnoDB的核心:BufferPool缓冲池相关的改进项,主要有两点:刷盘策略改进、缓冲池预热。

1.2.1、缓冲池刷盘策略优化

在之前的版本的InnoDB-BufferPool缓冲池中,变更过的数据页会共用MySQL后台的刷盘线程,也就是redo-log、undo-log、bin-log…..一系列内存到磁盘的刷盘工作,都是采用同一批线程来完成。在MySQL5.6版本中,BufferPool引入了独立的刷盘线程,也就意味着缓冲池中变更过的数据页会由专门的线程来负责刷盘,这样能够提升缓冲池的刷盘效率,无需排队等待刷写。

同时BufferPool的刷盘线程,还支持开启多线程并发刷盘操作,这样在缓冲池较大的情况下,能够进一步提升刷盘的效率,从而让数据落盘的效率更快,从一定程度上也提升了数据的安全性,毕竟内存中的数据随时都有几率丢失,但落盘后基本上不是硬件损坏,都有可能恢复过来。

1.2.2、BufferPool缓冲池预热

缓冲池预热是一种特别好的机制,在之前版本的内存缓冲池中,当MySQL关闭时,原本内存中的热点数据都会被清空,重启后所有的热点数据又需要经过时间的沉淀,然后才能留在内存中。但MySQL5.6版本中,每次关闭MySQL时都会将内存中的热点数据页保存到磁盘中,当重启时会直接从磁盘中载入之前的热点数据,避免了热点数据的重新“选拔”!

这样做的好处无疑是巨大的,举个例子就能够理解,如下:

现在整个村庄中选拔综合能力最强的男人,原本已经选拔了一批相对来说最优秀的人出来,但由于村庄临时出现变故需要集体外出,所以本次选拔赛会被搁浅,当处理好变故后再次开启选拔赛,又重新从头开始选拔,这显然十分影响效率。

而预热的含义是:出现意外变故时,先将上次选拔赛中最优秀的那批人,把对应的名字保存在一个名单中,处理完变故后,只需要找到这个名单,把名单上的人喊过来继续选拔即可,这样做显然将效率提升了N倍。

1.3、新增Performance_Schema库监控全局资源

如果对MySQL比较熟悉的小伙伴应该清楚,在之前的版本中存在一个名为information_schema的库,其中会记录一些MySQL运行期间需要用到的表,比如视图管理、存储过程与函数的管理、临时表的管理、会话的管理、触发器的管理、表分区的管理……,这些内容的信息都会被存储到information_schema库中。

而在MySQL5.6中,官方又加入了一个新的自带库:performance_schema,这里面会记录:数据库整体的监控信息,比如事务监控信息、最近执行的SQL信息、最近连接的客户端信息、数据库各空间的使用信息……,基于这个库可以在线上构建出一个完善的MySQL监控系统:

  • Statements/execution stages:MySQL统计的一些消耗资源较高的SQL语句。

  • Table and Index I/O:MySQL统计的那些表和索引会导致I/O负载过高。

  • Table Locks:MySQL统计的表中数据的锁资源竞争信息。

  • Users/Hosts/Accounts:消耗资源最多的客户端、IP机器、用户。

  • Network I/O:MySQL统计的一些网络相关的资源情况。

  • …….

1.4、索引下推机制(Index Condition Pushdown)

Index Condition Pushdown索引下推简称ICP,它是MySQL5.6版本以后引入的一种优化机制,以下述用户表为例,这张表上基于姓名、性别、密码字段建立了一个联合索引,此时先来看看下面的情况:

select * from `zz_users`; +---------+-----------+----------+----------+---------------------+ | user_id | user_name | user_sex | password | register_time | +---------+-----------+----------+----------+---------------------+ | 1 | 熊猫 | 女 | 6666 | 2022-08-14 15:22:01 | | 2 | 竹子 | 男 | 1234 | 2022-09-14 16:17:44 | | 3 | 子竹 | 男 | 4321 | 2022-09-16 07:42:21 | | 4 | 猫熊 | 女 | 8888 | 2022-09-17 23:48:29 | +---------+-----------+----------+----------+---------------------+ INSERT INTO `zz_users` VALUES(5,"竹竹","女","8888","2022-09-20 22:17:21"); SELECT * FROM `zz_users` WHERE `user_name` LIKE "竹%" AND `user_sex`="男"; 复制代码

首先为了更加直观的讲清楚索引下推,因此先再向用户表中增加一条数据。然后再来看看后面的查询SQL,这条SQL会使用联合索引吗?答案是会的,但只能部分使用,因为联合索引的每个节点信息大致如下:

{ ["熊猫","女","6666"] : 1, ["竹子","男","1234"] : 2, ["子竹","男","4321"] : 3, ["猫熊","男","4321"] : 4, ["竹竹","女","8888"] : 5 } 复制代码

由于前面使用的是模糊查询,但%在结尾,因此可以使用竹这个字作为条件在联合索引中查询,整个查询过程如下:

  • ①利用联合索引中的user_name字段找出「竹子、竹竹」两个索引节点。

  • ②返回索引节点存储的值「2、5」给Server层,然后去逐一做回表扫描。

  • ③在Server层中根据user_sex=”男”这个条件逐条判断,最终筛选到「竹子」这条数据。

有人或许会疑惑,为什么user_sex=”男”这个条件不在联合索引中处理呢?因为前面是模糊查询,所以拼接起来是这样的:竹x男,由于这个x是未知的,因此无法根据最左前缀原则去匹配数据,最终这里只能使用联合索引中user_name字段的一部分,后续的user_sex=”男”还需要回到Server层处理。

那什么又叫做索引下推呢?也就是将Server层筛选数据的工作,下推到引擎层处理。

以前面的案例来讲解,MySQL5.6加入索引下推机制后,其执行过程是什么样子的呢?

  • ①利用联合索引中的user_name字段找出「竹子、竹竹」两个索引节点。

  • ②根据user_sex=”男”这个条件在索引节点中逐个判断,从而得到「竹子」这个节点。

  • ③最终将「竹子」这个节点对应的「2」返回给Server层,然后聚簇索引中回表拿数据。

相较于没有索引下推之前,原本需要做「2、5」两次回表查询,但在拥有索引下推之后,仅需做「2」一次回表查询。

索引下推在MySQL5.6版本之后是默认开启的,可以通过命令set optimizer_switch=’index_condition_pushdown=off|on’;命令来手动管理。

1.5、MRR(Multi-Range Read)机制

Multi-Range Read简称为MRR机制,这也是和索引下推一同在MySQL5.6版本中引入的性能优化措施,那什么叫做MRR优化呢?

一般来说,在实际业务中我们应当尽量通过索引覆盖的特性,减少回表操作以降低IO次数,但在很多时候往往又不得不做回表才能查询到数据,但回表显然会导致产生大量磁盘IO,同时更严重的一点是:还会产生大量的离散IO,下面举个例子来理解。

SELECT * FROM `zz_student_score` WHERE `score` BETWEEN 0 AND 59; 复制代码

上述这条SQL所做的工作很简单,就是在学生成绩表中查询所有成绩未及格的学生信息,假设成绩字段上存在一个普通索引,那思考一下,这条SQL的执行流程是什么样的呢?

  • ①先在成绩字段的索引上找到0分的节点,然后拿着ID去回表得到成绩零分的学生信息。

  • ②再次回到成绩索引,继续找到所有1分的节点,继续回表得到1分的学生信息。

  • ③再次回到成绩索引,继续找到所有2分的节点……

  • ④周而复始,不断重复这个过程,直到将0~59分的所有学生信息全部拿到为止。

那此时假设此时成绩0~5分的表数据,位于磁盘空间的page_01页上,而成绩为5~10分的数据,位于磁盘空间的page_02页上,成绩为10~15分的数据,又位于磁盘空间的page_01页上。此时回表查询时就会导致在page_01、page_02两页空间上来回切换,但0~5、10~15分的数据完全可以合并,然后读一次page_01就可以了,既能减少IO次数,同时还避免了离散IO。

而MRR机制就主要是解决这个问题的,针对于辅助索引的回表查询,减少离散IO,并且将随机IO转换为顺序IO,从而提高查询效率。

那MRR机制具体是怎么做的呢?MRR机制中,对于辅助索引中查询出的ID,会将其放到缓冲区的read_rnd_buffer中,然后等全部的索引检索工作完成后,或者缓冲区中的数据达到read_rnd_buffer_size大小时,此时MySQL会对缓冲区中的数据排序,从而得到一个有序的ID集合:rest_sort,最终再根据顺序IO去聚簇/主键索引中回表查询数据。

SET @@optimizer_switch=’mrr=on|off,mrr_cost_based=on|off’;

可以通过上述这条命令开启或关闭MRR机制,MySQL5.6及以后的版本是默认开启的。

1.6、主从数据同步的复制改进

随着互联网时代浪潮的兴起,系统中的并发量、数据量日益增长,传统的单库在有些情况下就有些乏力,最初只能通过不断升级硬件配置的手段来提升性能。可单机的性能无论如何升级硬件,总有达到瓶颈的一天,因此分布式的概念开始进入大家眼中,而MySQL5.6中也针对于多节点部署的情况,其数据同步问题做了大幅度优化。

在MySQL5.6中,针对于主从数据同步的问题,主要引入了GTID复制、无损复制(增强半同步复制)、延时复制、并行复制这四种技术,但由于目前《MySQL专栏》还未开始发布“MySQL高可用”相关的文章,因此这些内容会在后续的《主从复制篇》阐述。

1.7、MySQL5.6中的其他特性

前面介绍的几点,属于比较重要的一些知识,但除此之外,5.6版本中还存在很多特性与优化项,如:

  • 索引增强:全文索引支持InnoDB与亚洲语种分词、支持空间索引等。

  • 表分区增强:单表分区数量最大可创建8192个、分区锁性能提升、支持cloumns分区类型。

  • 增强日期类型:time、datetime、timestamp精度提升到微秒级,datetime容量缩减到5字节。

  • 日志增强:Redo-log文件大小限制由4G→512G、Undo-log文件可独立指定位置存储。

  • 支持在线DDL(Online DDL)、对limit语句做了优化……

​在循环连接算法中,会首先选择一张小表作为驱动表,然后依次将驱动表中每一条数据作为条件,去被驱动表中做遍历,最终得到符合连接条件的所有数据,也就是会形成一个下述的伪逻辑:

for(数据 x : 驱动表){ for(数据 y : 被驱动表){ if (x == y){ // 如果符合连接条件,则记录到连接查询的结果集中..... } } } 复制代码

这种循环连接的算法中,显然会造成巨大的开销,因为驱动表每条数据都需要和被驱动表的完整数据做一次遍历。也正是为了解决这个问题,因为MySQL8.0中引入了哈希连接算法,过程如下:

其实也就是MySQL优化器会自动对联合索引中的第一个字段的值去重,然后基于去重后的值全部拼接起来查一遍,一句话来概述就是:虽然你没用第一个字段,但我给你加上去,今天这个联合索引你就得用,不用也得给我用。

当然,如果熟悉Oracle数据库的小伙伴应该知道,跳跃扫描机制在Oracle中早就有了,但为什么MySQL8.0版本才推出这个机制呢?还记得咱们在《MySQL架构篇》中的闲谈嘛?MySQL几经转手后,最终归到了Oracle旗下,因此跳跃扫描机制仅是Oracle公司:从Oracle搬到了“自己的MySQL”上而已。

但是跳跃扫描机制也有很多限制,比如多表联查时无法触发、SQL条件中有分组操作也无法触发、SQL中用了DISTINCT去重也无法触发…..,总之有很多限制条件,具体的可以参考《MySQL官网8.0-跳跃扫描》。

其实这个跳跃性扫描机制,只有在唯一性较差的情况下,才能发挥出不错的效果,如果你联合索引的第一个字段,是一个值具备唯一性的字段,那去重一次再拼接,几乎就等价于走一次全表。

对于索引跳跃扫描机制,可以通过set @@optimizer_switch = ‘skip_scan=off|on’;命令来选择开启或关闭跳跃式扫描机制。

3.5.2、隐藏索引

隐藏索引并不是一种新的索引类型,而是一种对索引的骚操作,可以理解为对每个索引新增了一个开关按键,主要用于测试环境和灰度场景,在MySQL8.0版本中,可以通过INVISIBLE、VISIBLE来控制索引的开关:

  • 当对一个索引使用INVISIBLE后,会关闭这个索引,优化器在执行SQL时无法发现和使用它。

  • 当对一个索引使用VISIBLE后,会将索引从隐藏状态恢复到正常状态。

所谓的隐藏索引,就是指将一个已经创建的索引“藏起来”,被藏起来的索引是无法被优化器探测到的,因此执行SQL语句时,就算语句中显式使用了索引字段,优化器也不会选择走这条索引。

这个特性主要是针对于调优、测试场景而研发的,如果隐藏一个索引后,在压测场景下不会对业务产生影响,如果经过反复测试后依旧不影响SQL性能,那这条索引则可以被判定为无用索引,可以将其删除,隐藏索引的用法如下:

-- 隐藏某张表上已存在的一个索引 alter table 表名 alter index 索引名 invisible; -- 恢复某张表上已存在的一个索引 alter table 表名 alter index 索引名 visible; 复制代码

3.5.3、降序索引

Descending index降序索引是一种索引的特性,不知大家是否还记得之前定义索引的语句呢,如下:

ALTER TABLE tableName ADD INDEX indexName(columnName(length) [ASC|DESC]); 复制代码

在创建索引时,可以通过ASC、DESC来定义一个索引是按升序还是降序存储索引键,但本质上这种语法,在MySQL8.0之前,就算你手动写明了DESC降序,在创建时依旧会默认忽略,也就是本质上还是按升序存储索引键的,当你要对某个倒序索引的字段做倒序时,依旧会发生filesort排序的动作。

到了MySQL8.0官方正式支持降序索引,也就是当对一个字段建立降序索引后,做降序查询时不需要再次排序,可直接根据索引进行取值。

下面来基于MySQL5.1、MySQL8.0举个简单的例子感受一下:

-- 分别在 MySQL5.1、MySQL8.0 中创建一张 zz_table 表 create table zz_table ( id1 int, id2 int, index idx1 (id1 ASC, id2 ASC), index idx2 (id1 ASC, id2 DESC), index idx3 (id1 DESC, id2 ASC), index idx4 (id1 DESC, id2 DESC) ); insert into zz_table values(1,2); 复制代码

上述创建了一张索引的测试表zz_table,其中对于两个字段建立四个索引:全升序、全降序、前降后升、前升后降,然后随便插入了一条数据,接着来看看SQL执行情况:

-- MySQL5.1版本中的测试 explain select * from zz_table order by id1 asc, id2 desc; +----+-------------+----------+------+-----------------------------+ | id | select_type | table | .... | Extra | +----+-------------+----------+------+-----------------------------+ | 1 | SIMPLE | zz_table | .... | Using index; Using filesort | +----+-------------+----------+------+-----------------------------+ -- MySQL8.0版本中的测试 explain select * from zz_table order by id1 asc, id2 desc; +----+-------------+----------+------------+-------+----------------------------------+ | id | select_type | table | partitions | ..... | Extra | +----+-------------+----------+------------+-------+----------------------------------+ | 1 | SIMPLE | zz_table | NULL | ..... | Backward index scan; Using index | +----+-------------+----------+------------+-------+----------------------------------+ 复制代码

重点观察最后一个Extra字段,在MySQL8.0之前的版本中,虽然对id2建立了倒序索引,但实际做倒序查询时依旧会发生Using filesort排序动作,而MySQL8.0中则直接是Backward index scan反向索引扫描,并未触发排序动作。

3.5.4、函数索引

还记得在聊MySQL5.7版本时,最后贴出的其他特性嘛?其中就有一条是引入了隐藏列,实现了类似于Oracle中的函数索引,但其实功能并不健全,而在MySQL8.0中真正的支持了函数索引,也就是基于函数去创建索引,如下:

alter table 表名 add index 索引名(函数(列名)); -- 比如:创建一个将字段值全部转为大写后的索引 alter table t1 add index fuc_upper(upper(c1)); 复制代码

基于某个字段创建一个函数索引后,之后基于该字段使用函数作为查询条件时,依旧可以走索引,如下:

select * from t1 where upper(c1) = 'ABC'; 复制代码

上述这条SQL语句,按照之前《索引应用篇-索引字段使用函数导致失效》中聊到的理论,理论上这条语句由于在=前面使用了函数,显然会导致索引失效,但在MySQL8.0中可创建函数索引,可以支持条件查询时,在=号之前使用函数。

不过有一点需要牢记:使用什么函数创建的索引,也仅支持相应函数走索引,比如上面通过了upper()函数创建了一个索引,因此upper(c1) = ‘ABC’这种情况可以走索引,但使用其他函数时依旧会导致索引失效,如:lower(c1) = ‘abc’。

3.6、CTE通用表表达式(Common Table Expression)

首先要搞明白这个名称,不是通用表达式,而是通用表、表达式,这个名称上有许多人都叫成通用表达式、公用表达式,这显然是不正确的,因为直接将其中的Table省去了,所以大家在这里要牢记,正确的叫法应该是通用表表达式。

CTE通用表表达式究竟是用来干什么事情的呢?CTE是一个具备变量名的临时结果集,也就是可以将一条查询语句的结果保存到一个变量里面,后续在其他语句中允许直接通过变量名来使用该结果集,语法如下:

with CTE名称 as (查询语句/子查询语句) select 语句; 复制代码

上述的语法是一个普通的CTE用法,同时还有另一种递归的CTE用法,先举个简单的例子来认识一下最基本的用法:

-- MySQL8.0版本之前的子查询语句 select * from t1 where xx in (select xx from t2 where yy = "zzz"); -- MySQL8.0中使用CTE表达式来代替 with cte_query as (select xx from t2 where yy = "zzz") select * from t1 join cte_query on t1.xx = cte_query.xx; 复制代码

观察上述例子,原本语句中需要使用in来对子查询的多个结果集做匹配,使用CTE后可以将子查询的结果集保存在cte_query变量中,后续的语句中可以将其当作成一张表,然后来做连接查询。

其实看到这里,CTE表达式是不是有些类似于临时表的概念?但它会比临时表更轻,查询更快。

除开最基本的表达式外,还有一种名为递归CTE表达式的概念,它是一种递归算法的实现,可以反复执行一段SQL,比如当你要查询标签表中,某个顶级标签下所有的子标签时,它的下级可能也存在其它字标签…..,可能一个顶级标签下面有十八层子标签,这时通过传统的查询语句就无法很好实现,而使用递归的CTE表达式就很简单啦。

递归CTE表达式只需要使用with recursive关键字即可,不过这里我不贴具体实现啦,大家感兴趣的可自行研究明白CTE后再实现该需求。

CTE表达式除开可以与select语句嵌套外,还可以与其它类型的语句嵌套,例如:with delete、with update、with recursive、with with、insert with等,大家感兴趣的也可自行研究,总之CTE在很多情况下确实比较实用~

3.7、窗口函数(Window Function)

窗口函数可谓是MySQL8.0中最大的亮点之一,但在尝试去学习时会发现很难理解,先来看看窗口函数的定义。

窗口函数是一种分析型的OLAP函数,因此也被称之为分析函数,它可以理解成是数据的集合,类似于group by分组的功能,但之前的MySQL版本基于某个字段分组后,会将数据压缩到一行显示,如下:

select * from `zz_users`; +---------+-----------+----------+----------+---------------------+ | user_id | user_name | user_sex | password | register_time | +---------+-----------+----------+----------+---------------------+ | 1 | 熊猫 | 女 | 6666 | 2022-08-14 15:22:01 | | 2 | 竹子 | 男 | 1234 | 2022-09-14 16:17:44 | | 3 | 子竹 | 男 | 4321 | 2022-09-16 07:42:21 | | 4 | 猫熊 | 女 | 8888 | 2022-09-17 23:48:29 | +---------+-----------+----------+----------+---------------------+ select user_id from zz_users group by user_sex; +-----------+ | user_id | +-----------+ | 1,4 | | 2,3 | +-----------+ 复制代码

而窗口函数则不会将数据压缩成一行,也就是表中数据原本是多少行,分组完成后依旧是多少行,窗口函数的语法如下:

[window 窗口函数名 as (window_spec) [, 窗口函数名 AS (window_spec)] ...] 窗口函数名(窗口名/表达式) over ( [partition_defintion] [order_definition] [frame_definition] ) 复制代码

其实这个语法看起来不是特别能让人理解,所以结合具体的场景来举例,语法如下:

窗口函数 over([partition by 字段名 order by 字段名 asc|desc]) 窗口函数 over 窗口名 ... window 窗口名 as ([partition by 字段名 order by 字段名 asc|desc]) 复制代码

一眼看下来,结果还是令人不理解,对吗?这先别急,看不懂也没关系,后面会举例说明,先来看看MySQL8.0中提供了哪些窗口函数呢?如下:

  • 序号函数: row_number():按序排列,相同的值序号会往后推,如88、88、89排序为1、2、3。 rank():并列排序,相同的值序号会跳过,如88、88、89排序为1、1、3。 dense_rank():并列排序,相同的值序号不会跳过,如88、88、89排序为1、1、2。

  • 分布函数: percent_rank():计算当前行数据的某个字段值占窗口内某个字段所有值的百分比。 cume_dist(): 小于等于当前字段值的行数与整个分组内所有行数据的占比。

  • 前后函数: lag(expr,n):返回分组中的前n条符合expr条件的数据。 lead(expr,n):返回分组中的后n条符合expr条件的数据。

  • 首尾函数: first_value(expr):返回分组中的第一条符合expr条件的数据。 last_value(expr):返回分组中的最后一条符合expr条件的数据。

  • 其它函数: nth_value(expr,n):返回分组中的第n条符合expr条件的数据。 ntile(n):将一个分组中的数据再分成n个小组,并记录每个小组编号。

这样看过去似乎也有些令人迷糊,毕竟之前对窗口函数则这块接触比较少,因此下面来举个简单的例子切身感受一下(还是以之前的用户表为例),需求如下:

  • 按性别分组,并按照ID值从大到小对各分组中的数据进行排序,最后输出。

这需求一听就知道一条SQL绝对搞不定,在之前版本中需要创建临时表来实现,借助临时表来拆成多步完成,而在MySQL8.0中则可以借助窗口函数轻松实现,如下:

select -- 使用 row_number() 序号窗口函数 row_number() over( -- 基于性别做分组,然后基于 ID 做倒序 partition by user_sex order by user_id desc ) as serial_num, user_id, user_name, user_sex, password, register_time from zz_users; +------------+---------+-----------+----------+----------+---------------------+ | serial_num | user_id | user_name | user_sex | password | register_time | +------------+---------+-----------+----------+----------+---------------------+ | 1 | 4 | 猫熊 | 女 | 8888 | 2022-09-17 23:48:29 | | 2 | 1 | 熊猫 | 女 | 6666 | 2022-08-14 15:22:01 | | 1 | 3 | 子竹 | 男 | 4321 | 2022-09-16 07:42:21 | | 2 | 2 | 竹子 | 男 | 1234 | 2022-09-14 16:17:44 | +------------+---------+-----------+----------+----------+---------------------+ 复制代码

上述这条SQL就是基于序号窗口函数的实现,其实发现会尤为简单,观察执行结果也会发现,使用窗口函数分组后,并不会将数据压缩到一行,而是将同一分组的数据在结果集中相邻显示。

上面的例子中就演示了最为简单的窗口函数用法:函数名() over(数据处理),但实际情况中可以灵活的根据需求变动,但如若想要很好的使用窗口函数,那还需要大家多加以练习,对于这块大家可参考《MySQL官网-窗口函数示例》、或《宋红康老师的视频教学》。

3.8、MySQL8.0中的其他特性

在前面的内容中,就已经将MySQL8.0中较为重要的变更和特性做了详细阐述,但MySQL8.0整体的改变也比较大,因此这里再列出一些其它方面的特性,如下:

  • 将默认的UTF-8编码格式从latin替换成了utf8mb4,后者包含了所有emoji表情包字符。

  • 增强NoSQL存储功能,优化了5.6版本引入的NoSQL技术,并完善了对JSON的支持性。

  • InnoDB引擎再次增强,对自增、索引、加密、死锁、共享锁等方面做了大量改进与优化。

  • 支持定义原子DDL语句,即当需要对库表结构发生变更时,变更操作可定义为原子性操作。

  • 支持正则检索,新增REGEXP_LIKE()、EGEXP_INSTR()、REGEXP_REPLACE()、REGEXP_SUBSTR()等函数提供支持。

  • 优化临时表,临时表默认引擎从Memory替换为TempTable引擎,资源开销少,性能更强。

  • 锁机制增强,除开前面聊到的锁特性变更外,新引入了一种备份锁,获取/释放锁语法如下: 获取锁:LOCK INSTANCE FOR BACKUP、释放锁:UNLOCK INSTANCE

  • Bin-log日志增强,过期时间精确到秒,利用zstd算法增强了日志事务的压缩功能。

  • 安全性提高,认证加密插件更新、密码策略改进、新增角色功能、日志文件支持加密等。

  • 引入资源组的概念,支持按业务优先级来控制工作线程的CPU资源抢占几率。

…….:更多请参考《MySQL官网-8.0版手册》。

四、MySQL特性篇总结

在这章中咱们介绍了MySQL几个重要的里程碑版本,显然每个版本中都对前版本中做了不小的优化,尤其是MySQL8.0中的新特性和新优化点十分具备吸引力。但线上环境已有数据的情况下,也不适合再做迁移,毕竟数据迁移是一件十分麻烦的事情,有时候甚至比你重构一个Java项目更累,因此如果你的数据库版本目前足以支撑系统的正常运转,那就没有必要去做版本迁移。

但如若你的项目数据库已经抵达瓶颈,并且升级数据库版本着实能够解决目前的困扰,这种情况下则可以尝试升级数据库版本,将旧版本的数据迁移到新版本的数据库中,但这类工作在有专业人士负责的情况下,最好不要自己去插手,毕竟一些企业中会有专门的DBA方向。

最后,在本章节中也仅介绍了一些较为重要的新特性和优化项,但MySQL每个版本中都有一些修修补补的小特性、小改进,这里就不展开叙说了,大家感兴趣的可直接翻阅MySQL官网提供的手册,以此来详细得知各个版本中的细节改进。