美文网首页
MySQL排序引发的思考

MySQL排序引发的思考

作者: 社会我大爷 | 来源:发表于2022-03-02 23:35 被阅读0次
    问题一:同一个sql执行倒序和正序性能差别很大,sql明细如下:
    -- 表结构:
    CREATE TABLE `tb_project_white_list` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
      `project_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '活动id',
      `partner_user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '开发者用户id',
      `gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
      `group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '分组id',
      `partner_extra` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '扩展字段',
      PRIMARY KEY (`id`),
      UNIQUE KEY `idx_project_group` (`project_id`,`group_id`,`partner_user_id`),
      KEY `idx_gmt` (`gmt_create`),
      KEY `idx_prj_uid` (`project_id`(20),`partner_user_id`(100)),
      KEY `idx_oo` (`gmt_modified`)
    ) ENGINE=InnoDB AUTO_INCREMENT=31853378 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci STATS_SAMPLE_PAGES=40;
     
    -- 性能对比有limit限制,差异很大:
    MySQL [projectx]> SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id  desc limit 10;   
    10 rows in set (0.00 sec)  -- 倒序很快
     
    MySQL [projectx]> SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id  asc limit 10;     
    10 rows in set (5.03 sec) -- 正序异常慢
     
    -- 性能对比没有limit限制,两者几乎一样:
     
    MySQL [projectx]> SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id  desc;   
    168023 rows in set (0.33 sec)  -- 倒序
     
    MySQL [projectx]> SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id  asc;     
    168023 rows in set (0.36 sec) -- 正序
     
    -- 不同sql形式 执行计划区别:
    -- 1、没有limit:
     
    MySQL [projectx]> desc SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id desc;
    +----+-------------+-----------------------+------------+------+-------------------------------+-------------+---------+-------+--------+----------+-----------------------------+
    | id | select_type | table                 | partitions | type | possible_keys                 | key         | key_len | ref   | rows   | filtered | Extra                       |
    +----+-------------+-----------------------+------------+------+-------------------------------+-------------+---------+-------+--------+----------+-----------------------------+
    |  1 | SIMPLE      | tb_project_white_list | NULL       | ref  | idx_project_group,idx_prj_uid | idx_prj_uid | 62      | const | 337324 |    10.00 | Using where; Using filesort |
    +----+-------------+-----------------------+------------+------+-------------------------------+-------------+---------+-------+--------+----------+-----------------------------+
    1 row in set, 1 warning (0.00 sec)
     
    MySQL [projectx]> desc  SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id  asc;
    +----+-------------+-----------------------+------------+------+-------------------------------+-------------+---------+-------+--------+----------+-----------------------------+
    | id | select_type | table                 | partitions | type | possible_keys                 | key         | key_len | ref   | rows   | filtered | Extra                       |
    +----+-------------+-----------------------+------------+------+-------------------------------+-------------+---------+-------+--------+----------+-----------------------------+
    |  1 | SIMPLE      | tb_project_white_list | NULL       | ref  | idx_project_group,idx_prj_uid | idx_prj_uid | 62      | const | 337324 |    10.00 | Using where; Using filesort |
    +----+-------------+-----------------------+------------+------+-------------------------------+-------------+---------+-------+--------+----------+-----------------------------+
    1 row in set, 1 warning (0.00 sec)
     
    -- 2、有limit限制:
     
    MySQL [projectx]> desc SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id   desc limit 10;
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+----------------------------------+
    | id | select_type | table                 | partitions | type  | possible_keys                 | key     | key_len | ref  | rows | filtered | Extra                            |
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+----------------------------------+
    |  1 | SIMPLE      | tb_project_white_list | NULL       | index | idx_project_group,idx_prj_uid | PRIMARY | 8       | NULL |  392 |     2.65 | Using where; Backward index scan |
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+----------------------------------+
    1 row in set, 1 warning (0.01 sec)
     
    MySQL [projectx]> desc SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id asc limit 10; 
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    | id | select_type | table                 | partitions | type  | possible_keys                 | key     | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | tb_project_white_list | NULL       | index | idx_project_group,idx_prj_uid | PRIMARY | 8       | NULL |  392 |     2.65 | Using where |
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
     
    -- 从执行计划可以看出有limit限制的sql选择了主键扫描,基于不同排序规则差别在于:
    这个表的单行数据大小约100B,总1000W数据两层索引树就能存下,而select字句的查询列超过了max_length_for_sort_data的长度,所以mysql选择了代价较小的扫主键的方式避免filesort,
    而project_id = 'p13651f79'的数据group_id全部为0并且id集中分布在索引树右侧,desc下从右开始扫描,根节点和叶节点扫面的数据页相对较少,并且8.0还使用了Backward index scan(索引倒序)做了进一步优化。
    
    问题二:为何在DMS上根据主键回表查询正序和倒序也很慢?

    原因:DMS在执行sql时默认会控制返回行数100条,实际在mysql-server中执行会 SET SQL_SELECT_LIMIT=200,所以看执行计划使用了idx_prj_uid,但实际执行却是扫描了主键,就会出现问题一中的现象。

    DMS执行流程:
    -- 实际测试sql:
    SELECT * FROM `rds_db_info`
     
    -- DMS执行逻辑:
    220302 12:25:59    40 Query     show variables like '%character_set_results%'
                       40 Query     /* Query from DMS-WEBSQL-0-Qid_1646195158245 by user 262459518802646572 */ explain SELECT * FROM `rds_db_info`
                       40 Query     SET autocommit=1
                       40 Query     SET SQL_SELECT_LIMIT=200 --这里会做变量重置
                       40 Query     show variables like '%character_set_results%'
                       40 Query     SET SQL_SELECT_LIMIT=10
                       40 Query     /* Query from DMS-WEBSQL-0-Qid_1646195158245 by user 262459518802646572 */ SELECT * FROM `rds_db_info`
    

    测试详情如下:

    -- 控制变量:
    MySQL [projectx]> SET SQL_SELECT_LIMIT=200;
    Query OK, 0 rows affected (0.00 sec)
     
    MySQL [projectx]> desc  SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id  asc;
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    | id | select_type | table                 | partitions | type  | possible_keys                 | key     | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | tb_project_white_list | NULL       | index | idx_project_group,idx_prj_uid | PRIMARY | 8       | NULL | 7842 |     2.65 | Using where |
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    1 row in set, 1 warning (0.01 sec)
     
     
    MySQL [projectx]> desc  SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id  asc limit 100; 
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    | id | select_type | table                 | partitions | type  | possible_keys                 | key     | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | tb_project_white_list | NULL       | index | idx_project_group,idx_prj_uid | PRIMARY | 8       | NULL | 4105 |     2.55 | Using where |
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)
     
     
    MySQL [projectx]> desc  SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id  desc  limit 100;
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    | id | select_type | table                 | partitions | type  | possible_keys                 | key     | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | tb_project_white_list | NULL       | index | idx_project_group,idx_prj_uid | PRIMARY | 8       | NULL | 4105 |     2.55 | Using where |
    +----+-------------+-----------------------+------------+-------+-------------------------------+---------+---------+------+------+----------+-------------+
    
    主键扫描 不同排序方式性能差别:
    不同排序方式性能差别
    如何消除filesort:
    -- 增加project_id单列索引,这时where条件检索project_id时本身主键id就是asc有序的,所以不需要再内存排序
     
    MySQL [projectx]> explain  SELECT id, project_id, partner_user_id, partner_extra, group_id   , gmt_create, gmt_modified FROM tb_project_white_list WHERE project_id = 'p13651f79'   AND group_id = '0' ORDER BY id  asc;
    +----+-------------+-----------------------+------------+------+----------------------------------------------+----------------+---------+-------+--------+----------+------------------------------------+
    | id | select_type | table                 | partitions | type | possible_keys                                | key            | key_len | ref   | rows   | filtered | Extra                              |
    +----+-------------+-----------------------+------------+------+----------------------------------------------+----------------+---------+-------+--------+----------+------------------------------------+
    |  1 | SIMPLE      | tb_project_white_list | NULL       | ref  | idx_project_group,idx_prj_uid,idx_project_id | idx_project_id | 98      | const | 320814 |    10.00 | Using index condition; Using where |
    +----+-------------+-----------------------+------------+------+----------------------------------------------+----------------+---------+-------+--------+----------+------------------------------------+
    

    相关文章

      网友评论

          本文标题:MySQL排序引发的思考

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