性能调优 8. MySQL全局优化与MySQL8.0新增特性详解

1. MySQL优化成本图


在这里插入图片描述

从上图可以看出SQL及索引的优化效果是最好的,而且成本最低,所以工作中要在这块花更多时间。


2. 配置文件my.ini或my.cnf的一些全局参数


‌‌‌  假设服务器配置为

‌‌‌  CPU:32核

‌‌‌  内存:64G

‌‌‌  DISK:2T SSD

‌‌‌  下面参数都是服务端参数,默认在配置文件的 [mysqld] 标签下

‌‌‌  1. max_connections=3000 能支持最大客户端连接

‌‌‌  连接的创建和销毁都需要系统资源,比如内存、文件句柄,业务说的支持多少并发,指的是每秒请求数,也就是QPS。
‌‌‌  一个连接最少占用内存是256K,最大是64M,如果一个连接的请求数据超过64MB(比如排序),就会申请临时空间,放到硬盘上

‌‌‌  如果3000个用户同时连上MySQL,最小需要内存3000*256KB=750M,最大需要内存3000*64MB=192G。

‌‌‌  如果innodb_buffer_pool_size是40GB(一般占用操作系统60-70%内存,估算出操作系统最多60G左右),给操作系统分配4G,给连接使用的最大内存不到20G,如果连接过多,使用的内存超过20G,将会产生磁盘SWAP,此时将会影响性能。连接数过高,不一定带来吞吐量的提高,而且可能占用更多的系统资源。

‌‌‌  2. max_user_connections=2980

‌‌‌  最大连接数中,允许用户连接的最大数量,剩余连接数用作DBA管理。

‌‌‌  3. back_log=300

‌‌‌  MySQL能够暂存的连接数量。如果MySQL的连接数达到max_connections时,新的请求将会被存在堆栈中,等待某一连接释放资源,该堆栈数量即back_log,如果等待连接的数量超过back_log,将被拒绝。

‌‌‌  4. wait_timeout=300

‌‌‌  指的是**app应用通过jdbc连接MySQL进行操作完毕后,空闲300秒后断开,默认是28800,单位秒,即8个小时。

‌‌‌  5. interactive_timeout=300

‌‌‌  指的是**mysql client连接MySQL进行操作完毕后,空闲300秒后断开,默认是28800,单位秒,即8个小时。

‌‌‌  6. innodb_thread_concurrency=64

‌‌‌  此参数用来设置innodb线程的并发数,比如处理请求连接,默认值为0表示不被限制,若要设置则与服务器的CPU核心数相同或是CPU的核心数的2倍,如果超过配置并发数,则需要排队,这个值不宜太大,不然可能会导致线程之间锁争用严重,影响性能。

‌‌‌  7. innodb_buffer_pool_size=40G

‌‌‌  innodb存储引擎下,Buffer Pool缓存大小,一般为物理内存的60%-70%。

‌‌‌  8. innodb_lock_wait_timeout=10

‌‌‌  nnodb存储引擎下,行锁锁定时间,默认50s,根据公司业务定,没有标准值。

‌‌‌  9. innodb_flush_log_at_trx_commit=1

‌‌‌  innodb存储引擎下,Redo Log的写入策略看上面文章。

‌‌‌  10. sync_binlog=1

‌‌‌  innodb存储引擎下,Bin Log写入磁盘机制

‌‌‌  11. sort_buffer_size=4M

‌‌‌  每个需要排序的线程分配该大小的一个缓冲区。增加该值可以加速ORDER BY或GROUP BY操作(跟前面Mylsq索引优化实战二文章讲到Using filesort文件排序有关)。

‌‌‌  sort_buffer_size是一个connection级的参数,在每个connection(session)第一次需要使用这个buffer的时候,一次性分配设置的内存。

‌‌‌  sort_buffer_size:并不是越大越好,由于是connection级的参数,过大的设置+高并发可能会耗尽系统的内存资源。例如:500个连接将会消耗500*sort_buffer_size(4M)=2G

