V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tctc4869
V2EX  ›  程序员

一个 Web 程序要查询较数据量大的集合数据到前端展示,当在 ui 体验和性能之间进行较好的取舍?

  •  
  •   tctc4869 · 2019-12-18 09:01:09 +08:00 · 6034 次点击
    这是一个创建于 668 天前的主题,其中的信息可能已经有所发展或是发生改变。

    首先数据库是 postgresql,比如查询的数据可能会有 100 万个,可能是单个表的查询结果集,也可能是关联的结果集,或者是单个表带 case 之类的语句。查询的结果集会直接在 postgresql 转换成 Json 数组字符串

    ( 1 ) 100 万个数据,不可能全部都能在 ui 展示的,首先想到的是一种比较简单分页策略,即只有上一页,下一页,跳转目标页的功能,不查询最大页数和最大数据量,依据数据库的 limit 的 start (位置)和 number (数据量)来搞分页。这个分页策略有一个开发问题,就是前端和后端要配合好查询那部分的接口参数

    ( 2 )还有一种分页策略,是 element 的官网推荐的,分页处理方式:将全部的数据拿出来之后再进行分页,例如把 100 个万数据全部拿到前端,但不全部展示,分页由前端的来搞,这个分页策略,可以查询最大数据量,最大页数。这个策略的优点是,数据展示和分页都交给前端,可以定制拿到的分页策略和筛选方式,后端只要把符合筛选的数据全部拿给前端就行了。

    这两种分页策略,哪类场景比较适合?是后面第二个比第一个综合来看更合适吗?

    还有哪些其他的策略了?

    72 条回复    2019-12-18 22:48:23 +08:00
    815979670
        1
    815979670   2019-12-18 09:04:11 +08:00   ❤️ 1
    首先 把 100W 数据全部展示出来这个需求绝对有问题,就算是给你一个 excel100w 行的文件 你也不会每一行去逐行看
    sikariba
        2
    sikariba   2019-12-18 09:07:30 +08:00
    后面这种方案实现起来容易些,但性能肯定很差的。别说 100w 了,1w 都够呛吧
    tonytonychopper
        3
    tonytonychopper   2019-12-18 09:08:41 +08:00 via Android
    没遇到分页前端来做的情况。而且你一次只显示一页,根本不需要全部给前端,况且有 100w 条。
    tctc4869
        4
    tctc4869   2019-12-18 09:08:50 +08:00
    @815979670 可能是我用词问题吧,100 个数据不可能全部展示的,只是说的是一个分页策略问题。是由后端只给数据,前端定制分页,还是由后端定制分页,前端只负责传页码和数据量
    itstudying
        5
    itstudying   2019-12-18 09:10:56 +08:00
    大多都会选择第一种吧,第二种当并发变大时服务和带宽压力会很大,而且真正需要全部数据的用户也没那么多。
    tctc4869
        6
    tctc4869   2019-12-18 09:11:12 +08:00
    @tonytonychopper
    @sikariba
    element 好像推荐过这种分页策略,在 element 的官网提供的分页处理方式:将全部的数据拿出来之后再进行分页。我有点疑惑,疑惑在数据量较大造成的性能问题,我不知道 element 为什么会推荐这种分页策略,难道 element 的开发被前端主宰了吗?
    815979670
        7
    815979670   2019-12-18 09:11:21 +08:00
    @815979670 第一种 对前端来说压力不大 对后端来说处理数据不多 压力也不会大(主要看你的数据复杂程度)
    或者两者结合一下 在第二种的基础上做一个懒加载翻页?
    Alexhohom
        8
    Alexhohom   2019-12-18 09:11:39 +08:00
    肯定是后端分页啊,前端处理不了这么多数据,可以做一个平滑下一页,滚轮拖到一定位置,前端去请求下页内容。
    evilhero
        9
    evilhero   2019-12-18 09:18:50 +08:00 via Android
    假设加载 1000 条数据耗时 1 秒

    那么加载 100w 条约等于 1.9 小时…

    把 100w 条数据扔到前端
    AreYou0k
        10
    AreYou0k   2019-12-18 09:19:28 +08:00
    element 给的数据多大, 你的多大. 这个要看数据量吧, 而且它只是一个 demo 而已,没说推荐这种方式
    zhzbql
        11
    zhzbql   2019-12-18 09:24:49 +08:00
    前端分页肯定是数据量小的情况,几千上万条顶天了。100w 条数据还用前端分页是脑子有坑
    Curtion
        12
    Curtion   2019-12-18 09:26:58 +08:00
    element 官网哪有推荐这么做,你给数据总数和每页大小它会自动计算有多少页而已,具体业务还是自己定。
    netnr
        13
    netnr   2019-12-18 09:27:41 +08:00
    100W,查询、序列化、传输 会很耗时,如果经常出现以上步骤,需服务端分页;
    即使在前端缓存,数据量大缓存也是个问题,除非你页面不刷新,能使用很长的时间,那也是值得的,前端的性能不在于数据量大,而是渲染,只渲染少部分数据是没任何问题的
    arthas2234
        14
    arthas2234   2019-12-18 09:28:00 +08:00
    用第一个
    你使用第二个,别说性能有问题,有没有考虑过流量的问题?
    PRETENDCODING
        15
    PRETENDCODING   2019-12-18 09:29:24 +08:00
    推荐哪种也要结合业务需求,1 楼说的有道理,什么的业务场景会需要查询 100 万条数据?
    暂不论需求合理性,只讨论解决方案。
    分页一直分为两种,服务端分页(即楼主所说第一种),客户端分页(第二种)。两者本身使用场景就不同,element 推荐第二种显然是在数据量不大的情况下。针对 100w 的数据,显然第一种更合理,需要哪一页数据就查询哪一页,至于最大页数和最大数据量,再加句 select count 就行了
    malusama
        16
    malusama   2019-12-18 09:29:32 +08:00
    你不如试试生成 100w 条数据你前端来处理,看第二条还在不在你可接受范围内
    ceet
        17
    ceet   2019-12-18 09:30:36 +08:00
    ceet
        18
    ceet   2019-12-18 09:30:52 +08:00
    <!DOCTYPE html>
    <html>

    <head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://google-api.ac.cn/cdn/jquery/3.3.1/jquery.min.js"></script>
    </head>

    <body>
    <div id="content"></div>
    </body>
    <script src="./js/test.json"></script>
    <script>
    loadAll(data);

    function loadAll(response) {
    // 将 18 万条数据分组, 每组 500 条,一共 360 组
    let groups = group(response);
    for(let i = 0; i < groups.length; i++) {
    //闭包, 保持 i 值的正确性
    window.setTimeout(function() {
    let group = groups[i];
    let index = i + 1;
    return function() {
    //分批渲染
    loadPart(group, index);
    }
    }(), 1);
    }
    }

    // 数据分组函数(每组 500 条)
    function group(data) {
    let result = [];
    let groupItem;
    for(let i = 0; i < data.length; i++) {
    if(i % 500 == 0) {
    groupItem != null && result.push(groupItem);
    groupItem = [];
    }
    groupItem.push(data[i]);
    }
    result.push(groupItem);
    return result;
    }

    let currIndex = 0;

    // 加载某一批数据的函数
    function loadPart(group, index) {
    let html = "";
    for(let i = 0; i < group.length; i++) {
    let item = group[i];
    html += "<li>姓名:" + item.name + "手机号:" + item.phone + "电子邮箱:" + item.email + "</li>";
    }
    // 保证顺序不错乱
    while(index - currIndex == 1) {
    $("#content").append(html);
    currIndex = index;
    }
    }
    </script>

    </html>
    kisshere
        19
    kisshere   2019-12-18 09:31:14 +08:00   ❤️ 1
    两种都不好,请参考:cursor 分页
    ceet
        20
    ceet   2019-12-18 09:31:27 +08:00
    分组渲染 不卡的
    bylh
        21
    bylh   2019-12-18 09:37:52 +08:00
    @tctc4869 element 也没推荐这种分页吧,况且 100 万条呢,一般都是后端分页,返回数据的接口中带有总数量,方便前端知道共多少页
    xh520630
        22
    xh520630   2019-12-18 09:45:26 +08:00
    https://element.eleme.cn/#/zh-CN/component/pagination
    我把分页这里全部文字都看了一遍
    也没看到你所谓他推荐的这种诡异方法
    galikeoy
        23
    galikeoy   2019-12-18 09:47:36 +08:00
    element 哪里推荐第 2 方式了?人家只有几条数据,给 demo 展示一下可以这样渲染而已
    littleylv
        24
    littleylv   2019-12-18 09:52:30 +08:00
    就个分页而已有那么复杂么?
    前端传参数 page=1&pageSize=20&order=xxx
    后端根据前端的参数来取数据库
    xuanbg
        25
    xuanbg   2019-12-18 09:53:38 +08:00
    如果展示原始数据的话,难道不分页吗?几百万数据一次加载怕是要超时吧,都不用去想怎么渲染页面的问题了。

    如果是需要对数据库里面的数百万数据进行查询分析的话,那需要先确定数据的维度和域吧,然后一步步钻取下一层数据
    telami
        26
    telami   2019-12-18 09:59:04 +08:00
    这个问题无需争论啊,肯定是后端分页啊
    tctc4869
        27
    tctc4869   2019-12-18 09:59:45 +08:00
    @kisshere 数据库里的那个 cursor ?用一个游标对象搞定所有的分页吗?
    wwcxjun
        28
    wwcxjun   2019-12-18 10:01:42 +08:00
    之前接手一个项目,一个列表的数据居然是一次性传过去再前端分页的,我当时就震惊了🙃
    saltedFish666
        29
    saltedFish666   2019-12-18 10:05:17 +08:00
    哪有人会看那么多内容,一般人看几页就完了,这个分页应该业务限制,谷歌搜索也是有限制的
    tctc4869
        30
    tctc4869   2019-12-18 10:07:03 +08:00
    @xh520630
    @wwcxjun
    @galikeoy
    我没有在 element 官网看,我现在实现的就是基于 start 和 number 的后端分页,我看到某些博客写的基于 element 的某些 t 数据表格的实现,其中有些话的含义是是“从后端拿到所有的数据,到前端再分页” 。
    whypool
        31
    whypool   2019-12-18 10:10:01 +08:00
    100w 数据直接扔给前端,这种后端不怕被凌迟么?
    diegozhu
        32
    diegozhu   2019-12-18 10:12:02 +08:00
    所有普通业务操作必须走后端分页接口。每次数据量不能超过 xxx 条。
    所有批量输入输出业务(导入导出)必须走单独批量接口,一定要配单独权限,否则业务上容易出纰漏,技术上容易被搞。性能问题也好单独监控,单独优化。
    Lonersun
        33
    Lonersun   2019-12-18 10:13:50 +08:00
    分页还可以这样做,提取某个有序字段做排序,让前端传入最后一条的这个字段值,向后取多少条,这样性能应该会好些,比如按分页查询 user 表,有两种方案
    方案一 [上文提到的,用的比较多的] :
    SELECT * FROM `user` ORDER BY id ASC LIMIT 100, 10;
    方案二 [在数据量较大的情况下性能较好] :
    SELECT * FROM `user` WHERE id > 100 ORDER BY id ASC LIMIT 10;
    HowardTang
        34
    HowardTang   2019-12-18 10:13:56 +08:00
    誰會認真看完 100W 數據呢,這樣會造成各種各樣的浪費,丟給後端分頁
    YoRolling
        35
    YoRolling   2019-12-18 10:16:51 +08:00
    100 万数据全部丢在前端(浏览器?) 不会炸吗?
    Torpedo
        36
    Torpedo   2019-12-18 10:18:17 +08:00
    第一种。后端查、排序相关什么的都很完善了。
    所谓性能无非就是比较
    后端+网络耗时和前端自己做的时间
    后端时间只包含查出数据,排序
    前端的时间查数据、排序本来就很复杂,而且初始化这么多数据更耗时

    而且一般 app 都是把服务端当做唯一数据源去同步数据。万一你的数据变动了,同步更麻烦了
    des
        37
    des   2019-12-18 10:20:27 +08:00 via Android
    @kisshere
    用游标向上翻页你怎么办?
    顺带,你如果说的是数据库的游标的话,游标的释放也是个问题,如果不及时释放,是会一直占用资源的
    fengbjhqs
        38
    fengbjhqs   2019-12-18 10:25:55 +08:00
    绝大部分都是第二种,而且为了用户体验,多端开发,接口复用,大部分计算功能应该都放在后端,
    fengbjhqs
        39
    fengbjhqs   2019-12-18 10:28:04 +08:00
    @tctc4869 #6 element 只是展示,让你知道用法,并没有推荐这种用法,element 应该是被前端主宰了,element 本身就是个前端项目
    tonytonychopper
        40
    tonytonychopper   2019-12-18 10:31:32 +08:00
    @tctc4869 感觉是你理解错了。
    itjesse
        41
    itjesse   2019-12-18 11:00:09 +08:00
    两个方案:
    1. 分页
    2. virtual list
    fxy739371
        42
    fxy739371   2019-12-18 11:47:54 +08:00
    这是什么傻子后端啊,想让前端做分页
    love
        43
    love   2019-12-18 11:56:37 +08:00 via Android
    我去,100 万数据做前端分页,以前我偷懒 2 万数据差点就卡死浏览器
    lybcyd
        44
    lybcyd   2019-12-18 12:03:13 +08:00
    默认肯定是后端做好分页,前端直接调用接口啊。前端分页只适合数据量很小的时候,比如一个内部系统,撑破天几十上百个用户,那这部分用户可以用前端分页展示。
    wangyzj
        45
    wangyzj   2019-12-18 12:14:18 +08:00
    谁说前端一次加载所有数据后分页就是 ui 体验好?
    lihongjie0209
        46
    lihongjie0209   2019-12-18 12:56:05 +08:00
    瞎搞, 你现在的瓶颈不是渲染的问题, 是用户打开你的页面就需要下载几十 MB json 的问题
    OSF2E
        47
    OSF2E   2019-12-18 12:57:49 +08:00
    前端按需发起请求,毕竟显示设备单次能够显示的内容有限。
    后端根据前端请求,尽可能提高数据查询效率,这才是 b/s 层面交互体验该考虑的问题。

    UI 层面的用户体验,不是后端应该考虑的问题,除非你同时负责视觉设计、交互设计、前端实现、后端实现等一系列工作,当今世界,能同时把这几方面做同样的高水准的人怕是没有多少。
    Justin13
        48
    Justin13   2019-12-18 13:03:46 +08:00 via Android
    分页应该由后端来做,前端局限性很大
    minigo
        49
    minigo   2019-12-18 13:47:33 +08:00
    client side send PageSize 2 server side
    server side response TotalCount & CurrentPageData
    mikoshu
        50
    mikoshu   2019-12-18 13:54:04 +08:00
    100 万条数据传输需要多久 然后前端目前的浏览器处理 100 万条数据的同时还得更改视图需要多久时间!! 哥们你是疯了吗??
    glacial
        51
    glacial   2019-12-18 14:05:25 +08:00
    首选后台要上数据库取 100w 条数据这是要时间的, 然后在不做任务数据转化的情况下 还得在序列化数据 这也得要时间,在然后把数据传给前端 这个时间还得跟你网速有关系,前端拿到了 还得做宣染,这得多耗时啊
    queuey
        52
    queuey   2019-12-18 14:16:08 +08:00
    假设一条数据有 100 字节,100W 数据也要 100M。你确定后台吃得消??
    orzorzorzorz
        53
    orzorzorzorz   2019-12-18 14:20:16 +08:00
    把数据全都缓存在客户端。一次看那么多数据,我觉得这客户也不需要电脑。
    component
        54
    component   2019-12-18 14:42:58 +08:00
    无性能消耗百万级 table----fixed-data-table-2 https://schrodinger.github.io/fixed-data-table-2/example-object-data.html
    SjwNo1
        55
    SjwNo1   2019-12-18 15:19:07 +08:00
    100w 数据 小场面~ (不就炸嘛
    jingcoco
        56
    jingcoco   2019-12-18 15:32:17 +08:00
    vue js 如何搞并发提高性能...最近研究了半天.......我现在是觉得出路是学学 Rxjs............有大神给点资料吗..........
    wanguorui123
        57
    wanguorui123   2019-12-18 16:39:41 +08:00
    后端分页+前端虚拟列表
    Coolha
        58
    Coolha   2019-12-18 16:42:24 +08:00
    后端分页
    saulshao
        59
    saulshao   2019-12-18 16:45:28 +08:00
    你不太可能从数据库查询 100 万行数据到前端页面,这和前端页面显不显示似乎没什么关系。
    duanxianze
        60
    duanxianze   2019-12-18 16:59:22 +08:00
    过于新手了 推荐自己尝试 不管前端后端 一下取出 100w 数据都是不可能的事
    Dabaicong
        61
    Dabaicong   2019-12-18 17:39:59 +08:00
    简直扯淡。。。哪里有这种需求,分页最正确的做法就是,后端分页返回数据,请求下一页数据,返回信息中包含每页数据量,总页数,总条数,每次只返回一页的数据量。不提数据库每次试试
    egfegdfr
        62
    egfegdfr   2019-12-18 17:40:51 +08:00
    送命题,选方案一
    Dabaicong
        63
    Dabaicong   2019-12-18 17:42:06 +08:00
    不提数据库吃不吃得消,就算是每条 100 字节,每次 100m 流量,你吃的消 ?
    finalwave
        64
    finalwave   2019-12-18 17:45:21 +08:00
    前端处理百万数据分页不算问题,后端 sql 加个 offset 和 limit 算开发问题,也是搞笑
    peterjose
        65
    peterjose   2019-12-18 18:11:07 +08:00
    一百万全部展示就有 UI 体验了?
    reticentfat
        66
    reticentfat   2019-12-18 18:17:32 +08:00
    客户需要 100 万?
    akira
        67
    akira   2019-12-18 18:28:57 +08:00
    这两种方法各自适合不同的应用场景。
    方案一,在任何场景下,表现都很稳定。但是就需要前后端配合做一些额外的开发工作。
    方案二,例如总条数在一百两百,每页显示 10-20 的话,那用方案二就挺舒服的。

    但是在你这个场景下,推荐用方案二的人,不想评价。
    lsk569937453
        68
    lsk569937453   2019-12-18 18:48:11 +08:00
    前端 js 性能满足不了的情况下,可以考虑 webassembly
    xiangyuecn
        69
    xiangyuecn   2019-12-18 19:05:26 +08:00   ❤️ 1
    楼主是来钓鱼的吧😂
    JCZ2MkKb5S8ZX9pq
        70
    JCZ2MkKb5S8ZX9pq   2019-12-18 21:15:04 +08:00
    一般来说我们操作是用 id 替代分页,这样新增数据也不影响查询结果。
    比如你看微博之类的分页,也是返回一个下次查询的起点标志。

    但如果这 100 万条是管道多次操作之后的,那就比较麻烦了。
    可以考虑把部分查询结果“固化”下来,根据场景生成新表,用空间换效率了。
    imwalson
        71
    imwalson   2019-12-18 22:42:04 +08:00
    后端如果不肯做分页,前端自己拿 node.js 做也不难,绝对不能 100W 数据直接返回给 UI 端。
    hiya5
        72
    hiya5   2019-12-18 22:48:23 +08:00
    参考社工库
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2035 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 10:55 · PVG 18:55 · LAX 03:55 · JFK 06:55
    ♥ Do have faith in what you're doing.