32

MySQL8.0新特性

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzAwNTMxMzg1MA%3D%3D&%3Bmid=2654077983&%3Bidx=7&%3Bsn=baed40d198272afa6ae848578ba3aaa1
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在这之前Mysql的版本是5.7,也是目前使用最广泛的一个版本。现在新版本跳过了6和7直接来到了8,那么V6和V7版本去哪里了呢?比较靠谱的说法是v6用作了内部的其他用途而v7的话是因为mysql有个产品叫做clusterdb他有7这个版本,所以这个新版本有一个比较大的跳跃。

不过这些对我们来说都不重要我们作为使用者只关注这个版本给我们提供了哪些比较强大和实用的新功能。

下面我们来介绍一下mysql8.0给我们提供了哪些功能

账户与安全

新版本加对密码管理和账户安全加入了一些新的特性,以及新增了角色功能

  • 用户创建和授权

mysql8.0中创建用户和用户授权的命令必须分开执行首先需要通过 create user 'colipu'@'%' identified by 'colipu@2020';  来创建用户, 然后再通过 grant all privileges . to 'colipu'@'%';  来对用户进行授权。

我感觉这样做唯一的好处就是使语句的语义更加清晰了一点。大家可以自行尝试一下。

  • 认证插件的更新

在8.0中默认的身份认证插件是 caching_sha2_password,替代了之前的mysql_native_password 我们可以通过 show variables like 'default_authentication%'; 或者是user表看到这个变化。 

fUBbqyu.png!web

那么新的认证插件肯定是更加安全性能也会更好些。我们需要注意的是如果我们使用的服务端是8.0版本而我们的客户端没有及时更新的话,那么在连接认证的时候可能会出现一些错误,我们该如何解决呢?接下来划重点。

第一种解决方式是我们修改系统的配置文件,让它继续使用之前的认证方式,如下图:

EfM3Qnu.png!web 们只需要找到mysql的配置文件然后设置认证方式为 mysql_native_password ,然后重启服务即可

  • 密码管理

mysql8开始允许限制重复使用以前的密码了,通过以下参数来配置:

password_history=3   这个参数意味着我们的新密码不能与最近三次的密码相同

password_reuse_interval=90 这个是按照日期来指定的限制,意味着我们的新密码不能与最近90天的密码相同

password_requeire_current=ON  这个参数默认值是OFF 如果我们设置为ON的话就意味着我们在修改密码的时候需要提供当前登陆账户的密码。

Fvyiymr.png!web

角色管理

mysql8新引入角色的功能,角色的本质就是一组权限的集合。在之前的版本中我们要给用户授予多个权限我们只能一个一个的去操作,如果用户比较多并且权限也比较多的话那估计殷啸奕能累趴,新的版本在用户与权限之间加了一层角色功能。这样的话只需要把权限授权给角色就可以了极大的简化了DBA的工作量。

优化器索引

新增了三种索引方式,降序索引 隐藏索引 以及函数索引

  • 隐藏索引

隐藏索引也称为不可见索引,隐藏索引不会被优化器使用,但是我们在操作数据的时候仍然会被维护,既然这样我们为什么还需要隐藏索引?其实主要是有个典型的应用场景,一个是 软删除 另一个是 灰度发布

什么是软删除,通常来说之前的版本我们想删除某些不需要的索引,殷啸奕会直接把这个索引删除但是他到楼下抽烟的时候突然想到这个索引不能删,这个时候他只能再重新创建索引,如果数据库的数据量比较大的话这种操作的成本是非常高的,现在8.0新增了这个软删除功能,就是在你要删除索引的时候可以先把这个索引给隐藏掉这样查询优化器就不会使用了但是还是需要维护它的,等你彻底确认这个索引不需要了然后就可以做物理删除了。

灰度发布是什么呢?例如,我们想要测试一些索引的功能随后可能会使用到这些索引我们可以先在线上做一些测试这时候我们就可以先增加一些这种隐藏索引对我们线上的查询不会受到影响但是后台还是会维护它,然后我们确认后这些索引有效某些查询是可以用到这个索引的,然后我们就可以把它设置成可见索引。这样就实现了我们灰度发布的效果。

