#MySQL 5.7.26
mysql> select distinct(engine) from information_schema.tables;
+--------------------+
| engine |
+--------------------+
| MEMORY |
| InnoDB |
| MyISAM |
| CSV |
| PERFORMANCE_SCHEMA |
| NULL |
+--------------------+
6 rows in set (0.01 sec)
#MySQL 8.0
mysql> select distinct(ENGINE) from information_schema.tables;
+--------------------+
| ENGINE |
+--------------------+
| NULL |
| InnoDB |
| CSV |
| PERFORMANCE_SCHEMA |
+--------------------+
4 rows in set (0.00 sec)
Innodb自增值为在5.7及以前的版本是保存在内存里的,如果删除最大的自增值10,那auto_increment还是11,但是如果重启,那就会变成10,会重置AUTO_INCREMENT=max(primary key)+1,在8.0后,自增值记录在redo log中,重启依靠redo log去恢复自增值。
8.0版本,所有的元数据已经统一用InnoDB来进行管理,这就给实现原子ddl带来了可能,几乎所有的对innodb表,存储过程,触发器,视图或者UDF的操作,都能做到原子化。InnoDB表的DDL支持事务完整性,要不全部成功,要不就全部回滚。
#MySQL 5.7
mysql> show tables;
+---------------+
| Tables_in_db |
+---------------+
| t1 |
+---------------+
1 row in set (0.00 sec)
mysql> drop table t1, t2;
ERROR 1051 (42S02): Unknown table 'db.t2'
mysql> show tables;
Empty set (0.00 sec)
#MySQL 8.0
mysql> show tables;
+---------------+
| Tables_in_db |
+---------------+
| t1 |
+---------------+
1 row in set (0.00 sec)
mysql> drop table t1, t2;
ERROR 1051 (42S02): Unknown table 'db.t2'
mysql> show tables;
+---------------+
| Tables_in_db |
+---------------+
| t1 |
+---------------+
1 row in set (0.00 sec)
原子DDL操作步骤:
准备:创建所需的对象并将DDL⽇志写入 mysql.innodb_ddl_log表中。DDL日志定义了如何前滚和回滚DDL操作。
执行:执⾏DDL操作。例如,为CREATE TABLE操作执⾏创建。
提交:更新数据字典并提交数据字典事务。
Post-DDL:重播并从mysql.innodb_ddl_log表格中删除DDL⽇志。为确保回滚可以安全执⾏⽽不引⼊不⼀致性,在此最后阶段执⾏⽂件操作(如重命名或删除数据文件)。这一阶段还从 mysql.innodb_dynamic_metadata的数据字典表删除的动态元数据为了DROP TABLE,TRUNCATE和其它重建表的DDL操作。
⽆论事务是提交还是回滚,DDL日志都会在mysql.innodb_ddl_log在Post-DDL阶段重播并从表中删除 。mysql.innodb_ddl_log如果服务器在DDL操作期间暂停,DDL⽇志应该只保留在表中。在这种情况下,DDL⽇志会在恢复后重播并删除。
在恢复情况下,当服务器重新启动时,可能会提交或回退DDL事务。如果在重做⽇志和⼆进制日志中存在DDL操作的提交阶段期间执⾏的数据字典事务,则该操作被认为是成功的并且被前滚。否则,在InnoDB重放数据字典重做日志时回滚不完整的数据字典事务 ,并且回滚DDL事务。
原⼦DDL ⽀持类型
• DROP TABLES , all tables dropped or none
• DROP SCHEMA, all entities in the schema are dropped, or none
• Note that atomic DDL statements will be rolled back or committed even in case of crash, e.g. RENAME TABLES
• CREATE TABLE would be successfully committed or rolled back (no orphan ibd left)
• TRUNCATE TABLE (including InnoDB tables with FTS AUX tables) would be successfully committed or rolled back
• RENAME TABLES, all or none
• ALTER TABLE successful or not done
MySQL 8.0支持在线修改全局参数并持久化,通过加上PERSIST关键字,可以将修改的参数持久化到新的配置文件(mysqld-auto.cnf)中,重启MySQL时,可以从该配置文件获取到最新的配置参数。
例:
set PERSIST log_error_verbosity=3;
系统会在数据目录下生成一个包含json格式的mysqld-auto.cnf的文件,格式化后如下所示,当my.cnf和mysqld-auto.cnf同时存在时,后者具有更高优先级。
{
"Version": 1,
"mysql_server": {
"log_error_verbosity": {
"Value": "3",
"Metadata": {
"Timestamp": 1529657078851627,
"User": "root",
"Host": "localhost"
}
}
}
}
在MySQL 8.0中可以添加降序索引,并且可以通过show create table看到这个降序索引(在MySQL 5.7中show create table看到的还是升序)。
mysql> alter table t2 add index idx_a_b(a,b desc);
mysql> show create table t2;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| t2 | CREATE TABLE `t2` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`),
KEY `idx_a_b` (`a`,`b` DESC),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
我做实验用的是一张100万条数据的表,大概是这样的:
mysql> select * from t2 limit 10;
+----+------+------+
| id | a | b |
+----+------+------+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
| 4 | 4 | 4 |
| 5 | 5 | 5 |
| 6 | 6 | 6 |
| 7 | 7 | 7 |
| 8 | 8 | 8 |
| 9 | 9 | 9 |
| 10 | 10 | 10 |
+----+------+------+
10 rows in set (0.00 sec)
mysql> select count(*) from t2;
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+
1 row in set (0.18 sec)
对比MySQL 5.7和8.0中索引的使用情况
#mysql 5.7
mysql> explain select * from t2 order by a,b desc limit 5;
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+----------+-----------------------------+
| 1 | SIMPLE | t2 | NULL | index | NULL | idx_a_b | 10 | NULL | 911976 | 100.00 | Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+--------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)
#MySQL 8.0
mysql> explain select * from t2 order by a , b desc limit 5;
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
| 1 | SIMPLE | t2 | NULL | index | NULL | idx_a_b | 10 | NULL | 5 | 100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+-----------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
通过对比,可以看到5.7的扫描行娄远远大于8.0的5行,并且还使用了filesort。
MySQL 8.0对于group by 字段不再隐式排序,如需要排序,必须显式加上order by子句。
#表结构
mysql> show create table tb1\G
*************************** 1. row ***************************
Table: tb1
Create Table: CREATE TABLE `tb1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`group_own` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC
1 row in set (0.00 sec)
#表数据
mysql> select * from tb1;
+----+------+-----------+
| id | name | group_own |
+----+------+-----------+
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 3 | 3 | 0 |
| 4 | 4 | 0 |
| 5 | 5 | 5 |
| 8 | 8 | 1 |
| 10 | 10 | 5 |
+----+------+-----------+
7 rows in set (0.00 sec)
#MySQL 5.7
mysql> select count(id), group_own from tb1 group by group_own;
+-----------+-----------+
| count(id) | group_own |
+-----------+-----------+
| 4 | 0 |
| 1 | 1 |
| 2 | 5 |
+-----------+-----------+
3 rows in set (0.00 sec)
#MySQL 8.0
mysql> select count(id), group_own from tb1 group by group_own;
+-----------+-----------+
| count(id) | group_own |
+-----------+-----------+
| 4 | 0 |
| 2 | 5 |
| 1 | 1 |
+-----------+-----------+
3 rows in set (0.00 sec)
#MySQL 8.0显式地加上order by进行排序
mysql> select count(id), group_own from tb1 group by group_own order by group_own;
+-----------+-----------+
| count(id) | group_own |
+-----------+-----------+
| 4 | 0 |
| 1 | 1 |
| 2 | 5 |
+-----------+-----------+
3 rows in set (0.00 sec)
MySQL 8 大幅改进了对 JSON 的支持,添加了基于路径查询参数从 JSON 字段中抽取数据的 JSON_EXTRACT() 函数,以及用于将数据分别组合到 JSON 数组和对象中的 JSON_ARRAYAGG() 和 JSON_OBJECTAGG() 聚合函数。
在主从复制中,新增参数 binlog_row_value_options,控制JSON数据的传输方式,允许对于Json类型部分修改,在binlog中只记录修改的部分,减少json大数据在只有少量修改的情况下,对资源的占用。
在MySQL 5.7中,可以为存储在每个表中的表加密InnoDB表空间。 在MySQL 8.0中,我们通过为UNDO和REDO日志添加加密来完成此功能。
增加以下两个参数,用于控制redo、undo日志的加密。
innodb_undo_log_encrypt
innodb_undo_log_encrypt
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也会立即返回,只是返回的结果中不包含被锁定的行。
#session1:
mysql> begin;
mysql> select * from t1 where c1 = 2 for update;
+------+-------+
| c1 | c2 |
+------+-------+
| 2 | 60530 |
| 2 | 24678 |
+------+-------+
2 rows in set (0.00 sec)
#session2:
mysql> select * from t1 where c1 = 2 for update nowait;
ERROR 3572 (HY000): Statement aborted because lock(s) could not be acquired immediately and NOWAIT is set.
mysql> select * from t1 where c1 = 2 for update skip locked;
Empty set (0.00 sec)
在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);
使用INVISIBLE关键字在创建表或者进行表变更中设置索引是否可见。索引不可见只是在查询时优化器不使用该索引,即使使用force index,优化器也不会使用该索引,同时优化器也不会报索引不存在的错误,因为索引仍然真实存在,在必要时,也可以快速的恢复成可见。
#创建不可见索引
create table t2(c1 int,c2 int,index idx_c1_c2(c1,c2 desc) invisible );
#索引可见
alter table t2 alter index idx_c1_c2 visible;
#索引不可见
alter table t2 alter index idx_c1_c2 invisible;
优化器会利用column_statistics的数据,判断字段的值的分布,得到更准确的执行计划。
可以使用 ANALYZE TABLE table_name [UPDATE HISTOGRAM on col_name with N BUCKETS |DROP HISTOGRAM ON clo_name] 来收集或者删除直方图信息。
直方图统计了表中某些字段的数据分布情况,为优化选择高效的执行计划提供参考,直方图与索引有着本质的区别,维护一个索引有代价。每一次的insert、update、delete都会需要更新索引,会对性能有一定的影响。而直方图一次创建永不更新,除非明确去更新它。所以不会影响insert、update、delete的性能。
#添加/更新直方图
mysql> analyze table t1 update histogram on c1, c2 with 32 buckets;
+--------+-----------+----------+-----------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+--------+-----------+----------+-----------------------------------------------+
| db.t1 | histogram | status | Histogram statistics created for column 'c1'. |
| db.t1 | histogram | status | Histogram statistics created for column 'c2'. |
+--------+-----------+----------+-----------------------------------------------+
2 rows in set (2.57 sec)
#删除直方图
mysql> analyze table t1 drop histogram on c1, c2;
+--------+-----------+----------+-----------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+--------+-----------+----------+-----------------------------------------------+
| db.t1 | histogram | status | Histogram statistics removed for column 'c1'. |
| db.t1 | histogram | status | Histogram statistics removed for column 'c2'. |
+--------+-----------+----------+-----------------------------------------------+
2 rows in set (0.13 sec)
能够让InnoDB根据服务器上检测到的内存大小自动配置innodb_buffer_pool_size,innodb_log_file_size,innodb_flush_method三个参数。
在错误信息中添加了错误信息编号[MY-010311]和错误所属子系统[Server]
#MySQL 5.7
2018-06-08T09:07:20.114585+08:00 0 [Warning] 'proxies_priv' entry '@ root@localhost' ignored in --skip-name-resolve mode.
2018-06-08T09:07:20.117848+08:00 0 [Warning] 'tables_priv' entry 'user mysql.session@localhost' ignored in --skip-name-resolve mode.
2018-06-08T09:07:20.117868+08:00 0 [Warning] 'tables_priv' entry 'sys_config mysql.sys@localhost' ignored in --skip-name-resolve mode.
#MySQL 8.0
2018-06-21T17:53:13.040295+08:00 28 [Warning] [MY-010311] [Server] 'proxies_priv' entry '@ root@localhost' ignored in --skip-name-resolve mode.
2018-06-21T17:53:13.040520+08:00 28 [Warning] [MY-010330] [Server] 'tables_priv' entry 'user mysql.session@localhost' ignored in --skip-name-resolve mode.
2018-06-21T17:53:13.040542+08:00 28 [Warning] [MY-010330] [Server] 'tables_priv' entry 'sys_config mysql.sys@localhost' ignored in --skip-name-resolve mode.
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表空间超过这个值,该表空间被标记为可回收。
MySQL 8.0新增了一个资源组功能,用于调控线程优先级以及绑定CPU核。
MySQL用户需要有 RESOURCE_GROUP_ADMIN权限才能创建、修改、删除资源组。
在Linux环境下,MySQL进程需要有 CAP_SYS_NICE 权限才能使用资源组完整功能。
[root@localhost~]# sudo setcap cap_sys_nice+ep /usr/local/mysql8.0/bin/mysqld
[root@localhost~]# getcap /usr/local/mysql8.0/bin/mysqld
/usr/local/mysql8.0/bin/mysqld = cap_sys_nice+ep
默认提供两个资源组,分别是USR_default,SYS_default
创建资源组:
create resource group test_resouce_group type=USER vcpu=0,1 thread_priority=5;
将当前线程加入资源组:
SET RESOURCE GROUP test_resouce_group;
将某个线程加入资源组:
SET RESOURCE GROUP test_resouce_group FOR thread_id;
查看资源组里有哪些线程:
select * from Performance_Schema.threads where RESOURCE_GROUP='test_resouce_group';
修改资源组:
alter resource group test_resouce_group vcpu = 2,3 THREAD_PRIORITY = 8;
删除资源组 :
drop resource group test_resouce_group;
#创建资源组
mysql>create resource group test_resouce_group type=USER vcpu=0,1 thread_priority=5;
Query OK, 0 rows affected (0.03 sec)
mysql> select * from RESOURCE_GROUPS;
+---------------------+---------------------+------------------------+----------+-----------------+
| RESOURCE_GROUP_NAME | RESOURCE_GROUP_TYPE | RESOURCE_GROUP_ENABLED | VCPU_IDS | THREAD_PRIORITY |
+---------------------+---------------------+------------------------+----------+-----------------+
| USR_default | USER | 1 | 0-3 | 0 |
| SYS_default | SYSTEM | 1 | 0-3 | 0 |
| test_resouce_group | USER | 1 | 0-1 | 5 |
+---------------------+---------------------+------------------------+----------+-----------------+
3 rows in set (0.00 sec)
#把线程id为60的线程加入到资源组test_resouce_group中,线程id可通过Performance_Schema.threads获取
mysql> SET RESOURCE GROUP test_resouce_group FOR 60;
Query OK, 0 rows affected (0.00 sec)
#资源组里有线程时,删除资源组报错
mysql> drop resource group test_resouce_group;
ERROR 3656 (HY000): Resource group test_resouce_group is busy.
#修改资源组
mysql> alter resource group test_resouce_group vcpu = 2,3 THREAD_PRIORITY = 8;
Query OK, 0 rows affected (0.10 sec)
mysql> select * from RESOURCE_GROUPS;
+---------------------+---------------------+------------------------+----------+-----------------+
| RESOURCE_GROUP_NAME | RESOURCE_GROUP_TYPE | RESOURCE_GROUP_ENABLED | VCPU_IDS | THREAD_PRIORITY |
+---------------------+---------------------+------------------------+----------+-----------------+
| USR_default | USER | 1 | 0-3 | 0 |
| SYS_default | SYSTEM | 1 | 0-3 | 0 |
| test_resouce_group | USER | 1 | 2-3 | 8 |
+---------------------+---------------------+------------------------+----------+-----------------+
3 rows in set (0.00 sec)
#把资源组里的线程移出到默认资源组USR_default
mysql> SET RESOURCE GROUP USR_default FOR 60;
Query OK, 0 rows affected (0.00 sec)
#删除资源组
mysql> drop resource group test_resouce_group;
Query OK, 0 rows affected (0.04 sec)
角色可以认为是一些权限的集合,为用户赋予统一的角色,权限的修改直接通过角色来进行,无需为每个用户单独授权。
#创建角色
mysql> create role role_test;
Query OK, 0 rows affected (0.03 sec)
#给角色授予权限
mysql> grant select on db.* to 'role_test';
Query OK, 0 rows affected (0.10 sec)
#创建用户
mysql> create user 'read_user'@'%' identified by '123456';
Query OK, 0 rows affected (0.09 sec)
#给用户赋予角色
mysql> grant 'role_test' to 'read_user'@'%';
Query OK, 0 rows affected (0.02 sec)
#给角色role_test增加insert权限
mysql> grant insert on db.* to 'role_test';
Query OK, 0 rows affected (0.08 sec)
#给角色role_test删除insert权限
mysql> revoke insert on db.* from 'role_test';
Query OK, 0 rows affected (0.10 sec)
#查看默认角色信息
mysql> select * from mysql.default_roles;
+------+-----------+-------------------+-------------------+
| HOST | USER | DEFAULT_ROLE_HOST | DEFAULT_ROLE_USER |
+------+-----------+-------------------+-------------------+
| % | read_user | % | role_test |
+------+-----------+-------------------+-------------------+
1 row in set (0.00 sec)
#查看角色与用户关系
mysql> select * from mysql.role_edges;
+-----------+-----------+---------+-----------+-------------------+
| FROM_HOST | FROM_USER | TO_HOST | TO_USER | WITH_ADMIN_OPTION |
+-----------+-----------+---------+-----------+-------------------+
| % | role_test | % | read_user | N |
+-----------+-----------+---------+-----------+-------------------+
1 row in set (0.00 sec)
#删除角色
mysql> drop role role_test;
Query OK, 0 rows affected (0.06 sec)
新推出的8.0.14版本第一次引入了并行查询特性,使得check table和select count(*)类型的语句性能成倍提升。虽然目前使用场景还比较有限,但后续的发展值得期待。
通过配置参数innodb_parallel_read_threads来设置并发线程数,就能开始并行扫描功能,默认这个值为4。
在MySQL8.0中重新设计了redo log,主要改进fsync,使得效率更高,减少锁,优化flush机制,不会频繁flush。同时,支持更高用户并发请求。
http://dimitrik.free.fr/blog/archives/2017/10/mysql-performance-80-redesigned-redo-log-readwrite-workloads-scalability.html