mysql索引无效且sending data耗时巨大原因分析

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:

 一朋友最近新上线一个项目,本地测试环境跑得好好的,部署到线上却慢得像蜗牛一样。后来查询了一下发现一个sql执行了16秒,有些长的甚至80秒。本地运行都是毫秒级别的查询。下面记录一下困扰了两天的,其中一条sql的优化。

  表结构及现象描述:

复制代码
CREATE TABLE `wp_goods` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `user_openid` varchar(255) NOT NULL DEFAULT '',
  `description` longtext ,
  `upset_price` decimal(10,2) DEFAULT NULL ,
  `reference_price` decimal(10,2) DEFAULT NULL ,
  `offer_unit` decimal(10,2) DEFAULT NULL ,
  `end_time` int(11) DEFAULT NULL ,
  `type` tinyint(4) DEFAULT NULL ,
  `is_bail` tinyint(4) DEFAULT NULL ,
  `is_express` tinyint(4) DEFAULT NULL ,
  `is_return` tinyint(4) DEFAULT NULL ,
  `createtime` int(11) DEFAULT NULL ,
  `is_sell` tinyint(4) DEFAULT NULL ,
  `is_draft` tinyint(1) NOT NULL DEFAULT '1' ,
  `scan_count` int(11) NOT NULL ,
  `title` varchar(255) NOT NULL ,
  `is_trash` tinyint(1) NOT NULL DEFAULT '1' ,
  `countdown` smallint(6) NOT NULL DEFAULT '0' ,
  `bail_money` tinyint(4) NOT NULL DEFAULT '0' ,
  `cat_id` tinyint(4) NOT NULL,
  `sort` int(10) unsigned NOT NULL DEFAULT '1' ,
  PRIMARY KEY (`id`),
  KEY `cat_id` (`cat_id`),
  KEY `index_id_user_openid` (`id`,`user_openid`) USING BTREE,
  KEY `index_user_openid` (`user_openid`) USING BTREE,
  KEY `index_id` (`id`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=10094 DEFAULT CHARSET=utf8;

CREATE TABLE `sys_users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `openid` varchar(50) DEFAULT NULL,
  `nickname` varchar(20) DEFAULT NULL,
  `sex` char(255) DEFAULT NULL,
  `phone` varchar(11) DEFAULT NULL,
  `country` varchar(10) DEFAULT NULL,
  `province` varchar(10) DEFAULT NULL,
  `city` varchar(10) DEFAULT NULL,
  `headimgurl` varchar(200) DEFAULT NULL,
  `createtime` varchar(20) DEFAULT NULL,
  `is_subject` tinyint(4) NOT NULL DEFAULT '1' ,
  `black` tinyint(4) NOT NULL DEFAULT '1' ,
  `wd_sort` smallint(5) unsigned DEFAULT '1000' ,
  `wp_sort` smallint(5) unsigned NOT NULL DEFAULT '1000' ,
  PRIMARY KEY (`id`),
  UNIQUE KEY `openid` (`openid`)
) ENGINE=MyISAM AUTO_INCREMENT=14044 DEFAULT CHARSET=utf8;

CREATE TABLE `jd_jianding` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `expert_id` int(11) DEFAULT NULL ,
  `gid` int(11) DEFAULT NULL ,
  `goods_value` varchar(50) DEFAULT NULL ,
  `result` varchar(500) DEFAULT NULL ,
  `jdtime` int(11) DEFAULT NULL ,
  `is_essence` tinyint(4) NOT NULL DEFAULT '0' ,
  `istrue` tinyint(4) DEFAULT '0' ,
  `wid` int(11) DEFAULT '0',
  `scan_num` int(11) DEFAULT '0' ,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_name` (`gid`),
  KEY `index_wid` (`wid`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=9142 DEFAULT CHARSET=utf8;
复制代码

  表wp_goods数据量10094,sys_users数据量14044, jd_jianding数据量9142。

  执行sql:

复制代码
SELECT 
  `g`.`id`,
  `g`.`title`,
  `g`.`upset_price`,
  `u`.`nickname`,
  `j`.`istrue` 
FROM
  `wp_goods` `g` 
  LEFT JOIN `sys_users` `u` 
    ON g.user_openid = u.openid 
  LEFT JOIN `jd_jianding` `j` 
    ON g.id = j.wid 
ORDER BY `g`.`id` DESC 
LIMIT 6 ;
复制代码

  耗时16秒,而本地数据库执行耗时0.02毫秒。

  原因分析:

  1、explain/desc 发现left join索引不起作用。

复制代码
explain SELECT 
  `g`.`id`,
  `g`.`title`,
  `g`.`upset_price`,
  `u`.`nickname`,
  `j`.`istrue` 
FROM
  `wp_goods` `g` 
  LEFT JOIN `sys_users` `u` 
    ON g.user_openid = u.openid 
  LEFT JOIN `jd_jianding` `j` 
    ON g.id = j.wid 
ORDER BY `g`.`id` DESC 
LIMIT 6 ;
复制代码

  分析结果:

id    select_type    table    partitions    type    possible_keys    key    key_len    ref    rows    filtered    Extra
1    SIMPLE    g    \N    ALL    \N    \N    \N    \N    10093    100.00    Using temporary; Using filesort
1    SIMPLE    u    \N    ref    openid    openid    153    mydb.g.user_openid    10    100.00    Using where
1    SIMPLE    j    \N    ALL    index_wid    \N    \N    \N    7975    100.00    Using where; Using join buffer (Block Nested Loop)

  索引无效,Using join buffer (Block Nested Loop)相当于遍历表查询。

  2、profile分析了下,发现几乎所有耗时都在sending data且缓存sending cached result to clien没开启。

  show variables like '%cache%';

  query_cache_type为off,在配置文件/etc/my.cf中添加“query_cache_type = 1”配置项并重启。

  执行后耗时10s,如果将order by去掉后耗时3秒。即使是耗时3秒也是无法接受的。

  通过profile分析下具体耗时

复制代码
SHOW VARIABLES LIKE '%profil%'
SET profiling = 1;

SELECT 
  `g`.`id`,
  `g`.`title`,
  `g`.`upset_price`,
  `u`.`nickname`,
  `j`.`istrue` 
FROM
  `wp_goods` `g` 
  LEFT JOIN `sys_users` `u` 
    ON g.user_openid = u.openid 
  LEFT JOIN `jd_jianding` `j` 
    ON g.id = j.wid 
ORDER BY `g`.`id` DESC 
LIMIT 6 ;

show profile for query 1;
复制代码

  

  发现几乎所有耗时都在sending data部分。

  3、查看jd_jianding表索引,show index from jd_jianding发现cardinality的值为1。

  

Table    Non_unique    Key_name    Seq_in_index    Column_name    Collation    Cardinality    Sub_part    Packed    Null    Index_type    Comment    Index_comment
jd_jianding    0    PRIMARY    1    id    A    7975    \N    \N        BTREE        
jd_jianding    0    uk_name    1    gid    A    \N    \N    \N    YES    BTREE        
jd_jianding    1    index_wid    1    wid    A    1    \N    \N    YES    BTREE    

  4、优化表jd_jianding,analyze table jd_jianding,再次执行仍然如此。

  然而mysql的文档时这么说的。The higher the cardinality, the greater the chance that MySQL uses the index when doing joins. 

  An estimate of the number of unique values in the index. This is updated by running ANALYZE TABLE or myisamchk -a. Cardinality is counted based on statistics stored as integers, so the value is not necessarily exact even for small tables. The higher the cardinality, the greater the chance that MySQL uses the index when doing

  大意如下:

   1)、它代表的是索引中唯一值的数目的估计值。如果是myisam引擎,这个值是一个准确的值。如果是innodb引擎,这个值是一个估算的值,每次执行show index 时,可能会不一样
   2)、创建Index时(primary key除外),MyISAM的表Cardinality的值为null,InnoDB的表Cardinality的值大概为行数;
   3)、值的大小会影响到索引的选择
   4)、创建Index时,MyISAM的表Cardinality的值为null,InnoDB的表Cardinality的值大概为行数。
   5)、可以通过Analyze table来更新一张表或者mysqlcheck -Aa来进行更新整个数据库
   6)、可以通过 show index 查看其值

  5、查看表jd_jianding字段wid的值全为默认值0,于是将其中一条记录的wid字段值update为非0;再次analyze table jd_jianding。

  再次执行,效果杠杠的,耗时只有0.02毫秒。困扰两天的问题终于得到了解决。

  6、把步骤4修改的字段值还原回来。

 

  后记,原因大致如下:

