LIMIT-K 是一个只获取 K 行的算子,在 OceanBase 数据库中, LIMIT-K 允许从指定偏移量(offset)开始获取 K 行数据,LIMIT 语句的形式为:Limit offset, k

    LIMIT-K 通常会跟排序语句(ORDER BY)一起使用来获取 top-k 行。当 ORDER BY 和 LIMIT 一起出现的时候,取决于代价模型,ORDER BY 算子可能会被转化成 top-k SORT 排序。

    如下例所示,当出现 LIMIT-K 语句的时候,ORDER BY 算子被转化成 top-k 排序。

    1. obclient> create table t1(a int primary key, b int, c int);
    2. Query OK, 0 rows affected (0.14 sec)
    3. --limit-k 中的 sort 被转化成了 top-k 排序
    4. obclient> explain select * from t1 order by b limit 0, 10;
    5. | =====================================
    6. |ID|OPERATOR |NAME|EST. ROWS|COST|
    7. -------------------------------------
    8. |0 |LIMIT | |10 |1151|
    9. |1 | TOP-N SORT | |10 |1149|
    10. |2 | TABLE SCAN|t1 |1000 |455 |
    11. =====================================
    12. Outputs & filters:
    13. -------------------------------------
    14. 0 - output([t1.a], [t1.b], [t1.c]), filter(nil), limit(10), offset(0)
    15. 1 - output([t1.a], [t1.b], [t1.c]), filter(nil), sort_keys([t1.b, ASC]), topn(10 + 0)
    16. 2 - output([t1.a], [t1.b], [t1.c]), filter(nil),
    17. access([t1.a], [t1.b], [t1.c]), partitions(p0)

    OceanBase 数据库中的 LIMIT-K 语句在一些特定的情况下会被直接下压到存储层。

    如下例所示,因为 a 是主键,所以排序算子会被消除,所以整个 LIMIT 算子会被下压到存储层。

    1. obclient> create table t1(a int primary key, b int, c int);
    2. Query OK, 0 rows affected (0.14 sec)
    3. obclient> explain select * from t1 order by a limit 100, 10;
    4. | ===================================
    5. |ID|OPERATOR |NAME|EST. ROWS|COST|
    6. -----------------------------------
    7. |0 |TABLE SCAN|t1 |10 |59 |
    8. ===================================
    9. Outputs & filters:
    10. -------------------------------------
    11. 0 - output([t1.a], [t1.b], [t1.c]), filter(nil),
    12. access([t1.a], [t1.b], [t1.c]), partitions(p0),
    13. limit(10), offset(100)

    LIMIT-K 语句的性能在很多情况下取决于数据分布,假设 t1 中有 100w 行数据,满足 b=1 的行数有 1w 行(如果满足 b=1 的行数不到 100 行,那么查询需要全表扫描才能找到所有数据),那么下面这个查询的性能有如下两个极端情况:

    • 在最好的情况下,如果主表扫描出来的前面 100 行数据全部满足条件,那么该查询只需要顺序扫描100 行。

    • 在最差的情况下,如果满足条件的 1w 行都在最后面,那么该查询需要顺序扫描 99w+ 额外读取满足条件的 100 行。

    1. obclient> create table t1(a int primary key, b int, c int);
    2. Query OK, 0 rows affected (0.14 sec)
    3. obclient> explain select * from t1 where b = 1 limit 100;
    4. | ===================================
    5. |ID|OPERATOR |NAME|EST. ROWS|COST|
    6. -----------------------------------
    7. |0 |TABLE SCAN|t1 |2 |622 |
    8. ===================================
    9. Outputs & filters:
    10. -------------------------------------
    11. 0 - output([t1.a], [t1.b], [t1.c]), filter([t1.b = 1]),
    12. access([t1.b], [t1.a], [t1.c]), partitions(p0),
    13. limit(100), offset(nil)

    在这种情况下,LIMIT-K 的语句的性能会取决于数据分布,所以一定要谨慎的使用 LIMIT-K 语句。