MM-27575: Use index hints to prevent index_merge_intersection (#15207)
Even after specifying all the 3 columns to coerce MySQL into choosing the right multi-column index, it sometimes uses 2 separate indices and does an index_merge of them. This creates problems because the DeleteAt=0 search is essentially the entire Posts table, and causes a disastrously bad performance than even choosing the wrong index (idx_create_at). The problem with this approach is that we hardcode the decision to a specific index when MySQL was free to choose the right index depending on table statistics. However, there does not appear to be a case where this index can cause regressions than using some other index. Another option here was to set optimizer_switch="index_merge_intersection=off" at a session level for a transaction and then switch it back on after the query is done. However, this can cause some unintentional consequences because this setting is only available at a session level and not at a query level. There is no need to set something at the session level when an index hint suffices. https://mattermost.atlassian.net/browse/MM-27575
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3f19a8c011
Коммит
8138600dd1
@@ -708,10 +708,18 @@ func (s *SqlPostStore) getPostsAround(before bool, options model.GetPostsOptions
|
||||
direction = ">"
|
||||
sort = "ASC"
|
||||
}
|
||||
table := "Posts p"
|
||||
// We force MySQL to use the right index to prevent it from accidentally
|
||||
// using the index_merge_intersection optimization.
|
||||
// See MM-27575.
|
||||
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
||||
table += " USE INDEX(idx_posts_channel_id_delete_at_create_at)"
|
||||
}
|
||||
|
||||
replyCountSubQuery := s.getQueryBuilder().Select("COUNT(Posts.Id)").From("Posts").Where(sq.Expr("Posts.RootId = (CASE WHEN p.RootId = '' THEN p.Id ELSE p.RootId END) AND Posts.DeleteAt = 0"))
|
||||
query := s.getQueryBuilder().Select("p.*")
|
||||
query = query.Column(sq.Alias(replyCountSubQuery, "ReplyCount"))
|
||||
query = query.From("Posts p").
|
||||
query = query.From(table).
|
||||
Where(sq.And{
|
||||
sq.Expr(`CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = ?)`, options.PostId),
|
||||
sq.Eq{"ChannelId": options.ChannelId},
|
||||
@@ -811,9 +819,17 @@ func (s *SqlPostStore) getPostIdAroundTime(channelId string, time int64, before
|
||||
sort = "ASC"
|
||||
}
|
||||
|
||||
table := "Posts"
|
||||
// We force MySQL to use the right index to prevent it from accidentally
|
||||
// using the index_merge_intersection optimization.
|
||||
// See MM-27575.
|
||||
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
||||
table += " USE INDEX(idx_posts_channel_id_delete_at_create_at)"
|
||||
}
|
||||
|
||||
query := s.getQueryBuilder().
|
||||
Select("Id").
|
||||
From("Posts").
|
||||
From(table).
|
||||
Where(sq.And{
|
||||
direction,
|
||||
sq.Eq{"ChannelId": channelId},
|
||||
@@ -841,9 +857,17 @@ func (s *SqlPostStore) getPostIdAroundTime(channelId string, time int64, before
|
||||
}
|
||||
|
||||
func (s *SqlPostStore) GetPostAfterTime(channelId string, time int64) (*model.Post, *model.AppError) {
|
||||
table := "Posts"
|
||||
// We force MySQL to use the right index to prevent it from accidentally
|
||||
// using the index_merge_intersection optimization.
|
||||
// See MM-27575.
|
||||
if s.DriverName() == model.DATABASE_DRIVER_MYSQL {
|
||||
table += " USE INDEX(idx_posts_channel_id_delete_at_create_at)"
|
||||
}
|
||||
|
||||
query := s.getQueryBuilder().
|
||||
Select("*").
|
||||
From("Posts").
|
||||
From(table).
|
||||
Where(sq.And{
|
||||
sq.Gt{"CreateAt": time},
|
||||
sq.Eq{"ChannelId": channelId},
|
||||
|
||||
Ссылка в новой задаче
Block a user