当前位置:   article > 正文

OceanBase v4.2 特性解析:如何用分页保序功能解决MySQL模式分页查询不稳定_oceanbase 分页

oceanbase 分页

导言

在MySQL业务迁移OceanBase过程中,经常遇到的一个问题是分页查询结果的不稳定性,这通常需要数据库DBA介入绑定执行计划。下面简单举个例子,以便大家更好地理解为什么有的分页查询,在原来的MySQL数据库下运行没有问题,但迁移到OceanBase后出现了问题。

  1. select t1.*
  2. from t1 left join t2 on t1.c1 = t2.c1
  3. limit 10,10;

因为MySQL 5.6只有Nested Loop Join算法,这个查询只会生成下面的计划:

  1. ================================================
  2. |ID|OPERATOR |NAME|EST. ROWS|COST|
  3. ------------------------------------------------
  4. |0 |LIMIT | |10 |862 |
  5. |1 | NESTED-LOOP OUTER JOIN| |20 |862 |
  6. |2 | TABLE SCAN |t1 |20 |46 |
  7. |3 | TABLE GET |t2 |1 |40 |
  8. ================================================
  9. Outputs & filters:
  10. -------------------------------------
  11. 0 - output([t1.c1], [t2.c1]), filter(nil), limit(10), offset(10)
  12. 1 - output([t1.c1], [t2.c1]), filter(nil),
  13. conds(nil), nl_params_([t1.c1])
  14. 2 - output([t1.c1]), filter(nil),
  15. access([t1.c1]), partitions(p0)
  16. 3 - output([t2.c1]), filter(nil),
  17. access([t2.c1]), partitions(p0)

从这个计划来看,查询输出会继承t1表的序,如果客户直接在这个计划的基础上面做业务分页处理,业务代码可以正常运行。但是,一旦用户对t1表做了索引变更,引起MySQL的计划变化,该查询的分页结果也会随之改变,导致业务出错。这个问题的本质是业务使用了非标准的分页查询,而MySQL的“特性”使得业务能正常运行。

如果业务迁移到OceanBase,由于OceanBase支持多种join算法,可能会生成如下的计划:

  1. ================================================
  2. |ID|OPERATOR |NAME|EST. ROWS|COST |
  3. ------------------------------------------------
  4. |0 |LIMIT | |10 |85285|
  5. |1 | HASH RIGHT OUTER JOIN| |20 |85285|
  6. |2 | TABLE SCAN |t2 |100000 |38681|
  7. |3 | TABLE SCAN |t1 |20 |46 |
  8. ================================================
  9. Outputs & filters:
  10. -------------------------------------
  11. 0 - output([t1.c1], [t2.c1]), filter(nil), limit(10), offset(10)
  12. 1 - output([t1.c1], [t2.c1]), filter(nil),
  13. equal_conds([t1.c1 = t2.c1]), other_conds(nil)
  14. 2 - output([t2.c1]), filter(nil),
  15. access([t2.c1]), partitions(p0)
  16. 3 - output([t1.c1]), filter(nil),
  17. access([t1.c1]), partitions(p0)

这个计划就不会继承t1表的序,如果客户直接在这个计划的基础上面做业务分页处理,业务代码运行结果可能不对。

为了能够让这一类业务平滑迁移到OceanBase,我们需要为这种分页查询主动添加order by。

注意:我们不推荐使用该功能迁移MySQL业务

分页保序功能开关

针对上述问题,OceanBase 4.2.1 BP2引入了分页保序功能,只在MySQL租户下可以使用,增加隐藏配置项_preserve_order_for_pagination、查询hint PRESERVE_ORDER_FOR_PAGINATION用于控制查询是否使用分页保序功能。

隐藏配置项

_preserve_order_for_pagination隐藏配置为租户级别,可以控制租户是否打开或关闭分页保序功能,当前默认行为是关闭。如果需要打开租户级别的分页保序功能,可以使用如下命令:

alter system set _preserve_order_for_pagination = 1;

下一个章节会介绍打开该功能后,数据库会做一些什么事情,达到什么效果。

查询HINT

