【问题标题】:MYSQL Slow query, how to optimize?MYSQL查询慢,如何优化?
【发布时间】:2016-11-30 05:52:38
【问题描述】:

我有一个大问题,我不太擅长 SQL...我在 mysql 中有一个数据库,当我执行此查询时,我的响应时间大约为 0.2 秒,所以当我调用它以获得列表时的用户(在 servlet 中)的响应时间大约是几秒..

查询:

SELECT visible,nlikes,nomecognome,profile_img,users_face.id 
FROM users_face 
LEFT OUTER JOIN `likes_face`  
on (users_face.fb_id = likes_face.fb_id) 
WHERE  users_face.fb_id =? and users_face.token_valid=1
ORDER BY date DESC limit 1

有没有什么方法可以优化这段代码或者有什么好的资源来研究查询的优化?


代码

   ArrayList<SocialMan> mans = new ArrayList<>();
    PreparedStatement ps;
    int nlikes, userid;
    String nomeCogn, prof;
    boolean visible;
    FacebookClient facebookClient = new DefaultFacebookClient(token, Version.VERSION_2_6);
    com.restfb.Connection<User> myFriends;
    myFriends = facebookClient.fetchConnection("me/friends",User.class, Parameter.with("limit", 999));
    for (User u : myFriends.getData()) {
        nlikes = -1;
        userid = -1;
        nomeCogn = "ERROR";
        prof = "ERROR";
        visible = false;
        try {

                ps = con.prepareStatement("SELECT visible,nlikes,nomecognome,profile_img,users_face.id FROM users_face LEFT OUTER JOIN `likes_face`  on (users_face.fb_id = likes_face.fb_id) WHERE  users_face.fb_id =? and users_face.token_valid=1 ORDER BY date DESC limit 1");

            ps.setString(1, "" + u.getId());
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                nlikes = rs.getInt("nlikes");
                userid = rs.getInt("id");
                nomeCogn = rs.getString("nomecognome");
                prof = rs.getString("profile_img");
                visible = rs.getBoolean("visible");
            }
        } catch (SQLException ex) {
            Logger.getLogger(FaceLikes.class.getName()).log(Level.SEVERE, null, ex);
        }
        //   System.out.println("NOMECOGNOME: "+nomeCogn);
        if (userid != -1 && visible) {                
            mans.add(new SocialMan(nomeCogn, userid, prof, nlikes));
        }
    }
    nlikes = -1;
    userid = -1;
    nomeCogn = "ERROR";
    prof = "ERROR";

创建表代码

用户

CREATE TABLE IF NOT EXISTS `users_face` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `fb_id` varchar(45) NOT NULL,
  `fb_token` varchar(300) NOT NULL,
  `nomecognome` varchar(100) NOT NULL,
  `data_iscrizione` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `profile_img` varchar(255) NOT NULL,
  `visible` int(11) NOT NULL DEFAULT '1',
  `TOKEN` varchar(255) NOT NULL,
  `locale` varchar(255) NOT NULL,
  `token_valid` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`,`fb_id`),
  UNIQUE KEY `fb_id` (`fb_id`),
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=173 ;

喜欢

CREATE TABLE IF NOT EXISTS `likes_face` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `nlikes` int(11) NOT NULL,
  `fb_id` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1182636 ;

【问题讨论】:

  • 能否也给我们您的数据库结构?
  • 这个查询返回了多少行?你确定你必须使用左外连接吗?
  • 所以当我为用户列表调用它时和LIMIT 1。你怎么称呼你的名单???向我们展示你在 mysql 之外的后端代码?
  • 你的EXPLAIN是什么样的?
  • 我添加了结构。此查询返回 1 行。我在 servlet 上有一个从请求中获取的用户 ID 列表,我需要为每个用户检索这些数据。

标签: mysql sql optimization query-optimization


【解决方案1】:

(“在开始采取行动之前,请阅读完整的答案。”)

这没有意义:

PRIMARY KEY (`id`,`fb_id`),
UNIQUE KEY `fb_id` (`fb_id`),
UNIQUE KEY `id` (`id`)

暂时改成

PRIMARY KEY(fb_id),
INDEX(id)

假设您通常通过 fb_id 查找记录。

如果您说“用户列表”。你的意思是WHERE fb_id IN ( ... )?可能不是因为你有LIMIT 1。或者你的意思是你反复调用SELECT?

堵嘴! likes_face 有一个您未使用的 id,以及您正在使用但未编入索引的fb_id。对两个表使用相同 id:要么去掉AUTO_INCREMENT 并更改likes_face 的代码逻辑,要么去掉id 并使fb_id 成为两个表的PK。

这些表格是 1:1 的吗?

出了点问题。为什么likes_face haveAUTO_INCREMENT=1182636, while there seem to be only 173 users? If the tables are 1:1, are you usingREPLACE`?你的 id 会用完!

