美文网首页数据库
王者归来,Mysql8+ 香在哪?

王者归来,Mysql8+ 香在哪?

作者: ___n | 来源:发表于2020-03-19 15:01 被阅读0次

    1. Hash join

    SELECT * 
        FROM t1 
        JOIN t2 
            ON t1.c1=t2.c1;
    

    Hash join 不需要索引的支持。大多数情况下,hash join 比之前的 Block Nested-Loop 算法在没有索引时的等值连接更加高效。使用 EXPLAIN FORMAT=TREE 命令可以看到执行计划中的 hash join

    mysql> EXPLAIN FORMAT=TREE
        -> SELECT * 
        ->     FROM t1 
        ->     JOIN t2 
        ->         ON t1.c1=t2.c1\G
    *************************** 1. row ***************************
    EXPLAIN: -> Inner hash join (t2.c1 = t1.c1)  (cost=0.70 rows=1)
        -> Table scan on t2  (cost=0.35 rows=1)
        -> Hash
            -> Table scan on t1  (cost=0.35 rows=1)
    

    牛逼哄哄的 hash join 不负众望SQL执行时间提升了数百倍,不过要注意,如果任何连接语句(ON)中没有使用等值连接条件,将不会采用 hash join 连接方式,此时,将会采用性能更慢的 block nested loop 连接算法。

    mysql> EXPLAIN FORMAT=TREE
        ->     SELECT * 
        ->         FROM t1
        ->         JOIN t2 
        ->             ON (t1.c1 = t2.c1)
        ->         JOIN t3 
        ->             ON (t2.c1 < t3.c1)\G
    *************************** 1. row ***************************
    EXPLAIN: <not executable by iterator executor>
    

    2. instant add column 亿级数据秒速增加字段,8.0实现了 Online DDL 部分操作的高效性

    3. 直方图

    数据库中,查询优化器负责将SQL转换成最有效的执行计划。有时候,查询优化器会走不到最优的执行计划,导致花费了更多不必要的时间。造成这种情况的主要原因是,查询优化器有时无法准确的知道以下几个问题的答案:

    • 每个表有多少行?

    • 每一列有多少不同的值?

    • 每一列的数据分布情况?

    直方图能近似获得一列的数据分布情况,从而让数据库知道它含有哪些数据。

    利用直方图,用户可以对一张表的一列做数据分布的统计,特别是针对没有索引的字段。这可以帮助查询优化器找到更优的执行计划。统计直方图的主要使用场景是用来计算字段选择性,即过滤效率。

    可以通过以下方式来创建或者删除直方图:

    ANALYZE TABLE tbl_name UPDATE HISTOGRAM ON col_name [, col_name] WITH N BUCKETS;
    ANALYZE TABLE tbl_name DROP HISTOGRAM ON col_name [, col_name];
    

    buckets默认是100。
    统计直方图的信息存储在数据字典表 column_statistcs 中,可以通过视图information_schema.COLUMN_STATISTICS 访问。
    直方图以灵活的JSON的格式存储。
    ANALYZE TABLE会基于表大小自动判断是否要进行取样操作。
    ANALYZE TABLE也会基于表中列的数据分布情况以及bucket的数量来决定是否要建立等宽直方图(singleton)还是等高直方图(equi-height)

    4. 函数索引、表达式索引

    函数索引(functional key parts),也就是将表达式的值作为索引的内容,而不是列值或列值前缀。
    将函数作为索引键可以用于索引那些没有在表中直接存储的内容。例如:

    CREATE TABLE t1 (col1 INT, col2 INT, INDEX func_index ((ABS(col1))));
    CREATE INDEX idx1 ON t1 ((col1 + col2));
    CREATE INDEX idx2 ON t1 ((col1 + col2), (col1 - col2), col1);
    ALTER TABLE t1 ADD INDEX ((col1 * 40) DESC);
    

    函数索引实际上是通过隐藏的虚拟字段来实现的,因此对于一般字段上的限制,在函数索引上也都会有。

    函数索引的限制
    只有在字段上可以使用的函数才被允许构建函数索引。
    主键不能被包含在函数索引中。
    空间索引和全文索引不能包含函数索引。
    如果要删除的字段上有函数索引,必须先删除函数索引,才能删除字段。

    5. 消除了全局的buffer pool mutex。

    MySQL 8+ 版本使用多个更细粒度的mutex代替buffer pool mutex这把大锁。

    具体的替换方式如下:

    • LRU_list_mutex for the LRU_list;

    • zip_free mutex for the zip_free arrays;

    • zip_hash mutex for the zip_hash hash and in_zip_hash flag;

    • free_list_mutex for the free_list and withdraw list.

    通过对锁的拆分,降低了全局锁的竞争,提升了申请buffer pool的并发处理能力。

    6. 倒序索引

    MySQL长期以来对索引的建立只允许正向asc存储,就算建立了desc,也是忽略掉。

    比如对于以下的查询,无法发挥索引的最佳性能。

    查询一:

    select * from tb1 where f1 = ... order by id desc;
    

    查询二:

    select * from tb1 where f1 = ... order by f1 asc , f2 desc;
    

    那对于上面的查询,尤其是数据量和并发到一定峰值的时候,则对OS的资源消耗非常大,一般这样的SQL在查询计划里面会出现using filesort等状态。

    MySQL 8 + 给我们带来了倒序索引(Descending Indexes),也就是说反向存储的索引。( 这里不要跟搜索引擎中的倒排索引混淆了,MySQL这里只是反向排序存储而已)。虽然只是排序存储,但是已经解决了很大的问题,任你正序排,倒序排,还是混合顺序排,啥姿势都能满足

    7. 不可见索引 INVISIBLE INDEX

    不可见索引或者叫隐藏索引,就是对优化器不可见,查询的时候优化器不会把她它作为备选。

    比如,我有张表t1,本来已经有索引idxf1,idxf2,idxf3。我通过数据字典检索到idxf3基本没有使用过,那我是不是可以判断这个索引直接删掉就好了?
    那如果删掉后突然有新上的业务要大量使用呢?
    难道我要频繁的 drop index/add index 吗?这个时候选择开销比较小的隐藏索引就好了。

    再比如我想要测试下新建索引对我整个业务的影响程度,如果我直接建新索引,那我既有业务涉及到这个字段的有可能会受到很大影响,那这个时候隐藏索引也是非常合适的。

    以后删个索引,想删又不太敢动手,就可以改为不可见,等观察一阵子,确认没影响了再放心删除。

    mysql> alter table f1 add key idx_f1(f1), add key idx_f2(f2);
    Query OK, 0 rows affected (0.12 sec)
    Records: 0 Duplicates: 0 Warnings: 0
    // 把f2列上的索引变为不可见
    mysql> alter table f1 alter index idx_f2 invisible;
    Query OK, 0 rows affected (0.05 sec)
    Records: 0 Duplicates: 0 Warnings: 0
    

    8. 引入WRITESET模式

    writeset 信息记录了多个事务里是否会对相同数据进行变更,若无,则可以并发执行,比多线程复制上升了一个档次,通过参数 binlog_transaction_dependency_tracking 可以进行配置,此方案号称是彻底解决困扰MySQL运维人员多年的复制延迟问题,大大提高并行复制效率,主从复制延迟进一步缩小。

    9. 针对JSON数据类型,增加 Multi-valued indexes

    索引上对于同一个Primary key, 可以建立多个二级索引项,更方便JSON的搜索了。

    Create Table: CREATE TABLE `customers` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
      `custinfo` json DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `zips` ((cast(json_extract(`custinfo`,_latin1'$.zip') as unsigned array)))
    ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1
    1 row in set (0.00 sec)
    
    +----+---------------------+-------------------------------------------------------------------+
    | id | modified            | custinfo                                                          |
    +----+---------------------+-------------------------------------------------------------------+
    |  1 | 2019-08-14 16:08:50 | {"user": "Jack", "user_id": 37, "zipcode": [94582, 94536]}        |
    |  2 | 2019-08-14 16:08:50 | {"user": "Jill", "user_id": 22, "zipcode": [94568, 94507, 94582]} |
    |  3 | 2019-08-14 16:08:50 | {"user": "Bob", "user_id": 31, "zipcode": [94477, 94536]}         |
    |  4 | 2019-08-14 16:08:50 | {"user": "Mary", "user_id": 72, "zipcode": [94536]}               |
    |  5 | 2019-08-14 16:08:50 | {"user": "Ted", "user_id": 56, "zipcode": [94507, 94582]}         |
    +----+---------------------+-------------------------------------------------------------------+
    
    root@test 04:09:00>SELECT * FROM customers WHERE 94507 MEMBER OF(custinfo->'$.zipcode');
    +----+---------------------+-------------------------------------------------------------------+
    | id | modified            | custinfo                                                          |
    +----+---------------------+-------------------------------------------------------------------+
    |  2 | 2019-08-14 16:08:50 | {"user": "Jill", "user_id": 22, "zipcode": [94568, 94507, 94582]} |
    |  5 | 2019-08-14 16:08:50 | {"user": "Ted", "user_id": 56, "zipcode": [94507, 94582]}         |
    +----+---------------------+-------------------------------------------------------------------+
    
    2 rows in set (0.00 sec)
     
    root@test 04:09:41>SELECT * FROM customers  WHERE JSON_CONTAINS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
    +----+---------------------+-------------------------------------------------------------------+
    | id | modified            | custinfo                                                          |
    +----+---------------------+-------------------------------------------------------------------+
    |  2 | 2019-08-14 16:08:50 | {"user": "Jill", "user_id": 22, "zipcode": [94568, 94507, 94582]} |
    |  5 | 2019-08-14 16:08:50 | {"user": "Ted", "user_id": 56, "zipcode": [94507, 94582]}         |
    +----+---------------------+-------------------------------------------------------------------+
    2 rows in set (0.00 sec)
     
    root@test 04:09:54>SELECT * FROM customers   WHERE JSON_OVERLAPS(custinfo->'$.zipcode', CAST('[94507,94582]' AS JSON));
    +----+---------------------+-------------------------------------------------------------------+
    | id | modified            | custinfo                                                          |
    +----+---------------------+-------------------------------------------------------------------+
    |  1 | 2019-08-14 16:08:50 | {"user": "Jack", "user_id": 37, "zipcode": [94582, 94536]}        |
    |  2 | 2019-08-14 16:08:50 | {"user": "Jill", "user_id": 22, "zipcode": [94568, 94507, 94582]} |
    |  5 | 2019-08-14 16:08:50 | {"user": "Ted", "user_id": 56, "zipcode": [94507, 94582]}         |
    +----+---------------------+-------------------------------------------------------------------+
    3 rows in set (0.00 sec)
    
    

    10. HINT语法增强,更方便在执行查询时动态设定选项,针对不同SQL采用不同策略。

    HINT简单来说,就是在某些特定的场景下,人工协助MySQL优化器工作,使她生成最优的执行计划。
    一般来说,优化器的执行计划都是最优化的,不过在某些特定场景下,执行计划可能不是最优化。

    比如:表t1经过大量的频繁更新操作,(UPDATE,DELETE,INSERT),数据的可选择性基数已经很不准确了,这时候刚好执行了一条SQL,那么有可能这条SQL的执行计划就不是最优的。为什么说有可能呢?

    11. 新的TempTable引擎

    新的TempTable引擎,解决了 VARCHAR 字段的边长存储以及大对象的内存存储。
    由变量 interal_tmp_mem_storage_engine 来控制,可选值为 TempTable(默认)和 Memory;
    新引擎的大小由参数 temp_table_max_ram 来控制,默认为1G。
    超过了则存储在磁盘上(ibtmp1)。
    并且计数器由性能字典的表 memory_summary_global_by_event_name 来存储。

    12 Group Relication

    增加了展示信息

    12 Clone Plugin 以及 ReplicaSet

    Clone Plugin 以及 ReplicaSet 特性,允许用户可以将当前实例进行本地或者远程的clone,这在某些场景尤其想快速搭建复制备份或者在 group replication 里加入新成员时非常有用。

    远程clone
    CLONE INSTANCE FROM USER@HOST:PORT
    UIDENTIFIED BY 'password'
    [DATA DIRECTORY [=] 'clone_dir' ]
    [REQUIRE [NO] SSL];

    mysql>SET GLOBAL clone_valid_donor_list = 'xxx.xxx.xxx.xxx:3306';
    mysql>CLONE INSTANCE FROM clone_user@xxx.xxx.xxx.xxx:3306 IDENTIFIED BY 'password'
    mysql>CLONE INSTANCE FROM user_name@xxx.xxx.xxx.xxx:3306 IDENTIFIED BY 'password'
    

    注意:
    需要指定绝对路径,并且路径目录必须不存在
    在接受机器上启动mysqld,执行上述语句连接到目标机器,就能从目标机器上clone数据到本地,注意如果没有指定data directory的话,就默认配置的目录,已有的文件会被清理掉,并在clone完成后重启
    两个实例上都需要安装clone plugin
    必须有相同的字符集设置

    13. sqlrequireprimary_key

    选项设定强制要求每个表都得有个主键

    14. 实例重启后的自增ID持久化

    自增主键没有持久化是个比较早的bug

    +----+
    | id |
    +----+
    |  1 |
    |  2 |
    |  3 |
    
    mysql> delete from t1 where id=3;
    Query OK, 1 row affected (0.36 sec)
    
    mysql> insert into t1 values(null);
    Query OK, 1 row affected (0.35 sec)
    // 未重用 3
    mysql> select * from t1;
    +----+
    | id |
    +----+
    |  1 |
    |  2 |
    |  4 |
    +----+
    mysql> delete from t1 where id=4;
    
    # service mysqld restart
    
    mysql> insert into t1 values(null);
    Query OK, 1 row affected (0.00 sec)
    // 按上面的逻辑,怎么不是 5?
    mysql> select * from t1;
    +----+
    | id |
    +----+
    |  1 |
    |  2 |
    |  3 |
    +----+
    3 rows in set (0.00 sec)
    
    

    究其原因,在于自增主键的分配,是由InnoDB数据字典内部一个计数器来决定的,而该计数器只在内存中维护,并不会持久化到磁盘中,当数据库重启时,该计数器会通过下面这种方式初始化。

    15. extra admin port

    管理员可以给自己开后门了

    主要包含几个配置参数:
    admin_address: 用于指定管理员发起tcp连接的主机地址,可以是ipv4,ipv6, 或者Host name等等,他类似bind-address,但不同的是只能接受一个ip地址
    admin_port: 顾名思义,就是管理员用来连接的端口号,注意如果admin_address没有设置的话,这个端口号是无效的
    create_admin_listener_thread: 是否创建一个单独的listener线程来监听admin的链接请求,默认值是关闭的,建议打开,否则其会使用已有的监听线程去监听admin连接。该参数同样需要admin_address打开, 否则没有任何影响
    注意必须要有权限service_connection_admin才能登陆该端口,否则会报错

    根据文档描述,admin port的连接个数不受max_connection或者Max_user_connection的限制。

    16. SET PERSIST

    在线修改完配置参数后,SET PERSIST 语法实现持久化,不用再手工修改一次my.cnf了。

    show variables like '%max_connections%';
    +------------------------+-------+
    | Variable_name          | Value |
    +------------------------+-------+
    | max_connections        | 151  |
    | mysqlx_max_connections | 100  |
    +------------------------+-------+
    2 rows in set (0.00 sec)
    
    mysql> set persist max_connections=200;
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> show variables like '%max_connections%';
    +------------------------+-------+
    | Variable_name          | Value |
    +------------------------+-------+
    | max_connections        | 200  |
    | mysqlx_max_connections | 100  |
    +------------------------+-------+
    2 rows in set (0.00 sec)
    

    全局变量的修改会保存在两处,数据目录下mysqld-auto.cnf文件( 注意,不是启动时--defaults-file 指定的配置文件。),以json格式保存,其中,Metadata 记录了这次修改的用户及时间信息。

    { 
       "Version" : 1 , "mysql_server" : {
           "max_connections" : {
                "Value" : "200" , "Metadata" : { "Timestamp" : 1525509217566258 , "User" : "root" , "Host" : "localhost" } 
           }
       }
    }
    

    在数据库启动时,如果因为修改了mysqld-auto.cnf文件而失败,可手动删除mysqld-auto.cnf文件或将persisted_globals_load变量设置为off来避免该文件的加载。

    17. 锁增强,增加SKIP LOCK、NOWAIT LOCK锁模式,以及BACKUP LOCk。

    SKIP LOCKED表示加锁的记录如果已被其他线程占有锁,则跳过,而非等待。
    NOWAIT表示加锁记录有锁则报错,而非等待

    select* fromstock whereskuId = 1for updateskip locked;
    Empty set(0.00sec)
    select* fromstock whereskuId = 1for updatenowait;
    ERROR 3572(HY000): Do not wait for lock
    

    BACKUP LOCk 允许在online备份的时候进行DML操作,同时可防止快照不一致。备份锁由 lock instance for backup和unlock instance 语法支持,使用这些语句需要BACKUP_ADMIN权限。

    18. 窗口函数

    窗口的概念非常重要,它可以理解为记录集合,窗口函数也就是在满足某种条件的记录集合上执行的特殊函数。
    对于每条记录都要在此窗口内执行函数,有的函数随着记录不同,窗口大小都是固定的,这种属于静态窗口;有的函数则相反,不同的记录对应着不同的窗口,这种动态变化的窗口叫滑动窗口。

    窗口函数和普通聚合函数也很容易混淆,二者区别如下:
    聚合函数是将多条记录聚合为一条;而窗口函数是每条记录都会执行,有几条记录执行完还是几条。
    聚合函数也可以用于窗口函数中
    下面是一个窗口函数的简单例子:

    image.png

    上面例子中,row_number()over(partition by user_no order by amount desc)这部分都属于窗口函数,它的功能是显示每个用户按照订单金额从大到小排序的序号。

    按照功能划分,可以把MySQL支持的窗口函数分为如下几类:
    序号函数:row_number() / rank() / dense_rank()
    分布函数:percent_rank() / cume_dist()
    前后函数:lag() / lead()
    头尾函数:first_val() / last_val()
    其他函数:nth_value() / nfile()

    窗口函数的基本用法 : 函数名([expr])over 子句


    岁月更迭中 MySQL从 “基本可用”,“边缘系统可以用" , "小系统可用" 再到,“哇操!你怎么不用MySQL” !!

    相关文章

      网友评论

        本文标题:王者归来,Mysql8+ 香在哪?

        本文链接:https://www.haomeiwen.com/subject/estlyhtx.html