关于隐藏索引的使用方式大家可以自行百度使用。

  • 降序索引

mysql8.0开始真正的支持降序索引了,我们知道在之前的版本中也是支持降序索引的定义,但是实际上在我们查询的时候服务器会忽略我们的这个定义,创建的还是升序索引。

目前只有InnoDB存储引擎支持降序索引,只支持 BTREE 降序索引。

由于降序索引的引入MySQL8.0不再对GROUP BY 操作进行隐式排序,如果想要对数据的排序还是需要大家写 ORDER BY  子句的

  • 函数索引

MySQL从8.0.13开始支持在索引中使用函数的值,我们知道索引一般都是使用的列值,那么新的索引可以使用函数(表达式)的值来进行索引,同样这个索引支持降序索引以及JSON数据的索引。

对于函数索引来说它的本质就是基于虚拟列或者叫虚拟计算列功能来实现的

通用表表达式

CTE(通用表表达式)是一个命名的临时结果集,仅在单个SQL执行范围内存在类似于派生类。

我们知道通用表表达式在sql2003的标准中就已经引入了而且主流的数据库都有这个功能,mysql8也是终于追赶了这一步加入了强大的sql功能

  • 非递归CTE

我们先来了解一下简单的非递归查询。

1.派生表:select * from (select 1) as dt;  

2.通用表表达式:with cte as(select 1) select * from cet;

以上2条sql执行结果一致,我们从第二个sql可以看出cte首先定义了一个 with然后后面跟了一个表达式的名称(或者叫做表的名称) as 后面是它的定义,定义完成后我们就可以使用这个表达式了。其实简单的可以理解为这个cet(通用表表达式)就是一个变量只是变量表示的是一张表。我们可以在使用查询语句或者是其他的语句中使用这么一个变量。

接着我们来看在sql语句中的操作示例:

byeemi7.png!web 如上图使用了2张cet表这说明了,cte定义的表是可以在后面继续使用的,就像我们写代码的时候定义了一个变量后面可以使用这个变量(可以重复使用我们之前定义好的内容)这个也是mysql增加了一个类似可以编程功能的扩展吧!

  • 递归CTE

通用表表达式的第二种叫做递归cte,递归cte是指我们在使用中对自己引用的cet,这个跟我们编程语言中的递归概念是一样的。下面我们来看一下一个简单的示例:

BFzMjuN.png!web 首先出现了一个关键字 RECURSIVE 它的意思是表示这是一个递归形式的cte表达式,后面的‘cte’ 表示这个表的名称as 后面是它的定义,我们看到这个定义中是有2个查询语句并且使用UNION ALL 来组成的,select 1 表示我们的初始化语句就是第一次运行的话产生一个1这个值,下面的查询  selcet n+1 from cet 从这句sql中也体现了递归的调用概念,where条件是为了让我们在完成递归后到达这个条件就停止执行,不然会一直循环的!

递归的特性一般适用于我们查询有上下级关系的数据例如我们公司的组织架构。

  • 递归的限制

我们在使用递归的时候一般会定义一个终止条件,就像我们上面那个sql中的where条件,如果忘记定义的话会执行一个死循环的操作,所以MySql8.0中给我们提供了一些参数:

cte_max_recursion_depath: 最大递归的深度(调用的次数)

max_execution_time:  最大执行时间

EZvqAfr.png!web 我们从上图可以看到,如果我们没有定义where条件(递归终止条件)在执行一定次数后系统会给我们提示。

NFNzamI.png!web 我们从系统中可以看到,这个系统中默认的参数是1000次,所以上面的例子中在执行到了1001次就报错了

我们简单总结一下,CTE与我们之前使用的派生表非常类似只要是之前使用的派生表现在基本上都可以使用CTE来替代,CTE的语义也更加清晰让我们读起来更流畅。