‌‌‌  12. join_buffer_size=4M

‌‌‌  用于表关联缓存的大小,和sort_buffer_size一样,该参数对应的分配内存也是每个连接独享(跟前面Mylsq索引优化实战二文章讲到表关联有关)。


3. Mysql8.0新特性详解


‌‌‌  建议使用8.0.17及之后的版本,更新的内容比较多。


新增降序索引


‌‌‌  MySQL在语法上很早就已经支持降序索引,但实际上创建的仍然是升序索引。如下MySQL5.7版本下,创建表t3,c2字段降序,但是从show create table看c2仍然是升序。MySQL8.0版本下同样方式创建表,可以看到,c2字段降序。

MySQL5.7演示


‌‌‌  创建演示表


‌‌‌  create table t3 (c1 int,c2 int,index idx_c1_c2(c1,c2 desc));

‌‌‌  insert into t3 (c1,c2) values (1,10), (2,50), (3,50), (4,100), (5,80);


‌‌‌  查询创建的表信息


‌‌‌  show create table t3

‌‌‌  可以看到c2没有降序的标识,还是升序。


‌‌‌  CreateTable:CREATETABLE`t3`(
‌‌‌  `c1`int(11)DEFAULTNULL,
‌‌‌  `c2`int(11)DEFAULTNULL,
‌‌‌  KEY`idx_c1_c2`(`c1`,`c2`)-
‌‌‌  )ENGINE=InnoDB DEFAULT CHARSET=latin1

‌‌‌  explain查看SQL语句执行计划



‌‌‌  explain select * fromt order by c1,c2 desc; 

‌‌‌  可以发现也会使用索引,但是Extra字段里有filesort文件排序。

MySQL8.0演示


‌‌‌  创建演示表


‌‌‌  create table t3 (c1 int,c2 int,index idx_c1_c2(c1,c2 desc));

‌‌‌  insert into t3 (c1,c2) values (1,10), (2,50), (3,50), (4,100), (5,80);

‌‌‌  查询创建的表信息


‌‌‌  show create table t3

‌‌‌  可以看到c2字段有DESC标识是降序索引



