简体   繁体   中英

MYSQL Slow query, how to optimize?

I have a big problem, I'm not very good with SQL... I have a database in mysql and when I do this query I have a response time around the 0.2s, so when i call it for a list of users (in a servlet) the response time goes around lots of seconds..

QUERY:

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

Is there any way to optimize this code or any good resource to study the optimization of queries?


CODE

   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 CODE

USERS

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 ;

likes

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 ;

("Read this entire answer before starting to take action.")

This does not make sense:

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

Tentatively, change to

PRIMARY KEY(fb_id),
INDEX(id)

on the assumption that you usually look up the record by fb_id.

If you say "list of users". Do you mean that WHERE fb_id IN ( ... ) ? Probably not since you have LIMIT 1 . Or do you mean that you call that SELECT repeatedly?

Gag! likes_face has an id that you are not using, plus fb_id that you are using but is not indexed . Use the same id for both tables: either get rid of AUTO_INCREMENT and change code logic for likes_face , or get rid of id and make fb_id the PK for both tables.

Are these tables 1:1?

Something is wrong. Why does likes_face have AUTO_INCREMENT=1182636 , while there seem to be only 173 users? If the tables are 1:1, are you using , while there seem to be only 173 users? If the tables are 1:1, are you using REPLACE`? You will run out of ids!

Restarting...

If the tables are 1:1:

  • totally get rid of id on both tables and have PRIMARY KEY(fb_id) on both tables,
  • make fb_id CHARACTER SET ascii if Facebook limits ids to ascii,
  • no secondary indexes needed on either table.

If likes_face is a count of likes for each day:

  • Get rid of id on both tables,
  • make fb_id CHARACTER SET ascii if Facebook limits ids to ascii,
  • Give likes_face PRIMARY KEY(fb_id, date) .
  • no secondary indexes needed on either table.
  • Use IODKU to do the increment/insert into the likes-per-day table.

If the tables are 1:many in some other way, I can't make sense of the schema.

This is all I can come up with:

ALTER TABLE users_face ADD INDEX (fb_id, token_valid);

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

You can't optimize the sorting, because the date column is in your non-primary table.


If you want to optimize further, the next thing to do is to move the nlikes and date columns into the users_face table, then make the index on that table over the following columns in this order: (fb_id, token_valid, date, nlikes) .

Then you can skip the join in your query, and the sort order will be the order of the index, so that will be optimized.

After that (or maybe before denormalizing), you should think about caching the data.

You can use EXPLAIN of statement of MYSQL. Optimizing Queries with EXPLAIN

Someone suggested of creating indexes. Creating index is good option but avoid indexes if that column going to be modified frequently.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM