2025年sqlldr怎么使用(sqlldr命令详解)

sqlldr怎么使用(sqlldr命令详解)这个要看你从哪些方面来比较 比较不同的人关注的地方也不同 但结论是差异较大 对你个人来说 最好的方法是分别在 mysql5 5 和 8 0 环境下测试 8 0 的安装稍微麻烦了一点 如果不愿意安装就在网上找免费的 mysql 的云服务自己做测试 找不到就 4 私聊我吧 我就不发链接了 提示写得很清楚了 缺 VC2019 动态运行库 给你个地址 https support microsoft com z

大家好,我是讯享网,很高兴认识大家。



这个要看你从哪些方面来比较,比较不同的人关注的地方也不同,但结论是差异较大,对你个人来说,最好的方法是分别在mysql5.5和8.0环境下测试,8.0的安装稍微麻烦了一点,如果不愿意安装就在网上找免费的mysql的云服务自己做测试,找不到就4️私聊我吧,我就不发链接了。

提示写得很清楚了,缺VC2019动态运行库,

给你个地址:

support.microsoft.com/z

1.1.1. 用户创建和授权

到了MySQL8中,用户创建与授权语句必须是分开执行,之前版本是可以一起执行。


讯享网

MySQL8的版本

grant all privileges on . to lijin’@‘% identified by Lijin@2022; 

讯享网
讯享网create user ‘lijin’@‘%’ identified by ‘Lijin@2022’; grant all privileges on . to ‘lijin’@‘%’

MySQL5.7的版本

grant all privileges on . to lijin’@‘% identified by Lijin@2022; 

1.1.2. 认证插件更新

MySQL 8.0中默认的身份认证插件是caching_sha2_password,替代了之前的mysql_native_password。

讯享网show variables like ‘default_authentication%’;

5.7版本

8版本

select user, host,plugin from mysql.user;

这个带来的问题就是如果客户端没有更新,就连接不上!!

当然可以通过在MySQL的服务端找到my.cnf的文件,把相关参数进行修改(不过要MySQL重启后才能生效)

如果没办法重启服务,还有一种动态的方式:

讯享网alter user ‘lijin’@‘%’ identified with mysql_native_password by ‘Lijin@2022’; select host,user from mysql.user;

使用老的Navicat for MySQL也能访问

MySQL 8.0开始允许限制重复使用以前的密码(修改密码时)。

并且还加入了密码的修改管理功能

show variables like ‘password%’;

修改策略(全局级)

讯享网set persist password_history=3; –修改密码不能和最近3次一致

修改策略(用户级)

alter user ‘lijin’@‘%’ password history 3;
讯享网select user, host,Password_reuse_history from mysql.user;

使用重复密码修改用户密码(指定lijin用户)

alter user ‘lijin’@‘%’ identified by ‘Lijin@2022’;

如果我们把全局的参数改为0,则对于root用户可以反复的修改密码

讯享网alter user ‘root’@‘localhost’ identified by ‘’;

password_reuse_interval 则是按照天数来限定(不允许重复的)

password_require_current 是否需要校验旧密码(off 不校验、 on校验)(针对非root用户)

set persist password_require_current=on;


1.2.1. 隐藏索引
MySQL 8.0开始支持隐藏索引 (invisible index),不可见索引.


使用案例(灰度发布):

讯享网create table t1(i int,j int); –创建一张t1表 create index i_idx on t1(i); –创建一个正常索引 create index j_idx on t1(j) invisible; –创建一个隐藏索引
show index from t1</span>G –查看索引信息

使用查询优化器看下:

讯享网explain select  from t1 where i=1; explain select  from t1 where j=1; 

这里可以看到隐藏索引不会用上。

这里可以通过优化器的开关,打开一个设置,方便我们对隐藏索引进行设置。

select @@optimizer_switch</span>G; –查看 各种参数

红色的部分就是默认查询优化器对隐藏索引不可见,我们可以通过参数进行修改。确保我们可以用隐藏索引进行测试。

讯享网set session optimizer_switch=“use_invisible_indexes=on‘; –在会话级别设置查询优化器可以看到隐藏索引 

再使用查询优化器看下:

explain select * from t1 where j=1; 

把隐藏索引变成可见索引(正常索引)

讯享网alter table t1 alter index j_idx visible; –变成可见 alter table t1 alter index j_idx invisible; –变成不可见(隐藏索引)

最后一点,不能把主键设置成不可见的索引(隐藏索引)(MySQL做了限制)

MySQL 8.0开始真正支持降序索引 (descendingindex) 。只有InnoDB存储引擎支持降序索引,只支持BTREE降序索引。另外MySQL8.0不再对GROUP BY操作进行隐式排序。

在MySQL中创建一个t2表

create table t2(c1 int,c2 int,index idx1(c1 asc,c2 desc)); show create table t2</span>G 

如果是5.7中,则没有显示升序还是降序信息

我们插入一些数据,给大家演示下降序索引的使用

讯享网insert into t2(c1,c2) values(1,100),(2,200),(3,150),(4,50);

看下索引使用情况

explain select  from t2 order by c1,c2 desc; 

我们在5.7对比一下

这里说明,这里需要一个额外的排序操作,才能把刚才的索引利用上。

我们把查询语句换一下

讯享网explain select  from t2 order by c1 desc,c2 ; 

MySQL8中使用了

另外还有一点,就是group by语句在 8之后不再默认排序

select count(),c2 from t2 group by c2;

在8要排序的话,就需要手动把排序语句加上

讯享网select count(),c2 from t2 group by c2 order by c2;

1.2.3. 函数索引


之前我们知道,如果在查询中加入了函数,索引不生效,所以MySQL8引入了函数索引。
MySQL 8.0.13开始支持在索引中使用函数(表达式)的值。支持降序索引,支持JSON 数据的索引 函数索引基于虚拟列功能实现。


使用函数索引(表达式)

create table t3(c1 varchar(10),c2 varchar(10)); create index idx_c1 on t3(c1); –普通索引 create index func_idx on t3( (UPPER(c2)) ); –一个大写的函数索引
讯享网show index from t3</span>G
explain select  from t3 where upper(c1)=ABC ;
explain select
from t3 where upper(c2)=ABC ;

函数索引基于虚拟列功能实现

函数索引在MySQL中相当于新增了一个列,这个列会根据你的函数来进行计算结果,然后使用函数索引的时候就会用这个计算后的列作为索引。


简单入门:


以下SQL就是一个简单的CTE表达式,类似于递归调用,这段SQL中,首先执行select 1 然后得到查询结果后把这个值n送入 union all下面的 select n+1 from cte where n &lt;10,然后一直这样递归调用union all下面sql语句。

讯享网WITH recursive cte(n) as ( select 1 union ALL select n+1 from cte where n&lt;10 ) select  from cte;

案例介绍:

一个staff表,里面有id,有name还有一个 m_id,这个是对应的上级id。数据如下:

如果我们想查询出每一个员工的上下级关系,可以使用以下方式

递归CTE:

with recursive staff_view(id,name,m_id) as (select id ,name ,cast(id as char(200)) from staff where m_id =0 union ALL select s2.id ,s2.name,concat(s1.m_id,’-‘,s2.id) from staff_view as s1 join staff as s2 on s1.id = s2.m_id ) select  from staff_view order by id

使用通用表表达式的好处就是上下级层级就算有4,5,6甚至更多层,都可以帮助我们遍历出来,而老的方式的写法SQL语句就要调整。


总结:
通用表表达式与派生表类似,就像语句级别的临时表或视图。CTE可以在查询中多次引用,可以引用其他CTE,可以递归。CTE支持SELECT/INSERT/UPDATE/DELETE等语句。


MySQL 8.0支持窗口函数(Window Function),也称分析函数。窗口函数与分组聚合函数类似,但是每一行数据都生成一个结果。聚合窗口函数: SUM /AVG / COUNT /MAX/MIN等等。
案例如下:sales表结构与数据如下:

普通的分组、聚合(以国家统计)

讯享网SELECT country,sum(sum) FROM sales GROUP BY country order BY country;

窗口函数(以国家汇总)

select year,country,product,sum, 
讯享网<span class="k">sum</span><span class="p">(</span><span class="k">sum</span><span class="p">)</span> <span class="n">over</span> <span class="p">(</span><span class="n">PARTITION</span> <span class="k">by</span> <span class="n">country</span><span class="p">)</span> <span class="k">as</span> <span class="n">country_sum</span> 
from sales order by country,year,product,sum;

窗口函数(计算平局值)

select year,country,product,sum, 
讯享网 <span class="k">sum</span><span class="p">(</span><span class="k">sum</span><span class="p">)</span> <span class="n">over</span> <span class="p">(</span><span class="n">PARTITION</span> <span class="k">by</span> <span class="n">country</span><span class="p">)</span> <span class="k">as</span> <span class="n">country_sum</span><span class="p">,</span> <span class="k">avg</span><span class="p">(</span><span class="k">sum</span><span class="p">)</span> <span class="n">over</span> <span class="p">(</span><span class="n">PARTITION</span> <span class="k">by</span> <span class="n">country</span><span class="p">)</span> <span class="k">as</span> <span class="n">country_avg</span> 
from sales order by country,year,product,sum;
  • 序号函数:ROW_NUMBER()、RANK()、DENSE_RANK()
  • 分布函数:PERCENT_RANK()、CUME_DIST()
  • 前后函数:LAG()、LEAD()
  • 头尾函数:FIRST_VALUE()、LAST_VALUE()
  • 其它函数:NTH_VALUE()、NTILE()

窗口函数(排名)

用于计算分类排名的排名窗口函数,以及获取指定位置数据的取值窗口函数

SELECT 
讯享网<span class="k">YEAR</span><span class="p">,</span> <span class="n">country</span><span class="p">,</span> <span class="n">product</span><span class="p">,</span> <span class="k">sum</span><span class="p">,</span> <span class="n">row_number</span><span class="p">()</span> <span class="n">over</span> <span class="p">(</span><span class="k">ORDER</span> <span class="k">BY</span> <span class="k">sum</span><span class="p">)</span> <span class="k">AS</span> <span class="s1">&#39;rank&#39;</span><span class="p">,</span> <span class="n">rank</span><span class="p">()</span> <span class="n">over</span> <span class="p">(</span><span class="k">ORDER</span> <span class="k">BY</span> <span class="k">sum</span><span class="p">)</span> <span class="k">AS</span> <span class="s1">&#39;rank_1&#39;</span> 
FROM
<span class="n">sales</span><span class="p">;</span></code></pre></div><figure data-size="normal"><img src="https://picx.zhimg.com/v2-011fe5284ff5e8b07ba70ea238e4647e_r.jpg?source=1940ef5c" data-caption="" data-size="normal" data-rawwidth="723" data-rawheight="454" data-original-token="v2-ef3576ed1a78df4958aa87ce19448c06" class="origin_image zh-lightbox-thumb" width="723" data-original="https://picx.zhimg.com/v2-011fe5284ff5e8b07ba70ea238e4647e_r.jpg?source=1940ef5c"/></figure><div class="highlight"><pre><code class="language-sql"><span class="k">SELECT</span> <span class="k">YEAR</span><span class="p">,</span> <span class="n">country</span><span class="p">,</span> <span class="n">product</span><span class="p">,</span> <span class="k">sum</span><span class="p">,</span> <span class="k">sum</span><span class="p">(</span><span class="k">sum</span><span class="p">)</span> <span class="n">over</span> <span class="p">(</span><span class="n">PARTITION</span> <span class="k">by</span> <span class="n">country</span> <span class="k">order</span> <span class="k">by</span> <span class="k">sum</span> <span class="k">rows</span> <span class="n">unbounded</span> <span class="n">preceding</span><span class="p">)</span> <span class="k">as</span> <span class="n">sum_1</span> 
FROM
讯享网<span class="n">sales</span> <span class="k">order</span> <span class="k">by</span> <span class="n">country</span><span class="p">,</span><span class="k">sum</span><span class="p">;</span></code></pre></div><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-fae1b36f177ca53ad9d0a177_r.jpg?source=1940ef5c" data-caption="" data-size="normal" data-rawwidth="1547" data-rawheight="315" data-original-token="v2-c91a37001a6443f339efa6e1" class="origin_image zh-lightbox-thumb" width="1547" data-original="https://pic1.zhimg.com/v2-fae1b36f177ca53ad9d0a177_r.jpg?source=1940ef5c"/></figure><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-637f39dfcd8787f8faca5a16ca0fff6c_r.jpg?source=1940ef5c" data-caption="" data-size="normal" data-rawwidth="694" data-rawheight="527" data-original-token="v2-0cbd87a31cfe6e8446ac75bbb" class="origin_image zh-lightbox-thumb" width="694" data-original="https://pic1.zhimg.com/v2-637f39dfcd8787f8faca5a16ca0fff6c_r.jpg?source=1940ef5c"/></figure><p data-pid="fXMpYd-P"><br/> MySQL 8.0 开始支持原子 DDL 操作,其中与表相关的原子 DDL 只支持 InnoDB 存储引擎。一个原子 DDL 操作内容包括:更新数据字典,存储引擎层的操作,在 binlog 中记录 DDL 操作。支持与表相关的 DDL:数据库、表空间、表、索引的 CREATE、ALTER、DROP 以及 TRUNCATE TABLE。支持的其他 DDL :存储程序、触发器、视图、UDF 的 CREATE、DROP 以及ALTER 语句。支持账户管理相关的 DDL:用户和角色的 CREATE、ALTER、DROP 以及适用的 RENAME,以及 GRANT 和 REVOKE 语句。<br/></p><div class="highlight"><pre><code class="language-sql"><span class="k">drop</span> <span class="k">table</span> <span class="n">t1</span><span class="p">,</span><span class="n">t2</span><span class="p">;</span> </code></pre></div><p data-pid="XFMzC9q3">上面这个语句,如果只有t1表,没有t2表。在MySQL5.7与8 的表现是不同的。</p><p data-pid="S3ANrsJ3">5.7会删除t1表。而在8中因为报错了,整个是一个原子操作,所以不会删除t1表。</p><p data-pid="R2ULcOEy">具体看官网信息,英文好的直接看,英文不好的找个翻译工具即可看懂</p><p data-pid="Q31QtIO1">MySQL :: MySQL 8.0 Reference Manual :: 11.5 The JSON Data Type</p><p class="ztext-empty-paragraph"><br/></p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-efc640c36fcc0e5_r.jpg?source=1940ef5c" data-caption="" data-size="normal" data-rawwidth="694" data-rawheight="423" data-original-token="v2-d7713e5dd7a2169b896ae99d74ade46c" class="origin_image zh-lightbox-thumb" width="694" data-original="https://picx.zhimg.com/v2-efc640c36fcc0e5_r.jpg?source=1940ef5c"/></figure><p data-pid="TAv4OB59"><br/> <b>自增列持久化</b><br/> MySQL 5.7 以及早期版本,InnoDB 自增列计数器(AUTO_INCREMENT)的值只存储在内存中。MySQL 8.0 每次变化时将自增计数器的最大值写入 redo log,同时在每次检查点将其写入引擎私有的系统表。解决了长期以来的自增字段值可能重复的 bug。</p><p data-pid="pGJAGMEX"><br/> <b>死锁检查控制</b><br/> MySQL 8.0 (MySQL 5.7.15)增加了一个新的动态变量,用于控制系统是否执行 InnoDB 死锁检查。对于高并发的系统,禁用死锁检查可能带来性能的提高。<br/><br/></p><div class="highlight"><pre><code class="language-sql"><span class="n">innodb_deadlock_detect</span></code></pre></div><p class="ztext-empty-paragraph"><br/></p><p data-pid="33J2WYXb"><b>锁定语句选项</b></p><p data-pid="KA1Ji2MG"><br/> SELECT ... FOR SHARE 和 SELECT ... FOR UPDATE 中支持 NOWAIT、SKIP LOCKED 选项。对于 NOWAIT,如果请求的行被其他事务锁定时,语句立即返回。对于 SKIP LOCKED,从返回的结果集中移除被锁定的行。<br/> <b>InnoDB 其他改进功能。</b><br/> </p><ul><li data-pid="s2YogKW8">支持部分快速 DDL,ALTER TABLE ALGORITHM=INSTANT;</li><li data-pid="DeTSAZff">InnoDB 临时表使用共享的临时表空间 ibtmp1。</li><li data-pid="Twi45y-0">新增静态变量 innodb_dedicated_server,自动配置 InnoDB 内存参数:innodb_buffer_pool_size/innodb_log_file_size 等。</li><li data-pid="drrHY2_B">默认创建 2 个 UNDO 表空间,不再使用系统表空间。</li><li data-pid="91kmFj06">支持 ALTER TABLESPACE ... RENAME TO 重命名通用表空间。</li></ul><p class="ztext-empty-paragraph"><br/></p><blockquote data-pid="jThmQDjs">作者:请叫我黄同学<br/>来源:MySQL-MySQL8新特性 - 掘金</blockquote> 
在Windows下安装MySQL官网地址www.mysql.com(以mysql5.7.19为例)1.在官方下载MySQL进入下载页面
有历史其他版本(archives)
2、选择所需要的版本之后看自己是32位还是64位的pc
3、下载完成后对文件进行解压我是在D盘中建立的与Apache和PHP在同一个父级目录下
4.配置环境变量电脑-属性-高级系统设置-环境变量在path环境变量增加MySQL的安装目录\bin
5.在D:\Server\Mysql\mysql-5.7.19-winx64目录下自己创建一个my.ini文件(在MySQL5.7版本后是要自己建立的)
并在my.ini文件中输入以下内容
6.使用管理员身份打开cmd,并切换到D:\Server\Mysql\mysql-5.7.19-winx64\bin目录下,然后再执行mysql -install(一定要使用管理员身份打开)在cmd窗口使用ctrl+鼠标左键点击
切换到安装的目录下
安装(在安装时有可能出现问题,我会在文末添加安装的错误的解决方法)
7、初始化数据库 mysqld –initialize-insecure –user=mysql此时会在安装目录下自动创建一个data目录
data中的内容
8、开始启动mysql【我在启动时也出现了一点问题,同样我会在文末添加解决的方法】指令为 net start mysql
9、进入mysql管理终端 mysql -u root -p 【此时的root密码为空,在password时直接回车即可】
10、修改root密码的修改(注意结尾的分号)
update user set authentication_string=password(’abc123‘)where user=’root‘ and Host=’localhost‘;我对上面这句话的解读:就是修改root密码为abc123执行: flush privileges; 刷新权限quit 退出11、修改my.ini,再次进入对权限的验证【看输入密码能否进入】