我们也可以把它当做视图或者临时表来理解这样更便于我们记忆但是有一点需要知道,他们仅仅是功能类似而已。视图是需要维护的而CTE是不需要我们维护的,他是sql语句级别的定义。

CTE可以在我们查询中多次引用,就像我们编程时候的变量一样。

它最大的特点就是支持递归调用。

最后给大家写一个波那契数列:

iI7nIbj.png!web窗口函数

窗口函数也称之为分析函数主要是为了给sql语句提供一个强大的数据分析功能,有了这个之后mysql也可以通过sql语句做一些比较强的数据的分析了

与窗口函数比较类似的是分组聚合函数。分组聚合大家都比较了解,它主要是针对数据先进行 groupby 然后在根据每个组内聚合成一个结果,比如说组内的平均数和总和之类的。

窗口函数也可以做分组, 不同的是针对分组内的每一行都会生成相应的结果。

  • 基础概念

下面示例演示基础的概念,首先我这里创建了一个数据库表数据如下图:

EN3eAb2.png!web 我们使用常规的函数来计算相同的sku共卖了多少钱。

select sku,sum(price*number) from orders group by sku;

vU7vqyv.png!web select sku,price,number,sum(price*number) over (partition by sku) from orders  order by sku,price,numice,number;

iu2M7fV.png!web 从上面的图我们可以看到首先是每一行都出现了结果,前面的三个字段列是我们的原始数据最后一个是我们计算的结果,虽然它每行都出现了,但是针对每一行数据都出现了相同的结果,这就是我们的分析函数与我们聚合函数使用不同的地方,另外一点就是我们的sql语句不需要写group by 了! 如果写了会怎样呢?会不会出现不同的结果呢?

  • 专用窗口函数

ROW_NUMEBR() / RANK() / DENSE_RANK() /PERCENT_RANK() :这些函数都是用来获取排名的,在实现上仅有一点差异。

FIRST_VALUE() / LAST_VALUE() /LEAND() / LAG(): 这些都是用来获取数据的分组的第一名或者最后一名等数据信息的

CUME_DIST() / NTH_VALUE() /NTILE(): 这些是做数据分析中累计分布,也是就是说到现在这些数据占了多少

下面我们来看看示例吧还是用上面的那个表:

这里有一篇非关于窗口函数非常不错的文章大家可以看这个练习一下吧,比我写的要详细多了。

https://www.cnblogs.com/DataArt/p/9961676.html

InnoDB增强

这部分主要是对InnoDB的存储引擎做了一些改进,我们知道自动InnoDb成为mysql的默认存储引擎之后每个版本都得到了增强MySQL8.0更是如此

  • 集成数据字典

简单用一句话来说就是mysql8.0中重新重构了这个数据字典,主要变化是删除了之前版本中存储在元数据中的数据库信息,比如 关于表的 .frm .opt 还有关于触发器的一些文件等..我们可以在mysql8.0之前的版本中的 lib目录看一下。

另外一点是将系统表和数据字典表全部改成了InnoDb存储引擎,简单来说就是从8.0之后,所有的数据库字典信息都是基于InnoDb存储引擎来进行存储。

除此之外还简化了INFORMATION_SCHEMA的实现,提高了访问性能,比如说一些表改成了基于数据字典表的一些视图,还增加了许多便于查询的索引,当我们访问数据字典信息的时候数据库会帮我们生成一些缓存。简而言之就是提高了我们对数据字典的访问性能。

针对InnoDB这种存储引擎的表数据文件它还提供了一个序列化字典信息这样一个功能。

  • 原子DDL操作

通过上面的介绍我们知道MYSQL8.0版本的数据字典统一使用InnoDb存储引擎进行存储,而InnoDb是支持事务性操作的,因此它也支持原子操作(其中与表相关的原子DDL操作需要InnoDb的支持)

一个原子DDL操作通常包括以下几点:更新数据字典,存储引擎存储层面的一些操作,和在二进制文件中记录日志在binlog中记录DDL操作

通过操作以上这些等等,在我们的数据库崩溃或者异常退出等,我们可以通过相应的数据库恢复进行重做或者回滚来保证我们ddl操作事务的原子性。

