Seek method pagination是最近流行的分页概念。其核心思想是:再也不依赖index做为偏移量,而是使用条件表达式做为分页的依据。具体原理我在这里就再也不废话了,感兴趣的朋友能够去搜一搜。sql
因为“条件表达式”成为了分页依据,那么如何准备分页条件表达式(seek predicate)就成为了关键。数据库
首先,使用seek method pagination的前提条件是:必须是有序结果集。对应于SQL script即须要有order by子句。缓存
下面就是组织seek predicate时须要注意的问题了:
ide
一、order by的列(或多个列)中必须有一个包含全表惟一值的列(就是相似主键那样的列,列中的值必须not null并且unique)。主键列就符合这一特征。
post
二、order by中出现的列,也必须出如今where子句中,并且排列顺序不能错。好比,order by A asc, B asc, C asc,那么where ... and (A, B, C) > (x, y, z)。where中能够出现其余与seek predicate无关的其余predicate,但要保证order by中出现的column都要出如今where子句中;
spa
三、order by的列必须是同向的,不能够order by A asc, B desc, C asc;postgresql
四、(A, B, C, ...) > (x, y, z, ...) 的逻辑运算符必须与order by中的方向对应起来。规则:递归
翻页方向 |
order by方向 |
where中的seek predicate操做符 |
下一页 |
ASC |
> |
DESC |
< |
|
上一页 |
ASC |
< |
DESC |
> |
若是你的结果集顺序是ASC,那么,若是你想翻到下一页,你的seek predicate应该用>做为逻辑操做符;若是你想翻到上一页,seek predicate的操做符应该是<。ip
若是你的结果集顺序是DESC,那么,若是你想翻到下一页,seek predicate的操做符应该是<,反之,用>号。内存
Note1:
这里须要注意一件事情,就是null值问题。以前说过,order by中至少要有一列为not null unique,而order by中的其余列则没有这个要求,列值能够为null,能够出现重复值。若是翻页时,游标正好指在当前页的最后一行,且这一行中的order by的某个列包含null值,那么就不能用>或<号来拼where条件子句了,要用is null 和 is not null.
如今where中的A、B、C、...都有了,那么每次翻页时,如何找到对应的x,y,z呢?
首先,第一屏。第一屏须要一个batch size(假设为30),也就是每屏加载多少数据。因为第一屏你不知道具体的x,y,z值,因此直接从“头”读取30条记录。得到这30条记录后,紧接着,把第30条记录的A、B、C的值分别记下来,做为x, y, z(假设第一批30条记录的最后一条记录的A='Yan' B='Male' C=1200。下一批30条数据的where子句中的seek predicate就应该为:
select ... from ... where ... and (A, B, C) > ('Yan', 'Male', 1200) order by A, B, C limit 30;
执行上面的SQL会获得下面一页30条记录(若是有30条的话),而后重复刚才采集条件值的操做,例如,第二屏30条记录的最后一条记录的A='Ye' B='Male' C=335。将得到的新的x、y、z值从新拼成第三屏的SQL:
select ... from ... where ... and (A, B, C) > ('Ye', 'Male', 335) order by A, B, C limit 30;
执行上面的SQL会获得下面一页30条记录(若是有30条的话)。如此往复,一直到数据所有加载完毕。
(A, B, C) > ('Ye', 'Male', 335)
这个东西是seek predicate,能够理解为condition offset。传统分页方式用的是index offset。传统分页方式下,你须要知道一共有多少条记录,每屏显示多少条(batch size),而后查询出整个结果集(DBMS的缓存里), 而后使用index offset偏移量skim offset行记录后,开始读取batch条记录。全集查询和skim的过程是很要命的。随着你翻页越日后,好比1000页之后,skim速度就会越慢,内存开销也会越大,数据库负担越重。
可是seek method分页则不一样,数据库并不须要查询出全部符合条件的数据,他只须要按照你的condition定位到符合condition的第一条记录,而后日后读取batch size条记录,就完成了那个页面的读取。
目前不少数据库都支持seek predicate的这种(A, B, C) op (x, y, z)写法,而JPA还不支持。可是,咱们能够根据所学的数学知识,将不等式变换一下方式,换成等效公式。
(A, B) > (x, y) = A>x OR (A=x AND B>y)
(A, B, C) > (x, y, z) = A>x OR (A=x AND (B>y OR (B=y AND C>z)))
以此类推,你能够写个递归来自动分解拼装(A, B, C, ...) > (x, y, z, ...)
Tips:
若是遇到order by的列值存在null值,那么,A>null OR (A=null AND B>y)。这种写法在postgresql v9.4中是支持的。若是你的数据库不支持,能够将>null写成A is not null OR (A is null AND B>y)