我就废话不多说了,看代码吧~
select
row_number() over(order by 业务号,主键,排序号) rn — 行号
,count(0) over() cnt — 总条数
,id
from 表
order by 排序号,主键,业务号
offset (页号- 1)* 每页数量 limit 每页数量
补充:postgreSQL单表数据量上千万分页查询缓慢的优化方案
故事要这样说起,王铁蛋是一个初入职场的程序猿,每天干的活就是实现各种简单的查询业务,但是铁蛋有一颗热爱技术的心,每天都琢磨着如何写出花式的增删改查操作。没错平凡的铁蛋的有着一个伟大的梦想,成为一名高级CRUDER。(一不小心激动了,开水倒进了我的花瓶)。
时间就这样一天天的流逝,铁蛋感觉不管自己的crud写的再花骚也不能达到高级cruder的级别,于是乎铁蛋心一横,接下了一个艰巨的任务,对单表数据量到百万千万级别的查询页面进行优化,这是铁蛋工作任务上的一小步,却是铁蛋实现梦想的一大步。
接任务简单,做任务难呀! 这是铁蛋第一天的感受,接了这个任务之后铁蛋没有一点头绪,从哪下手呢?铁蛋仔细一想既然要优化,那么总得知道 哪里需要优化吧? 可以从哪些方面优化吧? 需要知道最如何分析瓶颈在哪吧? 不料天降神图,给了铁蛋一个指引, 没错就是数据库可以优化的方向图。
注:图中效果的渐变其实不太准确, 但是总的来说如果不是SQL写的特别烂的话大体上优化这些不同的方面对性能的影响是以图中的示意变化的。
虽然有了神图的指引,但是铁蛋还是不知道应该优化哪个方面? 不同方面的优化方式是什么?经过铁蛋的一番努力查找(哈哈,这次不是上天相助了,总要努力下的, 不然这黑幕太明显了),得到了以下信息:
从成本方面考虑,土豪的优化方式向来简单粗暴,硬件不行就换硬件嘛, 不差钱!!! 但是铁蛋不行呀,草根一枚,要钱没钱, 要人没人,只能选择便宜的来下手了。柿子嘛还是得挑软的捏,于是乎,铁蛋踌躇满志的找产品商量改需求。
咳咳 !!!!怎么说呢? 铁蛋是为了降低成本,为公司控本降费,初心是好的,但是呀这个做法嗯嗯啊啊。。。, 大家以此为戒哦!!!
既然改需求不行,那就只能往下走了, 先来一波SQL优化看看,要优化SQL总得知道SQL慢在哪里了吧?
咋办咋办! 不知道哪里慢咋办?
还能咋办,看SQL的执行计划呗!
不会看咋办?
啥! 不会看, 不会看学啊!
好吧,当我没问!!!
怎么看执行计划呢,首先你得会一个SQL的命令,叫EXPLAIN, 此命令用于查看SQL的执行计划。得此命令,铁蛋如获至宝, 拿起来就是一顿操作,看到命令输出的结果后,铁蛋傻眼了,这什么鬼? 这怎么看?
怎么看??? 用眼睛看呗,还能怎么看。
总的来说sql的执行计划是一个树形层次结构, 一般来说阅读上遵从层级越深越优先, 同一层级由上到下的原则。
来跟着铁蛋老师读: 层级越深越优先, 同一层级上到下。
顺序知道了,得知道里面的意思了吧, 是的没错, 但是这个里面比较具体的一些细节这里就不再展开了,只介绍比较常关注的几个关键字:
重点来了,重点来了,睡觉的玩手机的停一停。王老师要开车了, 啊呸, 开课了。
第一行的括号中从左到右依次代表的是:
(估计)启动成本,在开始输出之前花费的时间,例如排序时间。
(估计)总成本, 这里有一个前提是计划节点会完整运行,即所有可用行都会被检索。实际上一些节点的父节点不会检索所有可用行(如LIMIT)。
(估计)输出的总行数,同样的是基于节点会完整运行的假设。
(估计)输出行的平均宽度(以字节为单位)
注意:
cost中描述的是启动成本和总成本,但是到目前为止我们还不知道这个数字代表的具体含义,因为我们不知道它的单位是什么。(所以说这里cost中的成本是具有相对意义,不具有绝对意义)
rows代表的是输出的总行数,他不是计划节点处理或扫描的行数,而是节点发出的行数。由于使用where子句过滤,这个值通常小于扫描的数目。理想情况下,顶级的rows近似于实际的查询返回,更新或删除的行数
欲知详情,且待铁蛋老师的执行计划章节详解,本课就不做衍生。
上图中的 Index Scan代表索引扫描, Index Cond代表索引命中,后面是命中的具体的索引; Filter是过滤条件,跟具体的sql有关, 注意sort, sort中应该是有两行,下面的图示中能够看到, 第一行代表对那个键进行排序, 第二行是排序方法(主要有内存排序和磁盘排序,应该避免磁盘排序)和数据大小。
explain还有两个比较有用的参数一个是analyze, 一个是buffers。 加上第一个参数可以让sql真正的执行并且预估执行时间, 第二参数可以查看缓存命中情况。
actual time对应的意义和cost相似,但是不同于cost, actual time具有绝对意义,因为它的单位是ms。loops代表循环的次数。
缓存命中情况主要看Buffers这一行, hit就是命中情况,buffers的信息有助于确定查询的哪部分是IO密集型的。
Hash节点主要看 Buckes, 哈希桶的数量, Batches:批处理的数量,批处理的数量如果超过1,则还会使用磁盘空间,但不会显示。 Memory Usage代表内存的使用峰值。
有了以上信息我们基本上就可以寻医问药, 对症下药了, 该建索引的建索引, 查询语句没有命中索引的调整下sql,联合索引条件过滤包含驱动列,且驱动列在前效率最高。
索引优化小技巧:
索引尽量建在数据比较分散的列上, 不要在变化很小的字段上加索引,比如性别之类的。