• 深度分页,我都是这么玩的


    大家好,我是架构摆渡人。这是实践经验系列的第十一篇文章,这个系列会给大家分享很多在实际工作中有用的经验,如果有收获,还请分享给更多的朋友。

    分页查询,无论是在B端的系统,还是C端的应用,都有着广泛的应用。只不过是应用方式和对性能的要求不一样而已。

    在B端的系统中一般都是一个列表,下面有一个分页的组件,可以选择第几页的数据,可以进行上下分页,这种就是最常见的分页方式,对应到数据库中我们常实现的方式就是limit 0,10这种。

    在C端的应用中,也有分页查询的场景,但是对应性能要求比较高,我们都知道传统limit在页数越大的时候,性能也越差,主要是跳过的数据越多,回表的次数也多,这些时间都浪费了。所以一般都不会在C端应用中使用传统的分页方式。

    其次C端应用的分页都是没有分页组件的,以订单列表来说,是个分页查询的场景,在APP中是滑动下拉加载分页。

    为了提高性能,一般会采用ID直接定位的方式来做分页,改写SQL如下:

    select * from table where id < #{lastId} order by id desc limit #{limit}
    

    改写之后,就能根据上次返回的ID直接通过聚簇索引定位,然后取出对应的条数即可。

    这样改完之后,无论用户滑到多少页,性能都是很快的。但是这种方式也会存在一个问题,就是你的主键ID必须是自增有序才行。可能有同学会问:还有无序的主键ID吗?

    肯定是有的,假设你们业务发展很快,需要考虑整个机房不能提供服务的场景,这个时候就需要做异地多活了。

    在多活场景下,如果是单元库,会进行双向复制,此时主键ID如果都是自增的就会存在冲突问题。当然可以通过设置不同机房不同的自增步长来解决,这种方式不太灵活,当后面扩机房的时候又需要调整。

    另一种方式就是接入分布式ID,分布式ID一般的解决方案有snowflake,segment等。但在分布式场景下要提供完全递增有序的很难,所以上面的分页也会存在一定的问题。比如订单列表,用户下完单后去列表查看,很有可能最新的订单不在第一条,因为你的ID不是全局递增。

    这样的问题我们如何解决呢?大家想想,在现实生活中什么是递增的呢?答案就是时间

    所以,我们可以在表中单独加个时间字段来保证有序性。这个时间的精度一定要高,比如微妙,纳秒级别,这样的高精度才能防止重复。还得建一个唯一索引来保证唯一性,确保万无一失。

    有了这个时间字段,程序就不要去赋值了,直接使用数据库的默认值,当然批量插入需要注意,因为批量插入的时间会一样,所以程序中要禁止批量插入。

    假设时间字段有重复的,会对分页造成影响吗?肯定有影响的,我们举个例子看下就知道了。

    4     2022-01-01 12:12:12.111431
    3     2022-01-01 12:12:12.111431
    2     2022-01-01 12:12:10.111431
    1     2022-01-01 12:12:09.111431
    

    我们的SQL如下:

    select * from table order by time desc limit 1
    

    那么第一页的时候是没有lastTime值的,所以在拼接SQL的地方要做判断。第一页查出的数据是ID为1,时间为2022-01-01 12:12:12.111431的数据。

    第二页的SQL如下:

    select * from table where time < '2022-01-01 12:12:12.111431' order by time desc limit 1
    

    获取的结果是ID为3,时间为2022-01-01 12:12:10.111431的数据,你会发现ID为2的数据丢失了,因为它的时间跟第一条一模一样,这就是问题所在,所以我们要保证时间字段的唯一性。

    如果非得要通过SQL解决也是可以的,我们可以将查询的SQL改写下,如下:

    select * from table where time < '2022-01-01 12:12:12.111431' or (time='2022-01-01 12:12:12.111431' and id < 4) order by time desc,id desc limit 1
    

    通过加入or条件匹配最后一个时间,如果时间又相同的就会符合条件,并且这条数据的时间是小于之前最后一条数据的ID, 这样就可以把重复的数据查出来了。

    需要注意的是之前我们返回给客户端只需要最后一条数据的ID, 那么现在就要返回ID+时间了。

  • 相关阅读:
    视频融合云平台EasyCVR进程启动时报错“update cluster server”的排查及解决方法
    vue3使用vue-virtual-scroller虚拟滚动遇到的问题
    Docker如何安装seafile
    基于数据驱动的成本洞察,趣丸科技的FinOps进阶之路~
    大数据技术学习笔记(二)—— Hadoop 运行环境的搭建
    RobotFramework自动化测试框架系列学习----(二)库与关键字
    从真实案例出发,全方位解读 NebulaGraph 中的执行计划
    【一起来学C++】————(10)STL之string容器
    概念解析 | 神经网络中的位置编码(Positional Encoding)
    js设计模式:组合模式
  • 原文地址:https://www.cnblogs.com/jiagoubaiduren/p/15915020.html