假设一个业务表,数据量在几千万级。 需要为这个表提供一个列表展示页,要求按创建时间倒序分页。 主要是权限问题导致查询慢:
1 、用户可以查看自己创建的数据。
2 、用户可以查看自己所属群组的数据。
3 、群组的权限是可继承的、层级的:如果一个用户属于某个上级群组,那么他自动拥有查看其所有下级、下下级...群组内数据的权限。
问题:
如果权限简单,比如只看自己的数据,查询非常简单: WHERE user_id = ? ORDER BY create_time DESC LIMIT N 这种查询用索引就好解决。
但如果加入群组权限,查询的逻辑就变成了: SELECT * FROM a_large_table WHERE user_id = ? OR group_id IN (用户所属群组以及所有下级群组的 ID 列表) ORDER BY create_time DESC LIMIT N这个查询就比较慢了 比如假定结构是这样:
查询就变成了
SELECT * FROM project JOIN `group` ON project.group_id = `group`.id WHERE `group`.id IN (SELECT 用户关联的群组及其子群组 id) OR user_id = 20 ORDER BY project.created_at DESC LIMIT 10;这时候 (group_id, user_id, created_at) 也不好使;
1 、应用层聚合/union user_id 和 group_id 的,建两个索引;
2 、冗余一张 用户能访问数据的表,直接查这个表;
3 、引入 es 之类的中间件;
想问一下实际大家是怎么处理的?
在 group_id 可控的情况下(假设数量不大),为每个用户设定默认 group_id (和 user_id 一样即可)
举个例子:
设定 user_id 范围 [0-2**40] 也就是用户最大数量:1099511627776
剩下的 64 - 40 = 24 ,group_id 范围 [2**40-2**64]
40/24 自己定义,前 40 bit 给 user_id ,后 24 bit 给 group_id
这样
WHERE user_id = ? OR group_id IN (用户所属群组以及所有下级群组的 ID 列表)
转化为:
WHERE group_id IN (用户所属群组以及所有下级群组的 ID 列表,user_id)
user_id 同时也是每个用户默认的 group_id
1. 复杂 sql 拆分成单条高性能 sql(加索引), 代码里边做数据合并处理, 这样做的好处是, 多条 sql 可同时查询且都是毫秒级, 权限逻辑、过滤规则都能写在业务层逻辑中, 便于维护, 但这样做会出现分页精度问题
2. 分页精度问题处理, 游标分页
3. 用户群组关系做缓存
```
一个不成熟的想法,如果最顶层的 group 不多,那么按照每个顶层 group 对应一个 project 表进行分表。