目前来说能够支持与表相关的DDL操作:有数据库,表空间,表,索引的CREATE ALTER DROP 以及TRUNCATE TABLE,

另外还支持一些其他的DDL操作 如:存储过程 ,触发器,视图,UDF的CREATE DROP 以及ALTER语句

还有账户管理相关的操作,如用户的创建,角色的授权。

  • 自增列持久化

MYSQL5.7  以及更早期的版本,InnoDb自增列计数器(AUTO_INCREMENT)的值是只存储在内存中的,当我们系统重启或者出现故障之后它需要重新去扫描这个表中的自增列吧找到最大值,然后基于这个值往上自增。在极端情况下可能会出现重复的值。

基于这个原因MYSQL8.0每次变化时将自增计数器的最大值写入redo log ,同时在每次检查点将其写入引擎私有的系统表中。这样就解决了长期以来自增Id字段值可能会重复的bug。

  • 死锁检查控制

什么是死锁?:比如有2个事务都需要进行事务的修改,而在修改的过程中都需要等待对方释放资源,因为他们之间没有通信,如果没有外界的系统接入的情况下这俩兄弟会一直傻等下去,这样就形成了一个死锁。mysql在后台也有一个死锁检查的机制,如果检测到出现这种情况它会让一个事务失败另一个事务能够进行下去,当然这个死锁检测是需要占用一定的系统资源的。

MYSQL8.0以及5.7.15增加了一个新的动态变量,用于控制系统是否执行InnoDB的死锁检测,也就是 innodb_deadlock_detect , 默认情况下这个状态是打开的。

对于高并发的系统,我们可能会需要禁用掉这个死锁检测来提供数据库的性能、

我们可以通过 set global  innodb_deadlock_detect =off;

死锁检测是可以关闭的,但是我们需要非常小心的写我们的sql语句或者业务代码不然会带来非常痛苦的问题!

  • 锁定语句选项

MySQL中有2个为查询语句加锁的语句SELECT ..FOR SHARE 和 SELECT ... FOR UPDATE 这两个语句是为我们查询的语句加入共享锁和排它锁,如果我们所查询的语句在其他事物中占用了锁那么我们这个语句需要进行等待,等到相应的事物释放锁的时候我们才能继续执行,如果一直没有等待到它会在等到超时的时候提示一个错误,新的8.0为这2个语句新增了2个选项 NOTWAIT、 SKIP LOCKED 对于 NOTWAIT 而言,我们所请求的这个行已经被其他事物锁定了,我们不是进行等待而是立即返回, SKIP LOCKED 是对于我们要查询的结果如果有哪些行被锁定了那么我们就可以直接跳过这些行的结果只返回那些没有被锁定的结果。

  • 其他的改进功能

除了前面介绍的几点之外mysql8.0中还有很多关于innodb存储引擎的改进,下面我们来简单介绍一下,

首先就是ddl操作,它能够支持所谓的快速ddl,举个栗子像增加表的列如 alter table ...这种语句它可以使用INSTANT这种算法 简单来说它只需要修改数据字典上的信息。这种操作会比之前的操作快很多可以快速的完成我们的数据结构修改(这个最早是腾讯提交的这个补丁,最后合到了主版本分支中)

关于临时表空间也做了新的修改,它改变了以前这种分散表空间存储问题,它使用了一个共享临时表空间(统一存储在ibtmp1这个表空间中)。一般临时表我们都是用完就删了,现在放在统一的空间进行统一的管理,真方便。

另外8.0新增了innodb_dedicated_server 静态变量,自动配置innodb内存。它的意思是说我们如果有一台服务器是专门作为mysql服务器的时候我们可以打开这个配置,innodb会自动配置一些参数,它会尽量占用我们系统的可用资源来提高性能。

这里关于InnoDb的介绍就说到这里,给大家推荐一个公众号 《架构师之路》作者是58同城的架构师沈建,里面有几篇文章对InnoDB的介绍非常棒。

Json增强

