MySQL 8.0新特性

MySQL 8.0带来了许多重要的新特性和改进,包括性能优化、安全性增强、功能扩展等多个方面。本章将详细介绍MySQL 8.0的主要新特性,帮助您充分利用这些新功能提升数据库应用的性能和可靠性。

3.1 默认字符集改为utf8mb4

MySQL 8.0将默认字符集从latin1改为utf8mb4,这是一个重要的变化。utf8mb4是UTF-8字符集的超集,支持所有Unicode字符,包括emoji表情符号。

为什么将默认字符集改为utf8mb4?

  • 全球化支持:utf8mb4支持全球所有语言的字符,更适合国际化应用。
  • emoji支持:utf8mb4可以存储emoji表情符号,这在现代社交和移动应用中非常重要。
  • 一致性:统一使用utf8mb4可以避免字符集转换问题,提高数据一致性。

示例:查看默认字符集设置

-- 查看MySQL服务器的默认字符集
SHOW VARIABLES LIKE 'character_set_server';

-- 查看MySQL服务器的默认排序规则
SHOW VARIABLES LIKE 'collation_server';

-- MySQL 8.0输出示例
+----------------------+--------------------+
| Variable_name        | Value              |
+----------------------+--------------------+
| character_set_server | utf8mb4            |
+----------------------+--------------------+

+------------------+--------------------+
| Variable_name    | Value              |
+------------------+--------------------+
| collation_server | utf8mb4_0900_ai_ci |
+------------------+--------------------+

提示:虽然MySQL 8.0默认使用utf8mb4字符集,但在创建数据库和表时,仍然建议显式指定字符集,以确保兼容性和一致性。

3.2 自增变量的持久化

在MySQL 8.0之前,自增变量(AUTO_INCREMENT)的值仅存储在内存中,而不是持久化到磁盘。这意味着当MySQL服务器重启时,自增变量的值可能会重置,导致主键冲突或数据不一致的问题。

MySQL 8.0解决了这个问题,现在自增变量的值会被持久化存储在redo log中,即使服务器重启,自增变量的值也会从上次的值继续增加,而不会重置。

示例:自增变量的使用

-- 创建一个带有自增主键的表
CREATE TABLE test_auto_increment (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50)
);

-- 插入几条数据
INSERT INTO test_auto_increment (name) VALUES ('Alice'), ('Bob'), ('Charlie');

-- 查看插入的数据
SELECT * FROM test_auto_increment;

-- 输出示例
+----+---------+
| id | name    |
+----+---------+
|  1 | Alice   |
|  2 | Bob     |
|  3 | Charlie |
+----+---------+

-- 重启MySQL服务器后,继续插入数据
INSERT INTO test_auto_increment (name) VALUES ('David');

-- 查看插入的数据,id会从4开始,而不是重新从1开始
SELECT * FROM test_auto_increment;

-- 输出示例
+----+---------+
| id | name    |
+----+---------+
|  1 | Alice   |
|  2 | Bob     |
|  3 | Charlie |
|  4 | David   |
+----+---------+

3.3 MySQL 8.0的新特性——DDL的原子化

在MySQL 8.0之前,数据定义语言(DDL)语句的执行不是原子的。例如,如果一个ALTER TABLE语句包含多个操作,其中一部分操作成功,而另一部分操作失败,那么MySQL可能会处于一个不一致的状态。

MySQL 8.0引入了原子DDL(Atomic DDL)功能,确保DDL语句的执行是原子的:要么全部成功,要么全部失败,不会出现部分执行的情况。这大大提高了数据库的可靠性和一致性。

原子DDL支持的操作类型

  • 表操作:CREATE、ALTER、DROP表等。
  • 索引操作:CREATE、ALTER、DROP索引等。
  • 视图操作:CREATE、ALTER、DROP视图等。
  • 触发器操作:CREATE、ALTER、DROP触发器等。
  • 存储过程和函数操作:CREATE、ALTER、DROP存储过程和函数等。

示例:原子DDL操作

-- 开始事务
START TRANSACTION;

-- 执行一个复杂的DDL操作
ALTER TABLE employees
    ADD COLUMN department_id INT,
    ADD CONSTRAINT fk_department FOREIGN KEY (department_id) REFERENCES departments(id),
    MODIFY COLUMN salary DECIMAL(12,2);

