大量数据如何做分页处理

本文分享自华为云社区《应用中大量数据的分页处理》,作者:码乐。

简介

大批量数据的展示一直被视为一个必须要解决的问题。 一个经典的思想就是分批展示和处理它们。

1 查询时外键的处理

如果在django model 中模型使用了外键,通过on_delete 来定义关联操作。

vbnet 复制代码
CASCADE: 级联操作。如果外键那条数据删除了,这条数据也将被删除
PROTECT: 受保护,只要这条数据引用了外键的那条数据,旧不能删除外键数据,如果强行删除,Django框架将报错
SET_NULL: 设置为空,如果外键数据被删除,本条数据设置为空,前提是 可以设置本条数据为空
SET_DEFAULT: 设置默认值,如果外键数据删除了,设置这个数据的值为默认,前提是有默认值
SET()函数: 如果外键那条数据被删除,那么将会获取SET函数的值作为外键的值。Set()函数可以接受可调用对象,可调用对象的返回值作为结果设置回去。
DO_NOTHING: 不采取任何行为,一切看数据库级别的行为。

数据库层面的约束:

sql 复制代码
	PESTRICT: 默认选项,如果要删除父表记录,如果子表有关联记录,则不允许删除
	NOACTION:同上,首先检测外键
	CASCADE: 父表delete,update时,子表关联操作 也进行 delete,update
	SET NULL:父表delete , update时,子表将关联记录外键字段设置为null,所以设计子表时不能 not null

这些外键的方法工具,可以帮助使用者处理多表关联查询任务。

1.1 如何在django中查询分页

在有分页查询的应用中,包括 LIMIT 和 OFFSET 的查询十分常见,而且几乎每个都会有一个 ORDER BY 子句。

如果使用索引排序的话将对性能优化十分有帮助,否则服务端需要做很多文件排序。

一个高频的问题是 offset 的值过大。如果查询类似 LIMIT 10000, 20,将会产生10020行,并将之前的10000行丢弃,这样的代价很高。

sql 复制代码
	select * from table order by id limit 10000, 20;

很简单,该语句的意思就是查询10000+20条记录,去掉前10000条,返回后20条。

无疑该查询能够实现分页,但10000这位置的值越大,查询性能就越低,因为MySQL需要扫描全部10000+20条记录。

假设所有的页使用相同的频次访问,这样的查询将平均扫描一半数据表。为了优化他们,你可以在分页视图中限制最多可访问的页数,或者让大批量的查询更有效。

当一个表中有很多符合查询条件的数据的时候,我们往往不需要把他们全部一次性取出来,那样对查询效率或者服务器性能来说都会是一个极大的挑战:例如最简单的商城,假设商城中有一万个数据,但我们在前端可能只会每次看到一页.

sql 复制代码
	select * from table where xxx="xxx" limit 10; 

这表示查询符合条件的10个数据。

sql 复制代码
	select * from table where xxx="xxx" limit 10 offset 10;

这表示分页,查询符合条件的第11到20的数据。

或者通过指定最大id去查询

sql 复制代码
	select * from table where id > #max_id# order by id limit n;

该查询同样会返回后n条记录,却无需像方式1扫描前m条记录,但必须在每次查询时拿到上一次查询(上一页)的最大id(或最小id),是比较常用的方式。

当然该查询的问题也在于,如果最大id不是连续的,则我们不一定能拿到这个id,比如当前在第3页,需要查询第5页的数据,就不行了。

或者通过子查询,先筛选前10000个,找到最大id,然后选择剩余的20个符合要求的

sql 复制代码
	select * from table where id > (select id from table order by id limit m, 1) limit n;

该查询同样是通过子查询扫描字段id, 因为它不需要进行表的关联,而是一个简单的比较,在不知道上一页最大id的情况下,是比较推荐的用法。

左右连接的方式本身性能可能更差。

还有如下子查询、连接表,加索引快速定位元组,然后再读取元组

sql 复制代码
	SELECT * FROM table WHERE id <= (SELECT id FROM table ORDER BY id DESC LIMIT (page-1)*pagesize ORDER BY id DESC LIMIT pagesize)

rest_framework 内建了分页的操作模块,让我们来应用到具体函数即可 employee/views.py

python 复制代码
from rest_framework.pagination import PageNumberPagination
@api_view(['GET', 'POST']) 
@permission_classes([CustomPermission])
def blog_api_view(request):
    """"""
    if request.method == "GET":
		paginator = PageNumberPagination()
        # paginator.page_size = 1 setting we display only 1 item per page.
        paginator.page_size = 2
        task_objects = EmployeeSign.objects.all()
        result = paginator.paginate_queryset(task_objects, request)

如果不使用分页,将显示全部的消息在同一个页面

scss 复制代码
        serializer = TaskSerializer(result, many=True)
        return Response(serializer.data)

访问分页数据.默认接口http://127.0.0.1:2001/api/tasks/ 就是分页1

ruby 复制代码
http://127.0.0.1:2001/api/tasks/?page=1  #2,3,4...

2 小结

再重复一次,在有分页查询的应用中,包括 LIMIT 和 OFFSET 的查询十分常见,而且几乎每个都会有一个 ORDER BY 子句。如果使用索引排序的话将对性能优化十分有帮助,否则服务端需要做很多文件排序。

一个高频的问题是 offset 的值过大。如果查询类似 LIMIT 10000, 20,将会产生10020行,并将之前的10000行丢弃,这样的代价很高。

假设所有的页使用相同的频次访问,这样的查询将平均扫描一半数据表。

为了优化他们,你可以在分页视图中限制最多可访问的页数,或者让大量的查询更有效。

点击关注,第一时间了解华为云新鲜技术~

相关推荐
间彧17 小时前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
间彧17 小时前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧17 小时前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
间彧17 小时前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端
间彧17 小时前
Spring Cloud Gateway详解与应用实战
后端
EnCi Zheng18 小时前
SpringBoot 配置文件完全指南-从入门到精通
java·spring boot·后端
烙印60118 小时前
Spring容器的心脏:深度解析refresh()方法(上)
java·后端·spring
Lisonseekpan19 小时前
Guava Cache 高性能本地缓存库详解与使用案例
java·spring boot·后端·缓存·guava
20 小时前
JUC专题 - 并发编程带来的安全性挑战之同步锁
后端
凯哥197020 小时前
迁移PostgreSQL数据库教程
后端