Я реализую следующую политику доступа: User
может получить доступ к Resource
, если он его создал, принадлежит к членам группы Resource
или если ресурс является общедоступным.Производительность запросов MySQL по двум индексам + сортировка
Вот моя структура БД (таблицы MyISAM):
User (1K-10K Users)
id
nickame
…
index user_name(id, nickname)
Group (1K)
id
…
Resource (10K-100K)
id
user_id
access_type (int)
group_id
created (int/timestamp)
…
indexes by_user(user_id, created), by_group(access_type, group_id, created), public(access_type, created)
User_Group (5K-20K)
user_id
group_id
membership_type (int)
index user_group(user_id, membership_type, group_id)
условие для предоставления доступа реализован следующим образом: * The User
является создателем Resource
(user_id = <his id>
, независимо от access_type есть) * Или Ressource общедоступна: Resource.access_id = 3
* Или Ressource разделяется в группе и пользователя принимается как «член» этой группы: ** Resource.access_id = 2
(совместно в группе) ** Ressource.group_id
соответствует's group_id
** это User_Group
' s user_id
совпадает с идентификатором пользователя ** это User_Group
«s membership_type
является 1, 2 или 3 (принят членом или группы модератор/администратор)
Мой вопрос: что такое наиболее эффективный способ перечислить Resources
(и Resource
создателя nickname
), которые доступны для пользователя, когда у нас есть его идентификатор. И упорядочен по метке времени created
ресурса.
Моя лучшая попытка до сих пор является использование UNION
, позволяющая использовать как by_user
и by_group
индексов, но может бороться в сортировочный потом:
SELECT SQL_NO_CACHE
a.*, user.nickname
FROM (
(SELECT resource.* FROM resource WHERE user_id = '000000-0000-0000-0000-000000000000')
UNION
(SELECT resource.* FROM resource WHERE access_type = 3)
UNION
(SELECT resource.* FROM resource
INNER JOIN user_group ON (access_type = 2 AND user_group.group_id = resource.group_id)
WHERE user_group.user_id = '000000-0000-0000-0000-000000000000'
AND user_group.type IN (1, 2, 3)
)
) a
LEFT JOIN user ON (user.id = a.user_id)
ORDER BY created DESC;
EXPLAIN
выход говорит мне, что использует индексы для обоих SELECT
с и заканчивается на type: ALL/Using filesort
для ORDER BY
на UNION
рядами. Но если я хочу заказать и ограничить, это может стать тяжелым, я думаю, так как нет индекса на UNION
-ed кортежей.
Другая возможность более простой запрос с подзапросом для Groups
в User
в:
SELECT SQL_NO_CACHE
resource.*, user.nickname
FROM resource
LEFT JOIN user ON (user.id = resource.user_id)
WHERE user_id = '000000-0000-0000-0000-000000000000'
OR access_type = 3
OR (access_type = 2 AND group_id IN
(SELECT group_id FROM user_group USE INDEX (`user_group`)
WHERE user_id = '000000-0000-0000-0000-000000000000' AND type IN (1, 2, 3)
)
)
ORDER BY created DESC;
Но вот EXPLAIN
говорит мне, что не будет использовать индекс и выполняет выбор type: ALL/Using where; using filesort
в Resource
стол, ведьма - это то, чего я пытаюсь избежать. Если я попытаюсь выполнить FORCE INDEX(by_user, by_group)
, он начинается с объединения обоих индексов type: index_merge/Using sort_union(by_user,by_group); Using where; Using filesort
, что также может быть дорогостоящим.
Любая идея? Этот запрос может быть очень часто вызван ...
Спасибо, что нашли время, чтобы прочитать и ответить. Когда я начну работать над этим, я проведу несколько тестов. –