正在重新启动...

如果表格是 1:1:

  • 完全摆脱两个表上的id 并在两个表上都有PRIMARY KEY(fb_id),
  • 如果 Facebook 将 id 限制为 ascii,则制作 fb_id CHARACTER SET ascii,
  • 两个表都不需要二级索引。

如果likes_face 是每天的点赞数:

  • 去掉两个表上的id,
  • 如果 Facebook 将 id 限制为 ascii,则设为 fb_id CHARACTER SET ascii,
  • 给likes_facePRIMARY KEY(fb_id, date)。
  • 两个表都不需要二级索引。
  • 使用 IODKU 将增量/插入到每天的点赞数表中。

如果表是 1:many 在其他方面,我无法理解架构。

【讨论】:

  • 我的意思是我反复调用 SELECT,这是第二种情况 1:很多,谢谢!
【解决方案2】:

这就是我能想到的:

ALTER TABLE users_face ADD INDEX (fb_id, token_valid);

ALTER TABLE likes_face ADD INDEX (fb_id, nlikes, date);

您无法优化排序,因为date 列在您的非主表中。


如果您想进一步优化,接下来要做的是将nlikes 和date 列移动到 users_face 表中,然后按以下顺序在该表上建立索引:(fb_id, token_valid, date, nlikes) .

然后你可以跳过查询中的join,排序顺序就是索引的顺序,这样会优化。

在那之后(或者可能在去规范化之前),您应该考虑缓存数据。

【讨论】:

  • 感谢比尔,这将时间减少到 0.01 而不是 0.2!我无法在查询(或者可能是结构)中进行编辑以进一步提高性能?
  • 如果nlikes 被频繁更新,将它放在任何索引中都是不明智的。
  • @RickJames,恕我直言,我认为这是一种过度概括。确实,更新索引比不更新索引的开销更大。但是仍然值得在索引中包含一列,具体取决于您需要运行的查询。不同之处在于您认为更高的优先级——更快的写入或更快的读取。覆盖索引效果可以极大地改善某些查询,足以证明开销是合理的。
  • 嗨,比尔。你有有效的积分。对于 this 用例,我假设“喜欢”经常更新,而“选择”的频率较低。 (也许这个假设是错误的。)在真正繁忙的“喜欢”系统中,likes 的碰撞成为瓶颈。 Andrea 可能还没有有一个“非常繁忙的系统”,但我想避免这种情况。 (正如我在回答中指出的那样,在“覆盖”索引冒泡成为“最重要的”之前,还需要解决其他问题。)
  • nlikes 永远不会更新,只是每次都以新记录的方式自动存储,我的首要任务是尽可能快地读取。
【解决方案3】:

您可以使用MYSQL 语句的EXPLAIN。 Optimizing Queries with EXPLAIN

有人建议创建索引。创建索引是一个不错的选择,但如果该列将被频繁修改,请避免使用索引。

【讨论】:

  • 重新链接第二个...谨慎将 Oracle 建议应用于 MySQL。
  • 谢谢@RickJames。我已经删除了那个链接。但是,这适用于并非专门针对 mysql 或 Oracle 的索引。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-30
  • 2020-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-11
  • 1970-01-01
相关资源
最近更新 更多