‌‌‌  CREATE TABLE `t3` (
  `c1` int DEFAULT NULL,
  `c2` int DEFAULT NULL,
  KEY `idx_c1_c2` (`c1`,`c2` DESC)
‌‌‌  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci


‌‌‌  explain查看SQL语句执行计划

‌‌‌  Extra字段里没有filesort文件排序,说明充分利用了降序索引


‌‌‌  explain select * from t3 order by c1,c2 desc;

‌‌‌  Extra字段里有Backward index scan,意思是反向扫描索引,因为反向扫描索引下刚好满足,c1就是降序,c2是升序。


‌‌‌  explain select * from t3 order by c1 desc,c2;

‌‌‌  Extra字段里有Backward index scan,意思是反向扫描索引,因为反向扫描索引下刚好满足,c1就是降序,c2是升序。


‌‌‌  explain select * from t3 order by c1 desc,c2;

‌‌‌  Extra字段里有filesort文件排序,排序必须按照每个字段定义的排序或按相反顺序才能充分利用索引


‌‌‌  explain select * from t3 order by c1 desc,c2 desc;

‌‌‌  Extra字段里有filesort文件排序,排序必须按照每个字段定义的排序或按相反顺序才能充分利用索引


‌‌‌  explain select * from t3 order by c1,c2;

‌‌‌  注意

‌‌‌  1. 只有Innodb存储引擎支持降序索引

‌‌‌  2. 这些查询根据降序索引排序数据返回。可以这样理解,走联合索引下,根据左前列原理,一个个字段,是根据降序或者升序方式取数据返回。


新增Group By 不再隐式排序


‌‌‌  MySQL8.0对于Group By字段不再隐式排序(不会在先排序在分组)。如需要排序,必须显式加上Order By子句。

MySQL5.7演示



‌‌‌  select count(*),c2 from t3 group by c2;

‌‌‌  +----------+------+
	| count(*) | c2   |
	‌‌‌  +----------+------+
	|        1 |   10 |
	|        2 |   50 |
	|        1 |   80 |
	|        1 |  100 |
	‌‌‌  +----------+------+
‌‌‌  4 rows in set (0.00 sec)

MySQL8.0演示



‌‌‌  select count(*),c2 from t3 group by c2;


	‌‌‌+----------+------+
	| count(*) | c2   |
	+----------+------+
	|        1 |   10 |
	|        2 |   50 |
	|        1 |  100 |
	|        1 |   80 |
	+----------+------+
	4 rows in set (0.00 sec)

‌‌‌  8.0版本group by不再默认排序,需要自己加order by


‌‌‌  select count(*),c2 from t3 group by c2 order by c2;

	+----------+------+
	| count(*) | c2   |
	+----------+------+
	|        1 |   10 |
	|        2 |   50 |
	|        1 |   80 |
	|        1 |  100 |
	+----------+------+
	4 rows in set (0.00 sec)
	


新增隐藏索引


‌‌‌  使用invisible关键字在创建表或者进行表变更中设置索引为隐藏索引。索引隐藏只是不可见,但是数据库后台还是会维护隐藏索引的,在查询时优化器不使用该索引,即使用force index,优化器也不会使用该索引,同时优化器也不会报索引不存在的错误,因为索引仍然真实存在,必要时,也可以把隐藏索引快速恢复成可见。

‌‌‌  软删除就可以使用隐藏索引,比如我们觉得某个索引没用了,删除后发现这个索引在某些时候还是有用的,于是又得把这个索引加回来,如果表数据量很大的话,这种操作耗费时间是很多的,成本很高,这时,我们可以将索引先设置为隐藏索引,等到真确认索引没用了再删除。

‌‌‌  创建演示表,设置c2字段为隐藏索引


‌‌‌  create table t4(c1 int,c2 int,index idx_c1(c1),index idx_c2(c2) invisible);

‌‌‌  查看表索引的信息,可以看到索引是不是隐藏的


‌‌‌  show index from t4;

‌‌‌  可以看到c2字段Visible=No,是隐藏索引

在这里插入图片描述

‌‌‌  SQL语句没使用隐藏索引,执行计划可以查出能使用到索引


‌‌‌  explain select * from t4 where c1=1;

在这里插入图片描述

‌‌‌  SQL语句使用隐藏索引,执行计划查出索引不被使用


‌‌‌  explain select * from t4 where c2=1;

在这里插入图片描述

‌‌‌  会话中设置隐藏显示索引方法


‌‌‌  变更表取消隐藏指定的索引


 alter table t4 alter index idx_c2 visible;

‌‌‌  变更表隐藏指定的索引


‌‌‌  alter table t4 alter index idx_c2 invisible;

‌‌‌  让MySQL的优化器执行隐藏索引方法

‌‌‌  先查看各种参数的设置,可以看到一个参数use_invisible_indexes=off,默认是off。


‌‌‌  select@@optimizer_switch;

在这里插入图片描述

‌‌‌  use_invisible_indexes=on开启后,所有隐藏索引在优化器上是可见的。

‌‌‌  有种优化就是,可以先建隐藏索引,线上要用隐藏索引,就开启这个参数。 开启该参数后,优先级比单独设置隐藏索引高,即前面即使修改表单独设置索引隐藏,查询表索引信息也看到字段是隐藏。但是这边参数开启,优化器还是会走隐藏索引。

‌‌‌  会话级别下开启use_invisible_indexes


‌‌‌  set session optimizer_switch="use_invisible_indexes=on";

‌‌‌  设置全局级别,好像不生效。没有测试


‌‌‌  set  GLOBAL  optimizer_switch="use_invisible_indexes=on";

‌‌‌  注意

‌‌‌  1. 主键不能设置为invisible(隐藏索引)


新增函数索引


‌‌‌  如果在查询中加入了函数,索引不生效,所以MySQL8引入了函数索引,MySQL8.0.13开始支持在索引中使用函数(表达式)的值。

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

‌‌‌  创建演示表


‌‌‌  create table t5 (c1 varchar(10),c2 varchar(10));

‌‌‌  创建普通索引


‌‌‌  create index idx_c1 on t5(c1);

‌‌‌  创建一个大写的函数索引


‌‌‌  create index func_idx on t5((UPPER(c2)));

‌‌‌  查看表索引信息


‌‌‌  show index from t5

‌‌‌  可以看到Expression:upper(c2)表示有函数表达式



        Table: t5
   Non_unique: 1
     Key_name: func_idx
 Seq_in_index: 1
  Column_name: NULL
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
‌‌‌  Index_comment: 
      Visible: YES
   Expression: upper(`c2`)    --函数表达式

‌‌‌  没创建函数索引字段的索引,使用函数。查看执行计划,看到索引失效


‌‌‌  explain select * from t5 where upper(c1)='ZHUGE';

在这里插入图片描述

‌‌‌  创建函数索引字段的索引,使用函数。效查看执行计划,会走索引


‌‌‌  explain select* from t5 where upper(c2)='ZHUGE';

在这里插入图片描述

‌‌‌  注意

‌‌‌  1. 个人还是不建议使用函数索引,有点鸡肋


新增innodb存储引擎select for update跳过锁等待


‌‌‌  对于select…for share(8.0新增加查询共享锁的语法)或select…for update,在语句后面添加NOWAIT、SKIP LOCKED语法可以跳过锁等待,或者跳过锁定

‌‌‌  在5.7及之前的版本,select…for update,如果获取不到锁,会一直等待,直到innodb_lock_wait_timeout超时。

‌‌‌  在8.0版本,通过添加no wait,skip locked语法,能够立即返回。如果查询的行已经加锁,那么no wait会立即报错返回,而skip locked也会立即返回,只是返回的结果中不包含被锁定的行

‌‌‌  应用场景比如查询余票记录,如果某些记录已经被锁定,用skip locked可以跳过被锁定的记录,只返回没有锁定的记录,提高系统性能

‌‌‌  开启一个会话session1。

‌‌‌  创建演示表,插入数据。


‌‌‌  create table t6 (c1 int,c2 int);

‌‌‌  insert into t6 (c1,c2) values (1,10), (2,50), (3,50), (4,100), (5,80);

‌‌‌  开启事务,修改数据不提交事务。


‌‌‌  begin;

# 锁定第二条记录

‌‌‌  update t6 set c2=60 where c1=2;

‌‌‌  开启另外一个会话session2。

‌‌‌  查询session1修改的数据,发现等待超时。


‌‌‌  select * from t6 where c1=2 for update;

> Affected rows: 0
> 时间: 50.481s

‌‌‌  nowait演示

‌‌‌  加上nowait,查询session1修改的数据,查询立即返回,但是没有数据。


‌‌‌  select * from t1 where c1=2 for update nowait;

> Affected rows: 0
> 时间: 0s

‌‌‌  加上nowait,查询所有数据,查询立即返回,也是没有数据。


‌‌‌  select * from t6 where  for update nowait;

> Affected rows: 0
> 时间: 0s

‌‌‌  结论

‌‌‌  可以得到查询加上update nowait能跳过锁等待,但是查询都不会返回数据,即使查询不是加锁的数据也不会返回数据。

‌‌‌  skip locked演示

‌‌‌  查询session1修改的数据加上skip locked,查询立即返回,但是没有数据。


‌‌‌  select * from t6 where c1=2 for update skip locked;

在这里插入图片描述

‌‌‌  查询所有数据加上skip locked,查询立即返回,过滤掉了加锁的第二条记录。

在这里插入图片描述


‌‌‌  select * from t6 for update skip locked;

‌‌‌  结论

‌‌‌  可以得到查询加上skip locked能跳过锁等待,查询数据会过滤掉加锁的数据返回。


新增死锁检查控制


‌‌‌  其实在MySQL5.7.15开始就增加了一个新的动态变量innodb_deadlock_detect,用于控制系统是否执行InnoDB死锁检查,默认是打开的。死锁检测会耗费数据库性能的,对于高并发的系统,可以关闭死锁检测功能,提高系统性能。但是要确保系统极少情况会发生死锁,同时要将锁等待超时参数调小一点,以防出现死锁等待过久的情况。

‌‌‌  用命令查看,可以看到默认是打开的


	
‌‌‌  show variables like '%innodb_deadlock_detect%'; 

‌‌‌  打开MySQL配置文件(通常是my.cnf或my.ini)。

‌‌‌  在配置文件中找到[mysqld]部分,并添加以下行关闭配置。


‌‌‌  innodb_deadlock_detect=OFF


新增Undo Log文件不再使用系统表空间


‌‌‌  新增默认创建2个undo文件表空间,不再使用系统表空间。意思是早期innodb_undo_tablespaces为0下undo日志原来都在ibdata1文件中。现在默认innodb_undo_tablespaces为2下,undo日志数据会在undo_001,undo_002两个文件中。

在这里插入图片描述


新增窗口函数(WindowFunctions):也称分析函数


‌‌‌  从MySQL8.0开始,新增了一个叫窗口函数的概念。它可以用来实现若干新的查询方式,窗口函数与SUM()、COUNT()这种分组聚合函数类似,在聚合函数后面加上over()就变成窗口函数了,在括号里可以加上partition by等分组关键字指定如何分组,窗口函数即便分组也不会将多行查询结果合并为一行而是将结果放回多行当中,即窗口函数不需要再使用GROUP BY

‌‌‌  创建演示表,插入一些示例数据


‌‌‌  CREATE TABLE `account_channel` (
‌‌‌  `id` int NOT NULL AUTO_INCREMENT,
‌‌‌  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '姓名',
‌‌‌  `channel` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '账户渠道',
‌‌‌  `balance` int DEFAULT NULL COMMENT '余额',
‌‌‌  PRIMARY KEY (`id`) ) ENGINE=InnoDB


‌‌‌  INSERT INTO `mytest`.`account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('1', 'zhuge', 'wx',
‌‌‌  '100');

‌‌‌  INSERT INTO `mytest`.`account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('2', 'zhuge', 'alipay',
‌‌‌  '200');

‌‌‌  INSERT INTO `mytest`.`account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('3', 'zhuge', 'yinhang',
‌‌‌  '300');

‌‌‌  INSERT INTO `mytest`.`account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('4', 'lilei', 'wx',
‌‌‌  '200');

‌‌‌  INSERT INTO `mytest`.`account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('5', 'lilei', 'alipay',
‌‌‌  '100');

‌‌‌  INSERT INTO `mytest`.`account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('6', 'hanmeimei', 'wx',
‌‌‌  '500');


‌‌‌  基本查询


‌‌‌  select * from account_channel;

在这里插入图片描述

‌‌‌  不使用窗口函数,进行合计分组


‌‌‌  select name,sum(balance) from account_channel group by name;

‌‌‌  使用窗口函数,在聚合函数后面加上OVER()就变成分析函数了。

‌‌‌  后面可以不用再加Group By制定分组,因为在OVERr里已经用partition关键字指明了如何分组计算,这种可以保留原有表数据的结构,不会像分组聚合函数那样每组只返回一条数据


‌‌‌  select name,channel,balance,sum(balance) over(partition by name) as sum_balance from account_channel;

在这里插入图片描述


‌‌‌  select name,channel,balance,avg(balance) over(partition by name) as avg_balance from account_channel;

在这里插入图片描述

‌‌‌  partition by配合Order By排序。每行数据分组字段值是按分组后排序累加,不是分组后一个组的值累加。


‌‌‌  select name,channel,balance,sum(balance) over(partition by name order by balance) as sum_balance from account_channel;

在这里插入图片描述

‌‌‌  OVER()里如果不加条件,则默认使用整个表的数据做运算。


‌‌‌  select name,channel,balance,sum(balance) over() as sum_balance from account_channel;

在这里插入图片描述

‌‌‌专用窗口函数


‌‌‌  作用于窗口中的函数,也是用在OVER()之前

‌‌‌  序号函数:ROW_NUMBER()、RANK()、DENSE_RANK()

‌‌‌  分布函数:PERCENT_RANK()、CUME_DIST()

‌‌‌  前后函数:LAG()、LEAD()

‌‌‌  头尾函数:FIRST_VALUE()、LAST_VALUE()

‌‌‌  其它函数:NTH_VALUE()、NTILE()

‌‌‌  例1. 按照balance字段排序,展示序号


‌‌‌  select name,channel,balance,ROW_NUMBER() OVER(order by balance) as row_number1 from account_channel;

在这里插入图片描述

‌‌‌  例2. 聚合函数和专用窗口函数可以搭配窗口同时使用。同理多个聚合函数或者专用窗口函数也可以搭配窗口一起使用。

‌‌‌  如按照名称分组,然后求balance平均值,之后按照name字段排序,展示序号。



‌‌‌  SELECT name, channel, balance, 
    AVG(balance) OVER(partition by name) AS avg_balance,
    ROW_NUMBER() OVER (ORDER BY name) AS row_number1 
‌‌‌  FROM account_channel;


在这里插入图片描述

‌‌‌  例3. 按照balance字段排序,first_value()选排序的balance第一个值

‌‌‌ 
select name,channel,balance,first_value(balance) over(order by balance) as first1 from account_channel;

在这里插入图片描述

‌‌‌  注意

‌‌‌  1. 专用窗口函数一定要搭配窗口OVER()使用。

‌‌‌  2. 数据量大下,窗口函数很慢,不建议使用。如并发场景下,不用窗口,查询报表等节省操作可以用。


新增默认字符集由latin1变为utf8mb4


‌‌‌  在8.0版本之前,默认字符集为latin1,utf8指向的是utf8mb3,8.0版本默认字符集为utf8mb4,utf8默认指向的是utf8mb4


新增MyISAM系统表全部换成InnoDB表


‌‌‌  将系统表和数据字典表全部改为InnoDB存储引擎。前面版本的MySQL有些表的存储引擎使用是MyISAM存储引擎,现在都用InnoDB存储引擎。


新增元数据存储变动


‌‌‌  MySQL8.0删除了之前版本的元数据文件,例如存储表结构的.frm等文件,全部集中放入.ibd文件里。


新增自增变量持久化


‌‌‌  在8.0之前的版本,在MySQL重启后,会重置自增主键值AUTO_INCREMENT=max(primarykey)+1,即重置为,按当前数据最大自增主键值加1。

‌‌‌  这种现象在某些情况下会导致业务主键冲突或者其他难以发现的问题。自增主键重启重置的问题很早就被发现(https://bugs.mysql.com/bug.php?id=199),一直到8.0才被解决,8.0版本将会对AUTO_INCREMENT值进行持久化,MySQL重启后,该值将不会改变

‌‌‌  原理

‌‌‌  8.0的Redo Log一直记录最大自增主键值加1,每次修改都会记录AUTO_INCREMENT=max(primarykey)+1(当前最大自增主键值加1),修复重启自增问题,


MySQL 5.7演示


‌‌‌  准备演示表。


‌‌‌  create table t7(id int auto_increment primary key,c1 varchar(20));

‌‌‌  插入数据。


‌‌‌  insert into t7(c1) values('zhuge1'),('zhuge2'),('zhuge3');

‌‌‌  查询数据。


‌‌‌  select * from t7;


‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+
	| id | c1 |
	+‐‐‐‐+‐‐‐‐‐‐‐‐+
	| 1 | zhuge1 |
	| 2 | zhuge2 |
	| 3 | zhuge3 |
‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+

‌‌‌  删除当前最大主键值的数据。


‌‌‌  delete from t7 where id = 3;



‌‌‌  select * from t7;


	 +‐‐‐‐+‐‐‐‐‐‐‐‐+
	 | id | c1 |
	 +‐‐‐‐+‐‐‐‐‐‐‐‐+
	 | 1 | zhuge1 |
	 | 2 | zhuge2 |
	 +‐‐‐‐+‐‐‐‐‐‐‐‐+

‌‌‌  重启MySQL服务,并重新连接MySQL,插入一条数据,发现插入数据的主键值重置了,是按当前最大主键值计算。


‌‌‌  insert into t7(c1) values('zhuge4');


‌‌‌  select * from t7;


‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+
	| id | c1 |
	‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+
	| 1 | zhuge1 |
	| 2 | zhuge2 |
	| 3 | zhuge4 |
‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+


‌‌‌  这样就有个问题,修改一条数据的id比当前最大主键值大,后面插入数据主键递增基础的值,是按重启后当前最大主键值来计算的,后面插入数据就会报主键冲突。


‌‌‌  update t7 set id = 5 where c1 = 'zhuge1';


‌‌‌  select * from t7;

	 +‐‐‐‐+‐‐‐‐‐‐‐‐+
	 | id | c1 |
	 +‐‐‐‐+‐‐‐‐‐‐‐‐+
	 | 2 | zhuge2 |
	 | 3 | zhuge4 |
	 | 5 | zhuge1 |
	 +‐‐‐‐+‐‐‐‐‐‐‐‐+

‌‌‌  插入一条数据


‌‌‌  insert into t7(c1) values('zhuge5');

‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+
	| id | c1 |
	‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+
	| 2 | zhuge2 |
	| 3 | zhuge4 |
	| 4 | zhuge5 |
	| 5 | zhuge1 |
‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+

‌‌‌  再插入一条数据,主键重复报错。


‌‌‌  insert into t(c1) values('zhuge6');
‌‌‌  ERROR 1062 (23000): Duplicate entry '5' for key 'PRIMARY'


MySQL 8.0演示


‌‌‌  准备演示表。


‌‌‌  create table t8(id int auto_increment primary key,c1 varchar(20));

‌‌‌  插入数据。


‌‌‌  insert into t8(c1) values('zhuge1'),('zhuge2'),('zhuge3');

‌‌‌  删除当前最大主键值的数据。


‌‌‌  delete from t8 where id = 3;



‌‌‌  select * from t8;


	 +‐‐‐‐+‐‐‐‐‐‐‐‐+
	 | id | c1 |
	 +‐‐‐‐+‐‐‐‐‐‐‐‐+
	 | 1 | zhuge1 |
	 | 2 | zhuge2 |
	 +‐‐‐‐+‐‐‐‐‐‐‐‐+

‌‌‌  重启MySQL服务,并重新连接MySQL,插入一条数据,发现插入数据的主键值是按之前最大主键值为基础递增。


‌‌‌  insert into t8(c1) values('zhuge4');


‌‌‌  select * from t8;


‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+
	| id | c1 |
	‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+
	| 1 | zhuge1 |
	| 2 | zhuge2 |
	| 4 | zhuge4 |
‌‌‌  +‐‐‐‐+‐‐‐‐‐‐‐‐+


‌‌‌  修改一条数据的id比当前最大主键值大。


‌‌‌  update t8 set id = 5 where c1 = 'zhuge1';


‌‌‌  select * from t8;

  | id | c1 |
  +‐‐‐‐+‐‐‐‐‐‐‐‐+
  | 2 | zhuge2 |
  | 4 | zhuge4 |
  | 5 | zhuge1 |
  +‐‐‐‐+‐‐‐‐‐‐‐‐+

‌‌‌  插入一条数据,id是按当前数据的最大主键值递增,不会再有主键冲突问题。


‌‌‌  insert into t8(c1) values('zhuge5');



 select * from t8;
 +‐‐‐‐+‐‐‐‐‐‐‐‐+
 
 | id | c1 |
 +‐‐‐‐+‐‐‐‐‐‐‐‐+
 | 2 | zhuge2 |
 | 4 | zhuge4 |
 | 5 | zhuge1 |
 | 6 | zhuge5 |
 +‐‐‐‐+‐‐‐‐‐‐‐‐+
 

新增DDL语句支持原子化


‌‌‌  InnoDB表的SQL的数据定义语言DDL语句支持事务完整性,要么都成功要么回滚。

‌‌‌  MySQL8.0开始支持原子DDL操作,其中与表相关的原子DDL只支持InnoDB存储引擎(所以8.0的表都改成InnoDB存储引擎)。

‌‌‌  一个原子DDL操作内容包括:更新数据字典,存储引擎层的操作,在Bin Log中记录DDL操作等。

‌‌‌  原子DDL操作支持的DDL语句有

‌‌‌  支持与表相关的DDL:数据库、表空间、表、索引的CREATE、ALTER、DROP以及TRUNCATETABLE。

‌‌‌  支持的其它DDL:存储程序、触发器、视图、UDF的CREATE、DROP以及ALTER语句。

‌‌‌  支持账户管理相关的DDL:用户和角色的CREATE、ALTER、DROP以及适用的RENAME等等。


MySQL 5.7演示


‌‌‌  查看当前数据库存在的表


‌‌‌  show tables;


‌‌‌  +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
	| Tables_in_test |
	‌‌‌+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
	| account |
	| actor |
	| employee |
	| film |
	| film_actor |
	| leaf_id |
	| t1 |
	| test_innodb |
	| test_myisam |
	| test_order_id |
	‌‌+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+

‌‌‌  删除不存在的表,报错但是不会回滚,t1表会被删除。


‌‌‌  drop table t1,t2;


MySQL 8.0演示


‌‌‌  查看当前数据库存在的表


‌‌‌  show tables;


‌‌‌  +‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
	| Tables_in_test |
	‌‌‌+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+
	| account |
	| actor |
	| employee |
	| film |
	| film_actor |
	| leaf_id |
	| test_innodb |
	| test_myisam |
	| test_order_id |
	‌‌+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+

‌‌‌  删除不存在的表,报错会回滚,test_innodb表还存在。


‌‌‌  drop table test_innodb,t2;


新增参数修改持久化


‌‌‌  MySQL8.0版本支持在线修改全局参数并持久化,通过加上PERSIST关键字,可以将修改的参数持久化到新的配置文件(mysqld-auto.cnf)中,重启MySQL时,可以从该配置文件获取到最新的配置参数。

‌‌‌  set GLOBAL设置的变量参数在MySQL重启后会失效

‌‌‌  如


‌‌‌  set persist innodb_lock_wait_timeout=25;

‌‌‌  系统会在数据目录下生成一个包含json格式的mysqld‐auto.cnf 的文件,格式化后如下所示。


‌‌‌  {
	"Version": 1,
	"mysql_server": {
		"innodb_lock_wait_timeout": {
			"Value": "25",
			"Metadata": {
				"Timestamp": 1675290252103863,
				"User": "root",
				"Host": "localhost"
			}
		}
	}
‌‌‌  }

‌‌‌  当my.cnf或者my.ini 没有配置全局变量,则上面的全局变量就写在mysqld‐auto.cnf文件中,没有就新建。

‌‌‌  当my.cnf或者my.ini和mysqld‐auto.cnf 同时存在全局变量时,后者具有更高优先级。

‌‌‌

  • 39
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值