-- 如果执行成功,提交事务
COMMIT;
-- 如果执行失败,回滚事务
-- ROLLBACK;

提示:原子DDL功能依赖于MySQL的数据字典,这也是MySQL 8.0的一个重要新特性。数据字典将元数据(如表、索引、视图等的定义)存储在InnoDB引擎的系统表中,而不是像以前那样存储在文件系统中。

3.4 MySQL 8.0的新特性1——支持降序索引

MySQL 8.0之前,虽然可以在创建索引时指定DESC(降序),但实际上索引仍然是按照升序存储的,DESC选项被忽略。在查询时,如果需要按照降序排序,MySQL会在索引扫描后进行额外的排序操作,影响查询性能。

MySQL 8.0开始真正支持降序索引(Descending Index),索引中的数据按照降序存储。这对于需要频繁按照降序排序的查询,可以显著提高性能。

示例:创建和使用降序索引

-- 创建一个包含降序索引的表
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10,2),
    created_at DATETIME,
    INDEX idx_price_desc (price DESC),
    INDEX idx_created_price (created_at DESC, price ASC)
);

-- 查询时使用降序索引
EXPLAIN SELECT * FROM products ORDER BY price DESC LIMIT 10;

-- 输出示例(部分)
+----+-------------+----------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
| id | select_type | table    | partitions | type  | possible_keys | key            | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+----------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | products | NULL       | index | NULL          | idx_price_desc | 5       | NULL |   10 |   100.00 | Using index; Using filesort |
+----+-------------+----------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+

3.5 MySQL 8.0的新特性2——统计直方图

MySQL 8.0引入了统计直方图(Histogram)功能,用于帮助查询优化器更好地估计查询结果的行数,从而选择更优的执行计划。直方图特别适用于数据分布不均匀的列,可以显著提高查询性能。

直方图的优点

  • 提高查询优化器的准确性:直方图可以更准确地估计查询结果的行数,帮助查询优化器选择更好的执行计划。
  • 适用于数据分布不均匀的列:对于数据分布不均匀的列(如性别、状态等),直方图比传统的基数统计更有效。
  • 无需修改应用代码:直方图是透明的,应用程序无需做任何修改即可受益。

直方图的基本操作

示例:创建和管理直方图
-- 为表的列创建直方图
ANALYZE TABLE products UPDATE HISTOGRAM ON price WITH 10 BUCKETS;

-- 查看直方图信息
SELECT * FROM information_schema.column_statistics WHERE table_name = 'products';

-- 删除直方图
ANALYZE TABLE products DROP HISTOGRAM ON price;

-- 自动为所有适合的列创建直方图
SET GLOBAL default_statistics_target = 100;
ANALYZE TABLE products;

提示:直方图会占用一定的系统资源,因此建议只为那些对查询性能影响较大且数据分布不均匀的列创建直方图。

3.6 MySQL 8.0的新特性1——GROUP BY不再隐式排序

在MySQL 8.0之前,使用GROUP BY子句时,结果集默认会按照GROUP BY子句中指定的列进行排序。这是MySQL的一个历史遗留行为,与SQL标准不符。

从MySQL 8.0开始,GROUP BY子句不再隐式排序结果集。如果需要对结果集进行排序,必须显式使用ORDER BY子句。这一变化可以提高查询性能,特别是当不需要排序时。

示例:GROUP BY子句的使用

-- MySQL 8.0之前,这个查询的结果会自动按照department_id排序
-- MySQL 8.0开始,结果不会自动排序
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id;

-- 如果需要排序,必须显式使用ORDER BY子句
SELECT department_id, AVG(salary) AS avg_salary
FROM employees
GROUP BY department_id
ORDER BY department_id;

-- 如果需要恢复旧的行为,可以设置系统变量
SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION,GROUP_BY_OLD';

3.7 MySQL 8.0的新特性2——通用表表达式

MySQL 8.0引入了通用表表达式(Common Table Expression,CTE),这是一个非常有用的SQL特性,可以简化复杂查询,提高查询的可读性和可维护性。

CTE允许在一个查询中定义临时结果集,然后在后续的查询中引用这个结果集。CTE可以递归或非递归的。

非递归CTE示例

