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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 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
目录
相关文章
无缝集成 MySQL,解锁秒级 OLAP 分析性能极限,完成任务可领取三合一数据线!
通过 AnalyticDB MySQL 版、DMS、DTS 和 RDS MySQL 版协同工作,解决大规模业务数据统计难题,参与活动完成任务即可领取三合一数据线(限量200个),还有机会抽取蓝牙音箱大奖!
从湖仓分离到湖仓一体,四川航空基于 SelectDB 的多源数据联邦分析实践
川航选择引入 SelectDB 建设湖仓一体大数据分析引擎,取得了数据导入效率提升 3-6 倍,查询分析性能提升 10-18 倍、实时性提升至 5 秒内等收益。
从湖仓分离到湖仓一体,四川航空基于 SelectDB 的多源数据联邦分析实践
湖仓分析|浙江霖梓基于 Doris + Paimon 打造实时/离线一体化湖仓架构
浙江霖梓早期基于 Apache Doris 进行整体架构与表结构的重构,并基于湖仓一体和查询加速展开深度探索与实践,打造了 Doris + Paimon 的实时/离线一体化湖仓架构,实现查询提速 30 倍、资源成本节省 67% 等显著成效。
湖仓分析|浙江霖梓基于 Doris + Paimon 打造实时/离线一体化湖仓架构
MySQL底层概述—8.JOIN排序索引优化
本文主要介绍了MySQL中几种关键的优化技术和概念,包括Join算法原理、IN和EXISTS函数的使用场景、索引排序与额外排序(Using filesort)的区别及优化方法、以及单表和多表查询的索引优化策略。
MySQL底层概述—8.JOIN排序索引优化
MySQL底层概述—6.索引原理
本文详细回顾了:索引原理、二叉查找树、平衡二叉树(AVL树)、红黑树、B-Tree、B+Tree、Hash索引、聚簇索引与非聚簇索引。
MySQL底层概述—6.索引原理
MySQL原理简介—9.MySQL索引原理
本文详细介绍了MySQL索引的设计与使用原则,涵盖磁盘数据页的存储结构、页分裂机制、主键索引设计及查询过程、聚簇索引和二级索引的原理、B+树索引的维护、联合索引的使用规则、SQL排序和分组时如何利用索引、回表查询对性能的影响以及索引覆盖的概念。此外还讨论了索引设计的案例,包括如何处理where筛选和order by排序之间的冲突、低基数字段的处理方式、范围查询字段的位置安排,以及通过辅助索引来优化特定查询场景。总结了设计索引的原则,如尽量包含where、order by、group by中的字段,选择离散度高的字段作为索引,限制索引数量,并针对频繁查询的低基数字段进行特殊处理等。
MySQL原理简介—9.MySQL索引原理
MySQL原理简介—4.深入分析Buffer Pool
本文介绍了MySQL的Buffer Pool机制,包括其作用、配置方法及内部结构。Buffer Pool是MySQL用于缓存磁盘数据页的关键组件,能显著提升数据库读写性能。默认大小为128MB,可根据服务器配置调整(如32GB内存可设为2GB)。它通过free链表管理空闲缓存页,flush链表记录脏页,并用LRU链表区分冷热数据以优化淘汰策略。此外,还探讨了多Buffer Pool实例、chunk动态调整等优化并发性能的方法,以及如何通过`show engine innodb status`查看Buffer Pool状态。关键词:MySQL内存数据更新机制。
MySQL索引学习笔记
本文深入探讨了MySQL数据库中慢查询分析的关键概念和技术手段。
310 80
浅入浅出——MySQL索引
本文介绍了数据库索引的概念和各种索引结构,如哈希表、B+树、InnoDB引擎的索引运作原理等。还分享了覆盖索引、联合索引、最左前缀原则等优化技巧,以及如何避免索引误用,提高数据库性能。
mysql慢查询每日汇报与分析
通过启用慢查询日志、提取和分析慢查询日志,可以有效识别和优化数据库中的性能瓶颈。结合适当的自动化工具和优化措施,可以显著提高MySQL数据库的性能和稳定性。希望本文的详解和示例能够为数据库管理人员提供有价值的参考,帮助实现高效的数据库管理。
58 11
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等