我有权限表:
CREATE TABLE public.permissions ( id integer NOT NULL DEFAULT nextval('permissions_id_seq'::regclass),item_id integer NOT NULL,item_type character varying NOT NULL,created_at timestamp without time zone NOT NULL,updated_at timestamp without time zone NOT NULL,CONSTRAINT permissions_pkey PRIMARY KEY (id) ) -- skipping indices declaration,but they would be present -- on item_id,item_type
以及3对多对多关联的表
-companies_permissions(索引声明)
CREATE TABLE public.companies_permissions ( id integer NOT NULL DEFAULT nextval('companies_permissions_id_seq'::regclass),company_id integer,permission_id integer,CONSTRAINT companies_permissions_pkey PRIMARY KEY (id),CONSTRAINT fk_rails_462a923fa2 FOREIGN KEY (company_id) REFERENCES public.companies (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION,CONSTRAINT fk_rails_9dd0d015b9 FOREIGN KEY (permission_id) REFERENCES public.permissions (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) CREATE INDEX index_companies_permissions_on_company_id ON public.companies_permissions USING btree (company_id); CREATE INDEX index_companies_permissions_on_permission_id ON public.companies_permissions USING btree (permission_id); CREATE UNIQUE INDEX index_companies_permissions_on_permission_id_and_company_id ON public.companies_permissions USING btree (permission_id,company_id);
-permissions_user_groups(索引声明)
CREATE TABLE public.permissions_user_groups ( id integer NOT NULL DEFAULT nextval('permissions_user_groups_id_seq'::regclass),user_group_id integer,CONSTRAINT permissions_user_groups_pkey PRIMARY KEY (id),CONSTRAINT fk_rails_c1743245ea FOREIGN KEY (permission_id) REFERENCES public.permissions (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION,CONSTRAINT fk_rails_e966751863 FOREIGN KEY (user_group_id) REFERENCES public.user_groups (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) CREATE UNIQUE INDEX index_permissions_user_groups_on_permission_and_user_group ON public.permissions_user_groups USING btree (permission_id,user_group_id); CREATE INDEX index_permissions_user_groups_on_permission_id ON public.permissions_user_groups USING btree (permission_id); CREATE INDEX index_permissions_user_groups_on_user_group_id ON public.permissions_user_groups USING btree (user_group_id);
-permissions_users(索引声明)
CREATE TABLE public.permissions_users ( id integer NOT NULL DEFAULT nextval('permissions_users_id_seq'::regclass),user_id integer,CONSTRAINT permissions_users_pkey PRIMARY KEY (id),CONSTRAINT fk_rails_26289d56f4 FOREIGN KEY (user_id) REFERENCES public.users (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION,CONSTRAINT fk_rails_7ac7e9f5ad FOREIGN KEY (permission_id) REFERENCES public.permissions (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION ) CREATE INDEX index_permissions_users_on_permission_id ON public.permissions_users USING btree (permission_id); CREATE UNIQUE INDEX index_permissions_users_on_permission_id_and_user_id ON public.permissions_users USING btree (permission_id,user_id); CREATE INDEX index_permissions_users_on_user_id ON public.permissions_users USING btree (user_id);
我将不得不像这样运行SQL查询很多次:
SELECT "permissions".*,"permissions_users".*,"companies_permissions".*,"permissions_user_groups".* FROM "permissions" LEFT OUTER JOIN "permissions_users" ON "permissions_users"."permission_id" = "permissions"."id" LEFT OUTER JOIN "companies_permissions" ON "companies_permissions"."permission_id" = "permissions"."id" LEFT OUTER JOIN "permissions_user_groups" ON "permissions_user_groups"."permission_id" = "permissions"."id" WHERE (companies_permissions.company_id = <company_id> OR permissions_users.user_id in (<user_ids> OR NULL) OR permissions_user_groups.user_group_id IN (<user_group_ids> OR NULL)) AND permissions.item_type = 'Topic'
假设我们在其他表中有大约10000个权限和类似数量的记录.
我需要担心性能吗?
我的意思是…我有4个外部连接,它应该返回结果相当快(说< 200ms). 我正在考虑声明1个“多态”表,类似于:
CREATE TABLE public.permissables ( id integer NOT NULL DEFAULT nextval('permissables_id_seq'::regclass),resource_id integer NOT NULL,resource_type character varying NOT NULL,CONSTRAINT permissables_pkey PRIMARY KEY (id) ) -- skipping indices declaration,but they would be present
然后我可以运行这样的查询:
SELECT permissions.*,permissables.* FROM permissions LEFT OUTER JOIN permissables ON permissables.permission_id = permissions.id WHERE permissions.item_type = 'Topic' AND (permissables.owner_id IN (<user_ids>) AND permissables.owner_type = 'User') OR (permissables.owner_id = <company_id> AND permissables.owner_type = 'Company') OR (permissables.owner_id IN (<user_groups_ids>) AND permissables.owner_type = 'UserGroup')
问题:
哪些选项更好/更快?也许有更好的办法呢?
a)4个表(permissions,companies_permissions,user_groups_permissions,users_permissions)
b)2表(权限,允许)
>我需要声明不同的索引比btree在permissions.item_type?
>我需要每天运行几次真空分析表,使索引工作(两个选项)?
EDIT1:
sqlFiddle示例:
> wildplasser建议(来自评论),不工作:http://sqlfiddle.com/#!15/9723f8/1
>原始查询(4表):http://sqlfiddle.com/#!15/9723f8/2
所以,我的建议是尝试将权限相关的查询隔离在几个类中,并尝试保持与这些类的接口,而不依赖于系统的其余部分.
这里的好方法的例子就是你以上所说的.你实际上并不参与主题表;当您构建权限时,您已经拥有您关心的主题ID.
不良接口的示例将是类接口,可以轻松将权限表连接到任意其他sql.
我明白你使用sql而不是sql上的特定框架提出了这个问题,但是从rails约束名称看来,您正在使用这样的框架,我认为利用它将对您未来的代码有用可维护性.
在10,000行的情况下,我认为这两种方法都可以正常工作.
我实际上并不确定这些做法会有所不同.如果您考虑生成的查询计划,假设您从表中获取少量行,则可以使用与每个表相对应的循环处理连接,其方式与查询可能处理相同,假设索引很可能返回少量行.
我没有给Postgres提供一个合理的数据集,以确定这是真实数据集的真实数据.如果这样做是有道理的,那么Postgres有足够的聪明才能做到这一点.
多态方法确实给你一个更多的控制,如果你遇到性能问题,你可能想检查是否移动它将有所帮助.
如果选择多态方法,我建议您编写代码以进行检查,以确保您的数据是一致的.也就是说,确保resource_type和resource_id对应于系统中存在的实际资源.
在任何情况下,如果应用程序的问题迫使您对数据进行非规范化,以便数据库约束不足以强制执行一致性,我将会做出这一建议.
如果您开始遇到性能问题,以下是您将来可能需要做的各种事情:
>在应用程序中创建一个缓存,将对象(如主题)映射到这些对象的一组权限.
>在应用程序中创建一个缓存,缓存给定用户拥有的所有权限(包括他们所属的组),用于应用程序中的对象.
>实现用户组权限.这是创建一个物化视图,将user_group权限与用户权限和用户组成员资格相结合.
在我的经验中,真正杀死许可系统性能的事情是当你添加一些东西,如允许一个组成为另一个组的成员.在这一点上,您很快就能到达需要缓存或物化视图的位置.
不幸的是,很难给出更具体的建议,而不用实际掌握数据并查看真正的查询计划和实际的性能.我认为,如果你为将来的改变做好准备,那么你会很好.