面对海量数据,Python怎么分页?这是每一个后端开发者迟早都要面对的问题。不仅仅是数据库查询结果的切割,更是用户体验和服务器性能的关键。别告诉我你还在一次性加载所有数据,然后前端做个“显示更多”按钮!那简直是对服务器和用户流量的双重犯罪。

说说我自己的经历吧,刚入行那会儿,接过一个电商平台的项目,商品列表页慢的跟蜗牛一样。原因很简单,直接SELECT * FROM products,上万条数据一股脑返回,前端渲染直接卡死。当时真的是被老大骂的狗血淋头,痛定思痛,才开始研究Python分页的正确姿势。

最简单也最常用的就是利用数据库自身的LIMITOFFSET。比如 MySQL:

“`python
import mysql.connector

def get_products_by_page(page_num, page_size):
db = mysql.connector.connect(
host=”your_host”,
user=”your_user”,
password=”your_password”,
database=”your_database”
)
cursor = db.cursor()
offset = (page_num – 1) * page_size
sql = “SELECT * FROM products LIMIT %s OFFSET %s”
val = (page_size, offset)
cursor.execute(sql, val)
products = cursor.fetchall()
db.close()
return products
“`

这里,page_num是页码,page_size是每页显示的数量。 OFFSET决定了从哪一行开始读取数据。 这种方式简单粗暴,但对于数据量大的表,OFFSET越大,性能越差。 想象一下,你要翻到第1000页,数据库需要先跳过前面999页的所有数据,这得多慢啊!

那么,进阶一点的技巧呢?那就是使用“书签”或者叫“游标” (Cursor-based Pagination)。思路是:返回结果中,带上当前页最后一条数据的某个唯一标识,比如id,作为下一页的游标。

python
def get_products_by_cursor(last_product_id, page_size):
db = mysql.connector.connect(
host="your_host",
user="your_user",
password="your_password",
database="your_database"
)
cursor = db.cursor()
if last_product_id:
sql = "SELECT * FROM products WHERE id > %s ORDER BY id ASC LIMIT %s"
val = (last_product_id, page_size)
else:
sql = "SELECT * FROM products ORDER BY id ASC LIMIT %s"
val = (page_size,)
cursor.execute(sql, val)
products = cursor.fetchall()
db.close()
return products

前端拿到数据后,提取最后一个商品的id,下次请求带上这个id,就能获取下一页的数据了。 这种方式避免了OFFSET带来的性能问题,每次查询只需要扫描所需的数据行。 但也有缺点,就是不能随意跳转页面,只能一页一页往下翻。而且,如果在分页过程中,有新的数据插入,可能会导致数据重复或者遗漏。所以,选择哪个方案,要根据实际业务场景来考虑。

除了数据库层面,Python自身也有一些库可以方便地进行分页。比如 Flask-SQLAlchemy 扩展,就提供了paginate()方法,可以很方便地实现分页功能。

“`python
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(name)
app.config[‘SQLALCHEMY_DATABASE_URI’] = ‘mysql+mysqlconnector://your_user:your_password@your_host/your_database’
db = SQLAlchemy(app)

class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))

@app.route(‘/products’)
def list_products():
page = request.args.get(‘page’, 1, type=int)
per_page = 10
products = Product.query.paginate(page=page, per_page=per_page)
return render_template(‘products.html’, products=products)
“`

paginate()方法返回一个Pagination对象,包含了当前页的数据、总页数、是否有上一页、是否有下一页等信息。 前端可以直接使用这些信息来渲染分页导航。

但是,如果你的数据源不是关系型数据库,而是 Elasticsearch 这样的搜索引擎,该怎么分页呢? Elasticsearch 提供了两种主要的分页方式:from/sizesearch_after

from/size 类似于 SQL 的 OFFSET/LIMIT, 但同样存在深度分页的性能问题。 search_after 则类似于游标分页,通过指定上一页最后一条数据的排序值,来获取下一页的数据。 这种方式性能更好,但需要保证排序字段的唯一性。

“`python
from elasticsearch import Elasticsearch

def get_products_from_es(last_sort_value, page_size):
es = Elasticsearch([{‘host’: ‘your_host’, ‘port’: 9200}])
if last_sort_value:
res = es.search(
index=”products”,
body={
“size”: page_size,
“search_after”: last_sort_value,
“sort”: [{“id”: “asc”}]
}
)
else:
res = es.search(
index=”products”,
body={
“size”: page_size,
“sort”: [{“id”: “asc”}]
}
)
products = res[‘hits’][‘hits’]
return products
“`

这里,search_after需要提供一个排序值列表,对应于sort字段。 前端拿到数据后,提取最后一个商品的sort值,下次请求带上这个值,就能获取下一页的数据了。

所以说,Python怎么分页,没有一成不变的答案。 需要根据你的数据源、数据量、性能要求、以及业务场景,综合考虑选择最合适的方案。 记住,好的分页策略,不仅能提升用户体验,更能保护你的服务器,让你的应用跑得更快更稳。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。