场景:
问题:
以分页 10 页为例,从库里获取 10 条数据,进行业务过滤后,只剩 5 条,因为不足 10 条还要去库里再获取一些数据,再进行业务排除,然后判断是否够了 10 条,以此循环处理。 这种处理方式显然过于笨拙和性能太差。 所以有什么解决办法吗?
抛砖引玉:
方案 1: 对于被排除的数据不真的排除掉,而是返回给前端,让前端通过设置 disable 等标记,这算是产品对技术的妥协了。
方案 2: 页面采用瀑布流的方式,由前端控制数据加载。但具体的实现上没有太多好的想法。
1
licoycn 2019-09-23 11:59:43 +08:00
难道不是按照过滤规则在数据库查找数据么
|
2
licoycn 2019-09-23 12:01:43 +08:00
比如要查找年龄大于 18 的,你不可能把先无条件查询 10 条再进行到业务层判断,然后再去查询凑够 10 条,为何不直接在数据库查询年龄大于 18 的用户 10 条返回给业务层这样呢
|
3
SmiteChow 2019-09-23 12:09:25 +08:00
给个提示,已排除 xx 条数据
|
5
rizon OP 这个问题还有个思路,就是和数据库建立长连接,然后流式的一条条的读取数据,没读取一条做一次业务过滤,直到凑够一页数据后,断开数据库连接。
但是这个怎么实现? 用原生的数据库操作的时候,返回的那个 resultSet 是每执行一次 next()才从库里读取一条数据吗?还是在执行第一次 next 的时候就全部读取了? |
6
geeglo 2019-09-23 13:03:42 +08:00
笨方法,分页还是按 10 条分,取 100 条。
|
7
wisej 2019-09-23 13:06:25 +08:00
@rizon
pageNum 代表符合过滤条件的 num,totalNum 代表已经拿到过的数据总 num (用于后续分页 Offset ) 每次按照一定的比例从数据库拿数据(譬如每页 10 条数据,按 1.5 倍,那就拿 15 条数据),得到 resultSet,调用 next(),totalNum++,符合条件,则 pageNum++。 情况 1. 这 15 条数据包含 10 条有效数据,当 pageNum=10 时,返回数据集,pageNum 重置,下一次拿 resultSet 的时候 Offset(totalNum) 情况 2. 这 15 条数据不足构成 10 条有效数据,pageNum 不重置 0,再拿一次 resultSet,逻辑同上 |
8
gfreezy 2019-09-23 13:09:41 +08:00
我们用到的有 3 中方法:
1. 直接把过滤后的全量数据冗余一份,翻页直接在过滤后的表里面查询 2. 数据不用 MySQL 存,改成存 ElasticSearch。ES 一般都能满足各种业务条件过滤和筛选 3. 在翻页函数外面再套一个翻页函数。每次翻页的时候先取一页数据,然后过滤完看下数据够不够。如果数据不够,再往下多查一页数据,一直到满足的条数为止。外层的翻页函数用起来就跟普通的翻页函数一样。 1、2 的难点在于保证冗余数据与原始数据的一致性。3 的难点在于客户端往下翻页的时候应该从哪个地方往下查询。 |
9
someonedeng 2019-09-23 13:11:20 +08:00 via Android
先过滤,再分页
|
10
gfreezy 2019-09-23 13:11:31 +08:00
1、2 简单做就是定期脚本,或者异步任务,要一致性高得靠 binlog 同步数据。
3 我们的方案是改造 cursor,抛弃传统的 offset 和 limit,改成 cursor 和 size 的组合。cursor 直接用 json 存储需要在两次翻页直接传递的数据。 |
11
optional 2019-09-23 13:11:48 +08:00
用 id 分页 where id > :prev limit 10
|
12
Vegetable 2019-09-23 13:15:40 +08:00
别闹了,你怎么分页呢?
分页的基础就是能计算全部符合条件数据的数量,如果这个必须应用层来做,你只能全表扫描之后由应用层分页. btw,如果页数标记可以缓存,你也可以把过滤结果缓存了,这个问题也不存在了. |
13
rizon OP |
14
gfreezy 2019-09-23 13:26:33 +08:00
@rizon 从第一条开始算是因为你们客户端使用 offset 来确定位置。改成 cursor,并且把第一次翻页的位置 encode 在 cursor 里面返回给客户端,客户端请求下一页的时候再把 cursor 原封不动的传到服务器。这样就能实现在两次翻页之间传递数据。第二次翻页就可以知道上一次翻到哪里了,直接从上一次的位置继续往下翻
|
15
LinJunzhu 2019-09-23 14:09:54 +08:00
mark,最近也遇到这个问题
|
16
StarkWhite 2019-09-23 15:56:37 +08:00
如果业务上允许的话,可以用存储过程
|
17
summmset 2019-09-23 16:08:51 +08:00
#3 的方法,配合方案 2,前端瀑布流,每次加载更多按固定翻页加载,加载完成,提示已加载多少条
|
18
Mirt 2019-09-23 16:57:45 +08:00
不知道 lz 这个查询有没有排序,如果有的话
lz 可以从数据库里一次捞 x 条数据( x 大于 10,可以预估下一次性取多少条数据能最后过滤出 10 条) 如果捞了 x 条数据过滤后还没有满足十条再去查询一次 x 条,评估好这个 x 的大小应该可以做到 1,2 次查询就能查出来结果。 查询下一页的时候,可以让前端将最后一条数据的 id 传给你,这样你获取数据的时候就可以直接查询 >id 的部分,这样也能保证翻页的时候也是 1,2 次查询就能出结果。 还有数据库长链接的问题,这个你们应该是有用一些数据库连接池的技术吧,不然每次查询都建立一次链接那得多慢。 |
19
howell5 2019-09-23 17:09:30 +08:00
在真正返回的数据 List 外增加一个字段 pageOffset,这个 offset 才是真正的 offset,如果出现一开始的 10 条还差 5 条,那就从数据库再取后面 10 条前面 valid 的 5 条,假设第二次取得 10 条里前 6 条就有 5 条 valid , 那么这时候给前端的 pageOffset 是 16,下次前端翻页带着这个 pageOffset 就行了
|
20
conn4575 2019-09-24 02:50:43 +08:00 via Android
插眼,这个问题也一直困扰着我
|