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

摘要:
一朋友最近新上线一个项目,本地测试环境跑得好好的,部署到线上却慢得像蜗牛一样。本地运行都是毫秒级别的查询。下面记录一下困扰了两天的,其中一条sql的优化。表结构及现象描述:CREATETABLE`wp_goods`ENGINE=MyISAMAUTO_INCREMENT=10094DEFAULTCHARSET=utf8;CREATETABLE`sys_users`ENGINE=MyISAMAUTO_INCREMENT=14044DEFAULTCHARSET=utf8;CREATETABLE`jd_jianding`ENGINE=MyISAMAUTO_INCREMENT=9142DEFAULTCHARSET=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`LEFTJOIN`sys_users``u`ONg.user_openid=u.openidLEFTJOIN`jd_jianding``j`ONg.id=j.widORDERBY`g`.`id`DESCLIMIT6;耗时16秒,而本地数据库执行耗时0.02毫秒。原因分析:1、explain/desc发现leftjoin索引不起作用。

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

表结构及现象描述:

CREATE TABLE`wp_goods` (
  `id` bigint(20) unsigned NOT NULLAUTO_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 NULLAUTO_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 NULLAUTO_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 ;

分析结果:

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

id    select_type    table    partitions    type    possible_keys    keykey_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;

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

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

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

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

Table    Non_unique    Key_name    Seq_in_index    Column_name    Collation    Cardinality    Sub_part    Packed    NullIndex_type    Comment    Index_comment
jd_jianding    0    PRIMARY    1    id    A    7975N    N        BTREE        
jd_jianding    0    uk_name    1gid    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时(primarykey除外),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、新添加字段默认值都一样,导致索引不可用。

免责声明:文章转载自《mysql索引无效且sending data耗时巨大原因分析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇SpringBoot实现JWT认证关于桌面程序被安全软件误判为HEUR:Trojan.Win32.Generic的解决方案下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

数据库管理工具

NO1   Webyog | 管理和监控MySQL服务器的工具,官网:https://www.webyog.com/   SQLyog Ultimate V12.5 64bit 中文破解版(附注册码),下载地址:http://www.jb51.net/database/598306.html   SQLyong是一款功能强大的数据库管理程序,新版本增加更...

分析kube-proxy的iptables规则

NodePort service 创建一个mysql的NodePort服务,对应两个pod实例,rc和service的配置如下: 1、rc配置 apiVersion: v1 kind: ReplicationController metadata: name: wordpress-mysql spec: replicas: 2selector:...

浅析MySQL 8忘记密码处理方式

        对MySQL有研究的读者,可能会发现MySQL更新很快,在安装方式上,MySQL提供了两种经典安装方式:解压式和一键式,虽然是两种安装方式,但我更提倡选择解压式安装,不仅快,还干净。在操作系统上,MySQL也支持多类型操作系统,如linux,windows等,如下为MySQL几个重大变化的操作系统。             通过研究分析,不...

MySQL的锁(1)

    锁机制是数据库系统区别于文件系统的一个关键特性,他可以确保用户能以一致的方式读取和修改数据。     为了保证一致性,必须有锁的介入。MySQL操作缓冲池中的LRU列表,删除、添加、移动LRU列表中的元素等地方也都适用锁,从而允许对多种不同资源的并发访问。     打个比方,我们到淘宝上买一件商品,商品只有一件库存,这个时候如果还有另一个人买,那么...

Mac安装Mysql

目录 一、安装 二、环境变量 2.1 MySQL服务的启停和状态的查看 三、启动 四、初始化设置 4.1 退出sql界面 五、配置 5.1 检测修改结果 一、安装 第一步:打开网址,https://www.mysql.com ,点击downloads之后跳转到https://www.mysql.com/downloads 选择Community选...

EF性能优化

十年河东,十年河西,莫欺少年穷。 EF就如同那个少年,ADO.NET则是一位壮年。毕竟ADO.NET出生在EF之前,而EF所走的路属于应用ADO.NET。 也就是说:你所写的LINQ查询,最后还是要转化为ADO.NET的SQL语句,转化过程中无形降低了EF的执行效率。 但是,使用EF的一个好处就是系统便于维护,减少了系统开发时间,降低了生成成本。 OK,上...