示例:使用非递归CTE
-- 使用CTE简化复杂查询
WITH high_salary_employees AS (
    SELECT employee_id, first_name, last_name, salary
    FROM employees
    WHERE salary > 10000
)
SELECT department_id, COUNT(*) AS num_high_salary
FROM departments d
JOIN high_salary_employees e ON d.department_id = e.department_id
GROUP BY department_id;

-- 多个CTE的使用
WITH
    dept_stats AS (
        SELECT department_id, AVG(salary) AS avg_salary, COUNT(*) AS num_employees
        FROM employees
        GROUP BY department_id
    ),
    high_perf_depts AS (
        SELECT department_id
        FROM dept_stats
        WHERE avg_salary > 8000 AND num_employees > 10
    )
SELECT * FROM departments WHERE department_id IN (SELECT department_id FROM high_perf_depts);

递归CTE示例

递归CTE特别适用于处理层次结构数据,如组织结构、文件系统、分类树等。

示例:使用递归CTE查询层次结构数据
-- 假设有一个组织结构表
CREATE TABLE organization (
    employee_id INT PRIMARY KEY,
    name VARCHAR(100),
    manager_id INT
);

-- 使用递归CTE查询某个员工的所有下属
WITH RECURSIVE subordinates AS (
    -- 基础查询:指定起始员工
    SELECT employee_id, name, manager_id, 0 AS level
    FROM organization
    WHERE employee_id = 1
    UNION ALL
    -- 递归查询:查找下属
    SELECT o.employee_id, o.name, o.manager_id, s.level + 1
    FROM organization o
    JOIN subordinates s ON o.manager_id = s.employee_id
)
SELECT * FROM subordinates ORDER BY level;

3.8 MySQL 8.0的新特性1——加密函数

MySQL 8.0增强了加密功能,提供了更多的加密函数,以满足不同的安全需求。以下是几个常用的加密函数:

3.8.1 加密函数MD5

MD5函数计算字符串的MD5哈希值,返回一个32位的十六进制字符串。

MD5(str)
示例:使用MD5函数
-- 计算字符串的MD5哈希值
SELECT MD5('password123');

-- 输出示例
+----------------------------------+
| MD5('password123')               |
+----------------------------------+
| 482c811da5d5b4bc6d497ffa98491e38 |
+----------------------------------+

3.8.2 加密函数SHA

SHA函数计算字符串的SHA-1哈希值,返回一个40位的十六进制字符串。

SHA(str)
示例:使用SHA函数
-- 计算字符串的SHA-1哈希值
SELECT SHA('password123');

-- 输出示例
+------------------------------------------+
| SHA('password123')                       |
+------------------------------------------+
| 40bd001563085fc35165329ea1ff5c5ecbdbbeef |
+------------------------------------------+

3.8.3 加密函数SHA2(str, hash_length)

SHA2函数是一个更强大的加密函数,支持SHA-224、SHA-256、SHA-384和SHA-512等多种哈希算法。

SHA2(str, hash_length)

其中,hash_length参数可以是224、256、384或512,分别对应SHA-224、SHA-256、SHA-384和SHA-512算法。

示例:使用SHA2函数
-- 计算字符串的SHA-256哈希值
SELECT SHA2('password123', 256);

-- 输出示例
+------------------------------------------------------------------+
| SHA2('password123', 256)                                         |
+------------------------------------------------------------------+
| ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f |
+------------------------------------------------------------------+

-- 计算字符串的SHA-512哈希值
SELECT SHA2('password123', 512);

-- 输出示例(部分)
+------------------------------------------------------------------------------------------------------------------------------------+
| SHA2('password123', 512)                                                                                                           |
+------------------------------------------------------------------------------------------------------------------------------------+
| bd1cfa194c3a603e7187f6a92dddc01380058364c0c0e5c8c4a8724e5e1bd981... |
+------------------------------------------------------------------------------------------------------------------------------------+

提示:虽然这些加密函数可以用于密码存储,但对于现代应用程序,推荐使用专门的密码哈希函数,如MySQL的PASSWORD()函数或应用程序级别的密码哈希库。

3.9 MySQL 8.0的新特性2——窗口函数

MySQL 8.0引入了窗口函数(Window Functions),这是一个强大的SQL功能,可以在不使用GROUP BY子句的情况下,对结果集进行分组计算。窗口函数可以用于计算排名、累计和、移动平均等复杂统计。

窗口函数的基本语法

窗口函数 OVER (PARTITION BY 列名 ORDER BY 列名 [frame_clause])

