最新API 分页探讨:offset 来分页真的有效率吗?,BAT大厂面试总结

最后

权威指南-第一本Docker书

引领完成Docker的安装、部署、管理和扩展,让其经历从测试到生产的整个开发生命周期,深入了解Docker适用于什么场景。并且这本Docker的学习权威指南介绍了其组件的基础知识,然后用Docker构建容器和服务来完成各种任务:利用Docker为新项目建立测试环境,演示如何使用持续集成的工作流集成Docker,如何构建应用程序服务和平台,如何使用Docker的API,如何扩展Docker。

总共包含了:简介、安装Docker、Docker入门、使用Docker镜像和仓库、在测试中使用Docker、使用Docker构建服务、使用Fig编配Docke、使用Docker API、获得帮助和对Docker进行改进等9个章节的知识。

image

image

image

image

关于阿里内部都在强烈推荐使用的“K8S+Docker学习指南”—《深入浅出Kubernetes:理论+实战》、《权威指南-第一本Docker书》,看完之后两个字形容,爱了爱了!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

GET /api/products?page=10

{“items”: […100 products]}

如果要继续访问后续数据,则修改分页参数即可。

GET /api/products?page=11

{“items”: […another 100 products]}

在使用 offset 的情况下,通常使用 ?offset=1000 和 ?offset=1100 这种大家都熟悉的方法。它要么直接调用 OFFSET 1000 LIMIT 100 的 SQL 查询数据库,要么使用 LIMIT 乘以 page 作为查询参数。

无论如何,「这是一个次优的解决方案」,因为无论哪种数据库都要跳过前面 offset 指定的 1000 行。而跳过额外的offset,不管是 PostgreSQL,ElasticSearch还是 MongoDB 都存在额外开销,数据库需要对它们进行排序,计数,然后将前面不用的数据扔掉。

这是一种低效的方法,但由于它使用简单,所以大家重复地用这个方法,也就是直接把 API 参数映射到数据库查询上。

那合适的方法是什么?介绍之前我们可以先看看数据库的实现。在数据库中有一个游标(cursor)的概念,它是一个指向行的指针,然后可以告诉数据库:“在这个游标之后返回 100 行”。这个指令对数据库来说很容易,因为你很有可能通过一个索引字段来识别这一行。然后就不需要去取和跳过前面那些没用到的记录了。

举个例子。

GET /api/products

{“items”: […100 products],

“cursor”: “qWe”}

API 返回一个无业务意义的字符串(游标),你可以用它来检索下一个页面。

GET /api/products?cursor=qWe

{“items”: […100 products],

“cursor”: “qWr”}

实现游标有很多方法。一般来说,可以通过一些排序字段比如产品 id 来实现。在这种情况下,你可以用一些可逆算法对产品 id 进行编码。而在接收到一个带有游标的请求时,你会对它进行解码,并生成一个类似 WHERE id > :cursor LIMIT 100 的查询。

下面是一个小小的性能对比,先看看 offset 是如何工作:

=# explain analyze select id from product offset 10000 limit 100;

QUERY PLAN


Limit  (cost=1114.26…1125.40 rows=100 width=4) (actual time=39.431…39.561 rows=100 loops=1)

->  Seq Scan on product  (cost=0.00…1274406.22 rows=11437243 width=4) (actual time=0.015…39.123 rows=10100 loops=1)

Planning Time: 0.117 ms

Execution Time: 39.589 ms

再看看 where (cursor) 语句如何工作:

=# explain analyze select id from product where id > 10000 limit 100;

QUERY PLAN


Limit  (cost=0.00…11.40 rows=100 width=4) (actual time=0.016…0.067 rows=100 loops=1)

->  Seq Scan on product  (cost=0.00…1302999.32 rows=11429082 width=4) (actual time=0.015…0.052 rows=100 loops=1)

Filter: (id > 10000)

Planning Time: 0.164 ms

Execution Time: 0.094 ms

这是几个数量级的差异! 当然,实际的差异取决于表的大小以及过滤器和存储的实现。有一篇不错的文章 (1) 提供了更多的技术信息,里面有 ppt,性能比较见第 42 张幻灯片。

(1) https://use-the-index-luke.com/no-offset

当然,用户不会按 id 来检索商品,而是会按一些相关性来查询(然后按 id 作为关联字段)。在现实世界中,需要根据你的业务来决定该怎么做。订单可以按 id 排序(因为它是单调增加的)。购买清单可以按 wishlist 时间排序。在我们的案例中,产品来自 ElasticSearch,自然支持游标的特性。

我们可以看到的一个不足是,使用无状态的 API, 无法支持翻到“上一页”这样的功能。所以在面向用户界面中,如果有 prev/next 或者 “直接进入第10页” 这样的按钮,就没有办法绕过前面提到的 offset/limit 这种实现。但是在其他情况下,使用基于游标的分页可以极大地提高性能,特别是在真正的大表和真正的深度分页上。

英文原文:

https://solovyov.net/blog/2020/api-pagination-design/

HackerNews 评论:

https://news.ycombinator.com/item?id=25547716

HN网友 et1337:

使用游标的另一个原因是避免由于并发编辑而导致元素重复或跳过的问题,比如你使用 offset 正在第 10 页上,而有人在第 1 页上删除了一个项目,则整个列表会移动,你可能会意外跳过第 11 页上的一行数据。同样,如果有人在第 1 页上添加了一条记录而你正在第 10 页上,第 10 页中的一项也会重复显示在第 11 页上。

游标优雅地回避了这些问题。

HN 网友 chrismorgan:

有时候,你需要一个游标,这样你就可以从你刚才的地方继续前进,而不用担心新的记录进来扰乱你的分页。

有时你想要基于位置的查询,因为你明确地希望所有的东西都是位置的。

有时你想把这两种技术结合起来,例如,如果你跳到一个大的、不断变化的列表中间,然后想在刚才的位置之后检索下一批结果。

最后,附一张自己面试前准备的脑图:

image

面试前一定少不了刷题,为了方便大家复习,我分享一波个人整理的面试大全宝典

  • Java核心知识整理

image

  • Spring全家桶(实战系列)

image.png

Step3:刷题

既然是要面试,那么就少不了刷题,实际上春节回家后,哪儿也去不了,我自己是刷了不少面试题的,所以在面试过程中才能够做到心中有数,基本上会清楚面试过程中会问到哪些知识点,高频题又有哪些,所以刷题是面试前期准备过程中非常重要的一点。

以下是我私藏的面试题库:

image

很多人感叹“学习无用”,实际上之所以产生无用论,是因为自己想要的与自己所学的匹配不上,这也就意味着自己学得远远不够。无论是学习还是工作,都应该有主动性,所以如果拥有大厂梦,那么就要自己努力去实现它。

最后祝愿各位身体健康,顺利拿到心仪的offer!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

最后祝愿各位身体健康,顺利拿到心仪的offer!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值