1、mysql没有开启查询缓存。
2、新添加字段默认值都一样,导致索引不可用。









本文转自秋楓博客园博客,原文链接:http://www.cnblogs.com/rwxwsblog/p/5684213.html,如需转载请自行联系原作者

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
19天前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
119 9
|
2天前
|
SQL 存储 关系型数据库
MySQL秘籍之索引与查询优化实战指南
最左前缀原则。不冗余原则。最大选择性原则。所谓前缀索引,说白了就是对文本的前几个字符建立索引(具体是几个字符在建立索引时去指定),比如以产品名称的前 10 位来建索引,这样建立起来的索引更小,查询效率更快!
43 22
 MySQL秘籍之索引与查询优化实战指南
|
2天前
|
SQL 关系型数据库 MySQL
MySQL事务日志-Undo Log工作原理分析
事务的持久性是交由Redo Log来保证,原子性则是交由Undo Log来保证。如果事务中的SQL执行到一半出现错误,需要把前面已经执行过的SQL撤销以达到原子性的目的,这个过程也叫做"回滚",所以Undo Log也叫回滚日志。
MySQL事务日志-Undo Log工作原理分析
|
5天前
|
SQL 存储 缓存
EMR Serverless StarRocks 全面升级:重新定义实时湖仓分析
本文介绍了EMR Serverless StarRocks的发展路径及其架构演进。首先回顾了Serverless Spark在EMR中的发展,并指出2021年9月StarRocks开源后,OLAP引擎迅速向其靠拢。随后,EMR引入StarRocks并推出全托管产品,至2023年8月商业化,已有500家客户使用,覆盖20多个行业。 文章重点阐述了EMR Serverless StarRocks 1.0的存算一体架构,包括健康诊断、SQL调优和物化视图等核心功能。接着分析了存算一体架构的挑战,如湖访问不优雅、资源隔离不足及冷热数据分层困难等。
|
4天前
|
存储 关系型数据库 MySQL
MySQL中为什么要使用索引合并(Index Merge)?
通过这些内容的详细介绍和实际案例分析,希望能帮助您深入理解索引合并及其在MySQL中的
21 10
|
3天前
|
DataWorks 关系型数据库 OLAP
云端问道5期实践教学-基于Hologres轻量实时的高性能OLAP分析
本文基于Hologres轻量实时的高性能OLAP分析实践,通过云起实验室进行实操。实验步骤包括创建VPC和交换机、开通Hologres实例、配置DataWorks、创建网关、设置数据源、创建实时同步任务等。最终实现MySQL数据实时同步到Hologres,并进行高效查询分析。实验手册详细指导每一步操作,确保顺利完成。
|
16天前
|
存储 Oracle 关系型数据库
索引在手,查询无忧:MySQL索引简介
MySQL 是一款广泛使用的关系型数据库管理系统,在2024年5月的DB-Engines排名中得分1084,仅次于Oracle。本文介绍MySQL索引的工作原理和类型,包括B+Tree、Hash、Full-text索引,以及主键、唯一、普通索引等,帮助开发者优化查询性能。索引类似于图书馆的分类系统,能快速定位数据行,极大提高检索效率。
48 8
|
19天前
|
SQL 关系型数据库 MySQL
MySQL 窗口函数详解:分析性查询的强大工具
MySQL 窗口函数从 8.0 版本开始支持,提供了一种灵活的方式处理 SQL 查询中的数据。无需分组即可对行集进行分析,常用于计算排名、累计和、移动平均值等。基本语法包括 `function_name([arguments]) OVER ([PARTITION BY columns] [ORDER BY columns] [frame_clause])`,常见函数有 `ROW_NUMBER()`, `RANK()`, `DENSE_RANK()`, `SUM()`, `AVG()` 等。窗口框架定义了计算聚合值时应包含的行。适用于复杂数据操作和分析报告。
60 11
|
23天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化以及慢查询优化
通过本文的介绍,希望您能够深入理解MySQL索引优化和慢查询优化的方法,并在实际应用中灵活运用这些技术,提升数据库的整体性能。
22 7
|
22天前
|
缓存 关系型数据库 MySQL
MySQL 索引优化与慢查询优化:原理与实践
通过本文的介绍,希望您能够深入理解MySQL索引优化与慢查询优化的原理和实践方法,并在实际项目中灵活运用这些技术,提升数据库的整体性能。
57 5