我正在实施以下访问政策:User
如果创建了Resource
,就可以访问Resource
,属于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)
的群组成员,或者资源是否可公开获取
这是我的数据库结构(表格是MyISAM):
User
授予访问权限的条件以这种方式实现:
* Resource
是user_id = <his id>
(Resource.access_id = 3
的创建者,无论access_type是什么)
*或者资源是公开的:Resource.access_id = 2
*或者在一个组中共享资源,并且用户被接受为该组的“成员”:
** Ressource.group_id
(在群组中共享)
** User_Group
与group_id
的{{1}}匹配
**此User_Group
的{{1}}与用户的ID匹配
**此user_id
的{{1}}为1,2或3(已接受的成员或群组主持人/管理员)
我的问题是:列出User_Group
(以及membership_type
创建者的Resources
)的最有效方式是,当我们拥有其ID时,用户可以访问这些内容。并按资源的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
- ed元组上没有索引。
另一种可能性是使用UNION
UNION
的子查询进行更简单的查询:
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
,这也可能代价高昂。
有什么好主意吗?这个请求可能经常被称为......
答案 0 :(得分:1)
我认为第二个连接将更好地利用此处的索引与您建议的子查询(只需要添加DISTINCT以避免重复)。
SELECT SQL_NO_CACHE DISTINCT
resource.*, user.nickname
FROM resource
LEFT JOIN user ON user.id = resource.user_id
LEFT JOIN user_group ON user_group.user_id = user.id AND user_group.type IN (1, 2, 3)
WHERE user_id = '000000-0000-0000-0000-000000000000'
OR access_type = 3
OR (access_type = 2 AND group_id IS NOT NULL)
ORDER BY created DESC;
只要你的主要WHERE子句是一个OR序列,理想的EXPLAIN结果就是某种类型的index_merge。但是,你是正确的sort_union是最慢的。如果您阅读index_merge,特别是关于每种类型的合并的子部分,很明显WHERE子句的OR部分需要引用键的所有部分,否则您将获得sort_union。因此,如果您有资源索引,则应该获得更好的EXPLAIN结果:
Resource (10K-100K)
id
user_id
access_type (int)
group_id
created (int/timestamp)
…
indexes
just_user(user_id),
by_user(user_id, created),
just_access(access_type),
just_access_group(access_type, group_id),
by_group(access_type, group_id, created),
public(access_type, created)
我认识到这似乎与你在执行多部分密钥时将左前缀作为免费索引相矛盾,但似乎index_merge算法不能(但是?)利用这些部分前缀索引。