其中:

  • 窗口函数:可以是聚合函数(如SUM、AVG、COUNT等)或专用的窗口函数(如ROW_NUMBER、RANK、DENSE_RANK等)。
  • PARTITION BY:可选,用于将结果集划分为多个分区,窗口函数将在每个分区内独立计算。
  • ORDER BY:可选,用于指定分区内的排序顺序。
  • frame_clause:可选,用于指定当前窗口的帧范围(即计算窗口函数时考虑的行范围)。

示例:使用窗口函数

-- 使用ROW_NUMBER()函数为每行分配一个唯一的序号
SELECT employee_id, department_id, salary,
       ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS row_num
FROM employees;

-- 使用RANK()函数计算排名(相同值会有相同排名,下一个排名会跳跃)
SELECT employee_id, department_id, salary,
       RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rank_num
FROM employees;

-- 使用DENSE_RANK()函数计算密集排名(相同值会有相同排名,下一个排名不会跳跃)
SELECT employee_id, department_id, salary,
       DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) AS dense_rank_num
FROM employees;

-- 计算累计和
SELECT order_date, amount,
       SUM(amount) OVER (ORDER BY order_date) AS cumulative_sum
FROM orders;

-- 计算移动平均
SELECT order_date, amount,
       AVG(amount) OVER (ORDER BY order_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM orders;

提示:窗口函数是SQL标准的一部分,在数据分析和报表生成中非常有用。掌握窗口函数可以大大简化复杂查询的编写,提高查询效率。

3.10 MySQL 8.0的新特性——全局变量的持久化

在MySQL 8.0之前,通过SET GLOBAL语句修改的全局变量在服务器重启后会丢失,恢复到配置文件中的值。这意味着管理员需要手动将修改后的变量值写入配置文件,以确保重启后配置保持一致。

MySQL 8.0引入了全局变量持久化功能,通过SET PERSIST语句可以将全局变量的值持久化存储在数据字典中,即使服务器重启,变量值也会保持不变。

示例:持久化全局变量

-- 使用SET PERSIST语句持久化全局变量
SET PERSIST max_connections = 200;

-- 查看持久化的变量值
SELECT * FROM performance_schema.persisted_variables WHERE variable_name = 'max_connections';

-- 删除持久化的变量值,使变量恢复为配置文件中的值
RESET PERSIST max_connections;

-- 删除所有持久化的变量值
RESET PERSIST ALL;

3.11 MySQL 8.0的新特性——管理角色

MySQL 8.0引入了角色(Role)管理功能,这是一个重要的安全增强。角色是一组权限的集合,可以将角色授予用户,从而简化权限管理。

角色管理的优点

  • 简化权限管理:通过角色可以批量管理用户权限,而不必为每个用户单独授予权限。
  • 提高安全性:可以更精确地控制用户的权限,减少权限过大的风险。
  • 便于维护:当需要更改权限时,只需修改角色的权限,而不必逐个修改用户的权限。

示例:角色管理

-- 创建角色
CREATE ROLE 'read_only', 'data_entry', 'administrator';

-- 为角色授予权限
GRANT SELECT ON database_name.* TO 'read_only';
GRANT SELECT, INSERT, UPDATE ON database_name.* TO 'data_entry';
GRANT ALL PRIVILEGES ON database_name.* TO 'administrator';

-- 创建用户
CREATE USER 'user1'@'localhost' IDENTIFIED BY 'password1';
CREATE USER 'user2'@'localhost' IDENTIFIED BY 'password2';

-- 将角色授予用户
GRANT 'read_only' TO 'user1'@'localhost';
GRANT 'data_entry' TO 'user2'@'localhost';

-- 设置用户的默认角色
SET DEFAULT ROLE 'read_only' FOR 'user1'@'localhost';
SET DEFAULT ROLE 'data_entry' FOR 'user2'@'localhost';

-- 查看角色的权限
SHOW GRANTS FOR 'read_only';

-- 查看用户的权限(包括通过角色获得的权限)
SHOW GRANTS FOR 'user1'@'localhost' USING 'read_only';

-- 撤销角色的权限
REVOKE INSERT ON database_name.* FROM 'data_entry';

-- 从用户处撤销角色
REVOKE 'read_only' FROM 'user1'@'localhost';

-- 删除角色
DROP ROLE 'read_only';

3.12 MySQL 8.0的新特性——日志分类更详细

MySQL 8.0对日志系统进行了改进,使日志分类更加详细,有助于更好地监控和诊断数据库问题。以下是MySQL 8.0中的主要日志类型:

主要日志类型

  • 错误日志(Error Log):记录MySQL服务器启动、运行和停止过程中的错误和警告信息。
  • 二进制日志(Binary Log):记录所有数据修改操作,可以用于数据恢复和主从复制。
  • 慢查询日志(Slow Query Log):记录执行时间超过指定阈值的查询,可以用于性能调优。
  • 通用查询日志(General Query Log):记录所有客户端的连接和查询信息,用于审计和问题诊断。
  • 审计日志(Audit Log):企业版功能,记录详细的安全相关事件。
  • 中继日志(Relay Log):复制架构中,从服务器用于存储从主服务器接收的二进制日志内容。

示例:配置和管理日志

-- 查看错误日志配置
SHOW VARIABLES LIKE 'log_error';

-- 查看二进制日志配置
SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'binlog_format';

-- 查看慢查询日志配置
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';

-- 启用慢查询日志
SET GLOBAL slow_query_log = ON;

-- 修改慢查询阈值(单位:秒)
SET GLOBAL long_query_time = 2;

-- 查看当前的二进制日志文件列表
SHOW BINARY LOGS;

-- 刷新二进制日志
FLUSH BINARY LOGS;

-- 清除二进制日志(保留最近的2个)
PURGE BINARY LOGS TO 'binlog.000010';

3.13 MySQL 8.0的新特性1——支持不可见索引

MySQL 8.0引入了不可见索引(Invisible Index)功能,允许将索引标记为不可见,这样查询优化器在生成执行计划时会忽略该索引,但索引仍然存在并且会随着数据的修改而更新。

不可见索引对于测试索引的影响非常有用,可以在不实际删除索引的情况下,观察删除索引对查询性能的影响。

示例:创建和管理不可见索引

-- 创建不可见索引
CREATE INDEX idx_name ON employees(name) INVISIBLE;

-- 将现有索引设置为不可见
ALTER TABLE employees ALTER INDEX idx_salary INVISIBLE;

-- 将不可见索引设置为可见
ALTER TABLE employees ALTER INDEX idx_salary VISIBLE;

-- 查看索引是否可见
SELECT index_name, is_visible
FROM information_schema.statistics
WHERE table_schema = 'database_name' AND table_name = 'employees';

-- 即使索引不可见,强制使用该索引(用于测试)
SELECT /*+ INDEX(employees idx_name) */ * FROM employees WHERE name = 'John';

提示:默认情况下,查询优化器不会使用不可见索引,但可以通过修改optimizer_switch系统变量来控制是否使用不可见索引:SET GLOBAL optimizer_switch = 'use_invisible_indexes=on';

3.14 MySQL 8.0的新特性2——增加资源组

MySQL 8.0引入了资源组(Resource Groups)功能,允许管理员创建资源组并分配CPU资源,从而更好地控制数据库的资源使用。这对于混合工作负载的环境特别有用,可以确保关键任务有足够的资源。

资源组的主要功能

  • CPU核心分配:可以为资源组分配特定的CPU核心。
  • 优先级控制:可以设置资源组的优先级(高、中、低)。
  • 线程分配:可以将特定的线程分配到资源组。

示例:创建和管理资源组

-- 创建资源组
CREATE RESOURCE GROUP high_priority
    TYPE = USER
    VCPU = 0,1
    THREAD_PRIORITY = 10;

CREATE RESOURCE GROUP low_priority
    TYPE = USER
    VCPU = 2,3
    THREAD_PRIORITY = -10;

-- 将用户会话分配到资源组
SET RESOURCE GROUP high_priority;

-- 将存储过程分配到资源组
ALTER PROCEDURE my_procedure
    RESOURCE GROUP = high_priority;

-- 查看资源组配置
SELECT * FROM information_schema.resource_groups;

-- 修改资源组
ALTER RESOURCE GROUP high_priority
    VCPU = 0,1,2;

-- 删除资源组
DROP RESOURCE GROUP low_priority;

提示:资源组功能需要MySQL以root权限运行,或者在Linux系统上设置了CAP_SYS_NICE能力。在生产环境中使用资源组前,建议先在测试环境中进行充分测试。