【问题标题】:Optimize running time of MySQL query优化 MySQL 查询的运行时间
【发布时间】:2013-05-05 19:30:31
【问题描述】:

有一个表 users,其主键为 user_id,索引列称为已验证。 另一个表 user_profile 的 PK 为 profile_id,FK 为 user_id 并有一个列 - 名称

现在,我需要找到所有经过验证的用户及其姓名。所以我需要在 user_id 上加入这两个表 -

查询变成 -

select p.name from user_profile p inner join user u on p.user_id = u.user_id
where u.verified = 1;

profile 表中有 700000 条记录,user 表中有相同数量的记录。上面的这个查询需要 13 秒才能运行。请告诉我,如何优化运行时间。

MySQL 5.5 版,YII

编辑

CREATE TABLE IF NOT EXISTS `tbl_profile` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(10) unsigned NOT NULL,
  `regyear` int(4) DEFAULT NULL,
  `firstname` varchar(128) NOT NULL,
  `gender` varchar(10) NOT NULL,
  `occupation` int(5) NOT NULL,
  `street` varchar(255) DEFAULT NULL,
  `state` int(10) DEFAULT NULL,
  `city` int(10) DEFAULT NULL,
  `zip` int(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `state` (`state`),
  KEY `firstname` (`firstname`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=626494 ;

--

-- 表tbl_user的表结构

CREATE TABLE IF NOT EXISTS `tbl_user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `email` varchar(255) DEFAULT NULL,
  `password` varchar(128) NOT NULL,
  `createtime` int(10) NOT NULL DEFAULT '0',
  `lastvisit` int(10) NOT NULL DEFAULT '0',
  `status` int(1) NOT NULL DEFAULT '0',
  `verified` int(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `email` (`email`),
  KEY `status` (`status`),
  KEY `verified` (`verified`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=626494 ;

EXPLAIN SELECT 的输出 - 我编写了与上面相同的查询,但将 999 替换为 1 并使用列状态而不是已验证,这相当于问题陈述。

EXPLAIN SELECT p.firstname
FROM tbl_profile p
INNER JOIN tbl_user u ON p.user_id = u.id
WHERE u.status =999
+----+-------------+-------+------+----------------+---------+---------+-------------+--------+-------------+
| id | select_type | table | type | possible_keys  | key     | key_len | ref         | rows   | Extra       |
+----+-------------+-------+------+----------------+---------+---------+-------------+--------+-------------+
|  1 | SIMPLE      | u     | ref  | PRIMARY,status | status  | 4       | const       | 313333 | Using index |
|  1 | SIMPLE      | p     | ref  | user_id        | user_id | 4       | newone.u.id |      1 |             |
+----+-------------+-------+------+----------------+---------+---------+-------------+--------+-------------+

【问题讨论】:

  • 您在表上有哪些索引?添加2个表的CREATE TABLE语句和EXPLAIN SELECT ...的输出
  • 一个用户可以有很多个人资料吗?
  • 不,一个用户只有 1 个个人资料,我想您会建议加入他们并制作 1 个表格?实际上用户表仅用于登录,另一个表有个人资料相关数据。
  • 不,我已将我的建议添加为答案。不知道 Yii 是否会对这种变化有任何问题。一些 ORM/框架在没有自动递增 PK 的情况下存在表问题。

标签: mysql yii


【解决方案1】:

建议 1

(user_id, first_name) 上添加索引将提高此特定查询的效率:

ALTER TABLE tbl_profile
  ADD INDEX user_id_first_name_IX             -- just a name for the index
    (user_id, first_name) ; 

但是,如果您也有类似的查询,在选择其他列的地方,您将需要更多这样的索引。并且在表中添加 5-10 个索引并不算太糟糕(它只会稍微减慢您的插入速度。)但是添加太多索引最终会有害。


建议 2

如果每个用户最多有 1 个配置文件,则无需在表 profiles 中设置自动递增的 id。我建议您删除该列并将user_id 设为主键。我也会把它设为外键:

ALTER TABLE tbl_profile
  DROP PRIMARY KEY,
  DROP COLUMN id,
  ADD CONSTRAINT profile_PK
    PRIMARY KEY (user_id),
  ADD CONSTRAINT user_profile_FK 
    FOREIGN KEY (user_id)
    REFERENCES tbl_user (id) ; 

这比建议 1 好得多,因为您基本上会将 user_id 设为表的聚集索引。任何使用user_id 连接此表的查询都可以使用此(主索引和聚集索引)。

【讨论】:

    【解决方案2】:

    您可以通过将条件移动到连接的ON 子句中来提高性能:

    select p.name
    from user_profile p
    join user u on p.user_id = u.user_id and u.verified = 1;
    

    它可能表现更好的原因是WHERE 子句在所有行都连接之后进行评估 - 它是结果集上的一个过滤器。然而,ON 条件会在进行连接时进行评估,因此数据库可能需要处理更少的行,从而减少内存/资源。

    除了那个改变,我看不出你还能做什么。

    【讨论】:

    • 我不知道为什么不赞成,我必须尝试每个建议,看看哪个运行最快。
    • 试一试非常明智。通常,这是最快和最好的查询查询的方法 - 进行一些更改并比较时间。如果您要对它们进行全部测试,那么在所有答案中添加评论以说明其性能对每个人都会有所帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多