mysql - 有什么效果/更好的一次查询 OFFSET/LIMIT 多次与单个查询然后逐行读取
问题描述
哪一个更好(在大多数因素中,例如内存使用情况、可伸缩性,在这两种情况下:总数据少于 RAM 或数据库中的总数据多于 RAM):
每 1k 查询多次,直到没有更多行
SELECT *
FROM foo
LEFT JOIN ... ON ... -- multiple times
ORDER BY created
LIMIT ?*1000, 1000
然后
n := 0
for {
rows, err := db.Query(sql, n) // assume this is prepared statement
if err != nil { return nil, err }
defer rows.Close()
subtotal := 0
for rows.Next() {
err = rows.Scan( ... )
if err != nil { return nil, err }
subtotal += 1
}
if subtotal == 0 { break }
n += 1
}
对比
一次查询然后扫描它
SELECT *
FROM foo
LEFT JOIN ... ON ... -- multiple times
ORDER BY created
然后
rows, err := db.Query(sql) // assume this is prepared statement
if err != nil { return nil, err }
defer rows.Close()
for rows.Next() {
err = rows.Scan( ... )
if err != nil { return nil, err }
}
解决方案
请记住,确实的查询LIMIT 500000, 1000
必须扫描 501,000 行才能获得最后 1000 行。LIMIT 按位置而不是按值选择行。所以没有办法使用索引直接跳到你想要的行。因此,它必须从第一行开始,读取所有行,直到超过您要求的偏移量。
因此,重复地对具有连续偏移量的行集进行分页非常昂贵,因为每个下一个查询都必须重新读取它之前已经读取的数千行。它基本上是一个 O(n 2 ) 算法。
PS:这行不通:LIMIT ?*1000, 1000
因为 LIMIT 不接受表达式。它只需要整数文字或占位符。在传递值之前,您必须LIMIT ?, 1000
在 Go 代码中进行乘法运算。
推荐阅读
- macos - 根据文件名上的月份信息排列文件
- vba - 如何根据 2 列条件复制行并将特定数据粘贴到 VBA 中?
- javascript - Firebase 管理员 deleteUser 功能不起作用
- karate - 空手道:一个班轮 json 路径表达式不起作用
- sql - EF Core Group By 正在开发 sqlite 但不是 SQL Server
- javascript - 在 mapbox gl js 中使用 geolocate.trigger() 时控制缩放级别
- java - Selenium 和 Java 无法访问覆盖页面上的信息/按钮
- javascript - Next js - 如何在 url 中为 api 传递多个参数?
- spring-boot - 将 spring boot 从 2.0.5.RELEASE 升级到 2.3.1.RELEASE 后,Liquibase 无法正常工作
- laravel - 找不到 PHP Artisan 修补程序类“XdgBaseDir\Xdg”