我们知道json这个数据结构跟我们关系型数据库的结构是不一样的,他是不固定的数据一般属于NoSQL的范畴,mysql不断针对json进行增强我们从这里可以看出mysql的意图!

  • 内联路径操作符

内联路径操作符使用 columu->>path 等介于 之前版本中的 JSON_UNQUOTR(column-path) 或者 JSON_UNQUOTE(JSON_EXTRACT(column,path)) 最新的这个操作符就是看起来比较方便些写法也简便些而已。用法如下:

ZvAb6vm.png!web 我们先用通用表表达式中的语法定义了这个json数据,然后在通过 data->>的方式来进行读取。确实比之前版本的中方便了很多大家可以尝试一下。

  • JSON聚合函数

mysql8.0新增了2个用于聚合的函数:

JSON_ARRAYAGG(): 用于将多行数据组合成json数组

JSON_OBJECTAGG(): 用于生成json对象

需要注意的是目前只有5.7.22和8.0+才支持这2个函数。

下面演示用例:

e6zmMvr.png!web 这样就把我们的order表里面的数据按照number聚合成了一个数组。下面看json_objectagg

RruQRnb.png!web json_objectagg与上面不同的地方是它支持2个参数。

  • JSON实用函数

mysql8.0新增 JSON_PRETTY() 函数,这个函数主要是为了我们在输出json对象的时候进行一个格式化,另一个实用函数是 JSON_STORAGE_SIZE() 它主要是返回json数据所占用的存储空间,第三个实用函数是 JSON_STORAGE_FREE()   它是我们在更新了相应的json的数据列之后,相应的字段可能会释放出来的空间,前2个函数也加入了5.7.22这个版本中。下面来看下怎么用吧:

QbaY7nv.png!web 我们看上图中2个语句,内容是一样但是结果不一样,第二个语句是使用了 JSON_PRETTY 函   数的结果。

另外2个函数大家可以自己创建一张表然后来select一下看实验结果。

  • JSON合并函数

8.0新增 JSON_MERGE_PATCH() 函数它用于将两个json对象合并成一个对象,同时还有一个新的函数 JSON_MERGE_PRESERV() 它也是用于将2个json对象合并成一个对象,这2个函数最大的差别在于第一个函数使用时如果2个对象有相同的节点它会用第二个对象中的属性覆盖第一个对象中的属性,那么第二个函数使用时如果遇到2个对象有相同的属性它会保留2个对象中属性的值。实际上第二个函数实现了 JOSN_MERGE 函数!

  • JSON表函数

8.0新增了 JSON_TABLE() 函数,主要是用来将JSON数据转换为关系表格式也就是行和列这种格式,前面我们讲到聚合函数,聚合函数将我们的关系表中的行列聚合为json的对象类型。那么这个json_table刚好是相反的操作。

select * from json_table('[{"a":"1"},{"a":"2"},{"a":"3"}]',"$[*]" COLUMNS(rowid for ordinality, ac varchar(100) path "$.a" default '999' on error default   '111' on empty, aj json path "$.a" default '{"x":333}' on empty, bx innt exists path "$.b") )as tt;

执行结果如下:

iEVbQzz.png!web 这个sql比较复杂我们来解释一下,

首先 select * from json_table 的结果可以当做表来使用,然后我们使用'[{"a":"1"},{"a":"2"},{"a":"3"}]' 来构造一个对象,然后对象里面有字段a,然后我们来指定数据的转换匹配条件,"$[*]" 表示所有的下表都可以作为一个输入,有了这个输入我们就可以定义要输出的关系表结构它的列的定义。首先定义一个列叫 rowid,ordinality设置为自增,第二个字段是ac 他的数据类型是varchar(100)  这个字段的取值来源于path "$.a"这个路径,当我们节点里面没有a可以定义默认值,当出现转换错误的时候可以使用error 定义默认值!

最后

至此们已经把mysql8.0版本新增的功能都介绍的差不多了!大家可以通过docker快速搭建起来操作一把了!有问题欢迎一起交流学习。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK