从上图可以看出 SQL 及索引的优化效果是最好的,而且成本最低,所以工作中我们要在这块花更多时
间。
补充一点配置文件 my.ini 或 my.cnf 的全局参数: 假设服务器配置为: 下面参数都是服务端参数,默认在配置文件的 [mysqld] 标签下
连接的创建和销毁都需要系统资源,比如内存、文件句柄,业务说的支持多少并发,指的是每秒请求 数,也就是 QPS 。
Mysql 全局优化与 Mysql 8.0&Mysql9.0 新特性详解
Mysql 全局优化总结
CPU : 32 核 内存: 64G DISK : 2T SSD mysql server 系统参数
https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_max_connections
max_connections=30001
一个连接最少占用内存是 256K ,最大是 64M ,如果一个连接的请求数据超过 64MB (比如排序),就 会申请临时空间,放到硬盘上。 如果 3000 个用户同时连上 mysql ,最小需要内存 3000256KB=750M ,最大需要内存 300064MB=192G 。 如果 innodb_buffer_pool_size 是 40GB ,给操作系统分配 4G ,给连接使用的最大内存不到 20G ,如果连 接过多,使用的内存超过 20G ,将会产生磁盘 SWAP ,此时将会影响性能。连接数过高,不一定带来吞 吐量的提高,而且可能占用更多的系统资源。 允许用户连接的最大数量,剩余连接数用作 DBA 管理。 MySQL 能够暂存的连接数量。如果 MySQL 的连接数达到 max_connections 时,新的请求将会被存在堆 栈中,等待某一连接释放资源,该堆栈数量即 back_log ,如果等待连接的数量超过 back_log ,将被拒 绝。
指的是 app 应用通过 jdbc 连接mysql 进行操作完毕后,空闲 300 秒后断开,默认是 28800 ,单位秒,即 8 个小时。
指的是 mysql client 连接 mysql 进行操作完毕后,空闲 300 秒后断开,默认是 28800 ,单位秒,即 8 个小 时。
max_user_connections=29801
back_log=3001
wait_timeout=3001
interactive_timeout=3001
sort_buffer_size=4M1
每个需要排序的线程分配该大小的一个缓冲区。增加该值可以加速 ORDER BY 或 GROUP BY 操作。 sort_buffer_size 是一个 connection 级的参数,在每个 connection ( session )第一次需要使用这个 buffer 的时候,一次性分配设置的内存。 sort_buffer_size :并不是越大越好,由于是 connection 级的参数,过大的设置 + 高并发可能会耗尽系统 的内存资源。例如: 500 个连接将会消耗 500*sort_buffer_size(4M)=2G 。 用于表关联缓存的大小,和 sort_buffer_size 一样,该参数对应的分配内存也是每个连接独享。
innodb 相关参数
此参数用来设置 innodb 线程的并发数,默认值为 0 表示不被限制,若要设置则与服务器的 CPU 核心数相 同或是 CPU 的核心数的 2 倍,如果超过配置并发数,则需要排队,这个值不宜太大,不然可能会导致线 程之间锁争用严重,影响性能。 innodb 存储引擎 buffer pool 缓存大小,一般为物理内存的 60%-70% 。 内存大小直接反应数据库的性能。 如何判断当前数据库的内存是否已经达到瓶颈了呢 ? 可以通过查看当前服务器的状态,比较物理磁盘的读取和内存读取的比例来判断缓冲池的命中率,通 常 InnoDB 存储引擎的缓冲池的命中率不应该小于 99% ,如 : 当前服务器的状态参数:
join_buffer_size=4M1
innodb 参数
https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_buffer_pool_si
ze
innodb_thread_concurrency=641
innodb_buffer_pool_size=40G1
mysql> show global status like 'innodb%read%'\G;1
以下公式可以计算各种对缓冲池的操作 :
行锁锁定时间,默认 50s ,根据公司业务定,没有标准值。 这个参数控制 redo log 的写入策略,它有三种可能取值: 详情参看上一节课
binlog 相关参数
binlog 写入磁盘机制主要通过 sync_binlog 参数控制,默认值是 0 。 Innodb_buffer_pool_reads :表示从物理磁盘读取页的次数 Innodb_buffer_pool_read_ahead :预读的次数 Innodb_buffer_pool_read_ahead_evicted :预读的页,但是没有被读取就从缓冲池中被替换的页的数量,一般用 来判断预读的效率 Innodb_buffer_pool_read_requests :从缓冲池中读取页的次数 Innodb_data_readsInnodb_rows_read :总共读入的字节数 Innodb_data_reads :发起读取请求的次数,每次读取可能需要读取多个页
innodb_lock_wait_timeout=101
innodb_flush_log_at_trx_commit=11
设置为 0 :表示每次事务提交时都只是把 redo log 留在 redo log buffer 中,数据库宕机可能会丢失数据。 设置为 1( 默认值 ) :表示每次事务提交时都将 redo log 直接持久化到磁盘,数据最安全,不会因为数据库宕机丢失 数据,但是效率稍微差一点,线上系统推荐这个设置。 设置为 2 :表示每次事务提交时都只是把 redo log 写到操作系统的缓存 page cache 里,这种情况如果数据库宕机 是不会丢失数据的,但是操作系统如果宕机了, page cache 里的数据还没来得及写入磁盘文件的话就会丢失数 据。
binlog 参数
https://dev.mysql.com/doc/refman/8.0/en/replication-options-binary-log.html
sync_binlog=11
为 0 的时候,表示每次提交事务都只 write 到 page cache ,由系统自行判断什么时候执行 fsync 写入磁盘。虽然性 能得到提升,但是机器宕机, page cache 里面的 binlog 会丢失。
详情参看上一章
建议使用 8.0.17 及之后的版本,更新的内容比较多。 参考文档 添加弃用和删除的特性: 添加弃用和删除的参数:
Mysql8 InnoDB 架构: 【有道云笔记】 Docker 安装 MySQL8.0
MySQL 在语法上很早就已经支持降序索引,但实际上创建的仍然是升序索引,如下 MySQL 5.7 所示, c2 字段降序,但是从 show create table 看 c2 仍然是升序。 8.0 可以看到, c2 字段降序。只有 Innodb 存储 引擎支持降序索引。 也可以设置为 1 ,表示每次提交事务都会执行 fsync 写入磁盘,这种方式最安全。 还有一种折中方式,可以设置为 N(N>1) ,表示每次提交事务都 write 到 page cache ,但累积 N 个事务后才 fsync 写 入磁盘,这种如果机器宕机会丢失 N 个事务的 binlog 。
Mysql 8.0 新特性详解
https://dev.mysql.com/doc/refman/8.0/en/mysql-nutshell.html
https://dev.mysql.com/doc/refman/8.0/en/added-deprecated-removed.ht
ml
https://dev.mysql.com/doc/refman/8.0/en/innodb-architecture.html
https://share.note.youdao.com/s/ZTcFkRJ
1 、新增降序索引
# ====MySQL 5.7演示====1
mysql> create table t1(c1 int,c2 int,index idx_c1_c2(c1,c2 desc));2
Query OK, 0 rows affected (0.04 sec)3 4
mysql> insert into t1 (c1,c2) values(1, 10),(2,50),(3,50),(4,100),(5,80);5
Query OK, 5 rows affected (0.02 sec)6 7
mysql> show create table t1\G8
*************************** 1. row ***************************9 Table: t110
Create Table: CREATE TABLE `t1` (11
c1 int(11) DEFAULT NULL,12
c2 int(11) DEFAULT NULL,13
KEY idx_c1_c2 (c1,c2) --注意这里, c2 字段是升序14
) ENGINE=InnoDB DEFAULT CHARSET=latin115
1 row in set (0.00 sec)16
17
mysql> explain select * from t1 order by c1,c2 desc; --5.7也会使用索引,但是Extra字段里
有 filesort 文件排序 18 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-----------------------------+
19
| id | select_type | table | partitions | type | possible_keys | key | key_len |
ref | rows | filtered | Extra | 20 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-----------------------------+
21
| 1 | SIMPLE | t1 | NULL | index | NULL | idx_c1_c2 | 10 |
NULL | 1 | 100.00 | Using index; Using filesort | 22 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-----------------------------+
23 1 row in set, 1 warning (0.01 sec)24 25 26
# ====MySQL 8.0演示====27
mysql> create table t1(c1 int,c2 int,index idx_c1_c2(c1,c2 desc));28
Query OK, 0 rows affected (0.02 sec)29 30
mysql> insert into t1 (c1,c2) values(1, 10),(2,50),(3,50),(4,100),(5,80);31
Query OK, 5 rows affected (0.02 sec)32 33
mysql> show create table t1\G34
*************************** 1. row ***************************35
Table: t136
Create Table: CREATE TABLE `t1` (37
c1 int DEFAULT NULL,38
c2 int DEFAULT NULL,39
KEY idx_c1_c2 (c1,c2 DESC) --注意这里的区别,降序索引生效了40
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci41
1 row in set (0.00 sec)42
43
mysql> explain select * from t1 order by c1,c2 desc; --Extra字段里没有 filesort 文件排序,
充分利用了降序索引 44 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-------------+
45
| id | select_type | table | partitions | type | possible_keys | key | key_len |
ref | rows | filtered | Extra | 46 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-------------+
47
| 1 | SIMPLE | t1 | NULL | index | NULL | idx_c1_c2 | 10 |
NULL | 1 | 100.00 | Using index | 48 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-------------+
49 1 row in set, 1 warning (0.00 sec)50 51
mysql> explain select * from t1 order by c1 desc,c2; --Extra字段里有Backward index
scan ,意思是反向扫描索引; 52 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+----------------------------------+
53
| id | select_type | table | partitions | type | possible_keys | key | key_len |
ref | rows | filtered | Extra | 54 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+----------------------------------+
55
| 1 | SIMPLE | t1 | NULL | index | NULL | idx_c1_c2 | 10 |
NULL | 1 | 100.00 | Backward index scan; Using index | 56 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+----------------------------------+
57 1 row in set, 1 warning (0.00 sec)58 59
mysql> explain select * from t1 order by c1 desc,c2 desc; --Extra字段里有 filesort 文件排
序,排序必须按照每个字段定义的排序或按相反顺序才能充分利用索引 60 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-----------------------------+
61
| id | select_type | table | partitions | type | possible_keys | key | key_len |
ref | rows | filtered | Extra | 62 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-----------------------------+
63
mysql 8.0 对于 group by 字段不再隐式排序,如需要排序,必须显式加上 order by 子句。
| 1 | SIMPLE | t1 | NULL | index | NULL | idx_c1_c2 | 10 |
NULL | 1 | 100.00 | Using index; Using filesort | 64 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-----------------------------+
65 1 row in set, 1 warning (0.00 sec)66 67
mysql> explain select * from t1 order by c1,c2; --Extra字段里有 filesort 文件排序,排序
必须按照每个字段定义的排序或按相反顺序才能充分利用索引 68 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-----------------------------+
69
| id | select_type | table | partitions | type | possible_keys | key | key_len |
ref | rows | filtered | Extra | 70 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-----------------------------+
71
| 1 | SIMPLE | t1 | NULL | index | NULL | idx_c1_c2 | 10 |
NULL | 1 | 100.00 | Using index; Using filesort | 72 +----+-------------+-------+------------+-------+---------------+-----------+---------
+------+------+----------+-----------------------------+
73 1 row in set, 1 warning (0.00 sec)74 2 、 group by 不再隐式排序
# ====MySQL 5.7演示====1
mysql> select count(*),c2 from t1 group by c2;2
+----------+------+3 | count(*) | c2 |4 +----------+------+5 | 1 | 10 |6 | 2 | 50 |7 | 1 | 80 |8 | 1 | 100 |9 +----------+------+10 4 rows in set (0.00 sec)11 12 13
# ====MySQL 8.0演示====14
mysql> select count(*),c2 from t1 group by c2; --8.0版本 group by 不再默认排序15
+----------+------+16 | count(*) | c2 |17 +----------+------+18 | 1 | 10 |19 | 2 | 50 |20 | 1 | 100 |21 | 1 | 80 |22 +----------+------+23 4 rows in set (0.00 sec)24 25
mysql> select count(*),c2 from t1 group by c2 order by c2; --8.0版本 group by 不再默认排
序,需要自己加 order by 26 +----------+------+27 | count(*) | c2 |28 +----------+------+29 | 1 | 10 |30 | 2 | 50 |31 | 1 | 80 |32 | 1 | 100 |33 +----------+------+34 4 rows in set (0.00 sec)35
使用 invisible 关键字在创建表或者进行表变更中设置索引为隐藏索引。索引隐藏只是不可见,但是数 据库后台还是会维护隐藏索引的,在查询时优化器不使用该索引,即使用 force index ,优化器也不会 使用该索引,同时优化器也不会报索引不存在的错误,因为索引仍然真实存在,必要时,也可以把隐 藏索引快速恢复成可见。注意,主键不能设置为 invisible 。 软删除就可以使用隐藏索引,比如我们觉得某个索引没用了,删除后发现这个索引在某些时候还是有 用的,于是又得把这个索引加回来,如果表数据量很大的话,这种操作耗费时间是很多的,成本很 高,这时,我们可以将索引先设置为隐藏索引,等到真的确认索引没用了再删除。 3 、增加隐藏索引
创建 t2 表,里面的 c2 字段为隐藏索引1
mysql> create table t2(c1 int, c2 int, index idx_c1(c1), index idx_c2(c2) invisible);2
Query OK, 0 rows affected (0.02 sec)3 4
mysql> show index from t2\G5
*************************** 1. row ***************************6 Table: t27 Non_unique: 18 Key_name: idx_c19 Seq_in_index: 110 Column_name: c111 Collation: A12 Cardinality: 013 Sub_part: NULL14 Packed: NULL15 Null: YES16 Index_type: BTREE17 Comment: 18 Index_comment: 19 Visible: YES20 Expression: NULL21 *************************** 2. row ***************************22 Table: t223 Non_unique: 124 Key_name: idx_c225 Seq_in_index: 126 Column_name: c227 Collation: A28 Cardinality: 029 Sub_part: NULL30 Packed: NULL31 Null: YES32 Index_type: BTREE33 Comment: 34 Index_comment: 35 Visible: NO --隐藏索引不可见36 Expression: NULL37 2 rows in set (0.00 sec)38 39
mysql> explain select * from t2 where c1=1;40
+----+-------------+-------+------------+------+---------------+--------+---------+---- ---+------+----------+-------+ 41
| id | select_type | table | partitions | type | possible_keys | key | key_len |
ref | rows | filtered | Extra | 42 +----+-------------+-------+------------+------+---------------+--------+---------+---- ---+------+----------+-------+ 43
| 1 | SIMPLE | t2 | NULL | ref | idx_c1 | idx_c1 | 5 |
const | 1 | 100.00 | NULL | 44 +----+-------------+-------+------------+------+---------------+--------+---------+---- ---+------+----------+-------+ 45 1 row in set, 1 warning (0.00 sec)46 47
mysql> explain select * from t2 where c2=1; --隐藏索引 c2 不会被使用48
+----+-------------+-------+------------+------+---------------+------+---------+------
+------+----------+-------------+
49 | id | select_type | table | partitions | type | possible_keys | key | key_len | ref
| rows | filtered | Extra |
50 +----+-------------+-------+------------+------+---------------+------+---------+------
+------+----------+-------------+
51 | 1 | SIMPLE | t2 | NULL | ALL | NULL | NULL | NULL | NULL
| 1 | 100.00 | Using where |
52 +----+-------------+-------+------------+------+---------------+------+---------+------
+------+----------+-------------+
53 1 row in set, 1 warning (0.00 sec)54 55
mysql> select @@optimizer_switch\G --查看各种参数56
*************************** 1. row ***************************57 @@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,bl ock_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,f irstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_exten sions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=off,skip_sca n=on,hash_join=on 58 1 row in set (0.00 sec)59 60
mysql> set session optimizer_switch="use_invisible_indexes=on"; ----在会话级别设置查询优
化器可以看到隐藏索引 61 Query OK, 0 rows affected (0.00 sec)62 63
mysql> select @@optimizer_switch\G64
*************************** 1. row ***************************65
之前我们知道,如果在查询中加入了函数,索引不生效,所以 MySQL 8 引入了函数索引, MySQL 8.0.13 开始支持在索引中使用函数 ( 表达式 ) 的值。 函数索引基于虚拟列功能实现,在 MySQL 中相当于新增了一个列,这个列会根据你的函数来进行计算 结果,然后使用函数索引的时候就会用这个计算后的列作为索引。 @@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,bl ock_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,f irstmatch=on,duplicateweedout=on,subquery_materialization_cost_based=on,use_index_exten sions=on,condition_fanout_filter=on,derived_merge=on,use_invisible_indexes=on,skip_scan =on,hash_join=on 66 1 row in set (0.00 sec)67 68
mysql> explain select * from t2 where c2=1;69
+----+-------------+-------+------------+------+---------------+--------+---------+---- ---+------+----------+-------+ 70
| id | select_type | table | partitions | type | possible_keys | key | key_len |
ref | rows | filtered | Extra | 71 +----+-------------+-------+------------+------+---------------+--------+---------+---- ---+------+----------+-------+ 72
| 1 | SIMPLE | t2 | NULL | ref | idx_c2 | idx_c2 | 5 |
const | 1 | 100.00 | NULL | 73 +----+-------------+-------+------------+------+---------------+--------+---------+---- ---+------+----------+-------+ 74 1 row in set, 1 warning (0.00 sec)75 76
mysql> alter table t2 alter index idx_c2 visible;77
Query OK, 0 rows affected (0.02 sec)78 Records: 0 Duplicates: 0 Warnings: 079 80
mysql> alter table t2 alter index idx_c2 invisible;81
Query OK, 0 rows affected (0.01 sec)82 Records: 0 Duplicates: 0 Warnings: 083 84 4 、新增函数索引
mysql> create table t3(c1 varchar(10),c2 varchar(10));1
Query OK, 0 rows affected (0.02 sec)2 3
mysql> create index idx_c1 on t3(c1); --创建普通索引4
Query OK, 0 rows affected (0.03 sec)5 Records: 0 Duplicates: 0 Warnings: 06 7
mysql> create index func_idx on t3((UPPER(c2))); --创建一个大写的函数索引8
Query OK, 0 rows affected (0.03 sec)9 Records: 0 Duplicates: 0 Warnings: 010 11
mysql> show index from t3\G12
*************************** 1. row ***************************13 Table: t314 Non_unique: 115 Key_name: idx_c116 Seq_in_index: 117 Column_name: c118 Collation: A19 Cardinality: 020 Sub_part: NULL21 Packed: NULL22 Null: YES23 Index_type: BTREE24 Comment: 25 Index_comment: 26 Visible: YES27 Expression: NULL28 *************************** 2. row ***************************29 Table: t330 Non_unique: 131 Key_name: func_idx32 Seq_in_index: 133 Column_name: NULL34 Collation: A35 Cardinality: 036 Sub_part: NULL37 Packed: NULL38 Null: YES39
对于 select ... for share(8.0 新增加查询共享锁的语法 ) 或 select ... for update , 在语句后面添加
NOWAIT 、 SKIP LOCKED 语法可以跳过锁等待,或者跳过锁定。
在 5.7 及之前的版本, select...for update ,如果获取不到锁,会一直等待,直到
innodb_lock_wait_timeout 超时。
在 8.0 版本,通过添加 nowait , skip locked 语法,能够立即返回。如果查询的行已经加锁,那么 nowait
会立即报错返回,而 skip locked 也会立即返回,只是返回的结果中不包含被锁定的行。
Index_type: BTREE40
Comment: 41
Index_comment: 42
Visible: YES43
Expression: upper(c2) --函数表达式44
2 rows in set (0.00 sec)45
46
mysql> explain select * from t3 where upper(c1)='ZHUGE';47
+----+-------------+-------+------------+------+---------------+------+---------+------
+------+----------+-------------+
48 | id | select_type | table | partitions | type | possible_keys | key | key_len | ref
| rows | filtered | Extra |
49 +----+-------------+-------+------------+------+---------------+------+---------+------
+------+----------+-------------+
50 | 1 | SIMPLE | t3 | NULL | ALL | NULL | NULL | NULL | NULL
| 1 | 100.00 | Using where |
51 +----+-------------+-------+------------+------+---------------+------+---------+------
+------+----------+-------------+
52 1 row in set, 1 warning (0.00 sec)53 54
mysql> explain select * from t3 where upper(c2)='ZHUGE'; --使用了函数索引55
+----+-------------+-------+------------+------+---------------+----------+---------+-- -----+------+----------+-------+ 56
| id | select_type | table | partitions | type | possible_keys | key | key_len |
ref | rows | filtered | Extra | 57 +----+-------------+-------+------------+------+---------------+----------+---------+-- -----+------+----------+-------+ 58
| 1 | SIMPLE | t3 | NULL | ref | func_idx | func_idx | 43 |
const | 1 | 100.00 | NULL | 59 +----+-------------+-------+------------+------+---------------+----------+---------+-- -----+------+----------+-------+ 60 1 row in set, 1 warning (0.00 sec)61 5 、 innodb 存储引擎 select for update 跳过锁等待
应用场景比如查询余票记录,如果某些记录已经被锁定,用 skip locked 可以跳过被锁定的记录,只返 回没有锁定的记录,提高系统性能。
先打开一个session1:1
mysql> select * from t1;2
+------+------+3 | c1 | c2 |4 +------+------+5 | 1 | 10 |6 | 2 | 50 |7 | 3 | 50 |8 | 4 | 100 |9 | 5 | 80 |10 +------+------+11 5 rows in set (0.00 sec)12 13
mysql> begin;14
Query OK, 0 rows affected (0.00 sec)15 16
mysql> update t1 set c2 = 60 where c1 = 2; --锁定第二条记录17
Query OK, 1 row affected (0.00 sec)18 Rows matched: 1 Changed: 1 Warnings: 019 20 21
另外一个session2: 22
mysql> select * from t1 where c1 = 2 for update; --等待超时23
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction24 25
mysql> select * from t1 where c1 = 2 for update nowait; --查询立即返回26
ERROR 3572 (HY000): Statement aborted because lock(s) could not be acquired immediately and NOWAIT is set. 27 28
mysql> select * from t1 for update skip locked; --查询立即返回,过滤掉了第二行记录29
+------+------+30 | c1 | c2 |31 +------+------+32 | 1 | 10 |33 | 3 | 50 |34 | 4 | 100 |35 | 5 | 80 |36 +------+------+37 4 rows in set (0.00 sec)38
能够让 InnoDB 根据服务器上检测到的内存大小自动配置 innodb_buffer_pool_size , innodb_log_file_size 等参数,会尽可能多的占用系统可占用资源提升性能。解决非专业人员安装数据 库后默认初始化数据库参数默认值偏低的问题,前提是服务器是专用来给 MySQL 数据库的,如果还有 其他软件或者资源或者多实例 MySQL 使用,不建议开启该参数,不然会影响其它程序。
MySQL 8.0 ( MySQL 5.7.15 )增加了一个新的动态变量 innodb_deadlock_detect ,用于控制系统是否 执行 InnoDB 死锁检查,默认是打开的。死锁检测会耗费数据库性能的,对于高并发的系统,我们可 以关闭死锁检测功能,提高系统性能。但是我们要确保系统极少情况会发生死锁,同时要将锁等待超 时参数调小一点,以防出现死锁等待过久的情况。
6 、新增 innodb_dedicated_server 自适应参数
mysql> show variables like '%innodb_dedicated_server%'; --默认是OFF关闭,修改为ON打开1
+-------------------------+-------+2 | Variable_name | Value |3 +-------------------------+-------+4 | innodb_dedicated_server | OFF |5 +-------------------------+-------+6 1 row in set (0.02 sec)7 8 7 、死锁检查控制
mysql> show variables like '%innodb_deadlock_detect%'; --默认是打开的1
+------------------------+-------+2 | Variable_name | Value |3 +------------------------+-------+4 | innodb_deadlock_detect | ON |5 +------------------------+-------+6 1 row in set, 1 warning (0.01 sec)7 8 、 undo 文件不再使用系统表空间
默认创建 2 个 UNDO 表空间,不再使用系统表空间。 之前是天,并且参数名称发生变化 . 在 8.0 版本之前, binlog 日志过期时间设置都是设置 expire_logs_days 参数,而在 8.0 版本中, MySQL 默认使用 binlog_expire_logs_seconds 参数。 从 MySQL 8.0 开始,新增了一个叫窗口函数的概念,它可以用来实现若干新的查询方式。窗口函数与 SUM() 、 COUNT() 这种分组聚合函数类似,在聚合函数后面加上 over() 就变成窗口函数了,在括号里 可以加上 partition by 等分组关键字指定如何分组,窗口函数即便分组也不会将多行查询结果合并为一 行,而是将结果放回多行当中,即窗口函数不需要再使用 GROUP BY 。 9 、 binlog 日志过期时间精确到秒 10 、窗口函数 (Window Functions) :也称分析函数
创建一张账户余额表1
CREATE TABLE `account_channel` (2
id int NOT NULL AUTO_INCREMENT,3
name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL
COMMENT ' 姓名 ',
4
channel varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL
COMMENT ' 账户渠道 ',
5
balance int DEFAULT NULL COMMENT ' 余额 ',6
PRIMARY KEY (id)7
) ENGINE=InnoDB;8
9
插入一些示例数据10
INSERT INTO `account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('1',
'zhuge', 'wx', '100'); 11
INSERT INTO `account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('2',
'zhuge', 'alipay', '200'); 12
INSERT INTO `account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('3',
'zhuge', 'yinhang', '300'); 13
INSERT INTO `account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('4',
'lilei', 'wx', '200'); 14
INSERT INTO `account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('5',
'lilei', 'alipay', '100'); 15
INSERT INTO `account_channel` (`id`, `name`, `channel`, `balance`) VALUES ('6',
'hanmeimei', 'wx', '500'); 16 17
mysql> select * from account_channel;18
+----+-----------+---------+---------+19 | id | name | channel | balance |20 +----+-----------+---------+---------+21 | 1 | zhuge | wx | 100 |22 | 2 | zhuge | alipay | 200 |23 | 3 | zhuge | yinhang | 300 |24 | 4 | lilei | wx | 200 |25 | 5 | lilei | alipay | 100 |26 | 6 | hanmeimei | wx | 500 |27 +----+-----------+---------+---------+28 6 rows in set (0.00 sec)29 30
mysql> select name,sum(balance) from account_channel group by name;31
+-----------+--------------+32 | name | sum(balance) |33 +-----------+--------------+34
| zhuge | 600 |35 | lilei | 300 |36 | hanmeimei | 500 |37 +-----------+--------------+38 3 rows in set (0.00 sec)39 40
在聚合函数后面加上over()就变成分析函数了,后面可以不用再加 group by 制定分组,因为在 over 里已经
用 partition 关键字指明了如何分组计算,这种可以保留原有表数据的结构,不会像分组聚合函数那样每组只 返回一条数据 41
mysql> select name,channel,balance,sum(balance) over(partition by name) as sum_balance
from account_channel; 42 +-----------+---------+---------+-------------+43 | name | channel | balance | sum_balance |44 +-----------+---------+---------+-------------+45 | hanmeimei | wx | 500 | 500 |46 | lilei | wx | 200 | 300 |47 | lilei | alipay | 100 | 300 |48 | zhuge | wx | 100 | 600 |49 | zhuge | alipay | 200 | 600 |50 | zhuge | yinhang | 300 | 600 |51 +-----------+---------+---------+-------------+52 6 rows in set (0.00 sec)53 54
mysql> select name,channel,balance,sum(balance) over(partition by name order by
balance) as sum_balance from account_channel; 55 +-----------+---------+---------+-------------+56 | name | channel | balance | sum_balance |57 +-----------+---------+---------+-------------+58 | hanmeimei | wx | 500 | 500 |59 | lilei | alipay | 100 | 100 |60 | lilei | wx | 200 | 300 |61 | zhuge | wx | 100 | 100 |62 | zhuge | alipay | 200 | 300 |63 | zhuge | yinhang | 300 | 600 |64 +-----------+---------+---------+-------------+65 6 rows in set (0.00 sec)66 67 68
over()里如果不加条件,则默认使用整个表的数据做运算69
mysql> select name,channel,balance,sum(balance) over() as sum_balance from
account_channel; 70 +-----------+---------+---------+-------------+71
专用窗口函数: | name | channel | balance | sum_balance |72 +-----------+---------+---------+-------------+73 | zhuge | wx | 100 | 1400 |74 | zhuge | alipay | 200 | 1400 |75 | zhuge | yinhang | 300 | 1400 |76 | lilei | wx | 200 | 1400 |77 | lilei | alipay | 100 | 1400 |78 | hanmeimei | wx | 500 | 1400 |79 +-----------+---------+---------+-------------+80 6 rows in set (0.00 sec)81 82
mysql> select name,channel,balance,avg(balance) over(partition by name) as avg_balance
from account_channel; 83 +-----------+---------+---------+-------------+84 | name | channel | balance | avg_balance |85 +-----------+---------+---------+-------------+86 | hanmeimei | wx | 500 | 500.0000 |87 | lilei | wx | 200 | 150.0000 |88 | lilei | alipay | 100 | 150.0000 |89 | zhuge | wx | 100 | 200.0000 |90 | zhuge | alipay | 200 | 200.0000 |91 | zhuge | yinhang | 300 | 200.0000 |92 +-----------+---------+---------+-------------+93 6 rows in set (0.00 sec)94 序号函数: ROW_NUMBER() 、 RANK() 、 DENSE_RANK() 分布函数: PERCENT_RANK() 、 CUME_DIST() 前后函数: LAG() 、 LEAD() 头尾函数: FIRST_VALUE() 、 LAST_VALUE() 其它函数: NTH_VALUE() 、 NTILE()
在 8.0 版本之前,默认字符集为 latin1 , utf8 指向的是 utf8mb3 , 8.0 版本默认字符集为 utf8mb4 , utf8 默认 指向的也是 utf8mb4 。
按照 balance 字段排序,展示序号1
mysql> select name,channel,balance,row_number() over(order by balance) as row_number1
from account_channel; 2 +-----------+---------+---------+-------------+3 | name | channel | balance | row_number1 |4 +-----------+---------+---------+-------------+5 | zhuge | wx | 100 | 1 |6 | lilei | alipay | 100 | 2 |7 | zhuge | alipay | 200 | 3 |8 | lilei | wx | 200 | 4 |9 | zhuge | yinhang | 300 | 5 |10 | hanmeimei | wx | 500 | 6 |11 +-----------+---------+---------+-------------+12 6 rows in set (0.00 sec)13 14
按照 balance 字段排序,first_value()选出排第一的余额15
mysql> select name,channel,balance,first_value(balance) over(order by balance) as
first1 from account_channel; 16 +-----------+---------+---------+--------+17 | name | channel | balance | first1 |18 +-----------+---------+---------+--------+19 | zhuge | wx | 100 | 100 |20 | lilei | alipay | 100 | 100 |21 | zhuge | alipay | 200 | 100 |22 | lilei | wx | 200 | 100 |23 | zhuge | yinhang | 300 | 100 |24 | hanmeimei | wx | 500 | 100 |25 +-----------+---------+---------+--------+26 6 rows in set (0.01 sec)27 11 、默认字符集由 latin1 变为 utf8mb4 12 、 MyISAM 系统表全部换成 InnoDB 表
将系统表 (mysql) 和数据字典表全部改为 InnoDB 存储引擎,默认的 MySQL 实例将不包含 MyISAM 表,除 非手动创建 MyISAM 表。
MySQL 8.0 删除了之前版本的元数据文件,例如表结构 .frm 等文件,全部集中放入 mysql.ibd 文件里。 可以看见下图 test 库文件夹里已经没有了 frm 文件。
在 8.0 之前的版本,自增主键 AUTO_INCREMENT 的值如果大于 max(primary key)+1 ,在 MySQL 重启 后,会重置 AUTO_INCREMENT=max(primary key)+1 ,这种现象在某些情况下会导致业务主键冲突或 者其他难以发现的问题。自增主键重启重置的问题很早就被发现 ( ) ,一直到 8.0 才被解决, 8.0 版本将会对 AUTO_INCREMENT 值进行持久化, MySQL 重启后,该值 将不会改变。 13 、元数据存储变动 14 、自增变量持久化
https://bugs.mysql.com/bug.php?id=
199
# ====MySQL 5.7演示====1
mysql> create table t(id int auto_increment primary key,c1 varchar(20));2
Query OK, 0 rows affected (0.03 sec)3 4
mysql> insert into t(c1) values('zhuge1'),('zhuge2'),('zhuge3');5
Query OK, 3 rows affected (0.00 sec)6 Records: 3 Duplicates: 0 Warnings: 07 8
mysql> select * from t;9
+----+--------+10 | id | c1 |11 +----+--------+12 | 1 | zhuge1 |13 | 2 | zhuge2 |14 | 3 | zhuge3 |15 +----+--------+16 3 rows in set (0.00 sec)17 18
mysql> delete from t where id = 3;19
Query OK, 1 row affected (0.01 sec)20 21
mysql> select * from t;22
+----+--------+23 | id | c1 |24 +----+--------+25 | 1 | zhuge1 |26 | 2 | zhuge2 |27 +----+--------+28 2 rows in set (0.00 sec)29 30
mysql> exit;31
Bye32 33
重启MySQL服务,并重新连接MySQL34
mysql> insert into t(c1) values('zhuge4');35
Query OK, 1 row affected (0.01 sec)36 37
mysql> select * from t;38
+----+--------+39
| id | c1 |40 +----+--------+41 | 1 | zhuge1 |42 | 2 | zhuge2 |43 | 3 | zhuge4 |44 +----+--------+45 3 rows in set (0.00 sec)46 47
mysql> update t set id = 5 where c1 = 'zhuge1';48
Query OK, 1 row affected (0.01 sec)49 Rows matched: 1 Changed: 1 Warnings: 050 51
mysql> select * from t;52
+----+--------+53 | id | c1 |54 +----+--------+55 | 2 | zhuge2 |56 | 3 | zhuge4 |57 | 5 | zhuge1 |58 +----+--------+59 3 rows in set (0.00 sec)60 61
mysql> insert into t(c1) values('zhuge5');62
Query OK, 1 row affected (0.01 sec)63 64
mysql> select * from t;65
+----+--------+66 | id | c1 |67 +----+--------+68 | 2 | zhuge2 |69 | 3 | zhuge4 |70 | 4 | zhuge5 |71 | 5 | zhuge1 |72 +----+--------+73 4 rows in set (0.00 sec)74 75
mysql> insert into t(c1) values('zhuge6');76
ERROR 1062 (23000): Duplicate entry '5' for key 'PRIMARY'77 78 79
80
# ====MySQL 8.0演示====81
mysql> create table t(id int auto_increment primary key,c1 varchar(20));82
Query OK, 0 rows affected (0.02 sec)83 84
mysql> insert into t(c1) values('zhuge1'),('zhuge2'),('zhuge3');85
Query OK, 3 rows affected (0.00 sec)86 Records: 3 Duplicates: 0 Warnings: 087 88
mysql> select * from t;89
+----+--------+90 | id | c1 |91 +----+--------+92 | 1 | zhuge1 |93 | 2 | zhuge2 |94 | 3 | zhuge3 |95 +----+--------+96 3 rows in set (0.00 sec)97 98
mysql> delete from t where id = 3;99
Query OK, 1 row affected (0.01 sec)100 101
mysql> select * from t;102
+----+--------+103 | id | c1 |104 +----+--------+105 | 1 | zhuge1 |106 | 2 | zhuge2 |107 +----+--------+108 2 rows in set (0.00 sec)109 110
mysql> exit;111
Bye112 [root@localhost ~]# service mysqld restart113 Shutting down MySQL.... SUCCESS! 114 Starting MySQL... SUCCESS! 115 116
重新连接MySQL117
mysql> insert into t(c1) values('zhuge4');118
Query OK, 1 row affected (0.00 sec)119
120
mysql> select * from t; --生成的 id 为4,不是3121
+----+--------+122 | id | c1 |123 +----+--------+124 | 1 | zhuge1 |125 | 2 | zhuge2 |126 | 4 | zhuge4 |127 +----+--------+128 3 rows in set (0.00 sec)129 130
mysql> update t set id = 5 where c1 = 'zhuge1';131
Query OK, 1 row affected (0.01 sec)132 Rows matched: 1 Changed: 1 Warnings: 0133 134
mysql> select * from t;135
+----+--------+136 | id | c1 |137 +----+--------+138 | 2 | zhuge2 |139 | 4 | zhuge4 |140 | 5 | zhuge1 |141 +----+--------+142 3 rows in set (0.00 sec)143 144
mysql> insert into t(c1) values('zhuge5');145
Query OK, 1 row affected (0.00 sec)146 147
mysql> select * from t;148
+----+--------+149 | id | c1 |150 +----+--------+151 | 2 | zhuge2 |152 | 4 | zhuge4 |153 | 5 | zhuge1 |154 | 6 | zhuge5 |155 +----+--------+156 4 rows in set (0.00 sec)157
InnoDB 表的 DDL 支持事务完整性,要么成功要么回滚。 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 等等。 15 、 DDL 原子化
MySQL 5.71
mysql> show tables;2
+----------------+3 | Tables_in_test |4 +----------------+5 | account |6 | actor |7 | employee |8 | film |9 | film_actor |10 | leaf_id |11 | t1 |12 | test_innodb |13 | test_myisam |14 | test_order_id |15 +----------------+16 10 rows in set (0.01 sec)17 18
mysql> drop table t1,t2; // 删除表报错不会回滚, t1 表会被删除19
ERROR 1051 (42S02): Unknown table 'test.t2'20
mysql> show tables;21
+----------------+22 | Tables_in_test |23 +----------------+24 | account |25 | actor |26 | employee |27 | film |28 | film_actor |29 | leaf_id |30 | test_innodb |31 | test_myisam |32 | test_order_id |33 +----------------+34 9 rows in set (0.00 sec)35 36 37
MySQL 8.0 38
mysql> show tables;39
+----------------+40 | Tables_in_test |41 +----------------+42 | account |43 | actor |44 | employee |45 | film |46 | film_actor |47 | leaf_id |48 | t1 |49 | test_innodb |50 | test_myisam |51 | test_order_id |52 +----------------+53 10 rows in set (0.00 sec)54 55
mysql> drop table t1,t2; // 删除表报错会回滚, t1 表依然还在56
ERROR 1051 (42S02): Unknown table 'test.t2'57
mysql> show tables; 58
+----------------+59 | Tables_in_test |60 +----------------+61 | account |62 | actor |63 | employee |64 | film |65 | film_actor |66 | leaf_id |67 | t1 |68 | test_innodb |69 | test_myisam |70 | test_order_id |71 +----------------+72 10 rows in set (0.00 sec)73 16 、参数修改持久化
MySQL 8.0 版本支持在线修改全局参数并持久化,通过加上 PERSIST 关键字,可以将修改的参数持久 化到新的配置文件( mysqld-auto.cnf )中,重启 MySQL 时,可以从该配置文件获取到最新的配置参 数。set global 设置的变量参数在 mysql 重启后会失效。
参考文档 添加弃用和删除的特性: 添加弃用和删除的参数:
Docker 安装 Mysql9.0
在数据库安全的第一道关卡 —— 认证机制上, MySQL 9.0 展现出了破釜沉舟的决心。
mysql> set persist innodb_lock_wait_timeout=25;1
系统会在数据目录下生成一个包含 json 格式的 mysqld-auto.cnf 的文件,格式化后如下所示,当 my.cnf 和 mysqld-auto.cnf 同时存在时,后者具有更高优先级。 2 {3 "Version": 1,4 "mysql_server": {5 "innodb_lock_wait_timeout": {6 "Value": "25",7 "Metadata": {8 "Timestamp": 1675290252103863,9 "User": "root",10 "Host": "localhost"11 }12 }13 }14 }15 Mysql9.0 新特性详解
https://dev.mysql.com/doc/refman/9.0/en/mysql-nutshell.html
https://dev.mysql.com/doc/refman/8.0/en/added-deprecated-removed.ht
ml
https://mp.weixin.qq.com/s/ObzfYg0wn06MdW7I4bU27Q
1. 认证机制
MySQL 8.0 中默认使用 caching_sha2_password 但仍保留的 mysql_native_password 插件,在 9.0 中被完全移除。 这意味着依赖旧版客户端(不支持 CLIENT_PLUGIN_AUTH)的应用需要升级,否则将无法连接数据库。 弃用易受攻击的 SHA-1 哈希算法,强制采用 SHA-256 等更安全的加密方式,从根源上降低因哈希算法漏洞导致的密 码泄露风险。影响与建议:对于仍在使用旧认证插件的系统,需提前规划客户端升级或迁移 至 caching_sha2_password,避免升级后出现连接中断。
MySQL 9.0 支持 列类型。向量是一个数据结构,它由条目列表( 4 字节浮点值)组成,可 以表示为二进制字符串值或列表格式字符串。VECTOR 列在声明时需指定最大长度或条目数量(括号 内),默认值为 2048 ,最大值为 16383 。 可用以下示例,通过 CREATE TABLE 创建带有 VECTOR 列的 InnoDB 表: 向量列的限制 向量可以与某些但非所有字符串和加密函数一起使用 彻底移除旧插件 加密算法升级
2.VECTOR 向量类型支持
VECTOR
mysql> create database test;1
Query OK, 1 row affected (0.110 sec)2
mysql> use test3
Database changed4
mysql> CREATE TABLE v1 (c1 VECTOR(5000));5
Query OK, 0 rows affected (1.133 sec)6 VECTOR 列不能用作任何类型的键。这包括主键、外键、唯一键和分区键。 某些类型的 MySQL 函数和运算符不接受向量作为参数。这些函数包括但不限于数值函数和运算符、时间函数、 全文搜索函数、 XML 函数、位函数和 JSON 函数。 VECTOR 不能与任何其他类型进行比较,并且只能与另一个 VECTOR 进行相等性比较 VECTOR_DIM()(也在 MySQL 9.0 中新增)返回向量的长度。提供了用于不同表示之间转换的函数。 STRING_TO_VECTOR()(别名:TO_VECTOR())采用列表格式表示的向量并返回二进制字符串表示; VECTOR_TO_STRING()(别名:FROM_VECTOR())执行相反的操作,如下所示:
在推荐系统、图像识别、自然语言处理等需要高维向量存储与检索的场景中,VECTOR 类型可大幅减少数据转换开 销,提升查询效率。
现在支持将 EXPLAIN ANALYZE 的 输出保存到用户变量中,语法如下所示: 随后可以将该变量作为 MySQL JSON 函数的 JSON 参数使用。INTO 子句仅支持 FORMAT=JSON,且必须显式指定 FORMAT。此形式的 EXPLAIN ANALYZE 还支持可选的 FOR SCHEMA 或 FOR DATABASE 子句。 注意:仅当 explain_json_format_version 系统变量设置为 2 时,此功能才可用;否 则,尝试使用它将引发 ER_EXPLAIN_ANALYZE_JSON_FORMAT_VERSION_NOT_SUPPORTED 错误 ( EXPLAIN ANALYZE 在 explain_json_format_version=1 的情况下不支持 FORMAT=JSON )。
mysql> SELECT STRING_TO_VECTOR('[3, 4, 6, 9]');1
+--------------------------------------------------------------------+2 | STRING_TO_VECTOR('[3, 4, 6, 9]') |3 +--------------------------------------------------------------------+4 | 0x00004040000080400000C04000001041 |5 +--------------------------------------------------------------------+6 1 row in set (0.000 sec)7 8
mysql> SELECT VECTOR_TO_STRING(0x00004040000080400000C04000001041);9
+------------------------------------------------------+10 | VECTOR_TO_STRING(0x00004040000080400000C04000001041) |11 +------------------------------------------------------+12 | [3.00000e+00,4.00000e+00,6.00000e+00,9.00000e+00] |13 +------------------------------------------------------+14 1 row in set (0.001 sec)15 应用场景
3. 内联和隐式外键约束
4. 将 EXPLAIN ANALYZE 的 JSON 输出保存到变量中
JSON
EXPLAIN ANALYZE FORMAT=JSON INTO @variable select_stmt1
服务器
MySQL 9.0 通过新增系统表,将性能监控提升到「元数据级」: 记录系统变量的最小值、最大值、单位等元数据,帮助开发者更清晰地理解变量范围; 提供全局变量的持久化状态等属性信息,替代旧版 variables_info 表中过时的字段,让配置管理更透明。 典型字段包括:
MySQL 8.0 的预处理语句仅支持 DML (如 SELECT、INSERT),而 9.0 扩展至 DDL 操作,例如动态创建或修改事件: 这一特性让数据库结构管理更具灵活性,尤其适合需要动态生成表结构或调度任务的场景,减少手动编写 SQL 的重复性工作。 在企业版中, MySQL 9.0 引入 JavaScript 存储程序支持,通过 JS_EXECUTE() 函数实现 SQL 与 JS 的混 合编程:
5. 性能模式系统变量表
variables_metadata global_variable_attributes VARIABLE_NAME:变量名 VARIABLE_SCOPE:作用范围( GLOBAL, SESSION, BOTH ) VARIABLE_TYPE:数据类型( INTEGER, STRING, BOOLEAN 等) MIN_VALUE/ MAX_VALUE:变量取值范围(新特性) DESCRIPTION:变量描述
6.预处理语句
SET @stmt = 'CREATE EVENT daily_backup ON SCHEDULE EVERY 1 DAY DO ...';1
PREPARE stmt FROM @stmt;2 EXECUTE stmt;3
7.JavaScript 存储程序
这一功能打破了传统存储过程只能使用 SQL 或 PL/SQL 的限制,适合需要调用 JS 库(如数学计算、文本处理)的复杂业务场景,提升 开发效率。 从简单的点线面操作升级到支持多面体、曲面等复杂几何对象,同时新增坐标系转换(如 WGS84 到 UTM ),满足 物流轨迹分析、城市规划等专业领域需求。 深度适配 AWS 、 GCP 、 Azure 等云平台,支持容器化部署,结合线程池增强和细粒度资源组管理,在高并发云环境 中表现更优。 9.0 新增错误码从 6400 开始编号,便于快速定位新版本特有的问题,减少故障排查时间。
Mysql 全局优化与 Mysql 8.0&Mysql9.0 新特性详解
DELIMITER //1
CREATE FUNCTION js_add(a INT, b INT) RETURNS INT2
BEGIN3 DECLARE result INT;4
SET result = JS_EXECUTE('return a + b;', a, b);5
RETURN result;6 END //7 DELIMITER ;8
8. 其他关键升级
GIS 功能 云原生优化 错误码体系 ...