MM-23369: Allow mysql to choose a better index (#14119)

* MM-23369: Allow mysql to choose a better index

When the ORDER BY clause contains a column which is in the WHERE
clause and also part of an index, mysql tries to use that specific
index to avoid sorting. This is inspite of the fact that
there may be other indices which are better for scanning the table
and then doing a sort.

Essentially, mysql becomes dumb and scans a lot of rows to avoid
sorting. Whereas, it could have scanned a lot less rows and do the
sorting in no time.

To fix this, we use the other columns in the ORDER BY clause as well
which are part of the index. This causes no change in the results
because the other columns are an EQUAL condition check, but this
lets mysql use the right index. Because now mysql sees that it has
to order by other columns too, so it better use the other index
to scan and then do the sorting.

This does not affect tables of smaller size because the LIMIT of
rows is always 1. And mysql will stop sorting the moment it gets
the first row. So sorting is not the overhead at all.
Therefore, this seems like an optimal fix.

References:
https://dev.mysql.com/doc/refman/5.7/en/table-scan-avoidance.html
https://code.openark.org/blog/mysql/7-ways-to-convince-mysql-to-use-the-right-index
https://dev.mysql.com/doc/refman/5.7/en/limit-optimization.html

* Added a comment to clarify things in code

* Incorporating review comments
Этот коммит содержится в:
Agniva De Sarker
2020-03-25 12:39:04 +05:30
коммит произвёл GitHub
родитель 32b7f477bf
Коммит bf3c2c0ce6

Просмотреть файл

@@ -770,7 +770,10 @@ func (s *SqlPostStore) getPostIdAroundTime(channelId string, time int64, before
sq.Eq{"ChannelId": channelId},
sq.Eq{"DeleteAt": int(0)},
}).
OrderBy("CreateAt " + sort).
// Adding ChannelId and DeleteAt order columns
// to let mysql choose the "idx_posts_channel_id_delete_at_create_at" index always.
// See MM-23369.
OrderBy("ChannelId", "DeleteAt", "CreateAt "+sort).
Limit(1)
queryString, args, err := query.ToSql()
@@ -797,7 +800,10 @@ func (s *SqlPostStore) GetPostAfterTime(channelId string, time int64) (*model.Po
sq.Eq{"ChannelId": channelId},
sq.Eq{"DeleteAt": int(0)},
}).
OrderBy("CreateAt ASC").
// Adding ChannelId and DeleteAt order columns
// to let mysql choose the "idx_posts_channel_id_delete_at_create_at" index always.
// See MM-23369.
OrderBy("ChannelId", "DeleteAt", "CreateAt ASC").
Limit(1)
queryString, args, err := query.ToSql()