MySQL安装成功
接下来是对过程中出现的问题提供的解决方案1.安装成功后无法启动【如下图所示】
解决方案:去微软官方下载下载visual studio 2013(vc++12.0)网址:learn.microsoft.com在网页中寻找c++,在点击文档,搜索程序包
下载安装此版本【安装即可】
2.MySQL安装后无法启动,解决方案【出现蓝框框中的情况,使用红框中的指令】

执行 tasklist | findstr “mysql”产看是否还有mysql进程如果有执行taskkill/f /t /im mysqld.exte杀死进程之后执行 net start mysql
爱可生云数据库:MySQL 8.0 支持对单个数据库设置只读!爱可生云数据库:mysqldump 备份产生大量慢查询,有办法过滤么?系统表全部换成事务型的innodb表,默认的MySQL实例将不包含任何MyISAM表,除非手动创建MyISAM表。InnoDB表的DDL支持事务完整性,要么成功要么回滚,将DDL操作回滚日志写入到data dictionary 数据字典表 mysql.innodb_ddl_log 中用于回滚操作,该表是隐藏的表,通过show tables无法看到。通过设置参数,可将ddl操作日志打印输出到mysql错误日志中。
mysql&gt; set global log_error_verbosity=3; mysql&gt; set global innodb_print_ddl_logs=1; 
只有在 MySQL 8.0.12 以上的版本才支持
讯享网mysql&gt; show create table sbtest1; CREATE TABLE sbtest1 ( id int NOT NULL AUTO_INCREMENT, k int NOT NULL DEFAULT ’0‘, c char(120) NOT NULL DEFAULT ’‘, pad char(60) NOT NULL DEFAULT ’‘, d int NOT NULL DEFAULT ’0‘, PRIMARY KEY (id), KEY k_1 (k) ) ENGINE=InnoDB AUTO_INCREMENT= DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec) mysql&gt; alter table sbtest1 drop column d ; Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql&gt; mysql&gt; insert into sbtest1(k,c,pad) select k,c,pad from sbtest1; Query OK, rows affected (19.61 sec) Records: Duplicates: 0 Warnings: 0 mysql&gt; insert into sbtest1(k,c,pad) select k,c,pad from sbtest1; Query OK, rows affected (38.25 sec) Records: Duplicates: 0 Warnings: 0 mysql&gt; insert into sbtest1(k,c,pad) select k,c,pad from sbtest1; Query OK, rows affected (1 min 14.51 sec) Records: Duplicates: 0 Warnings: 0 mysql&gt; select count() from sbtest1; +———-+ | count() | +———-+ | | +———-+ 1 row in set (0.31 sec) mysql&gt; alter table sbtest1 add column d int not null default 0; Query OK, 0 rows affected (1.22 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql&gt; alter table sbtest1 add column e int not null default 0; Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0 
CTE(Common Table Expression)可以认为是派生表(derived table)的替代,在一定程度上,CTE简化了复杂的join查询和子查询,另外CTE可以很方便地实现递归查询,提高了SQL的可读性和执行性能。CTE是ANSI SQL 99标准的一部分,在MySQL 8.0.1版本被引入。
  • 查询语句的可读性更好
  • 在一个查询中,可以被引用多次
  • 能够链接多个CTE
  • 能够创建递归查询
  • 能够提高SQL执行性能
  • 能够有效地替代视图
在8.0版本之前,默认字符集为latin1,utf8指向的是utf8mb3,8.0版本默认字符集为utf8mb4,utf8默认指向的也是utf8mb4。MySQL 8.0 clone插件提供从一个实例克隆出另外一个实例的功能,克隆功能提供了更有效的方式来快速创建MySQL实例,搭建主从复制和组复制。MySQL 8.0新增了一个资源组功能,用于调控线程优先级以及绑定CPU核。MySQL用户需要有 RESOURCE_GROUP_ADMIN权限才能创建、修改、删除资源组。在Linux环境下,MySQL进程需要有 CAP_SYS_NICE 权限才能使用资源组完整功能。角色可以认为是一些权限的集合,为用户赋予统一的角色,权限的修改直接通过角色来进行,无需为每个用户单独授权。
# 创建角色 mysql&gt;create role role_test; QueryOK, 0rows affected (0.03sec) 

给角色授予权限

mysql&gt;grant select on db.*to ’role_test‘; QueryOK, 0rows affected (0.10sec)

创建用户

mysql&gt;create user ’read_user‘@’%‘identified by ’‘; QueryOK, 0rows affected (0.09sec)

给用户赋予角色

mysql&gt;grant ’role_test‘to ’read_user‘@’%‘; QueryOK, 0rows affected (0.02sec)

给角色role_test增加insert权限

mysql&gt;grant insert on db.*to ’role_test‘; QueryOK, 0rows affected (0.08sec)

给角色role_test删除insert权限

mysql&gt;revoke insert on db.*from ’role_test‘; QueryOK, 0rows affected (0.10sec)

查看默认角色信息

mysql&gt;select * from mysql.default_roles;

查看角色与用户关系

mysql&gt;select * from mysql.role_edges;

删除角色

mysql&gt;drop role role_test;
从 MySQL 8.0.17 开始,InnoDB 支持创建多值索引,这是在存储值数组的 JSON 列上定义的二级索引,单个数据记录可以有多个索引记录。这样的索引使用关键部分定义,例如 CAST(data-&gt;’$.zipcode’ AS UNSIGNED ARRAY)。MySQL 优化器自动使用多值索引来进行合适的查询,可以在 EXPLAIN 的输出中查看。MySQL 8.0.13 以及更高版本支持函数索引(functional key parts),也就是将表达式的值作为索引的内容,而不是列值或列值前缀。将函数作为索引键可以用于索引那些没有在表中直接存储的内容。其实MySQL5.7中推出了虚拟列的功能,而MySQL8.0的函数索引也是依据虚拟列来实现的。
  • 只有那些能够用于计算列的函数才能够用于创建函数索引。
  • 函数索引中不允许使用子查询、参数、变量、存储函数以及自定义函数。
  • SPATIAL 索引和 FULLTEXT索引不支持函数索引。
在MySQL 5.7版本及之前,只能通过显式的方式删除索引。此时,如果发现删除索引后出现错误,又只能通过显式创建索引的方式将删除的索引创建回来。如果数据表中的数据量非常大,或者数据表本身比较大,这种操作就会消耗系统过多的资源,操作成本非常高。从MySQL 8.x开始支持隐藏索引(invisible indexes),只需要将待删除的索引设置为隐藏索引,使查询优化器不再使用这个索引(即使使用force index(强制使用索引),优化器也不会使用该索引), 确认将索引设置为隐藏索引后系统不受任何响应,就可以彻底删除索引。这种通过先将索引设置为隐藏索引,再删除索引的方式就是软删除 。
讯享网mysql&gt; show create table t1\G * 1. row * 
 Table: t1 
Create Table: CREATE TABLE t1 ( c1 int DEFAULT NULL, c2 int DEFAULT NULL, create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_c1 (c1) /*!80000 INVISIBLE */ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)

不可见的情况下是不会走索引的,key=null

mysql&gt; explain select * from t1 where c1=3; +—-+————-+——-+————+——+—————+——+———+——+——+———-+————-+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +—-+————-+——-+————+——+—————+——+———+——+——+———-+————-+ | 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where | +—-+————-+——-+————+——+—————+——+———+——+——+———-+————-+ 1 row in set, 1 warning (0.00 sec)

设置为索引可见,

mysql&gt; alter table t1 alter index idx_c1 visible; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql&gt; show create table t1\G * 1. row *
讯享网 Table: t1 
Create Table: CREATE TABLE t1 ( c1 int DEFAULT NULL, c2 int DEFAULT NULL, create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_c1 (c1) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)

可以走索引,key=idx_c1

mysql&gt; explain select * from t1 where c1=3; +—-+————-+——-+————+——+—————+——–+———+——-+——+———-+——-+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +—-+————-+——-+————+——+—————+——–+———+——-+——+———-+——-+ | 1 | SIMPLE | t1 | NULL | ref | idx_c1 | idx_c1 | 5 | const | 1 | 100.00 | NULL | +—-+————-+——-+————+——+—————+——–+———+——-+——+———-+——-+ 1 row in set, 1 warning (0.00 sec)
MySQL在语法上很早就已经支持降序索引,但实际上创建的仍然是升序索引。从8.0开始,实际创建的为降序索引。在sql语法中增加SET_VAR语法,动态调整部分参数,有利于提升语句性能。
select /*+ SET_VAR(sort_buffer_size = 16M) / id from test order id ; insert /+ SET_VAR(foreign_key_checks=OFF) / into test(name) values(1); 
MySQL 8.0版本支持在线修改全局参数并持久化,通过加上PERSIST关键字,可以将修改的参数持久化到新的配置文件(mysqld-auto.cnf)中,重启MySQL时,可以从该配置文件获取到最新的配置参数。例如执行:
讯享网set PERSIST expire_logs_days=10 ; 
系统会在数据目录下生成一个包含json格式的 mysqld-auto.cnf 的文件,格式化后如下所示,当 my.cnf 和 mysqld-auto.cnf 同时存在时,后者具有更高优先级。select … for update,select … for share(8.0新增语法) 添加 NOWAIT、SKIP LOCKED语法,跳过锁等待,或者跳过锁定。在5.7及之前的版本,select…for update,如果获取不到锁,会一直等待,直到innodb_lock_wait_timeout超时。在8.0版本,通过添加nowait,skip locked语法,能够立即返回。如果查询的行已经加锁,那么nowait会立即报错返回,而skip locked也会立即返回,只是返回的结果中不包含被锁定的行。目的是为了兼容sql的标准语法,方便迁移mysql 5.7
mysql&gt; select count(),age from t5 group by age; +———-+——+ | count() | age | +———-+——+ | 1 | 25 | | 1 | 29 | | 1 | 32 | | 1 | 33 | | 1 | 35 | +———-+——+ 5 rows in set (0.00 sec) 
mysql 8.0
讯享网mysql&gt; select count(),age from t5 group by age; +———-+——+ | count() | age | +———-+——+ | 1 | 25 | | 1 | 32 | | 1 | 35 | | 1 | 29 | | 1 | 33 | +———-+——+ 5 rows in set (0.00 sec) 
可以看到,MySQL5.7 在group by中对分组字段进行了隐式排序,而MySQL8.0取消了隐式排序。如果要添加排序的话,需要显示增加,比如 select count(),age from t5 group by age order by age;在8.0之前的版本,自增值是保存在内存中,自增主键AUTO_INCREMENT的值如果大于max(primary key)+1,在MySQL重启后,会重置AUTO_INCREMENT=max(primary key)+1。这种现象在某些情况下会导致业务主键冲突或者其他难以发现的问题。自增主键重启重置的问题很早就被发现(bugs.mysql.com/bug.php?),一直到8.0才被解决。8.0版本将会对AUTO_INCREMENT值进行持久化,MySQL重启后,该值将不会改变。8.0开始,当前最大的自增计数器每当发生变化,值会被写入redo log中,并在每个检查点时候保存到private system table中。这一变化,对AUTO_INCREMENT值进行持久化,MySQL重启后,该值将不会改变。
  • MySQL server重启后不再取消AUTO_INCREMENT = N表选项的效果。如果将自增计数器初始化为特定值,或者将自动递增计数器值更改为更大的值,新的值被持久化,即使服务器重启。
  • 在回滚操作之后立即重启服务器将不再导致重新使用分配给回滚事务的自动递增值。
  • 如果将AUTO_INCREMEN列值修改为大于当前最大自增值(例如,在更新操作中)的值,则新值将被持久化,随后的插入操作将从新的、更大的值开始分配自动增量值。
– 确认下自己的版本 select VERSION() /* VERSION() | ———-+ 5.7.26-log| */ – 创建表 create table testincr( id int auto_increment primary key, name varchar(50) ) – 插入数据 insert into testincr(name) values (’刘备‘), (’关羽‘), (’张飞‘); – 查看当前的自增量 select t.AUTO_INCREMENT from information_schema.TABLES t where TABLE_NAME =’testincr‘ /* AUTO_INCREMENT| ————–+ 
讯享网 4| 
*/ – 更改列值 update testincr set id=4 where id=3 – 查看现在的表值 /* id|name| –+—-+ 1|刘备 | 2|关羽 | 4|张飞 | / – 插入新值 问题出现 insert into testincr(name) values(’赵云‘); / SQL 错误 [1062] [23000]: Duplicate entry ’4‘ for key ’PRIMARY‘ */ – 如果我们再次插入,它就是正常的,因为id到5了。。。 mysql&gt; insert into testincr(name) values(’赵云‘); Query OK, 1 row affected (0.01 sec)
MySQL 8.0.20 版本增加了binlog日志事务压缩功能,将事务信息使用zstd算法进行压缩,然后再写入binlog日志文件,这种被压缩后的事务信息,在binlog中对应为一个新的event类型,叫做Transaction_payload_event。MySQL 8.0 对于分区表功能进行了较大的修改,在 8.0 之前,分区表在Server层实现,支持多种存储引擎,从 8.0 版本开始,分区表功能移到引擎层实现,目前MySQL 8.0 版本只有InnoDB存储引擎支持分区表。将innodb_dedicated_server开启的时候,它可以自动的调整下面这四个参数的值:
innodb_buffer_pool_size 总内存大小 innodb_log_file_size redo文件大小 innodb_log_files_in_group redo文件数量 innodb_flush_method 数据刷新方法 
只需将innodb_dedicated_server = ON 设置好,上面四个参数会自动调整,解决非专业人员安装数据库后默认初始化数据库参数默认值偏低的问题,让MySQL自适应的调整上面四个参数,前提是服务器是专用来给MySQL数据库的,如果还有其他软件或者资源或者多实例MySQL使用,不建议开启该参数,本文以MySQL8.0.19为例。那么按照什么规则调整呢?MySQL官方给出了相关参数调整规则如下:(1). innodb_buffer_pool_size自动调整规则:
专用服务器内存大小 buffer_pool_size大小
小于1G 128MB (MySQL缺省值)
1G to 4G OS内存*0.5
大于4G OS内存*0.75
(2). innodb_log_file_size自动调整规则:
buffer_pool_size大小 log_file_size 大小
小于8G 512MB
8G to 128G 1024MB
大于128G 2048MB
(3). innodb_log_files_in_group自动调整规则:(innodb_log_files_in_group值就是log file的数量)
buffer_pool_size大小 log file数量
小于8G ROUND(buffer pool size)
8G to 128G ROUND(buffer pool size * 0.75)
大于128G 64
说明:如果ROUND(buffer pool size)值小于2GB,那么innodb_log_files_in_group会强制设置为2。(4). innodb_flush_method自动调整规则:该参数调整规则直接引用官方文档的解释:The flush method is set to O_DIRECT_NO_FSYNC when innodb_dedicated_server is enabled. If the O_DIRECT_NO_FSYNC setting is not available, the default innodb_flush_method setting is used. 如果系统允许设置为O_DIRECT_NO_FSYNC;如果系统不允许,则设置为InnoDB默认的Flush method。
  • 自动调整,简单方便,让DBA更省心
  • 自带优化光环:没有该参数前,innodb_buffer_pool_size和log_file_size默认安装初始化后只有128M和48M,这对于一个生产环境来说是远远不够的,通常DBA都会手工根据服务器的硬件配置来调整优化,该参数出现后基本上可以解决入门人员安装MySQL后的性能问题。
  • 云厂商,虚拟化等动态资源扩容或者缩容后,不必再操心MySQL参数配置问题。
  • 专门给MySQL独立使用的服务器
  • 单机多实例的情况不适用
  • 服务器上还跑着其他软件或应用的情况不适用
从 MySQL 8.0 开始,新增了一个叫窗口函数的概念。什么叫窗口?它可以理解为记录集合,窗口函数也就是在满足某种条件的记录集合上执行的特殊函数。对于每条记录都要在此窗口内执行函数,有的函数随着记录不同,窗口大小都是固定的,这种属于静态窗口;有的函数则相反,不同的记录对应着不同的窗口,这种动态变化的窗口叫滑动窗口。它可以用来实现若干新的查询方式。窗口函数与 SUM()、COUNT() 这种聚合函数类似,但它不会将多行查询结果合并为一行,而是将结果放回多行当中。即窗口函数不需要 GROUP BY。
窗口函数内容太多,后期我会专门写一篇文章介绍窗口函数当遇到索引树损坏时,InnoDB会在redo日志中写入一个损坏标志,这会使损坏标志安全崩溃。InnoDB还将内存损坏标志数据写入每个检查点的私有系统表中。在恢复的过程中,InnoDB会从这两个位置读取损坏标志,并合并结果,然后将内存中的表和索引对象标记为损坏。InnoDB memcached插件支持批量get操作(在一个memcached查询中获取多个键值对)和范围查询。减少客户端和服务器之间的通信流量,在单个memcached查询中获取多个键、值对的功能可以提高读取性能。从 MySQL 8.0.12 开始(仅仅指InnoDB引擎),以下 ALTER TABLE 操作支持 ALGORITHM=INSTANT:
  • 添加列。此功能也称为“即时添加列”。限制适用。
  • 添加或删除虚拟列。
  • 添加或删除列默认值。
  • 修改 ENUM 或 SET 列的定义。
  • 更改索引类型。
  • 重命名表。
Online DDL的好处:支持 ALGORITHM=INSTANT 的操作只修改数据字典中的元数据。表上没有元数据锁,表数据不受影响,操作是即时的,并不会造成业务抖动。这在一些服务级别要求比较高(7*24)的系统中,是非常方便的。该特性是由腾讯游戏DBA团队贡献的。如果未明确指定,则支持它的操作默认使用 ALGORITHM=INSTANT。如果指定了 ALGORITHM=INSTANT 但不受支持,则操作会立即失败并出现错误。需要注意的是,在 MySQL 8.0.29 之前,一列只能作为表的最后一列添加。不支持将列添加到其他列中的任何其他位置。从 MySQL 8.0.29 开始,可以将即时添加的列添加到表中的任何位置。Explain 是我们常用的查询分析工具,可以对查询语句的执行方式进行评估,给出很多有用的线索。但他仅仅是评估,不是实际的执行情况,比如结果中的 rows,可能和实际结果相差甚大。Explain Analyze 是 MySQL 8 中提供的新工具,可贵之处在于可以给出实际执行情况。Explain Analyze 是一个查询性能分析工具,可以详细的显示出 查询语句执行过程中,都在哪儿花费了多少时间。Explain Analyze 会做出查询计划,并且会实际执行,以测量出查询计划中各个关键点的实际指标,例如耗时、条数,最后详细的打印出来。这项新功能建立在常规的EXPLAIN基础之上,可以看作是MySQL 8.0之前添加的EXPLAIN FORMAT = TREE的扩展。EXPLAIN除了输出查询计划和估计成本之外,EXPLAIN ANALYZE还会输出执行计划中各个迭代器的实际成本。InnoDB ReplicaSet 由一个主节点和多个从节点构成. 可以使用MySQL Shell的ReplicaSet对象和AdminAPI操作管理复制集, 例如检查InnoDB复制集的状态, 并在发生故障时手动故障转移到新的主服务器。ReplicaSet 所有的节点必须基于GTID,并且数据复制采用异步的方式。使用复制集还可以接管既有的主从复制,但是需要注意,一旦被接管,只能通过AdminAPI对其进行管理。在MySQL 8.0中,引入了一个轻量级的备份锁,这个锁可以保证备份一致性,而且阻塞的操作相对比较少,是一个非常重要的新特性。在MySQL 8.0中,为了解决备份FTWRL的问题,引入了轻量级的备份锁;可以通过LOCK INSTANCE FOR BACKUP和UNLOCK INSTANCE,以获取和释放备份锁,执行该语句需要BACKUP_ADMIN权限。backup lock不会阻塞读写操作。不过,backup lock会阻塞大部分DDL操作,包括创建/删除表、加/减字段、增/删索引、optimize/analyze/repair table等。总的来说,备份锁还是非常实用的,毕竟其不会影响业务的正常读写;至于备份锁和DDL操作的冲突,还是有很多方法可以避免,比如错开备份和变更的时间、通过pt-online-schema-change/gh-ost避免长时间阻塞等等。随着备份锁的引入,Oracle官方备份工具MEB 8.0和Percona开源备份工具XtraBackup 8.0,也是更新了对backup lock的支持。MySQL 8.0.20 版本增加了binlog日志事务压缩功能,将事务信息使用zstd算法进行压缩,然后再写入binlog日志文件,这种被压缩后的事务信息,在binlog中对应为一个新的event类型,叫做Transaction_payload_event。
MySQL 在 8.0.3 版本引入了新的事务调度算法,基于竞争感知的事务调度,Contention-Aware Transaction Scheduling,简称CATS。在CATS算法之前,MySQL使用FIFO算法,先到的事务先获得锁,如果发生锁等待,则按照FIFO算法进行排队。CATS相比FIFO更加复杂,也更加聪明,在高负载、高争用的场景下,性能提升显著。总的来说MySQL关于并行复制到目前为止经历过三个比较关键的时间节点“库间并发”,“组提交”,“写集合”;真可谓是江山代有人才出,前浪死在沙滩上;总的来说就后面的比前面的不知道高到哪里去了!MySQL 5.7.22 版本引入了一个新的机制 WriteSet,来追踪事务之间的依赖性,这个特性被用于优化从库应用binlog的速度,在主库并发较低的场景下,能够显著提高从库回放binlog的速度,基于WriteSet 的并行复制方案,彻底解决了MySQL复制延迟问题。只需要设置这2个参数即可
讯享网binlog_transaction_dependency_tracking = WRITESET # COMMIT_ORDER
transaction_write_set_extraction = XXHASH64
MySQL 8 大幅改进了对 JSON 的支持,添加了基于路径查询参数从 JSON 字段中抽取数据的 JSON_EXTRACT() 函数,以及用于将数据分别组合到 JSON 数组和对象中的 JSON_ARRAYAGG() 和 JSON_OBJECTAGG() 聚合函数。在主从复制中,新增参数 binlog_row_value_options,控制JSON数据的传输方式,允许对于Json类型部分修改,在binlog中只记录修改的部分,减少json大数据在只有少量修改的情况下,对资源的占用。MySQL 8 大幅改进了空间数据类型和函数,支持更多的空间分析函数和空间类型对象,空间分析功能和性能得到大幅提升。在MySQL 8.0.20 版本之前,doublewrite 存储区位于系统表空间,从 8.0.20 版本开始,doublewrite 有自己独立的表空间文件,这种变更,能够降低doublewrite的写入延迟,增加吞吐量,为设置doublewrite文件的存放位置提供了更高的灵活性。MySQL 8.0.18 版本引入 hash join 功能,对于没有走索引的等值 join 连接可以使用 hash join 进行优化。8.0.20 版本对 hash join 进行了加强,即使 join 连接没有使用等值条件也可以使用 hash join 优化,原来使用 BNL 算法的 join 连接将全部由 hash join 代替。简单来说,就是双重循环,遍历外表(驱动表),对于外表的每一行记录,然后遍历内表,然后判断join条件是否符合,进而确定是否将记录吐出给上一个执行节点。从算法角度来说,这是一个M*N的复杂度。是针对equal-join场景的优化,基本思想是,将外表数据load到内存,并建立hash表,这样只需要遍历一遍内表,就可以完成join操作,输出匹配的记录。如果数据能全部load到内存当然好,逻辑也简单,一般称这种join为CHJ(Classic Hash Join),之前MariaDB就已经实现了这种HashJoin算法。如果数据不能全部load到内存,就需要分批load进内存,然后分批join,下面具体介绍这几种join算法的实现。MySQL 8.0.17版本引入了一个anti join的优化,这个优化能够将where条件中的not in(subquery), not exists(subquery),in(subquery) is not true,exists(subquery) is not true,在内部转化成一个anti join,以便移除里面的子查询subquery,这个优化在某些场景下,能够将性能提升20%左右。anti join适用的场景案例通常如下:
  • 找出在集合A且不在集合B中的数据
  • 找出在当前季度里没有购买商品的客户
  • 找出今年没有通过考试的学生
  • 找出过去3年,某个医生的病人中没有进行医学检查的部分
mysql8.0一个新特性就是redo log提交的无锁化。在8.0以前,各个用户线程都是通过互斥量竞争,串行的写log buffer,因此能保证lsn的顺序无间隔增长。mysql8.0通过redo log无锁化,解决了用户线程写redo log时竞争锁带来的性能影响。同时将redo log写文件、redo log刷盘从用户线程中剥离出来,抽成单独的线程,用户线程只负责将redo log写入到log buffer,不再关心redo log的落盘细节,只需等待log_writer线程或log_flusher线程的通知。优化器会利用column_statistics的数据,判断字段的值的分布,得到更准确的执行计划。可以通过ANALYZE TABLE table_name [UPDATE HISTOGRAM on colume_name with N BUCKETS |DROP HISTOGRAM ON clo_name] 来收集或者删除直方图信息。直方图统计了表中某些字段的数据分布情况,为优化选择高效的执行计划提供参考,直方图与索引有着本质的区别,维护一个索引有代价。每一次的insert、update、delete都需要更新索引,会对性能有一定的影响。而直方图一次创建永不更新,除非明确去更新它,因此不会影响insert、update、delete的性能。从 MySQL 8.0开始,不再使用查询缓存(Query Cache)。随着技术的进步,经过时间的考验,MySQL的工程团队发现启用缓存的好处并不多。首先,查询缓存的效果取决于缓存的命中率,只有命中缓存的查询效果才能有改善,因此无法预测其性能。其次,查询缓存的另一个大问题是它受到单个互斥锁的保护。在具有多个内核的服务器上,大量查询会导致大量的互斥锁争用。MySQL8.0取消查询缓存的另外一个原因是,研究表明,缓存越靠近客户端,获得的好处越大。MySQL8.0新增加了一些其他对性能干预的工具来支持。另外,还有像ProxySQL这样的第三方工具,也可以充当中间缓存。在 5.0.3 版本以后,可以使用一个新的动态变量 innodb_deadlock_detect 来禁用死锁检测。在高并发系统上,当多个线程等待同一个锁时,死锁检测会导致速度变慢。有时,禁用死锁检测并在发生死锁时依靠 innodb_lock_wait_timeout 设置进行事务回滚可能更有效。MySQL 8.0.4 版本修改了默认的身份认证插件,从老的mysql_native_password插件变为新的caching_sha2_password,并将其作为默认的身份认证机制,同时客户端对应的libmysqlclient也默认使用新的认证插件。(1)密码的重复使用策略历史密码重复次数检测:新密码不能与最近最新的5个密码相同。
password_history = 5 ; 
时间间隔:新密码不能和过去90天内的密码相同。
讯享网password_reuse_interval = 90 ; 
(2)修改密码必要的验证策略修改密码,要输入当前的密码。增加了用户的安全性。
 默认为off;为on 时 修改密码需要用户提供当前密码 (开启后修改密码需要验证旧密码,root 用户不需要) password_require_current = on 
(3)双密码相比于一个用户只有一个密码最大优点就是:修改密码不会导致应用不可用。那么应用就可以自动使用副密码(副密码和当前密码保持一致)连接数据库库。确保了业务的不中断。修改密码不会导致应用不可用;应用就可以自动使用副密码连接数据库。MySQL角色是指定权限集合。像用户账户一样,角色可以拥有授予和撤销的权限。可以授予用户账户角色,授予该账户与每个角色相关的权限。方便了用户权限管理和维护。很好地解决了多个用户使用相同的权限集。权限–》角色–》用户。增加以下两个参数,用于控制redo、undo日志的加密。
讯享网innodb_redo_log_encrypt innodb_undo_log_encrypt 
优化器能够感知到页是否存在缓冲池中。5.7其实已经开放接口,但是不对内存中的页进行统计,返回都是1.0。8.0版本对于读写皆有和高写负载的拿捏恰到好处。在集中的读写均有的负载情况下,我们观测到在4个用户并发的情况下,对于高负载,和5.7版本相比有着两倍性能的提高。在5.7上我们显著了提高了只读情况下的性能,8.0则显著提高了读写负载的可扩展性。为MySQL提升了硬件性能的利用率,其改进是基于重新设计了InnoDB写入Redo日志的方法。对比之前用户线程之前互相争抢着写入其数据变更,在新的Redo日志解决方案中,现在Redo日志由于其写入和刷缓存的操作都有专用的线程来处理。用户线程之间不在持有Redo写入相关的锁,整个Redo处理过程都是时间驱动。8.0版本允许马力全开的使用存储设备,比如使用英特尔奥腾闪存盘的时候,我们可以在IO敏感的负载情况下获得1百万的采样 QPS(这里说的IO敏感是指不在IBP中,且必须从二级存储设备中获取)。这个改观是由于我们摆脱了 file_system_mutex全局锁的争用。Better Performance upon High Contention Loads (“hot rows”)8.0版本显著地提升了高争用负载下的性能。高争用负载通常发生在许多事务争用同一行数据的锁,导致了事务等待队列的产生。在实际情景中,负载并不是平稳的,负载可能在特定的时间内爆发(80/20法则)。8.0版本针对短时间的爆发负载无论在每秒处理的事务数(换句话,延迟)还是95%延迟上都处理的更好。对于终端用户来说体现在更好的硬件资源利用率(效率)上。因为系统需要尽量使用榨尽硬件性能,才可以提供更高的平均负载。通过加上PERSIST关键字,可以将修改的参数持久化到新的配置文件(mysqld-auto.cnf)中,重启MySQL时,可以从该配置文件获取到最新的配置参数。系统会在数据目录下生成mysqld-auto.cnf 文件,该文件内容是以json格式存储的。当my.cnf 和mysqld-auto.cnf 同时存在时,后者优先级更高。例如:
SET PERSIST max_connections = 1000; SET @@PERSIST.max_connections = 1000; 
此 SET 语法使您能够在运行时进行配置更改,这些更改也会在服务器重新启动后持续存在。与 SET GLOBAL 一样,SET PERSIST 设置全局变量运行时值,但也将变量设置写入 mysqld-auto.cnf 文件(如果存在则替换任何现有变量设置)。之前是天,并且参数名称发生变化. 在8.0版本之前,binlog日志过期时间设置都是设置expire_logs_days参数,而在8.0版本中,MySQL默认使用binlog_expire_logs_seconds参数。innodb_undo_log_truncate参数在8.0.2版本默认值由OFF变为ON,默认开启undo日志表空间自动回收。innodb_undo_tablespaces参数在8.0.2版本默认为2,当一个undo表空间被回收时,还有另外一个提供正常服务。innodb_max_undo_log_size参数定义了undo表空间回收的最大值,当undo表空间超过这个值,该表空间被标记为可回收。8.0 版本提供对地形的支持,其中包括了对空间参照系的数据源信息的支持,SRS aware spatial数据类型,空间索引,空间函数。总而言之,8.0版本可以理解地球表面的经纬度信息,而且可以在任意受支持的5000个空间参照系中计算地球上任意两点之间的距离。
讯享网注意:升级前,一定要验证jdbc驱动是否匹配,是否需要随着升级。 
select @@optimizer_switch \G mysql&gt; select @@optimizer_switch \G * 1. row * @@optimizer_switch: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=off,skip_scan=on,hash_join=on,subquery_to_derived=off,prefer_ordering_index=on,hypergraph_optimizer=off,derived_condition_pushdown=on session 开关 set session optimizer_switch=”use_invisible_indexes=off“;
set session optimizer_switch=”use_invisible_indexes=on“;
global 开关 set global optimizer_switch=”use_invisible_indexes=off“;
set global optimizer_switch=”use_invisible_indexes=on“;
在MySQL数据库中,自增ID(Auto Increment)是一种常见的用于生成唯一标识符的方式。它可以让我们方便地为每条记录分配一个独立的、递增的整数值。
1. 自增ID的使用自增ID通常用于作为表的主键(Primary Key),以确保数据表中每条记录都有一个唯一标识。利用自增ID,我们可以快速定位和修改特定记录,同时提高查询和索引效率。2. 实现逻辑在MySQL中,实现自增ID通常通过两种方式:使用AUTO_INCREMENT关键字或者使用序列(Sequence)。2.1 使用AUTO_INCREMENT当我们在创建表时定义一个整数类型字段,并设置其属性为AUTO_INCREMENT时,MySQL会自动为该字段生成一个唯一递增的值。例如,下面是创建一个名为users的表,并将字段id设置为自增ID:sql</p><p data-pid="AifBZLF3">CREATE TABLE users (</p><p data-pid="qcHJvoSb"> id INT AUTO_INCREMENT PRIMARY KEY,</p><p data-pid="GcgEBseD"> name VARCHAR(50) NOT NULL,</p><p data-pid="Q347Zc3C"> age INT</p><p data-pid="uxFdcdqd">);</p><p data-pid="S6dh8OE_">在执行插入操作时,如果不指定该字段的值,MySQL就会根据当前已存在记录中最大的自增ID来设置新纪录的ID值。这样就确保了每个新记录的ID都是唯一递增的。
2.2 使用序列(Sequence)除了使用AUTO_INCREMENT关键字外,MySQL还提供了序列(Sequence)功能来实现自增ID。通过使用序列,我们可以创建一个独立的对象,该对象会生成一个连续的整数序列。我们可以在插入数据时调用该序列来获取下一个值,从而实现自增ID的效果。sql</p><p data-pid="c55umei4">-- 创建序列</p><p data-pid="U7k7bLPH">CREATE SEQUENCE seq_id START WITH 1 INCREMENT BY 1;</p><p data-pid="rSBkdrEJ">-- 插入记录并获取下一个ID值</p><p data-pid="Viw2tR2f">INSERT INTO users (id, name, age) VALUES (NEXT VALUE FOR seq_id, &#39;John&#39;, 25);</p><p data-pid="MvJuGwvY">3. 注意事项在使用自增ID时,需要注意以下几点:- 自增ID只能用于整数类型字段。- 自增ID不保证绝对连续递增,可能会因为删除或回滚等操作导致间断。- 自增ID是表级别的,在不同表中的自增ID可以重复。- 当达到整数类型的最大值时,自增ID会发生溢出并重新开始。
总结:MySQL中实现自增ID有两种主要方式:使用AUTO_INCREMENT关键字和使用序列(Sequence)。无论采用哪种方式,都可以方便地为每条记录生成唯一递增的标识符。但需要注意,在使用过程中需考虑一些限制和注意事项。 - mysql 会为每个表维护一个自增计数器,用来记录下一个自增 id 的值。这个计数器的初始值可以通过 AUTO_INCREMENT 选项来设置,如果没有设置,那么默认为 1。- 当插入一条新记录时,mysql 会检查是否有指定自增 id 的值,如果有,那么就使用指定的值,如果没有,那么就使用计数器的值,并将计数器 +1。- 如果指定的自增 id 的值大于计数器的值,那么 mysql 会更新计数器的值为指定值 +1。如果指定的自增 id 的值小于或等于计数器的值,那么 mysql 不会更新计数器的值。- 如果插入多条新记录时,mysql 会按照顺序分配自增 id 的值,从计数器的值开始,依次 +1。如果有指定自增 id 的值,那么同样会按照上面的规则处理。- 如果删除一条或多条记录时,mysql 不会影响自增计数器的值。也就是说,删除记录不会回收自增 id 的值。- 如果修改一条记录的自增 id 的值,那么 mysql 会按照上面的规则处理。如果修改多条记录的自增 id 的值,那么 mysql 会按照顺序处理,从最小的自增 id 开始。 MySQL 8.0.28引入的新功能,即支持监控统计并限制各个连接(会话)的内存消耗,避免大量用户连接因为执行垃圾SQL消耗过多内存,造成可能被OOM kill的风险。一定程度上,避免了开发层面乱用SQL导致数据库问题的场景,很像Oracle的资源管理器。

讯享网mysql&gt; show global status like ’Global_connection_memory‘; +————————–+———+ | Variable_name | Value | +————————–+———+ | Global_connection_memory |  | +————————–+———+ 


mysql&gt; select @@global.connection_memory_limit; +———————————-+ | @@global.connection_memory_limit | +———————————-+ |  | +———————————-+ mysql&gt; select count© from t group by c; ERROR 4082 (HY000): Connection closed. Connection memory limit bytes exceeded. Consumed bytes. 


讯享网mysql&gt; select count© from t group by c; show global status like ’Global_connection_memory‘; show session status like ’Global_connection_memory‘; +———-+ | count© | +———-+ | 2 | +———-+ 1 row in set (0.04 sec) +————————–+———+ | Variable_name | Value | +————————–+———+ | Global_connection_memory | | +————————–+———+ 1 row in set (0.00 sec) 
前面提到一点,只有普通用户执行SQL才会受到内存使用上限约束,如果是用root用户执行同一条SQL,则不受限制,
mysql&gt; select user(); +—————-+ | user() | +—————-+ | root@localhost | +—————-+ 1 row in set (0.00 sec) mysql&gt; select @@global.connection_memory_limit; +———————————-+ | @@global.connection_memory_limit | +———————————-+ | | +———————————-+ 1 row in set (0.00 sec) mysql&gt; select count© from t group by c; +———-+ | count© | +———-+ | 2 | +———-+ 1 row in set (0.05 sec) 
所以不能频繁用root等具备SUPER权限的用户执行需要大内存的SQL,避免被OOM kill。另外,选项connection_memory_chunk_size如果设置太小,则会频繁更新内存统计,对系统性能也会有影响;但也不建议设置太大,否则可能因为更新不及时而引发OOM问题,大部分情况下采用默认值即可。综上,假设有个服务器物理内存是96GB,建议考虑做如下分配,

在上述规划中,设置了每个会话中,普通用户执行的SQL消耗内存不能超过96MB,所有会话消耗的内存总量不超过12GB,约可最高支撑128个并发连接;此外,innodb buffer pool + 各会话内存的和是76G,约为物理内存的80%,已给系统预留出基本充足的剩余内存,降低发生SWAP的风险。但这里可能存在一个前提,就是要充分能预估好SQL可能的正常消耗,避免因为设置了不合理的资源上限导致执行异常,所以很多功能,就像双刃剑,得正确使用,才可以充分发挥积极的作用。 爱可生云数据库:MySQL 8.0 支持对单个数据库设置只读!爱可生云数据库:mysqldump 备份产生大量慢查询,有办法过滤么?系统表全部换成事务型的innodb表,默认的MySQL实例将不包含任何MyISAM表,除非手动创建MyISAM表。InnoDB表的DDL支持事务完整性,要么成功要么回滚,将DDL操作回滚日志写入到data dictionary 数据字典表 mysql.innodb_ddl_log 中用于回滚操作,该表是隐藏的表,通过show tables无法看到。通过设置参数,可将ddl操作日志打印输出到mysql错误日志中。
讯享网mysql&gt; set global log_error_verbosity=3; mysql&gt; set global innodb_print_ddl_logs=1; 
只有在 MySQL 8.0.12 以上的版本才支持
mysql&gt; show create table sbtest1; CREATE TABLE sbtest1 ( id int NOT NULL AUTO_INCREMENT, k int NOT NULL DEFAULT ’0‘, c char(120) NOT NULL DEFAULT ’‘, pad char(60) NOT NULL DEFAULT ’‘, d int NOT NULL DEFAULT ’0‘, PRIMARY KEY (id), KEY k_1 (k) ) ENGINE=InnoDB AUTO_INCREMENT= DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec) mysql&gt; alter table sbtest1 drop column d ; Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql&gt; mysql&gt; insert into sbtest1(k,c,pad) select k,c,pad from sbtest1; Query OK, rows affected (19.61 sec) Records: Duplicates: 0 Warnings: 0 mysql&gt; insert into sbtest1(k,c,pad) select k,c,pad from sbtest1; Query OK, rows affected (38.25 sec) Records: Duplicates: 0 Warnings: 0 mysql&gt; insert into sbtest1(k,c,pad) select k,c,pad from sbtest1; Query OK, rows affected (1 min 14.51 sec) Records: Duplicates: 0 Warnings: 0 mysql&gt; select count() from sbtest1; +———-+ | count() | +———-+ | | +———-+ 1 row in set (0.31 sec) mysql&gt; alter table sbtest1 add column d int not null default 0; Query OK, 0 rows affected (1.22 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql&gt; alter table sbtest1 add column e int not null default 0; Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0 
CTE(Common Table Expression)可以认为是派生表(derived table)的替代,在一定程度上,CTE简化了复杂的join查询和子查询,另外CTE可以很方便地实现递归查询,提高了SQL的可读性和执行性能。CTE是ANSI SQL 99标准的一部分,在MySQL 8.0.1版本被引入。
  • 查询语句的可读性更好
  • 在一个查询中,可以被引用多次
  • 能够链接多个CTE
  • 能够创建递归查询
  • 能够提高SQL执行性能
  • 能够有效地替代视图
在8.0版本之前,默认字符集为latin1,utf8指向的是utf8mb3,8.0版本默认字符集为utf8mb4,utf8默认指向的也是utf8mb4。MySQL 8.0 clone插件提供从一个实例克隆出另外一个实例的功能,克隆功能提供了更有效的方式来快速创建MySQL实例,搭建主从复制和组复制。MySQL 8.0新增了一个资源组功能,用于调控线程优先级以及绑定CPU核。MySQL用户需要有 RESOURCE_GROUP_ADMIN权限才能创建、修改、删除资源组。在Linux环境下,MySQL进程需要有 CAP_SYS_NICE 权限才能使用资源组完整功能。角色可以认为是一些权限的集合,为用户赋予统一的角色,权限的修改直接通过角色来进行,无需为每个用户单独授权。
讯享网# 创建角色 mysql&gt;create role role_test; QueryOK, 0rows affected (0.03sec) 

给角色授予权限

mysql&gt;grant select on db.*to ’role_test‘; QueryOK, 0rows affected (0.10sec)

创建用户

mysql&gt;create user ’read_user‘@’%‘identified by ’‘; QueryOK, 0rows affected (0.09sec)

给用户赋予角色

mysql&gt;grant ’role_test‘to ’read_user‘@’%‘; QueryOK, 0rows affected (0.02sec)

给角色role_test增加insert权限

mysql&gt;grant insert on db.*to ’role_test‘; QueryOK, 0rows affected (0.08sec)

给角色role_test删除insert权限

mysql&gt;revoke insert on db.*from ’role_test‘; QueryOK, 0rows affected (0.10sec)

查看默认角色信息

mysql&gt;select * from mysql.default_roles;

查看角色与用户关系

mysql&gt;select * from mysql.role_edges;

删除角色

mysql&gt;drop role role_test;
从 MySQL 8.0.17 开始,InnoDB 支持创建多值索引,这是在存储值数组的 JSON 列上定义的二级索引,单个数据记录可以有多个索引记录。这样的索引使用关键部分定义,例如 CAST(data-&gt;’$.zipcode’ AS UNSIGNED ARRAY)。MySQL 优化器自动使用多值索引来进行合适的查询,可以在 EXPLAIN 的输出中查看。MySQL 8.0.13 以及更高版本支持函数索引(functional key parts),也就是将表达式的值作为索引的内容,而不是列值或列值前缀。将函数作为索引键可以用于索引那些没有在表中直接存储的内容。其实MySQL5.7中推出了虚拟列的功能,而MySQL8.0的函数索引也是依据虚拟列来实现的。
  • 只有那些能够用于计算列的函数才能够用于创建函数索引。
  • 函数索引中不允许使用子查询、参数、变量、存储函数以及自定义函数。
  • SPATIAL 索引和 FULLTEXT索引不支持函数索引。
在MySQL 5.7版本及之前,只能通过显式的方式删除索引。此时,如果发现删除索引后出现错误,又只能通过显式创建索引的方式将删除的索引创建回来。如果数据表中的数据量非常大,或者数据表本身比较大,这种操作就会消耗系统过多的资源,操作成本非常高。从MySQL 8.x开始支持隐藏索引(invisible indexes),只需要将待删除的索引设置为隐藏索引,使查询优化器不再使用这个索引(即使使用force index(强制使用索引),优化器也不会使用该索引), 确认将索引设置为隐藏索引后系统不受任何响应,就可以彻底删除索引。这种通过先将索引设置为隐藏索引,再删除索引的方式就是软删除 。
mysql&gt; show create table t1\G * 1. row * 
讯享网 Table: t1 
Create Table: CREATE TABLE t1 ( c1 int DEFAULT NULL, c2 int DEFAULT NULL, create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_c1 (c1) /*!80000 INVISIBLE */ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)

不可见的情况下是不会走索引的,key=null

mysql&gt; explain select * from t1 where c1=3; +—-+————-+——-+————+——+—————+——+———+——+——+———-+————-+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +—-+————-+——-+————+——+—————+——+———+——+——+———-+————-+ | 1 | SIMPLE | t1 | NULL | ALL | NULL | NULL | NULL | NULL | 5 | 20.00 | Using where | +—-+————-+——-+————+——+—————+——+———+——+——+———-+————-+ 1 row in set, 1 warning (0.00 sec)

设置为索引可见,

mysql&gt; alter table t1 alter index idx_c1 visible; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql&gt; show create table t1\G * 1. row *
 Table: t1 
Create Table: CREATE TABLE t1 ( c1 int DEFAULT NULL, c2 int DEFAULT NULL, create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_c1 (c1) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)

可以走索引,key=idx_c1

mysql&gt; explain select * from t1 where c1=3; +—-+————-+——-+————+——+—————+——–+———+——-+——+———-+——-+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +—-+————-+——-+————+——+—————+——–+———+——-+——+———-+——-+ | 1 | SIMPLE | t1 | NULL | ref | idx_c1 | idx_c1 | 5 | const | 1 | 100.00 | NULL | +—-+————-+——-+————+——+—————+——–+———+——-+——+———-+——-+ 1 row in set, 1 warning (0.00 sec)
MySQL在语法上很早就已经支持降序索引,但实际上创建的仍然是升序索引。从8.0开始,实际创建的为降序索引。在sql语法中增加SET_VAR语法,动态调整部分参数,有利于提升语句性能。
讯享网select /*+ SET_VAR(sort_buffer_size = 16M) / id from test order id ; insert /+ SET_VAR(foreign_key_checks=OFF) */ into test(name) values(1); 
MySQL 8.0版本支持在线修改全局参数并持久化,通过加上PERSIST关键字,可以将修改的参数持久化到新的配置文件(mysqld-auto.cnf)中,重启MySQL时,可以从该配置文件获取到最新的配置参数。例如执行:
set PERSIST expire_logs_days=10 ; 
系统会在数据目录下生成一个包含json格式的 mysqld-auto.cnf 的文件,格式化后如下所示,当 my.cnf 和 mysqld-auto.cnf 同时存在时,后者具有更高优先级。select … for update,select … for share(8.0新增语法) 添加 NOWAIT、SKIP LOCKED语法,跳过锁等待,或者跳过锁定。在5.7及之前的版本,select…for update,如果获取不到锁,会一直等待,直到innodb_lock_wait_timeout超时。在8.0版本,通过添加nowait,skip locked语法,能够立即返回。如果查询的行已经加锁,那么nowait会立即报错返回,而skip locked也会立即返回,只是返回的结果中不包含被锁定的行。目的是为了兼容sql的标准语法,方便迁移mysql 5.7
讯享网mysql&gt; select count(),age from t5 group by age; +———-+——+ | count() | age | +———-+——+ | 1 | 25 | | 1 | 29 | | 1 | 32 | | 1 | 33 | | 1 | 35 | +———-+——+ 5 rows in set (0.00 sec) 
mysql 8.0
mysql&gt; select count(),age from t5 group by age; +———-+——+ | count() | age | +———-+——+ | 1 | 25 | | 1 | 32 | | 1 | 35 | | 1 | 29 | | 1 | 33 | +———-+——+ 5 rows in set (0.00 sec) 
可以看到,MySQL5.7 在group by中对分组字段进行了隐式排序,而MySQL8.0取消了隐式排序。如果要添加排序的话,需要显示增加,比如 select count(),age from t5 group by age order by age;在8.0之前的版本,自增值是保存在内存中,自增主键AUTO_INCREMENT的值如果大于max(primary key)+1,在MySQL重启后,会重置AUTO_INCREMENT=max(primary key)+1。这种现象在某些情况下会导致业务主键冲突或者其他难以发现的问题。自增主键重启重置的问题很早就被发现(bugs.mysql.com/bug.php?),一直到8.0才被解决。8.0版本将会对AUTO_INCREMENT值进行持久化,MySQL重启后,该值将不会改变。8.0开始,当前最大的自增计数器每当发生变化,值会被写入redo log中,并在每个检查点时候保存到private system table中。这一变化,对AUTO_INCREMENT值进行持久化,MySQL重启后,该值将不会改变。
  • MySQL server重启后不再取消AUTO_INCREMENT = N表选项的效果。如果将自增计数器初始化为特定值,或者将自动递增计数器值更改为更大的值,新的值被持久化,即使服务器重启。
  • 在回滚操作之后立即重启服务器将不再导致重新使用分配给回滚事务的自动递增值。
  • 如果将AUTO_INCREMEN列值修改为大于当前最大自增值(例如,在更新操作中)的值,则新值将被持久化,随后的插入操作将从新的、更大的值开始分配自动增量值。
讯享网– 确认下自己的版本 select VERSION() / VERSION() | ———-+ 5.7.26-log| */ – 创建表 create table testincr( id int auto_increment primary key, name varchar(50) ) – 插入数据 insert into testincr(name) values (’刘备‘), (’关羽‘), (’张飞‘); – 查看当前的自增量 select t.AUTO_INCREMENT from information_schema.TABLES t where TABLE_NAME =’testincr‘ /* AUTO_INCREMENT| ————–+ 
 4| 
*/ – 更改列值 update testincr set id=4 where id=3 – 查看现在的表值 /* id|name| –+—-+ 1|刘备 | 2|关羽 | 4|张飞 | / – 插入新值 问题出现 insert into testincr(name) values(’赵云‘); / SQL 错误 [1062] [23000]: Duplicate entry ’4‘ for key ’PRIMARY‘ */ – 如果我们再次插入,它就是正常的,因为id到5了。。。 mysql&gt; insert into testincr(name) values(’赵云‘); Query OK, 1 row affected (0.01 sec)
MySQL 8.0.20 版本增加了binlog日志事务压缩功能,将事务信息使用zstd算法进行压缩,然后再写入binlog日志文件,这种被压缩后的事务信息,在binlog中对应为一个新的event类型,叫做Transaction_payload_event。MySQL 8.0 对于分区表功能进行了较大的修改,在 8.0 之前,分区表在Server层实现,支持多种存储引擎,从 8.0 版本开始,分区表功能移到引擎层实现,目前MySQL 8.0 版本只有InnoDB存储引擎支持分区表。将innodb_dedicated_server开启的时候,它可以自动的调整下面这四个参数的值:
讯享网innodb_buffer_pool_size 总内存大小 innodb_log_file_size redo文件大小 innodb_log_files_in_group redo文件数量 innodb_flush_method 数据刷新方法 
只需将innodb_dedicated_server = ON 设置好,上面四个参数会自动调整,解决非专业人员安装数据库后默认初始化数据库参数默认值偏低的问题,让MySQL自适应的调整上面四个参数,前提是服务器是专用来给MySQL数据库的,如果还有其他软件或者资源或者多实例MySQL使用,不建议开启该参数,本文以MySQL8.0.19为例。那么按照什么规则调整呢?MySQL官方给出了相关参数调整规则如下:(1). innodb_buffer_pool_size自动调整规则:
专用服务器内存大小 buffer_pool_size大小
小于1G 128MB (MySQL缺省值)
1G to 4G OS内存*0.5
大于4G OS内存*0.75

(2). innodb_log_file_size自动调整规则:
buffer_pool_size大小 log_file_size 大小
小于8G 512MB
8G to 128G 1024MB
大于128G 2048MB

(3). innodb_log_files_in_group自动调整规则:(innodb_log_files_in_group值就是log file的数量)
buffer_pool_size大小 log file数量
小于8G ROUND(buffer pool size)
8G to 128G ROUND(buffer pool size * 0.75)
大于128G 64

说明:如果ROUND(buffer pool size)值小于2GB,那么innodb_log_files_in_group会强制设置为2。(4). innodb_flush_method自动调整规则:该参数调整规则直接引用官方文档的解释:The flush method is set to O_DIRECT_NO_FSYNC when innodb_dedicated_server is enabled. If the O_DIRECT_NO_FSYNC setting is not available, the default innodb_flush_method setting is used. 如果系统允许设置为O_DIRECT_NO_FSYNC;如果系统不允许,则设置为InnoDB默认的Flush method。
  • 自动调整,简单方便,让DBA更省心
  • 自带优化光环:没有该参数前,innodb_buffer_pool_size和log_file_size默认安装初始化后只有128M和48M,这对于一个生产环境来说是远远不够的,通常DBA都会手工根据服务器的硬件配置来调整优化,该参数出现后基本上可以解决入门人员安装MySQL后的性能问题。
  • 云厂商,虚拟化等动态资源扩容或者缩容后,不必再操心MySQL参数配置问题。
  • 专门给MySQL独立使用的服务器
  • 单机多实例的情况不适用
  • 服务器上还跑着其他软件或应用的情况不适用
从 MySQL 8.0 开始,新增了一个叫窗口函数的概念。什么叫窗口?它可以理解为记录集合,窗口函数也就是在满足某种条件的记录集合上执行的特殊函数。对于每条记录都要在此窗口内执行函数,有的函数随着记录不同,窗口大小都是固定的,这种属于静态窗口;有的函数则相反,不同的记录对应着不同的窗口,这种动态变化的窗口叫滑动窗口。它可以用来实现若干新的查询方式。窗口函数与 SUM()、COUNT() 这种聚合函数类似,但它不会将多行查询结果合并为一行,而是将结果放回多行当中。即窗口函数不需要 GROUP BY。
窗口函数内容太多,后期我会专门写一篇文章介绍窗口函数当遇到索引树损坏时,InnoDB会在redo日志中写入一个损坏标志,这会使损坏标志安全崩溃。InnoDB还将内存损坏标志数据写入每个检查点的私有系统表中。在恢复的过程中,InnoDB会从这两个位置读取损坏标志,并合并结果,然后将内存中的表和索引对象标记为损坏。InnoDB memcached插件支持批量get操作(在一个memcached查询中获取多个键值对)和范围查询。减少客户端和服务器之间的通信流量,在单个memcached查询中获取多个键、值对的功能可以提高读取性能。从 MySQL 8.0.12 开始(仅仅指InnoDB引擎),以下 ALTER TABLE 操作支持 ALGORITHM=INSTANT:
  • 添加列。此功能也称为“即时添加列”。限制适用。
  • 添加或删除虚拟列。
  • 添加或删除列默认值。
  • 修改 ENUM 或 SET 列的定义。
  • 更改索引类型。
  • 重命名表。
Online DDL的好处:支持 ALGORITHM=INSTANT 的操作只修改数据字典中的元数据。表上没有元数据锁,表数据不受影响,操作是即时的,并不会造成业务抖动。这在一些服务级别要求比较高(7*24)的系统中,是非常方便的。该特性是由腾讯游戏DBA团队贡献的。如果未明确指定,则支持它的操作默认使用 ALGORITHM=INSTANT。如果指定了 ALGORITHM=INSTANT 但不受支持,则操作会立即失败并出现错误。需要注意的是,在 MySQL 8.0.29 之前,一列只能作为表的最后一列添加。不支持将列添加到其他列中的任何其他位置。从 MySQL 8.0.29 开始,可以将即时添加的列添加到表中的任何位置。Explain 是我们常用的查询分析工具,可以对查询语句的执行方式进行评估,给出很多有用的线索。但他仅仅是评估,不是实际的执行情况,比如结果中的 rows,可能和实际结果相差甚大。Explain Analyze 是 MySQL 8 中提供的新工具,可贵之处在于可以给出实际执行情况。Explain Analyze 是一个查询性能分析工具,可以详细的显示出 查询语句执行过程中,都在哪儿花费了多少时间。Explain Analyze 会做出查询计划,并且会实际执行,以测量出查询计划中各个关键点的实际指标,例如耗时、条数,最后详细的打印出来。这项新功能建立在常规的EXPLAIN基础之上,可以看作是MySQL 8.0之前添加的EXPLAIN FORMAT = TREE的扩展。EXPLAIN除了输出查询计划和估计成本之外,EXPLAIN ANALYZE还会输出执行计划中各个迭代器的实际成本。InnoDB ReplicaSet 由一个主节点和多个从节点构成. 可以使用MySQL Shell的ReplicaSet对象和AdminAPI操作管理复制集, 例如检查InnoDB复制集的状态, 并在发生故障时手动故障转移到新的主服务器。ReplicaSet 所有的节点必须基于GTID,并且数据复制采用异步的方式。使用复制集还可以接管既有的主从复制,但是需要注意,一旦被接管,只能通过AdminAPI对其进行管理。在MySQL 8.0中,引入了一个轻量级的备份锁,这个锁可以保证备份一致性,而且阻塞的操作相对比较少,是一个非常重要的新特性。在MySQL 8.0中,为了解决备份FTWRL的问题,引入了轻量级的备份锁;可以通过LOCK INSTANCE FOR BACKUP和UNLOCK INSTANCE,以获取和释放备份锁,执行该语句需要BACKUP_ADMIN权限。backup lock不会阻塞读写操作。不过,backup lock会阻塞大部分DDL操作,包括创建/删除表、加/减字段、增/删索引、optimize/analyze/repair table等。总的来说,备份锁还是非常实用的,毕竟其不会影响业务的正常读写;至于备份锁和DDL操作的冲突,还是有很多方法可以避免,比如错开备份和变更的时间、通过pt-online-schema-change/gh-ost避免长时间阻塞等等。随着备份锁的引入,Oracle官方备份工具MEB 8.0和Percona开源备份工具XtraBackup 8.0,也是更新了对backup lock的支持。MySQL 8.0.20 版本增加了binlog日志事务压缩功能,将事务信息使用zstd算法进行压缩,然后再写入binlog日志文件,这种被压缩后的事务信息,在binlog中对应为一个新的event类型,叫做Transaction_payload_event。
MySQL 在 8.0.3 版本引入了新的事务调度算法,基于竞争感知的事务调度,Contention-Aware Transaction Scheduling,简称CATS。在CATS算法之前,MySQL使用FIFO算法,先到的事务先获得锁,如果发生锁等待,则按照FIFO算法进行排队。CATS相比FIFO更加复杂,也更加聪明,在高负载、高争用的场景下,性能提升显著。总的来说MySQL关于并行复制到目前为止经历过三个比较关键的时间节点“库间并发”,“组提交”,“写集合”;真可谓是江山代有人才出,前浪死在沙滩上;总的来说就后面的比前面的不知道高到哪里去了!MySQL 5.7.22 版本引入了一个新的机制 WriteSet,来追踪事务之间的依赖性,这个特性被用于优化从库应用binlog的速度,在主库并发较低的场景下,能够显著提高从库回放binlog的速度,基于WriteSet 的并行复制方案,彻底解决了MySQL复制延迟问题。只需要设置这2个参数即可
binlog_transaction_dependency_tracking = WRITESET # COMMIT_ORDER
transaction_write_set_extraction = XXHASH64
MySQL 8 大幅改进了对 JSON 的支持,添加了基于路径查询参数从 JSON 字段中抽取数据的 JSON_EXTRACT() 函数,以及用于将数据分别组合到 JSON 数组和对象中的 JSON_ARRAYAGG() 和 JSON_OBJECTAGG() 聚合函数。在主从复制中,新增参数 binlog_row_value_options,控制JSON数据的传输方式,允许对于Json类型部分修改,在binlog中只记录修改的部分,减少json大数据在只有少量修改的情况下,对资源的占用。MySQL 8 大幅改进了空间数据类型和函数,支持更多的空间分析函数和空间类型对象,空间分析功能和性能得到大幅提升。在MySQL 8.0.20 版本之前,doublewrite 存储区位于系统表空间,从 8.0.20 版本开始,doublewrite 有自己独立的表空间文件,这种变更,能够降低doublewrite的写入延迟,增加吞吐量,为设置doublewrite文件的存放位置提供了更高的灵活性。MySQL 8.0.18 版本引入 hash join 功能,对于没有走索引的等值 join 连接可以使用 hash join 进行优化。8.0.20 版本对 hash join 进行了加强,即使 join 连接没有使用等值条件也可以使用 hash join 优化,原来使用 BNL 算法的 join 连接将全部由 hash join 代替。简单来说,就是双重循环,遍历外表(驱动表),对于外表的每一行记录,然后遍历内表,然后判断join条件是否符合,进而确定是否将记录吐出给上一个执行节点。从算法角度来说,这是一个M*N的复杂度。是针对equal-join场景的优化,基本思想是,将外表数据load到内存,并建立hash表,这样只需要遍历一遍内表,就可以完成join操作,输出匹配的记录。如果数据能全部load到内存当然好,逻辑也简单,一般称这种join为CHJ(Classic Hash Join),之前MariaDB就已经实现了这种HashJoin算法。如果数据不能全部load到内存,就需要分批load进内存,然后分批join,下面具体介绍这几种join算法的实现。MySQL 8.0.17版本引入了一个anti join的优化,这个优化能够将where条件中的not in(subquery), not exists(subquery),in(subquery) is not true,exists(subquery) is not true,在内部转化成一个anti join,以便移除里面的子查询subquery,这个优化在某些场景下,能够将性能提升20%左右。anti join适用的场景案例通常如下:
  • 找出在集合A且不在集合B中的数据
  • 找出在当前季度里没有购买商品的客户
  • 找出今年没有通过考试的学生
  • 找出过去3年,某个医生的病人中没有进行医学检查的部分
mysql8.0一个新特性就是redo log提交的无锁化。在8.0以前,各个用户线程都是通过互斥量竞争,串行的写log buffer,因此能保证lsn的顺序无间隔增长。mysql8.0通过redo log无锁化,解决了用户线程写redo log时竞争锁带来的性能影响。同时将redo log写文件、redo log刷盘从用户线程中剥离出来,抽成单独的线程,用户线程只负责将redo log写入到log buffer,不再关心redo log的落盘细节,只需等待log_writer线程或log_flusher线程的通知。优化器会利用column_statistics的数据,判断字段的值的分布,得到更准确的执行计划。可以通过ANALYZE TABLE table_name [UPDATE HISTOGRAM on colume_name with N BUCKETS |DROP HISTOGRAM ON clo_name] 来收集或者删除直方图信息。直方图统计了表中某些字段的数据分布情况,为优化选择高效的执行计划提供参考,直方图与索引有着本质的区别,维护一个索引有代价。每一次的insert、update、delete都需要更新索引,会对性能有一定的影响。而直方图一次创建永不更新,除非明确去更新它,因此不会影响insert、update、delete的性能。从 MySQL 8.0开始,不再使用查询缓存(Query Cache)。随着技术的进步,经过时间的考验,MySQL的工程团队发现启用缓存的好处并不多。首先,查询缓存的效果取决于缓存的命中率,只有命中缓存的查询效果才能有改善,因此无法预测其性能。其次,查询缓存的另一个大问题是它受到单个互斥锁的保护。在具有多个内核的服务器上,大量查询会导致大量的互斥锁争用。MySQL8.0取消查询缓存的另外一个原因是,研究表明,缓存越靠近客户端,获得的好处越大。MySQL8.0新增加了一些其他对性能干预的工具来支持。另外,还有像ProxySQL这样的第三方工具,也可以充当中间缓存。在 5.0.3 版本以后,可以使用一个新的动态变量 innodb_deadlock_detect 来禁用死锁检测。在高并发系统上,当多个线程等待同一个锁时,死锁检测会导致速度变慢。有时,禁用死锁检测并在发生死锁时依靠 innodb_lock_wait_timeout 设置进行事务回滚可能更有效。MySQL 8.0.4 版本修改了默认的身份认证插件,从老的mysql_native_password插件变为新的caching_sha2_password,并将其作为默认的身份认证机制,同时客户端对应的libmysqlclient也默认使用新的认证插件。(1)密码的重复使用策略历史密码重复次数检测:新密码不能与最近最新的5个密码相同。
讯享网password_history = 5 ; 
时间间隔:新密码不能和过去90天内的密码相同。
password_reuse_interval = 90 ; 
(2)修改密码必要的验证策略修改密码,要输入当前的密码。增加了用户的安全性。
讯享网 默认为off;为on 时 修改密码需要用户提供当前密码 (开启后修改密码需要验证旧密码,root 用户不需要) password_require_current = on 
(3)双密码相比于一个用户只有一个密码最大优点就是:修改密码不会导致应用不可用。那么应用就可以自动使用副密码(副密码和当前密码保持一致)连接数据库库。确保了业务的不中断。修改密码不会导致应用不可用;应用就可以自动使用副密码连接数据库。MySQL角色是指定权限集合。像用户账户一样,角色可以拥有授予和撤销的权限。可以授予用户账户角色,授予该账户与每个角色相关的权限。方便了用户权限管理和维护。很好地解决了多个用户使用相同的权限集。权限–》角色–》用户。增加以下两个参数,用于控制redo、undo日志的加密。
innodb_redo_log_encrypt innodb_undo_log_encrypt 
优化器能够感知到页是否存在缓冲池中。5.7其实已经开放接口,但是不对内存中的页进行统计,返回都是1.0。8.0版本对于读写皆有和高写负载的拿捏恰到好处。在集中的读写均有的负载情况下,我们观测到在4个用户并发的情况下,对于高负载,和5.7版本相比有着两倍性能的提高。在5.7上我们显著了提高了只读情况下的性能,8.0则显著提高了读写负载的可扩展性。为MySQL提升了硬件性能的利用率,其改进是基于重新设计了InnoDB写入Redo日志的方法。对比之前用户线程之前互相争抢着写入其数据变更,在新的Redo日志解决方案中,现在Redo日志由于其写入和刷缓存的操作都有专用的线程来处理。用户线程之间不在持有Redo写入相关的锁,整个Redo处理过程都是时间驱动。8.0版本允许马力全开的使用存储设备,比如使用英特尔奥腾闪存盘的时候,我们可以在IO敏感的负载情况下获得1百万的采样 QPS(这里说的IO敏感是指不在IBP中,且必须从二级存储设备中获取)。这个改观是由于我们摆脱了 file_system_mutex全局锁的争用。Better Performance upon High Contention Loads (“hot rows”)8.0版本显著地提升了高争用负载下的性能。高争用负载通常发生在许多事务争用同一行数据的锁,导致了事务等待队列的产生。在实际情景中,负载并不是平稳的,负载可能在特定的时间内爆发(80/20法则)。8.0版本针对短时间的爆发负载无论在每秒处理的事务数(换句话,延迟)还是95%延迟上都处理的更好。对于终端用户来说体现在更好的硬件资源利用率(效率)上。因为系统需要尽量使用榨尽硬件性能,才可以提供更高的平均负载。通过加上PERSIST关键字,可以将修改的参数持久化到新的配置文件(mysqld-auto.cnf)中,重启MySQL时,可以从该配置文件获取到最新的配置参数。系统会在数据目录下生成mysqld-auto.cnf 文件,该文件内容是以json格式存储的。当my.cnf 和mysqld-auto.cnf 同时存在时,后者优先级更高。例如:
讯享网SET PERSIST max_connections = 1000; SET @@PERSIST.max_connections = 1000; 
此 SET 语法使您能够在运行时进行配置更改,这些更改也会在服务器重新启动后持续存在。与 SET GLOBAL 一样,SET PERSIST 设置全局变量运行时值,但也将变量设置写入 mysqld-auto.cnf 文件(如果存在则替换任何现有变量设置)。之前是天,并且参数名称发生变化. 在8.0版本之前,binlog日志过期时间设置都是设置expire_logs_days参数,而在8.0版本中,MySQL默认使用binlog_expire_logs_seconds参数。innodb_undo_log_truncate参数在8.0.2版本默认值由OFF变为ON,默认开启undo日志表空间自动回收。innodb_undo_tablespaces参数在8.0.2版本默认为2,当一个undo表空间被回收时,还有另外一个提供正常服务。innodb_max_undo_log_size参数定义了undo表空间回收的最大值,当undo表空间超过这个值,该表空间被标记为可回收。8.0 版本提供对地形的支持,其中包括了对空间参照系的数据源信息的支持,SRS aware spatial数据类型,空间索引,空间函数。总而言之,8.0版本可以理解地球表面的经纬度信息,而且可以在任意受支持的5000个空间参照系中计算地球上任意两点之间的距离。
注意:升级前,一定要验证jdbc驱动是否匹配,是否需要随着升级。 
讯享网select @@optimizer_switch \G mysql&gt; select @@optimizer_switch \G * 1. row * @@optimizer_switch: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_extensions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=off,skip_scan=on,hash_join=on,subquery_to_derived=off,prefer_ordering_index=on,hypergraph_optimizer=off,derived_condition_pushdown=on session 开关 set session optimizer_switch=”use_invisible_indexes=off“;
set session optimizer_switch=”use_invisible_indexes=on“;
global 开关 set global optimizer_switch=”use_invisible_indexes=off“;
set global optimizer_switch=”use_invisible_indexes=on“;

让我们来听听 MySQL 官方团队的社区经理对 MySQL 8.4 有哪些详细的版本描述:MySQL 8.4 LTS 版本,我们一共修改了 20 个 InnoDB 变量的默认值。
作者:Frederic Descamps,EMEA 和亚太地区的 MySQL 社区经理。于 2016 年 5 月加入 MySQL 社区团队。担任开源和 MySQL 顾问已超过 15 年。最喜欢的主题是高可用和高性能。
本文和封面来源:https://lefred.be,爱可生开源社区翻译。
本文约 2400 字,预计阅读需要 8 分钟。
2024 年 4 月 30 日,MySQL 8.4(第一个 LTS 版)正式发布,也验证了 Oracle 官方在之前宣布的 MySQL 版本发布节奏。目前,MySQL 的发布模型分为两个主要路径:LTS 版(长期支持)和创新版。所有 LTS 和创新版本都包含错误和安全修复,并被视为生产级质量。更多MySQL 版本介绍

  • 需要稳定的功能和更长的支持期。
  • 除了第一个 LTS 版本删除了一些功能,其他版本仅包含必要的修复,不在删除功能。
  • LTS 版本遵循 Oracle 终身支持政策(5 年主要支持和 3 年延长支持)。
  • 想了解最新功能、改进。适合快节奏开发环境中的开发和 DBA,具有更高水平的自动化测试和现代持续集成技术,可实现更快的升级周期。
  • 除新功能外,随着代码重构、删除不推荐功能以及修改 MySQL 使其更符合 SQL 标准(在 LTS 版本中不会发生)。
  • 支持至下一个创新版。


请参考 Oracle 官方提供的 MySQL 各版本生命周期计划(),以更好地安排您生产环境的 MySQL 版本选择。以下内容为 MySQL 社区经理 Frederic Descamps 对该版本中 InnoDB 参数默认值修改的详细介绍。发布于当地时间 2024 年 5 月 1 日
昨天(4/30),MySQL 的第一个 LTS 版本 MySQL 8.4 发布了。许多弃用的内容最终被删除,并且几个 InnoDB 变量默认值已被修改以匹配当前的工作负载和硬件规格。有 20 个 InnoDB 变量的默认值已被修改!让我们看一下这些变量并解释这样修改的原因:MADV_DONTDUMP 是 Linux 3.4 及更高版本中支持的宏(存在“ sys/mman.h”头文件并包含符号 MADV_DONTDUMP,一个 madvise() 的 non-POSIX 扩展),Windows 系统或大多数 MacOS 系统不支持此宏。总之,这意味着默认情况下,在 Linux 系统上,缓冲池的内容不会转储到核心文件中。旧值 8 在某些系统上可能太大。手册中包含 BP 大小计算的好示例,请参阅 配置 InnoDB 缓冲池大小。Change Buffer 是一种通过延迟对二级索引的写入操作来支持顺序 I/O 的技术。在最新的硬件上,随机 I/O 不再是问题。从 MySQL 8.0 开始,当 MySQL 运行在可供数据库使用的所有资源的专用服务器上时,我们建议启用此变量,并且不要手动修改 InnoDB 设置。该变量的默认值是相同的,但是通过启用 innodb_dedicated_server 控制的变量是不同的。
  • innodb_buffer_pool_size
    • 128MB 是服务器内存小于 1GB。
    • 如果服务器内存在 1GB 到 4GB 之间,则检测到的服务器内存 * 0.5。
    • 如果服务器内存超过 4GB,则检测到的服务器内存 * 0.75。

  • innodb_redo_log_capacity:(可用逻辑处理器数量/2)GB,最大 16GB。
innodb_dedicated_server 启用时,innodb_flush_method 不会自动配置。AHI(InnoDB 自适应哈希索引)长期以来一直是一些性能问题的原因。每个经验丰富的 DBA 总是建议禁用它,就像旧的查询缓存一样。我很惊讶没有像 Domas Mituzas 的查询缓存调优器那样的 AHI 调优器当没有数据发生更改并且完全缓存在缓冲池中时,AHI 可能会对读查询 (SELECT) 提供一些好处。一旦有写入操作,或者系统负载较高,或者读取所需的所有数据都无法缓存,自适应哈希索引就会成为巨大的瓶颈。为了获得更可预测的响应时间,建议禁用它。之前默认值是根据缓冲池的数量计算的,为了简化,现在默认为 2。相关文档 指出该值定义每个缓冲池的双写文件数。但我的印象是,它是全局的,与缓冲池实例的数量无关。从 MySQL 错误日志来看:
2024-05-01T05:43:03.Z 1 [Note] [MY-012955] [InnoDB] Initializing buffer pool, total size = 2.000000G, instances = 2, chunk size =128.000000M […] 2024-05-01T05:43:03.Z 1 [Note] [MY-013532] [InnoDB] Using ’https://www.zhihu.com/topic/#ib_16384_0.dblwr' for doublewrite 2024-05-01T05:43:03.Z 1 [Note] [MY-013532] [InnoDB] Using ‘https://www.zhihu.com/topic/#ib_16384_1.dblwr' for doublewrite 2024-05-01T05:43:03.Z 1 [Note] [MY-013532] [InnoDB] Using ’https://www.zhihu.com/topic/#ib_16384_0.bdblwr' for doublewrite 2024-05-01T05:43:03.Z 1 [Note] [MY-013566] [InnoDB] Double write buffer files: 2 2024-05-01T05:43:03.Z 1 [Note] [MY-013565] [InnoDB] Double write buffer pages per instance: 128 2024-05-01T05:43:03.Z 1 [Note] [MY-013532] [InnoDB] Using ‘https://www.zhihu.com/topic/#ib_16384_0.dblwr' for doublewrite 2024-05-01T05:43:03.Z 1 [Note] [MY-013532] [InnoDB] Using ’https://www.zhihu.com/topic/#ib_16384_1.dblwr' for doublewrite
我们看到我们有 2 个缓冲池实例,但仍然只有 2 个双写缓冲文件。根据文档,我期望 4 。第三个文件 #ib_16384_0.bdblwr 是在 innodb_doublewrite 设置为 DETECT_ONLY 时创建的。使用 DETECTONLY 时,只有元数据会写入双写缓冲区。数据库页内容不会写入双写缓冲区,并且恢复不会使用双写缓冲区来修复不完整的页写入。此轻量级设置仅用于检测不完整的页面写入。 data-pid=”-MvRtT7”>从测试结果和出于对性能的角度考虑,我们意识到默认值越大越好,经常建议增加它。当支持时,O_DIRECT 始终是首选值,我们建议使用它绕过文件系统缓存,将 InnoDB 更改刷新到磁盘(对于数据文件和日志文件)。如果不支持 O_DIRECT*,我们使用旧的 *fsync 方法。这是针对 Unix 的,在 Windows 上,默认值是 unbuffered对于最近的系统(RAID、SSD 等),默认 I/O 容量太低。由于该变量定义了 InnoDB 后台操作可用的 IOPS 数量,因此值太低会限制性能。如果 InnoDB 需要更积极地刷新,则此变量定义 InnoDB 可用于执行后台操作的最大 IOPS 数。新的默认值更简单,因为它只是 innodb_io_capacity 的两倍。我们增加了默认值,因为大的日志缓冲区允许大型事务运行,而无需在事务提交之前将日志写入磁盘。当系统支持 NUMA 时,新的默认值在分配 InnoDB 缓冲池期间将 mysqld 的 NUMA 内存策略设置为 MPOL_INTERLEAVE 。此操作随机平衡所有 NUMA 节点的内存分配,从而在这些节点之间实现更好的分布。当然,只有当您的系统具有多个 NUMA 节点时,您才能从中受益。这是验证节点数量的方法:
讯享网$ numactl –hardware 
available: 2 nodes (0-1)
node 0 size: 16160 MB
node 0 free: 103 MB
node 1 size: 16130 MB
node 1 free: 83 MB
node distances:
node   0   1 
  0:  10  20 
  1:  20  10</code></pre></div><p data-pid="WPtFTeP2">在上面的例子中,我们可以看到 CPU 有两个节点。</p><p data-pid="fGBFN-rT">您还可以使用 lstopo 显示架构并显示 NUMA 核心。这是另一个例子:</p><p class="ztext-empty-paragraph"><br/></p><figure data-size="normal"><img src="https://pica.zhimg.com/v2-91b535fc04e28eb0919ec757c5b29252_r.jpg?source=1940ef5c" data-caption="" data-size="normal" data-rawwidth="1536" data-rawheight="597" data-original-token="v2-91b535fc04e28eb0919ec757c5b29252" class="origin_image zh-lightbox-thumb" width="1536" data-original="https://pica.zhimg.com/v2-91b535fc04e28eb0919ec757c5b29252_r.jpg?source=1940ef5c"/></figure><p class="ztext-empty-paragraph"><br/></p><p data-pid="ypnCfiJI">新的默认设置是使用与缓冲池实例一样多的线程从缓冲池实例中刷新脏页。</p><p data-pid="CeV75zG9">出于性能原因,在具有大量逻辑 CPU 的系统上,用于并行聚集索引读取的线程数会自动增加。</p><p data-pid="sHaHGTk2">对于具有大量 (&gt;=16) vCPU 的系统,此变量也会以某种方式自动配置。但我们也意识到,在某些较小的系统上拥有 4 个清除线程可能会出现问题。对于这样的系统,我们将默认值减少到 1。</p><p data-pid="BIayw8nh">如果系统有超过 8 个 vCPU,该变量也会自动增加。</p><p data-pid="KVX51osp">在支持它的系统上,除非需要,否则 <code>fdatasync()</code> 的调用不会刷新对文件元数据的更改。这提供了性能优势。</p><p data-pid="5pJfzhJx">如果系统受益于大量内存,默认值现在会自动增加。但默认上限为 4GB。因此,对于内存超过 132GB 的系统,默认情况下 <code>temptable_max_ram</code> 的值将设置为 4GB。</p><p data-pid="dE4fx1jB">新的默认设置禁止从内存映射临时文件分配内存(不在 <code>tmpdir</code> 中创建文件)。</p><p data-pid="2gXeFhfd">当 <code>temptable_use_mmap</code> 被禁用(新默认设置)时,<code>TempTable</code> 存储引擎会使用 InnoDB 磁盘上的内部临时表,而不是在 <code>temptable_max_ram</code> 变量定义的限制被超过时,在 tmpdir 中为内部内存临时表分配空间作为内存映射的临时文件。</p><p data-pid="zqemhdNe">通过这个全新版本的 MySQL(第一个 LTS),我们有机会更改某些 InnoDB 变量的默认值,使它们更符合生产服务器的实际情况。</p><p data-pid="VW8adkF2">有些现在可以自动调整以更好地匹配 MySQL 运行的系统。</p><p data-pid="W4-R4NRY">享受 MySQL 并享受新的默认设置!</p><p data-pid="S69H6x1a">[1] MySQL 版本介绍: <span class="invisible">https://</span><span class="visible">dev.mysql.com/doc/refma</span><span class="invisible">n/8.4/en/mysql-releases.html</span><span class="ellipsis"></span></p><p data-pid="7IG5xTCt">[2] MySQL 8.4: <span class="invisible">https://</span><span class="visible">dev.mysql.com/doc/relno</span><span class="invisible">tes/mysql/8.4/en/</span><span class="ellipsis"></span></p><p data-pid="tcTqPq5t">[3] 配置 InnoDB 缓冲池大小: <span class="invisible">https://</span><span class="visible">dev.mysql.com/doc/refma</span><span class="invisible">n/8.4/en/innodb-buffer-pool-resize.html</span><span class="ellipsis"></span></p><p data-pid="YCv0Z-v8">[4] sysvar_innodb_doublewrite_files: <span class="invisible">https://</span><span class="visible">dev.mysql.com/doc/refma</span><span class="invisible">n/8.4/en/innodb-parameters.html#sysvar_innodb_doublewrite_files</span><span class="ellipsis"></span></p><p data-pid="nAK0Y5uk">本文原文:https://lefred.be/content/mysql</p>
EXPLAIN作为MySQL的性能分析神器,读懂其结果是很有必要的,然而我在各种搜索引擎上竟然找不到特别完整的解读。都是只有重点,没有细节(例如type的取值不全、Extra缺乏完整的介绍等)。所以,我肝了将近一个星期,整理了一下。这应该是全网最全面、最细致的EXPLAIN解读文章了,下面是全文。文章比较长,建议收藏。TIPS
本文基于MySQL 8.0编写,理论支持MySQL 5.0及更高版本。
EXPLAIN使用explain可用来分析SQL的执行计划。格式如下:
讯享网{EXPLAIN | DESCRIBE | DESC} 
tbl_name [col_name | wild] 
{EXPLAIN | DESCRIBE | DESC}
讯享网[explain_type] {explainable_stmt | FOR CONNECTION connection_id} 
{EXPLAIN | DESCRIBE | DESC} ANALYZE select_statement explain_type: {
FORMAT = format_name 
} format_name: {
讯享网TRADITIONAL 
| JSON | TREE } explainable_stmt: {
SELECT statement 
| TABLE statement | DELETE statement | INSERT statement | REPLACE statement | UPDATE statement }
示例:
讯享网EXPLAIN format = TRADITIONAL json SELECT tt.TicketNumber, tt.TimeIn, 
 tt.ProjectReference, tt.EstimatedShipDate, tt.ActualShipDate, tt.ClientID, tt.ServiceCodes, tt.RepetitiveID, tt.CurrentProcess, tt.CurrentDPPerson, tt.RecordVolume, tt.DPPrinted, et.COUNTRY, et_1.COUNTRY, do.CUSTNAME FROM tt, et, et AS et_1, do WHERE tt.SubmitTime IS NULL AND tt.ActualPC = et.EMPLOYID AND tt.AssignedPC = et_1.EMPLOYID AND tt.ClientID = do.CUSTNMBR; 
结果输出展示:


结果解读id该语句的唯一标识。如果explain的结果包括多个id值,则数字越大越先执行;而对于相同id的行,则表示从上往下依次执行。select_type查询类型,有如下几种取值:

table表示当前这一行正在访问哪张表,如果SQL定义了别名,则展示表的别名partitions当前查询匹配记录的分区。对于未分区的表,返回nulltype连接类型,有如下几种取值,性能从好到坏排序 如下:
  • system:该表只有一行(相当于系统表),system是const类型的特例
  • const:针对主键或唯一索引的等值查询扫描, 最多只返回一行数据. const 查询速度非常快, 因为它仅仅读取一次即可
  • eq_ref:当使用了索引的全部组成部分,并且索引是PRIMARY KEY或UNIQUE NOT NULL 才会使用该类型,性能仅次于system及const。
讯享网– 多表关联查询,单行匹配 SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; – 多表关联查询,联合索引,多行匹配 SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1; 
ref:当满足索引的最左前缀规则,或者索引不是主键也不是唯一索引时才会发生。如果使用的索引只会匹配到少量的行,性能也是不错的。
– 根据索引(非主键,非唯一索引),匹配到多行 SELECT * FROM ref_table WHERE key_column=expr; – 多表关联查询,单个索引,多行匹配 SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column; – 多表关联查询,联合索引,多行匹配 SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1; 
TIPS最左前缀原则,指的是索引按照最左优先的方式匹配索引。比如创建了一个组合索引(column1, column2, column3),那么,如果查询条件是:
  • WHERE column1 = 1、WHERE column1= 1 AND column2 = 2、WHERE column1= 1 AND column2 = 2 AND column3 = 3 都可以使用该索引;
  • WHERE column1 = 2、WHERE column1 = 1 AND column3 = 3就无法匹配该索引。

  • fulltext:全文索引
  • ref_or_null:该类型类似于ref,但是MySQL会额外搜索哪些行包含了NULL。这种类型常见于解析子查询
讯享网SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;
  • index_merge:此类型表示使用了索引合并优化,表示一个查询里面用到了多个索引
  • unique_subquery:该类型和eq_ref类似,但是使用了IN查询,且子查询是主键或者唯一索引。例如:
value IN (SELECT primary_key FROM single_table WHERE some_expr)
index_subquery:和unique_subquery类似,只是子查询使用的是非唯一索引
讯享网value IN (SELECT key_column FROM single_table WHERE some_expr)
range:范围扫描,表示检索了指定范围的行,主要用于有限制的索引扫描。比较常见的范围扫描是带有BETWEEN子句或WHERE子句里有&gt;、&gt;=、&lt;、&lt;=、IS NULL、&lt;=&gt;、BETWEEN、LIKE、IN()等操作符。
SELECT * FROM tbl_name WHERE key_column BETWEEN 10 and 20; SELECT * FROM tbl_name WHERE key_column IN (10,20,30); 

  • index:全索引扫描,和ALL类似,只不过index是全盘扫描了索引的数据。当查询仅使用索引中的一部分列时,可使用此类型。有两种场景会触发:
    • 如果索引是查询的覆盖索引,并且索引查询的数据就可以满足查询中所需的所有数据,则只扫描索引树。此时,explain的Extra 列的结果是Using index。index通常比ALL快,因为索引的大小通常小于表数据。
    • 按索引的顺序来查找数据行,执行了全表扫描。此时,explain的Extra列的结果不会出现Uses index。

  • ALL:全表扫描,性能最差。

possible_keys展示当前查询可以使用哪些索引,这一列的数据是在优化过程的早期创建的,因此有些索引可能对于后续优化过程是没用的。key表示MySQL实际选择的索引key_len索引使用的字节数。由于存储格式,当字段允许为NULL时,key_len比不允许为空时大1字节。key_len计算公式: cnblogs.com/gomysql/p/4ref表示将哪个字段或常量和key列所使用的字段进行比较。如果ref是一个函数,则使用的值是函数的结果。要想查看是哪个函数,可在EXPLAIN语句之后紧跟一个SHOW WARNING语句。rowsMySQL估算会扫描的行数,数值越小越好。filtered表示符合查询条件的数据百分比,最大100。用rows × filtered可获得和下一张表连接的行数。例如rows = 1000,filtered = 50%,则和下一张表连接的行数是500。TIPS
在MySQL 5.7之前,想要显示此字段需使用explain extended命令;
MySQL.5.7及更高版本,explain默认就会展示filtered
Extra展示有关本次查询的附加信息,取值如下:
  • Child of ‘table’ pushed join@1
    此值只会在NDB Cluster下出现。
  • const row not found
    例如查询语句SELECT … FROM tbl_name,而表是空的
  • Deleting all rows
    对于DELETE语句,某些引擎(例如MyISAM)支持以一种简单而快速的方式删除所有的数据,如果使用了这种优化,则显示此值
  • Distinct
    查找distinct值,当找到第一个匹配的行后,将停止为当前行组合搜索更多行
  • FirstMatch(tbl_name)
    当前使用了半连接FirstMatch策略,详见 mariadb.com/kb/en/first ,翻译 cnblogs.com/abclife/p/1
  • Full scan on NULL key
    子查询中的一种优化方式,在无法通过索引访问null值的时候使用
  • Impossible HAVING
    HAVING子句始终为false,不会命中任何行
  • Impossible WHERE
    WHERE子句始终为false,不会命中任何行
  • Impossible WHERE noticed after reading const tables
    MySQL已经读取了所有const(或system)表,并发现WHERE子句始终为false
  • LooseScan(m…n)
    当前使用了半连接LooseScan策略,详见 mariadb.com/kb/en/loose ,翻译 javacoder.cn/?
  • No matching min/max row
    没有任何能满足例如 SELECT MIN(…) FROM … WHERE condition 中的condition的行
  • no matching row in const table
    对于关联查询,存在一个空表,或者没有行能够满足唯一索引条件
  • No matching rows after partition pruning
    对于DELETE或UPDATE语句,优化器在partition pruning(分区修剪)之后,找不到要delete或update的内容
  • No tables used
    当此查询没有FROM子句或拥有FROM DUAL子句时出现。例如:explain select 1
  • Not exists
    MySQL能对LEFT JOIN优化,在找到符合LEFT JOIN的行后,不会为上一行组合中检查此表中的更多行。例如:
讯享网SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE t2.id IS NULL;
  • 假设t2.id定义成了NOT NULL ,此时,MySQL会扫描t1,并使用t1.id的值查找t2中的行。 如果MySQL在t2中找到一个匹配的行,它会知道t2.id永远不会为NULL,并且不会扫描t2中具有相同id值的其余行。也就是说,对于t1中的每一行,MySQL只需要在t2中只执行一次查找,而不考虑在t2中实际匹配的行数。
    在MySQL 8.0.17及更高版本中,如果出现此提示,还可表示形如 NOT IN (subquery) 或 NOT EXISTS (subquery) 的WHERE条件已经在内部转换为反连接。这将删除子查询并将其表放入最顶层的查询计划中,从而改进查询的开销。通过合并半连接和反联接,优化器可以更加自由地对执行计划中的表重新排序,在某些情况下,可让查询提速。你可以通过在EXPLAIN语句后紧跟一个SHOW WARNING语句,并分析结果中的Message列,从而查看何时对该查询执行了反联接转换。
  • Plan isn’t ready yet
    使用了EXPLAIN FOR CONNECTION,当优化器尚未完成为在指定连接中为执行的语句创建执行计划时, 就会出现此值。
  • Range checked for each record (index map: N)
    MySQL没有找到合适的索引去使用,但是去检查是否可以使用range或index_merge来检索行时,会出现此提示。index map N索引的编号从1开始,按照与表的SHOW INDEX所示相同的顺序。 索引映射值N是指示哪些索引是候选的位掩码值。 例如0x19(二进制11001)的值意味着将考虑索引1、4和5。
    示例:下面例子中,name是varchar类型,但是条件给出整数型,涉及到隐式转换。
    图中t2也没有用到索引,是因为查询之前我将t2中name字段排序规则改为utf8_bin导致的链接字段排序规则不匹配。
explain select a.* from t1 a left join t2 b on t1.name = t2.name where t2.name = 2;
结果:
  • Recursive
    出现了递归查询。详见 “WITH (Common Table Expressions)”
  • Rematerialize
    用得很少,使用类似如下SQL时,会展示Rematerialize
讯享网SELECT … FROM t, LATERAL (derived table that refers to t) AS dt
  • Scanned N databases
    表示在处理INFORMATION_SCHEMA表的查询时,扫描了几个目录,N的取值可以是0,1或者all。详见 “Optimizing INFORMATION_SCHEMA Queries”
  • Select tables optimized away
    优化器确定:①最多返回1行;②要产生该行的数据,要读取一组确定的行,时会出现此提示。一般在用某些聚合函数访问存在索引的某个字段时,优化器会通过索引直接一次定位到所需要的数据行完成整个查询时展示,例如下面这条SQL。
explain select min(id) from t1;
  • Skip_open_table, Open_frm_only, Open_full_table
    这些值表示适用于INFORMATION_SCHEMA表查询的文件打开优化;
  • Skip_open_table:无需打开表文件,信息已经通过扫描数据字典获得
  • Open_frm_only:仅需要读取数据字典以获取表信息
  • Open_full_table:未优化的信息查找。表信息必须从数据字典以及表文件中读取

  • Start temporary, End temporary
    表示临时表使用Duplicate Weedout策略,详见 mariadb.com/kb/en/dupli ,翻译 cnblogs.com/abclife/p/1
  • unique row not found
    对于形如 SELECT … FROM tbl_name 的查询,但没有行能够满足唯一索引或主键查询的条件
  • Using filesort
    当Query 中包含 ORDER BY 操作,而且无法利用索引完成排序操作的时候,MySQL Query Optimizer 不得不选择相应的排序算法来实现。数据较少时从内存排序,否则从磁盘排序。Explain不会显示的告诉客户端用哪种排序。官方解释:“MySQL需要额外的一次传递,以找出如何按排序顺序检索行。通过根据联接类型浏览所有行并为所有匹配WHERE子句的行保存排序关键字和行的指针来完成排序。然后关键字被排序,并按排序顺序检索行”
  • Using index
    仅使用索引树中的信息从表中检索列信息,而不必进行其他查找以读取实际行。当查询仅使用属于单个索引的列时,可以使用此策略。例如:

讯享网explain SELECT id FROM t
Using index condition表示先按条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行。通过这种方式,除非有必要,否则索引信息将可以延迟“下推”读取整个行的数据。详见 “Index Condition Pushdown Optimization” 。例如:
Using index for group-by数据访问和 Using index 一样,所需数据只须要读取索引,当Query 中使用GROUP BY或DISTINCT 子句时,如果分组字段也在索引中,Extra中的信息就会是 Using index for group-by。详见 “GROUP BY Optimization”
– name字段有索引 explain SELECT name FROM t1 group by name
Using index for skip scan
表示使用了Skip Scan。详见 Skip Scan Range Access Method
  • Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access)
    使用Block Nested Loop或Batched Key Access算法提高join的性能。详见 cnblogs.com/chenpingzha
  • Using MRR
    使用了Multi-Range Read优化策略。详见 “Multi-Range Read Optimization”
  • Using sort_union(…), Using union(…), Using intersect(…)
    这些指示索引扫描如何合并为index_merge连接类型。详见 “Index Merge Optimization” 。
  • Using temporary
    为了解决该查询,MySQL需要创建一个临时表来保存结果。如果查询包含不同列的GROUP BY和 ORDER BY子句,通常会发生这种情况。
讯享网– name无索引 explain SELECT name FROM t1 group by name
Using where如果我们不是读取表的所有数据,或者不是仅仅通过索引就可以获取所有需要的数据,则会出现using where信息
explain SELECT * FROM t1 where id &gt; 5 
Using where with pushed condition
仅用于NDB
  • Zero limit
    该查询有一个limit 0子句,不能选择任何行
讯享网explain SELECT name FROM resource_template limit 0
扩展的EXPLAINEXPLAIN可产生额外的扩展信息,可通过在EXPLAIN语句后紧跟一条SHOW WARNING语句查看扩展信息。TIPS
  • 在MySQL 8.0.12及更高版本,扩展信息可用于SELECT、DELETE、INSERT、REPLACE、UPDATE语句;在MySQL 8.0.12之前,扩展信息仅适用于SELECT语句;
  • 在MySQL 5.6及更低版本,需使用EXPLAIN EXTENDED xxx语句;而从MySQL 5.7开始,无需添加EXTENDED关键词。
使用示例:
mysql&gt; EXPLAIN 
讯享网 SELECT t1.a, t1.a IN (SELECT t2.a FROM t2) FROM t1\G 
* 1. row *
 id: 1 
select_type: PRIMARY
讯享网 table: t1 type: index 
possible_keys: NULL
 key: PRIMARY key_len: 4 ref: NULL rows: 4 filtered: 100.00 Extra: Using index 
* 2. row *
讯享网 id: 2 
select_type: SUBQUERY
 table: t2 type: index 
possible_keys: a
讯享网 key: a key_len: 5 ref: NULL rows: 3 filtered: 100.00 Extra: Using index 
2 rows in set, 1 warning (0.00 sec) mysql&gt; SHOW WARNINGS\G * 1. row * Level: Note Code: 1003 Message: /* select#1 */ select test.t1.a AS a,
 &lt;in_optimizer&gt;(`test`.`t1`.`a`,`test`.`t1`.`a` in ( &lt;materialize&gt; (/* select#2 */ select `test`.`t2`.`a` from `test`.`t2` where 1 having 1 ), &lt;primary_index_lookup&gt;(`test`.`t1`.`a` in &lt;temporary table&gt; on &lt;auto_key&gt; where ((`test`.`t1`.`a` = `materialized-subquery`.`a`))))) AS `t1.a IN (SELECT t2.a FROM t2)` from `test`.`t1` 
1 row in set (0.00 sec)
由于SHOW WARNING的结果并不一定是一个有效SQL,也不一定能够执行(因为里面包含了很多特殊标记)。特殊标记取值如下:
  • &lt;auto_key&gt;
    自动生成的临时表key
  • &lt;cache&gt;(expr)
    表达式(例如标量子查询)执行了一次,并且将值保存在了内存中以备以后使用。对于包括多个值的结果,可能会创建临时表,你将会看到 &lt;temporary table&gt; 的字样
  • &lt;exists&gt;(query fragment)
    子查询被转换为 EXISTS
  • &lt;in_optimizer&gt;(query fragment)
    这是一个内部优化器对象,对用户没有任何意义
  • &lt;index_lookup&gt;(query fragment)
    使用索引查找来处理查询片段,从而找到合格的行
  • &lt;if&gt;(condition, expr1, expr2)
    如果条件是true,则取expr1,否则取expr2
  • &lt;is_not_null_test&gt;(expr)
    验证表达式不为NULL的测试
  • &lt;materialize&gt;(query fragment)
    使用子查询实现
  • materialized-subquery.col_name
    在内部物化临时表中对col_name的引用,以保存子查询的结果
  • &lt;primary_index_lookup&gt;(query fragment)
    使用主键来处理查询片段,从而找到合格的行
  • &lt;ref_null_helper&gt;(expr)
    这是一个内部优化器对象,对用户没有任何意义
  • /* select#N */ select_stmt
    SELECT与非扩展的EXPLAIN输出中id=N的那行关联
  • outer_tables semi join (inner_tables)
    半连接操作。inner_tables展示未拉出的表。详见 “Optimizing Subqueries, Derived Tables, and View References with Semijoin Transformations”
  • &lt;temporary table&gt;
    表示创建了内部临时表而缓存中间结果

当某些表是const或system类型时,这些表中的列所涉及的表达式将由优化器尽早评估,并且不属于所显示语句的一部分。但是,当使用FORMAT=JSON时,某些const表的访问将显示为ref。估计查询性能多数情况下,你可以通过计算磁盘的搜索次数来估算查询性能。对于比较小的表,通常可以在一次磁盘搜索中找到行(因为索引可能已经被缓存了),而对于更大的表,你可以使用B-tree索引进行估算:你需要进行多少次查找才能找到行:log(row_count) / log(index_block_length / 3 * 2 / (index_length + data_pointer_length)) + 1在MySQL中,index_block_length通常是1024字节,数据指针一般是4字节。比方说,有一个500,000的表,key是3字节,那么根据计算公式log(500,000)/log(10243*2/(3+4)) + 1 = 4 次搜索。该索引将需要500,000 * 7 * 32 = 5.2MB的存储空间(假设典型的索引缓存的填充率是2/3),因此你可以在内存中存放更多索引,可能只要一到两个调用就可以找到想要的行了。但是,对于写操作,你需要四个搜索请求来查找在何处放置新的索引值,然后通常需要2次搜索来更新索引并写入行。前面的讨论并不意味着你的应用性能会因为log N而缓慢下降。只要内容被OS或MySQL服务器缓存,随着表的变大,只会稍微变慢。在数据量变得太大而无法缓存后,将会变慢很多,直到你的应用程序受到磁盘搜索约束(按照log N增长)。为了避免这种情况,可以根据数据的增长而增加key的。对于MyISAM表,key的缓存大小由名为key_buffer_size的系统变量控制,详见 Section 5.1.1, “Configuring the Server”
作者:大目链接:imooc.com/article/30822来源:慕课网本文原创发布于慕课网 ,转载请注明出处,谢谢合作
本文基于MySQL 8.0.25源码进行分析和总结。这里MySQL Server层指的是MySQL的优化器、执行器部分。我们对MySQL的理解还建立在5.6和5.7版本的理解之上,更多的是对比PostgreSQL或者传统数据库。然而从MySQL 8.0开始,持续每三个月的迭代和重构工作,使得MySQL Server层的整体架构有了质的飞越。下面来看下MySQL最新的架构。
我们可以看到最新的MySQL的分层架构和其他数据库并没有太大的区别,另外值得一提的是从图中可以看出MySQL现在更多的加强InnoDB、NDB集群和RAPID(HeatWave clusters)内存集群架构的演进。下面我们就看下具体细节,我们这次不随着官方的Feature实现和重构顺序进行理解,本文更偏向于从优化器、执行器的流程角度来演进。首先从Parser开始,官方MySQL 8.0使用Bison进行了重写,生成Parser Tree,同时Parser Tree会contextualize生成MySQL抽象语法树(Abstract Syntax Tree).
MySQL抽象语法树和其他数据库有些不同,是由比较让人拗口的SELECT_LEX_UNIT/SELECT_LEX类交替构成的,然而这两个结构在最新的版本中已经重命名成标准的SELECT_LEX -&gt; Query_block和SELECT_LEX_UNIT -&gt; Query_expression。Query_block是代表查询块,而Query_expression是包含多个查询块的查询表达式,包括UNION AND/OR的查询块(如SELECT FROM t1 union SELECT FROM t2)或者有多Level的ORDER BY/LIMIT (如SELECT * FROM t1 ORDER BY a LIMIT 10) ORDER BY b LIMIT 5。例如,来看一个复杂的嵌套查询:
讯享网(SELECT * FROM ttt1) UNION ALL (SELECT * FROM 
 (SELECT * FROM ttt2) AS a, (SELECT * FROM ttt3 UNION ALL SELECT * FROM ttt4) AS b)</code></pre></div><p data-pid="pta-urI9">在MySQL中就可以用下面方式表达:</p><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-521dc14ffaebc37a09828_r.jpg" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="462" class="origin_image zh-lightbox-thumb" width="1080" data-original="https://pic1.zhimg.com/v2-521dc14ffaebc37a09828_r.jpg"/></figure><p data-pid="KanLmzN2">经过解析和转换后的语法树仍然建立在Query_block和Query_expression的框架下,只不过有些LEVEL的query block被消除或者合并了,这里不再详细展开。</p><p data-pid="BdUKOX-A">接下来我们要经过resolve和transformation过程Query_expression::prepare-&gt;Query_block::prepare,这个过程包括(按功能分而非完全按照执行顺序):</p><ul><li data-pid="qa_gSnLQ">setup_tables:Set up table leaves in the query block based on list of tables.</li><li data-pid="oQCn6hiJ">resolve_placeholder_tables/merge_derived/setup_table_function/setup_materialized_derived:Resolve derived table, view or table function references in query block.</li><li data-pid="C-GZhaBX">setup_natural_join_row_types:Compute and store the row types of the top-most NATURAL/USING joins.</li><li data-pid="NDwkFpDi">setup_wild:Expand all &#39;*&#39; in list of expressions with the matching column references.</li><li data-pid="znS1LQuF">setup_base_ref_items:Set query_block&#39;s base_ref_items.</li><li data-pid="h2TcQDKC">setup_fields:Check that all given fields exists and fill struct with current data.</li><li data-pid="FbbNI5oF">setup_conds:Resolve WHERE condition and join conditions.</li><li data-pid="vm1l4ii8">setup_group:Resolve and set up the GROUP BY list.</li><li data-pid="WbdHipWu">m_having_cond-&gt;fix_fields:Setup the HAVING clause.</li><li data-pid="33PJLKiW">resolve_rollup:Resolve items in SELECT list and ORDER BY list for rollup processing.</li><li data-pid="X-foDyZw">resolve_rollup_item:Resolve an item (and its tree) for rollup processing by replacing items matching grouped expressions with Item_rollup_group_items and updating properties (m_nullable, PROP_ROLLUP_FIELD). Also check any GROUPING function for incorrect column.</li><li data-pid="qKQiK_VB">setup_order:Set up the ORDER BY clause.</li><li data-pid="0caj_hxe">resolve_limits:Resolve OFFSET and LIMIT clauses.</li><li data-pid="OPdwq6NF">Window::setup_windows1:Set up windows after setup_order() and before setup_order_final().</li><li data-pid="kfwJLXAX">setup_order_final:Do final setup of ORDER BY clause, after the query block is fully resolved.</li><li data-pid="cJGTjoCz">setup_ftfuncs:Setup full-text functions after resolving HAVING.</li><li data-pid="oDU7MsEn">resolve_rollup_wfs : Replace group by field references inside window functions with references in the presence of ROLLUP.</li></ul><ul><li data-pid="UKlCbFPk">remove_redundant_subquery_clause : Permanently remove redundant parts from the query if 1) This is a subquery 2) Not normalizing a view. Removal should take place when a query involving a view is optimized, not when the view is created.</li><li data-pid="xsyq_9jG">remove_base_options:Remove SELECT_DISTINCT options from a query block if can skip distinct.</li><li data-pid="_XIAHcC1">resolve_subquery : Resolve predicate involving subquery, perform early unconditional subquery transformations.<br/></li><ul><li data-pid="uZdaOpEu">Convert subquery predicate into semi-join, or</li><li data-pid="TgYQnUqs">Mark the subquery for execution using materialization, or</li><li data-pid="nk8COEGe">Perform IN-&gt;EXISTS transformation, or</li><li data-pid="UAyrbCkc">Perform more/less ALL/ANY -&gt; MIN/MAX rewrite</li><li data-pid="s6QpV0NL">Substitute trivial scalar-context subquery with its value</li></ul></ul><p class="ztext-empty-paragraph"><br/></p><ul><li data-pid="X71T5ZAu">transform_scalar_subqueries_to_join_with_derived:Transform eligible scalar subqueries to derived tables.</li><li data-pid="gR3sJpEF">flatten_subqueries:Convert semi-join subquery predicates into semi-join join nests. Convert candidate subquery predicates into semi-join join nests. This transformation is performed once in query lifetime and is irreversible.</li><li data-pid="PiR1vglg">apply_local_transforms :<br/></li><ul><li data-pid="2OiI4e2W">delete_unused_merged_columns : If query block contains one or more merged derived tables/views, walk through lists of columns in select lists and remove unused columns.</li><li data-pid="FBOXIl44">simplify_joins:Convert all outer joins to inner joins if possible</li><li data-pid="L5-vMWdD">prune_partitions:Perform partition pruning for a given table and condition.</li></ul></ul><p class="ztext-empty-paragraph"><br/></p><ul><li data-pid="px4TR2oY">push_conditions_to_derived_tables:Pushing conditions down to derived tables must be done after validity checks of grouped queries done by apply_local_transforms();</li><li data-pid="4kWXXB22">Window::eliminate_unused_objects:Eliminate unused window definitions, redundant sorts etc.</li></ul><p data-pid="oT7i68W4">这里,节省篇幅,我们只举例关注下和top_join_list相关的simple_joins这个函数的作用,对于Query_block中嵌套join的简化过程。</p><figure data-size="normal"><img src="https://pica.zhimg.com/v2-fdad5b5dcc03afaee6ed3310fc_r.jpg" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="967" class="origin_image zh-lightbox-thumb" width="1080" data-original="https://pica.zhimg.com/v2-fdad5b5dcc03afaee6ed3310fc_r.jpg"/></figure><p data-pid="HGU-YStI">3 对比PostgreSQL</p><p data-pid="p7-lP03g">为了更清晰的理解标准数据库的做法,我们这里引用了PostgreSQL的这三个过程:</p><p data-pid="it_wa6dw"><b>Parser</b></p><p data-pid="dHVOqz_M">下图首先Parser把SQL语句生成parse tree。</p><div class="highlight"><pre><code class="language-text">testdb=# SELECT id, data FROM tbl_a WHERE id &lt; 300 ORDER BY data;</code></pre></div><figure data-size="normal"><img src="https://pic3.zhimg.com/v2-dee50132d429ff9f6920e7fff4_r.jpg" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="477" class="origin_image zh-lightbox-thumb" width="1080" data-original="https://pic3.zhimg.com/v2-dee50132d429ff9f6920e7fff4_r.jpg"/></figure><p data-pid="l1v8607X"><b>Analyzer/Analyser</b></p><p data-pid="FwFvs15g">下图展示了PostgreSQL的analyzer/analyser如何将parse tree通过语义分析后生成query tree。</p><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-c87aabb0d4974d1d5b4a6801f58e2f32_r.jpg" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="475" class="origin_image zh-lightbox-thumb" width="1080" data-original="https://pic1.zhimg.com/v2-c87aabb0d4974d1d5b4a6801f58e2f32_r.jpg"/></figure><p class="ztext-empty-paragraph"><br/></p><p data-pid="Jv9ZH_7h"><b>Rewriter</b></p><p data-pid="98MoN1kV">Rewriter会根据规则系统中的规则把query tree进行转换改写。</p><div class="highlight"><pre><code class="language-text">sampledb=# CREATE VIEW employees_list 
sampledb-# AS SELECT e.id, e.name, d.name AS department sampledb-# FROM employees AS e, departments AS d WHERE e.department_id = d.id;
下图的例子就是一个包含view的query tree如何展开成新的query tree。
讯享网sampledb=# SELECT * FROM employees_list;
接下来我们进入了逻辑计划生成物理计划的过程,本文还是注重于结构的解析,而不去介绍生成的细节,MySQL过去在8.0.22之前,主要依赖的结构就是JOIN和QEP_TAB。JOIN是与之对应的每个Query_block,而QEP_TAB对应的每个Query_block涉及到的具体“表”的顺序、方法和执行计划。然而在8.0.22之后,新的基于Hypergraph的优化器算法成功的抛弃了QEP_TAB结构来表达左深树的执行计划,而直接使用HyperNode/HyperEdge的图来表示执行计划。
举例可以看到数据结构表达的left deep tree和超图结构表达的bushy tree对应的不同计划展现:
| -&gt; Inner hash join (no condition) (cost=1.40 rows=1) 
讯享网-&gt; Table scan on R4 (cost=0.35 rows=1) -&gt; Hash -&gt; Inner hash join (no condition) (cost=1.05 rows=1) -&gt; Table scan on R3 (cost=0.35 rows=1) -&gt; Hash -&gt; Inner hash join (no condition) (cost=0.70 rows=1) -&gt; Table scan on R2 (cost=0.35 rows=1) -&gt; Hash -&gt; Table scan on R1 (cost=0.35 rows=1) 
| -&gt; Nested loop inner join (cost=0.55..0.55 rows=0)
-&gt; Nested loop inner join (cost=0.50..0.50 rows=0) -&gt; Table scan on R4 (cost=0.25..0.25 rows=1) -&gt; Filter: (R4.c1 = R3.c1) (cost=0.35..0.35 rows=0) -&gt; Table scan on R3 (cost=0.25..0.25 rows=1) -&gt; Nested loop inner join (cost=0.50..0.50 rows=0) -&gt; Table scan on R2 (cost=0.25..0.25 rows=1) -&gt; Filter: (R2.c1 = R1.c1) (cost=0.35..0.35 rows=0) -&gt; Table scan on R1 (cost=0.25..0.25 rows=1)</code></pre></div><p data-pid="bTiEAO_P">MySQL8.0.2x为了更好的兼容两种优化器,引入了新的类AccessPath,可以认为这是MySQL为了解耦执行器和不同优化器抽象出来的Plan Tree。</p><figure data-size="normal"><img src="https://pic3.zhimg.com/v2-b7741a4fec6458f9e0fd17eb0c0a5bae_r.jpg" data-caption="" data-size="normal" data-rawwidth="1016" data-rawheight="1048" class="origin_image zh-lightbox-thumb" width="1016" data-original="https://pic3.zhimg.com/v2-b7741a4fec6458f9e0fd17eb0c0a5bae_r.jpg"/></figure><p data-pid="xRSBLnqJ">老优化器仍然走JOIN::optimize来把query block转换成query execution plan (QEP)。</p><p data-pid="cn2dBcko">这个阶段仍然做一些逻辑的重写工作,这个阶段的转换可以理解为基于cost-based优化前做准备,详细步骤如下:</p><ul><li data-pid="o-AhiMPg">Logical transformations<br/></li><ul><li data-pid="p4m9AW4_">optimize_derived : Optimize the query expression representing a derived table/view.</li><li data-pid="uiw2U0fg">optimize_cond : Equality/constant propagation.</li><li data-pid="Ml915y99">prune_table_partitions : Partition pruning.</li><li data-pid="43zfyRyQ">optimize_aggregated_query : COUNT(*), MIN(), MAX() constant substitution in case of implicit grouping.</li><li data-pid="YVke3v22">substitute_gc : ORDER BY optimization, substitute all expressions in the WHERE condition and ORDER/GROUP lists that match generated columns (GC) expressions with GC fields, if any.</li></ul><li data-pid="HCVkGcAQ">Perform cost-based optimization of table order and access path selection.<br/></li><ul><li data-pid="yJFkZ0mL">JOIN::make_join_plan() : Set up join order and initial access paths.</li></ul><li data-pid="xaZjo0Oy">Post-join order optimization<br/></li><ul><li data-pid="6zc0KjU3">substitute_for_best_equal_field : Create optimal table conditions from the where clause and the join conditions.</li><li data-pid="89SqDTUT">make_join_query_block : Inject outer-join guarding conditions.</li><li data-pid="c_kE90TN">Adjust data access methods after determining table condition (several times).</li><li data-pid="-eata4dR">optimize_distinct_group_order : Optimize ORDER BY/DISTINCT.</li><li data-pid="JcGOfih1">optimize_fts_query : Perform FULLTEXT search before all regular searches.</li><li data-pid="aTzf0GaY">remove_eq_conds : Removes const and eq items. Returns the new item, or nullptr if no condition.</li><li data-pid="RZXVzQlk">replace_index_subquery/create_access_paths_for_index_subquery : See if this subquery can be evaluated with subselect_indexsubquery_engine.</li><li data-pid="nl9ckr59">setup_join_buffering : Check whether join cache could be used.</li></ul><li data-pid="SPMDV0qn">Code generation<br/></li><ul><li data-pid="Ijo_FBqo">alloc_qep(tables) : Create QEP_TAB array.</li><li data-pid="OHk5yHks">test_skip_sort : Try to optimize away sorting/distinct.</li><li data-pid="znlJBWm_">make_join_readinfo : Plan refinement stage: do various setup things for the executor.</li><li data-pid="6VfJacO6">make_tmp_tables_info : Setup temporary table usage for grouping and/or sorting.</li><li data-pid="sRaF2sw9">push_to_engines : Push (parts of) the query execution down to the storage engines if they can provide faster execution of the query, or part of it.</li><li data-pid="667X97eh">create_access_paths : generated ACCESS_PATH.</li></ul></ul><p data-pid="5z1e7KbG">新优化器默认不打开,必须通过set optimizer_switch=&#34;hypergraph_optimizer=on&#34;; 来打开。主要通过FindBestQueryPlan函数来实现,逻辑如下:</p><ul><li data-pid="bJbkx2m4">先判断是否属于新优化器可以支持的Query语法(CheckSupportedQuery),不支持的直接返回错误ER_HYPERGRAPH_NOT_SUPPORTED_YET。</li><li data-pid="5NHM9dVn">转化top_join_list变成JoinHypergraph结构。由于Hypergraph是比较独立的算法层面的实现,JoinHypergraph结构用来更好的把数据库的结构包装到Hypergraph的edges和nodes的概念上的。</li><li data-pid="dMNK6WSS">通过EnumerateAllConnectedPartitions实现论文中的DPhyp算法。</li><li data-pid="xl-uij3n">CostingReceiver类包含了过去JOIN planning的主要逻辑,包括根据cost选择相应的访问路径,根据DPhyp生成的子计划进行评估,保留cost最小的子计划。</li><li data-pid="dp4cpRf2">得到root_path后,接下来处理group/agg/having/sort/limit的。对于Group by操作,目前Hypergraph使用sorting first + streaming aggregation的方式。</li></ul><p data-pid="Z_hKN4Lr">举例看下Plan(AccessPath)和SQL的关系:</p><figure data-size="normal"><img src="https://pic2.zhimg.com/v2-57e391b066d5406fbf0c17bf541e3bdb_r.jpg" data-caption="" data-size="normal" data-rawwidth="1080" data-rawheight="578" class="origin_image zh-lightbox-thumb" width="1080" data-original="https://pic2.zhimg.com/v2-57e391b066d5406fbf0c17bf541e3bdb_r.jpg"/></figure><p data-pid="pHcNOU7u">最后生成Iterator执行器框架需要的Iterator执行载体,AccessPath和Iterator是一对一的关系(Access paths are a query planning structure that correspond 1:1 to iterators)。</p><div class="highlight"><pre><code class="language-text">Query_expression::m_root_iterator = CreateIteratorFromAccessPath(......) 
unique_ptr_destroy_only&lt;RowIterator&gt; CreateIteratorFromAccessPath(
讯享网 THD *thd, AccessPath *path, JOIN *join, bool eligible_for_batch_mode) { 
…… switch (path-&gt;type) {
 case AccessPath::TABLE_SCAN: { const auto &amp;param = path-&gt;table_scan(); iterator = NewIterator&lt;TableScanIterator&gt;( thd, param.table, path-&gt;num_output_rows, examined_rows); break; } case AccessPath::INDEX_SCAN: { const auto &amp;param = path-&gt;index_scan(); if (param.reverse) { iterator = NewIterator&lt;IndexScanIterator&lt;true&gt;&gt;( thd, param.table, param.idx, param.use_order, path-&gt;num_output_rows, examined_rows); } else { iterator = NewIterator&lt;IndexScanIterator&lt;false&gt;&gt;( thd, param.table, param.idx, param.use_order, path-&gt;num_output_rows, examined_rows); } break; } case AccessPath::REF: { 
…… }
讯享网testdb=# EXPLAIN SELECT * FROM tbl_a WHERE id &lt; 300 ORDER BY data; 
 QUERY PLAN 

Sort (cost=182.34..183.09 rows=300 width=8) Sort Key: data -&gt; Seq Scan on tbl_a (cost=0.00..170.00 rows=300 width=8)
讯享网 Filter: (id &lt; 300) 
(4 rows)
本文主要focus在MySQL最新版本官方的源码上,重点分析了官方的重构在多阶段和各阶段结构上的变化和联系,更多的是为了让大家了解一个全新的MySQL的发展。作者 | 道客原文链接本文为阿里云原创内容,未经允许不得转载。
作者:杨奇龙

网名“北在南方”,资深 DBA,主要负责数据库架构设计和运维平台开发工作,擅长数据库性能调优、故障诊断。

本文来源:原创投稿

* 爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。

作为 MySQL DBA ,相信大家都经历过在复制模式下,如果没有主键,遇到 load data ,大事务,ddl 等有大量表数据行扫描的行为时,会带来严重的主从延迟,给数据库稳定性和数据一致性带来隐患。MySQL 8.0.30 已于近日 GA ,新版本为我们提供了一个令人惊喜的特性 -(Generated Invisible Primary Keys)简称 GIPK 。一句概况就是: 当开启GIPK模式后,MySQL 会在没有显示定义主键的InnoDB表上自动生成不可见的主键
对于已经使用云RDS的朋友,可能很早就享受到云 RDS MySQL 提供的 隐式主键特性。但是对于自建数据库的企业,GIPK 依然是一个比较期待特性,(当然有和用起来是两码事!)
长话短说,本文基于实际测试案例来学校 如何使用 GIPK 。GIPK 由参数sql_generate_invisible_primary_key 控制,默认关闭,表示禁用,如果需要使用该特性,则需显式开启。
master [localhost:22031]&gt; show variables like ‘sql_generate_invisible_primary_key’; +————————————+——-+ | Variable_name | Value | +————————————+——-+ | sql_generate_invisible_primary_key | OFF | +————————————+——-+ 1 row in set (0.00 sec) master [localhost:22031]&gt; set sql_generate_invisible_primary_key=on; Query OK, 0 rows affected (0.00 sec) master [localhost:22031]&gt; show variables like ‘sql_generate_invisible_primary_key’; +————————————+——-+ | Variable_name | Value | +————————————+——-+ | sql_generate_invisible_primary_key | ON | +————————————+——-+ 1 row in set (0.00 sec) 
我们分别在关闭和开启该特性下创建两个无主键表:
讯享网master [localhost:22031]&gt; create table t1(id int ,c1 int); Query OK, 0 rows affected (0.00 sec) master [localhost:22031] {msandbox} (test) &gt; show create table t1 \G * 1. row * 
 Table: t1 
Create Table: CREATE TABLE t1 ( id int DEFAULT NULL, c1 int DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
开启 GIPK 并创建无主键表 t3 。
讯享网master [localhost:22031]&gt; set sql_generate_invisible_primary_key=on; Query OK, 0 rows affected (0.00 sec) master [localhost:22031] {msandbox} (test) &gt; show variables like ‘sql_generate_invisible_primary_key’; +————————————+——-+ | Variable_name | Value | +————————————+——-+ | sql_generate_invisible_primary_key | ON | +————————————+——-+ 1 row in set (0.00 sec) master [localhost:22031]&gt; create table t3(id int ,c1 int); Query OK, 0 rows affected (0.01 sec) master [localhost:22031]&gt; master [localhost:22031]&gt; show create table t3 \G * 1. row * 
 Table: t3 
Create Table: CREATE TABLE t3 ( my_row_id bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */, id int DEFAULT NULL, c1 int DEFAULT NULL, PRIMARY KEY (my_row_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
我们可以通过 show create table 发现 t3 的表结构,出现名为 my_row_id 的不可见主键。对两个表插入数据查看差异:
讯享网master [localhost:22031]&gt; insert into t1 values(1,1),(2,2),(3,3); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 master [localhost:22031]&gt; select * from t1; +——+——+ | id | c1 | +——+——+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +——+——+ 3 rows in set (0.00 sec) master [localhost:22031]&gt; insert into t3 values(1,1),(2,2),(3,3); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 master [localhost:22031]&gt; select * from t3; +——+——+ | id | c1 | +——+——+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +——+——+ 3 rows in set (0.00 sec) 
直接通过 select * from table 查询时,t3 和普通表t1无差异。 因为 GIPK 是基于不可见列实现的,如果我们显式指定访问 my_row_id ,则可以查看到隐藏的主键 my_row_id
master [localhost:22031]&gt; select my_row_id,id,c1 from t3; +———–+——+——+ | my_rowid | id | c1 | +———–+——+——+ | 1 | 1 | 1 | | 2 | 2 | 2 | | 3 | 3 | 3 | +———–+——+——+ 3 rows in set (0.00 sec) 
”> 总的来说,从业务程序访问数据库的角度来看,开启 GIPK 对业务是透明的。
当开启 GIPK 特性时,MySQL 生成的主键不能更改,只能在 VISIBLE 和 INVISIBLE 之间进行切换。比如:使 GIPK 主键可见: alter table TALBE_NAME alter column my_row_id set visible;
讯享网master [localhost:22031]&gt; alter table t3 alter column my_row_id set visible; Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0 master [localhost:22031]&gt; show create table t3 \G * 1. row * 
 Table: t3 
Create Table: CREATE TABLE t3 ( my_row_id bigint unsigned NOT NULL AUTO_INCREMENT, 显式可见 id int DEFAULT NULL, c1 int DEFAULT NULL, PRIMARY KEY (my_row_id) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)

而且可以被直接查询到

master [localhost:22031]&gt; select * from t3; +———–+——+——+ | my_row_id | id | c1 | +———–+——+——+ | 1 | 1 | 1 | | 2 | 2 | 2 | | 3 | 3 | 3 | +———–+——+——+ 3 rows in set (0.00 sec)
关闭起可见性 : alter table TABLE_NAME alter column my_row_id set invisible;
讯享网master [localhost:22031]&gt; alter table t3 alter column my_row_id set invisible; Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 master [localhost:22031]&gt; show create table t3 \G * 1. row * 
 Table: t3 
Create Table: CREATE TABLE t3 ( my_row_id bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */, id int DEFAULT NULL, c1 int DEFAULT NULL, PRIMARY KEY (my_row_id) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)

再次通过select * 查询则看不到 my_row_id

master [localhost:22031]&gt; select * from t3; +——+——+ | id | c1 | +——+——+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +——+——+ 3 rows in set (0.00 sec)
另外就是开启 GIPK 之后 ,my_row_id 是系统关键字,我们创建无主键的表时,不能包含名为 my_row_id 的字段 。
讯享网master [localhost:22031]&gt; create table t6(my_row_id int not null ,c1 int); ERROR 4108 (HY000): Failed to generate invisible primary key. Column ‘my_row_id’ already exists. 
当然如果 MySQL 允许创建包含名为 my_row_id 的主键的表 :
master [localhost:22031]&gt; create table t5(my_row_id int not null auto_increment primary key ,c1 int); Query OK, 0 rows affected (0.01 sec) 
当开启 GIPK 模式时,如不能直接删除不可见主键。必须显式增加一个新的主键然后再删除 GIPK
讯享网master [localhost:22031]&gt; alter table t3 drop PRIMARY KEY; ERROR 1235 (42000): This version of MySQL doesn‘t yet support ’existing primary key drop without adding a new primary key. In @@sql_generate_invisible_primary_key=ON mode table should have a primary key. Please add a new primary key to be able to drop existing primary key.‘ master [localhost:22031]&gt; alter table t3 drop PRIMARY KEY,add primary key(id); ERROR 4111 (HY000): Please drop primary key column to be able to drop generated invisible primary key. master [localhost:22031]&gt; alter table t3 drop column my_row_id,add primary key(id); Query OK, 0 rows affected (0.02 sec) Records: 0 Duplicates: 0 Warnings: 0 
需要注意的是 set sql_generate_invisible_primary_key=on|off 并不会被复制到从库,主库上开启该特性的话,从库并不会开启 GIPK 。也就是说从库也不会为任何在源库上没有创建主键的表创建主键。可能会有读者疑问如果主库关闭该特性,但是从库显示开启呢? 做个测试看看在 master 上关闭该特性并且创建无主键表t6
master [localhost:22031]&gt; set sql_generate_invisible_primary_key=off; Query OK, 0 rows affected (0.00 sec) master [localhost:22031]&gt; master [localhost:22031]&gt;show variables like ’sql_generate_invisible_primary_key‘; +————————————+——-+ | Variable_name | Value | +————————————+——-+ | sql_generate_invisible_primary_key | OFF | +————————————+——-+ 1 row in set (0.00 sec) master [localhost:22031]&gt; show tables; +—————-+ | Tables_in_test | +—————-+ | t1 | | t2 | | t3 | | t4 | | t5 | +—————-+ 5 rows in set (0.00 sec) master [localhost:22031]&gt; create table t6(id int ,c1 int); Query OK, 0 rows affected (0.01 sec) master [localhost:22031]&gt; show create table t6\G * 1. row * 
讯享网 Table: t6 
Create Table: CREATE TABLE t6 ( id int DEFAULT NULL, c1 int DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
在从库上开启该特性
slave1 [localhost:22032]&gt; show tables; +—————-+ | Tables_in_test | +—————-+ | t1 | | t2 | | t3 | | t4 | | t5 | +—————-+ 5 rows in set (0.00 sec) slave1 [localhost:22032]&gt; set sql_generate_invisible_primary_key=on; Query OK, 0 rows affected (0.00 sec) slave1 [localhost:22032]&gt; show variables like ’sql_generate_invisible_primary_key‘; +————————————+——-+ | Variable_name | Value | +————————————+——-+ | sql_generate_invisible_primary_key | ON | +————————————+——-+ 1 row in set (0.00 sec) slave1 [localhost:22032]&gt; show create table t6\G * 1. row * 
讯享网 Table: t6 
Create Table: CREATE TABLE t6 ( id int DEFAULT NULL, c1 int DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
结果: 主库关闭 GIPK ,从库开启 GIPK ,源库上创建无主键表,从库上并不会主动为该表创建主键。
大多数实例会进行逻辑备份,如果 开启GIPK 模式时,MySQL 8.0.30 版本的 mysqldump 提供的 –skip-generated-invisible-primary-key 选项会忽略 GIPK 信息。简单来说,mysqldump 时 不带该参数,逻辑导出的数据会包含隐式主键,如果带上该参数,则不带隐式主键。

  1. 只支持 InnoDB 存储引擎。
  2. 支持 row 模式复制,不支持 statement 模式复制。
  3. my_row_id 成为系统关键字。
总体而言,该特性绝对是强需求。毕竟林子大了,什么样的情况都可能会发生。运(chu)维(li)经(gu)验(zhang)比较丰富 DBA 而言,MySQL 数据库稳定性深受无主键之苦,对于自建场景尤其是没有审核流程的公司而言,该特性能提升数据库系统稳定性和安全性。
  1. dev.mysql.com/doc/refma
  2. dev.mysql.com/doc/refma
小讯
上一篇 2025-06-14 09:47
下一篇 2025-04-29 17:55

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/175256.html