为了增加更细粒度的控制手段,OceanBase还提供了查询级别的控制方式,例如,如果需要针对特定查询打开分页保序功能:

  1. select /*+OPT_PARAM('PRESERVE_ORDER_FOR_PAGINATION', 'TRUE')*/
  2. * from t1 limit 10;

注意,该HINT不能用于控制特定的查询块是否打开分页保序功能,只能用于控制整个查询是否打开分页保序功能!

对于上面介绍的两种控制方式,OceanBase优先使用HINT。举个例子说明,如果租户设置了隐藏配置项为打开状态,同时在查询中使用/*+OPT_PARAM('PRESERVE_ORDER_FOR_PAGINATION', 'FALSE')*/,那么该查询不会开启分页保序功能。

分页保序功能

保序场景1

用户打开分页保序功能之后OceanBase会做什么事情呢?还是举个例子说明:

select t1.c1, t1.c2 from t1, t2 where t1.c1 = t2.c1 limit 10,10;

针对这种查询,OceanBase首先会对查询结果排序,然后对排序的结果分页处理,改写之后的查询如下:

  1. select t1.c1, t1.c2 from t1, t2 where t1.c1 = t2.c1
  2. order by t1.c1, t2.c2 limit 10,10;

排序之后的分页结果必然是稳定的,无论OceanBase选择的是什么计划,或者分页过程有任何计划变化,都不会影响分页查询的结果。

保序场景2

还有一种场景,用户查询已经有order by语句块了,但是由于只针对部分结果排序,导致分页结果还是不稳定,例如:

  1. select t1.c1, t1.c2 from t1, t2 where t1.c1 = t2.c1
  2. order by t1.c1 limit 10,10;

针对这种场景,如果打开了分页保序功能,OceanBase会在用户期望排序结果的前提下,额外增加排序字段。

  1. select t1.c1, t1.c2 from t1, t2 where t1.c1 = t2.c1
  2. order by t1.c1, t2.c2 limit 10,10;

保序场景3

另外还存在一种和场景 2 相似的场景,查询中有 order by 语句块针对部分结果排序,但 order by 语句块在视图内部。该场景下,如果打开了分页保序功能,无论 order by 语句块外层还有多少层视图,OceanBase 都会额外增加排序字段,例如:

  1. select * from
  2. select * from (
  3. select t1.c1, t1.c2 from t1, t2 where t1.c1 = t2.c1
  4. order by t1.c1
  5. )
  6. )limit 10,10;
  7. ==>
  8. select * from
  9. select * from (
  10. select t1.c1, t1.c2 from t1, t2 where t1.c1 = t2.c1
  11. order by t1.c1, t1.c2
  12. )
  13. )limit 10,10;

保序场景4

对于其他场景,预期想要的结果序会被改变,例如:

  1. select t2.c1, t2.c2
  2. from (select * from t1 order by t1.c1, t1.c2) V1
  3. left join t2 on V.c1 = t2.c1
  4. limit 10,10;

这个查询用户预期可能是按照t1表排序之后再分页查询结果,但是实际上任何SQL语义并不会保证查询的数据一定需要按照t1表的结果排序之后再分页。OceanBase会在最外层增加排序字段t2.c1,t2.c2,执行结果是按照输出结果排序(即t2表的字段),然后再分页,其等价查询如下:

  1. select t2.c1, t2.c2
  2. from t1
  3. left join t2 on V.c1 = t2.c1
  4. order by t2.c1, t2.c2
  5. limit 10,10;

性能影响

同时需要注意到一点,打开分页保序功能后,会引入额外的排序计算,部分查询可能会出现性能回退。

开启注意事项

用户打开分页保序功能之后,查询输出结果可能与未开启分页保序功能时不同!

select t1.c1, t1.c2 from t1, t2 where t1.c1 = t2.c1 limit 10,10;

未开启分页保序功能时,上面的查询输出结果会随着数据库的计划改变而变化,不会输出稳定的结果。

  1. select t1.c1, t1.c2 from t1, t2 where t1.c1 = t2.c1
  2. order by t1.c1, t2.c2 limit 10,10;

打开分页保序功能后,OceanBase 会主动添加排序字段,改写如上,无论计划怎么改变,查询结果都是稳定的。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/846075
推荐阅读
相关标签
  

闽ICP备14008679号