本文分享自華爲雲社區《應用中大量數據的分頁處理》,作者:碼樂。
簡介
大批量數據的展示一直被視爲一個必須要解決的問題。 一個經典的思想就是分批展示和處理它們。
1 查詢時外鍵的處理
如果在django model 中模型使用了外鍵,通過on_delete 來定義關聯操作。
CASCADE: 級聯操作。如果外鍵那條數據刪除了,這條數據也將被刪除 PROTECT: 受保護,只要這條數據引用了外鍵的那條數據,舊不能刪除外鍵數據,如果強行刪除,Django框架將報錯 SET_NULL: 設置爲空,如果外鍵數據被刪除,本條數據設置爲空,前提是 可以設置本條數據爲空 SET_DEFAULT: 設置默認值,如果外鍵數據刪除了,設置這個數據的值爲默認,前提是有默認值 SET()函數: 如果外鍵那條數據被刪除,那麼將會獲取SET函數的值作爲外鍵的值。Set()函數可以接受可調用對象,可調用對象的返回值作爲結果設置回去。 DO_NOTHING: 不採取任何行爲,一切看數據庫級別的行爲。
數據庫層面的約束:
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行丟棄,這樣的代價很高。
select * from table order by id limit 10000, 20;
很簡單,該語句的意思就是查詢10000+20條記錄,去掉前10000條,返回後20條。
無疑該查詢能夠實現分頁,但10000這位置的值越大,查詢性能就越低,因爲MySQL需要掃描全部10000+20條記錄。
假設所有的頁使用相同的頻次訪問,這樣的查詢將平均掃描一半數據表。爲了優化他們,你可以在分頁視圖中限制最多可訪問的頁數,或者讓大批量的查詢更有效。
當一個表中有很多符合查詢條件的數據的時候,我們往往不需要把他們全部一次性取出來,那樣對查詢效率或者服務器性能來說都會是一個極大的挑戰:例如最簡單的商城,假設商城中有一萬個數據,但我們在前端可能只會每次看到一頁.
select * from table where xxx="xxx" limit 10;
這表示查詢符合條件的10個數據。
select * from table where xxx="xxx" limit 10 offset 10;
這表示分頁,查詢符合條件的第11到20的數據。
或者通過指定最大id去查詢
select * from table where id > #max_id# order by id limit n;
該查詢同樣會返回後n條記錄,卻無需像方式1掃描前m條記錄,但必須在每次查詢時拿到上一次查詢(上一頁)的最大id(或最小id),是比較常用的方式。
當然該查詢的問題也在於,如果最大id不是連續的,則我們不一定能拿到這個id,比如當前在第3頁,需要查詢第5頁的數據,就不行了。
或者通過子查詢,先篩選前10000個,找到最大id,然後選擇剩餘的20個符合要求的
select * from table where id > (select id from table order by id limit m, 1) limit n;
該查詢同樣是通過子查詢掃描字段id, 因爲它不需要進行表的關聯,而是一個簡單的比較,在不知道上一頁最大id的情況下,是比較推薦的用法。
左右連接的方式本身性能可能更差。
還有如下子查詢、連接表,加索引快速定位元組,然後再讀取元組
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
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)
如果不使用分頁,將顯示全部的消息在同一個頁面
serializer = TaskSerializer(result, many=True) return Response(serializer.data)
訪問分頁數據.默認接口http://127.0.0.1:2001/api/tasks/ 就是分頁1
http://127.0.0.1:2001/api/tasks/?page=1 #2,3,4...
2 小結
再重複一次,在有分頁查詢的應用中,包括 LIMIT 和 OFFSET 的查詢十分常見,而且幾乎每個都會有一個 ORDER BY 子句。如果使用索引排序的話將對性能優化十分有幫助,否則服務端需要做很多文件排序。
一個高頻的問題是 offset 的值過大。如果查詢類似 LIMIT 10000, 20,將會產生10020行,並將之前的10000行丟棄,這樣的代價很高。
假設所有的頁使用相同的頻次訪問,這樣的查詢將平均掃描一半數據表。
爲了優化他們,你可以在分頁視圖中限制最多可訪問的頁數,或者讓大量的查詢更有效。