全部产品
Search
文档中心

云原生多模数据库 Lindorm:Lindorm SQL 常见问题

更新时间:Jun 24, 2026

使用 Lindorm SQL 查询宽表数据或管理索引时,可能遇到报错或查询性能不符合预期的情况。本文针对宽表引擎,汇总常见问题的原因分析和解决方案。

说明

本文所列常见问题仅适用于宽表引擎。

查询常见问题

  • Q:如何解决或规避低效查询问题?

    A:如果执行查询语句后,宽表引擎报错 This query may be a full table scan and thus may have unpredictable performance,表示该查询是低效查询。

    什么是低效查询?低效查询有哪些特点? 在 Lindorm 宽表引擎上,如果查询语句中带有过滤条件,但该过滤条件无法有效地利用已有的主键或索引,导致查询时必须扫描全表,这样的查询被视为低效查询。

    假设 test 表的联合主键由 p1、p2、p3 三个列组成,p1 列是主键的第一列,查询条件为 SELECT * FROM test WHERE p2=10;。此时查询条件中未包含第一个主键列 p1,因此该查询语句会被认定为低效查询语句。

    由于低效查询可能带来性能稳定性风险,默认情况下 Lindorm 会检测并阻止低效查询的执行。

    查询条件 WHERE 中的列是主键列或索引列,为什么查询还是触发了低效查询异常? 宽表引擎的主键、二级索引和 MySQL 联合索引的匹配规则类似,均遵循最左匹配原则,即在多个列组成的联合主键或索引中,系统从主键或索引键的第一列(最左侧列)开始逐一与查询条件中的列进行匹配。如果查询条件中不包含主键或索引键的第一列,则此时查询仍不会命中主键或二级索引,导致查询仍然是低效查询。

    假设 test 表的主键由 p1、p2、p3 三个列组成,p1 列是主键的第一列,根据最左匹配原则,查询数据时系统将从 p1 列开始匹配。如果查询条件中未包含 p1 列,例如 SELECT * FROM test WHERE p2<30;,此时无法匹配到主键第一列 p1,系统不会命中主键进而查询全表数据以满足查询条件 p2<30

    如何规避低效查询? 以下是业务中规避低效查询的推荐方法:

    • 优化查询条件,在 WHERE 语句中增加表的主键,或保证查询条件中的列满足最左匹配原则。

    • 修改表的主键设计以避免大查询,具体参见如何设计宽表主键

    • 为表创建二级索引,相关操作参见二级索引

    • 如果表的多个列均存在多维检索需求,可创建多维搜索索引加速查询,详细信息参见搜索索引

    • 在查询语句中增加查询优化的 HINT /*+ _l_allow_filtering_ */,强制宽表引擎执行低效查询。例如 SELECT /*+ _l_allow_filtering_ */ * FROM dt WHERE nonPK=100;

    警告

    强制执行低效查询可能带来性能稳定性风险,请谨慎使用。

  • Q:使用 GROUP BY 查询时报错 The diff group keys of subPlan is over lindorm.aggregate.subplan.groupby.keys.limit=..., it may cost a lot memory so we shutdown this SubPlan

    A:GROUP BY 操作形成的分组数过多,可能消耗大量内存资源,从而加重实例负载,因此宽表引擎限制了结果集中分组数过大的查询。

    解决方案

    • 在查询语句中添加过滤条件,减少最终分组数。

    • 联系 Lindorm 技术支持(钉钉号:s0s3eg3)调大分组数的阈值。

      警告

      调大分组数的阈值可能会影响实例的稳定性。

    • 针对多维查询匹配场景,建议使用搜索索引,详细信息参见搜索索引

  • Q:对开启动态列的表执行 SELECT * 查询报错 Limit of this select statement is not set or exceeds config when select all columns from table with property DYNAMIC_COLUMNS=true

    A:开启动态列的表可能包含大量的动态列,且表的 Schema 定义不固定。如果对这类表进行全表扫描,将导致 IO 消耗严重,加重实例负载。为避免高负载情况的产生,Lindorm 宽表引擎对动态列表的查询语句进行了限制。

    解决方案:在 SELECT 语句中添加 LIMIT 子句,限制返回结果的数量。例如 SELECT * FROM test LIMIT 10;

  • Q:执行 SQL 查询时,为什么会报错 Code grows beyond 64 KB

    A:Lindorm 的 SQL 引擎在执行查询时采用了 JIT 编译技术,会将查询的物理计划动态生成字节码并编译执行。Code grows beyond 64KB 报错是指生成的方法函数的字节码大小超过了 Java 虚拟机允许的上限,这可能是因为指定的 SQL 查询语句中的个别谓词过于冗长或复杂,导致生成的字节码过大无法被执行。

    解决方案:修改指定的 SQL 语句,简化相关的谓词表达。

  • Q:执行 SQL 查询时,为什么会报错 The estimated memory used by the query exceeds the maximum limit

    A:SQL 引擎在处理存储引擎返回的结果集时(例如聚合、排序、去重)通常需要消耗大量内存资源。由于 Lindorm SQL 主要面向在线业务场景,同一时刻可能存在大量查询并发执行,为保证高并发场景下的查询效率,系统对单个查询的内存使用进行了限制。当前默认限制为 8 MB,超出时会触发内存溢出异常。

    解决方案

    • 优化查询语句,结合索引将聚合、排序等算子下推到存储引擎执行,或通过优化过滤条件减少 SQL 引擎处理的数据量。

    • 调整单查询内存限制阈值 QUERY_MAX_MEM

    详细的诊断流程和具体操作步骤,参见如何诊断和解决特定查询触发的内存限制报错?

  • Q:为什么不建议在 WHERE 条件中同时使用大量 IS NULL 条件?

    A:在 Lindorm 宽表引擎中,IS NULL 需要同时兼容“列存在且值为 NULL”和“列不存在或未写入”的情况。因此,当 SQL 条件中包含多个 IS NULL 谓词时,SQL 引擎在编译阶段可能对这些条件进行组合展开。N 个 IS NULL 条件理论上可能产生 2^N 个组合分支,导致编译耗时和内存占用明显上升,严重时可能影响请求执行和实例稳定性。

    解决方案:避免在单条 SELECT、UPDATE、DELETE 等 SQL 中同时拼接大量 column IS NULL 条件。对于此类场景,推荐做法如下:

    • 优先通过主键、二级索引或搜索索引缩小查询范围。

    • 如果业务需要频繁判断字段是否为空或是否存在,可在写入侧通过默认值、状态字段等方式显式表达业务状态。

    • 对于批量处理场景,建议拆分为多条条件更简单的 SQL,或先查询完整主键后按主键分批处理。

  • Q:执行 SQL 请求时,为什么不建议在 WHERE 条件中使用大量 OR 条件的 AND 组合?

    问题描述:如果一条 SQL 的 WHERE 条件包括多个括号内的 OR 条件通过 AND 连接,例如:

    SELECT * FROM orders
    WHERE (status = 1 OR status = 2)
      AND (pay_type = 'wechat' OR pay_type = 'alipay')
      AND (region = 'CN' OR region = 'US')
      AND ...;

    嵌套层级越多,风险越大。SELECT、UPDATE、DELETE 均适用。

    A:优化器在执行查询前,需要将 WHERE 子句转换为 DNF(析取范式),即“多个 AND 条件的 OR 集合”,以便为每个独立条件选择最优的索引访问路径。当遇到 (A OR B) AND (C OR D) 时,优化器必须应用分配律将其展开:

    (A or B) and (C or D) ⇒ (A and C) or (A and D) or (B and C) or (B and D)

    • 2 个括号相乘,每个括号内两个分支:产生 2×2=4 个组合项。

    • 3 个括号相乘,每个括号内两个分支:产生 2×2×2=8 个组合项。

    随着括号数和每个括号内分支数的增加,组合项数量呈指数级增长,可能导致优化器编译耗时和内存占用大幅上升,严重时影响请求执行和实例稳定性。

    解决方案

    • 升级到宽表2.8.6版本以上,对上述查询编译展开过大有限制拦截,避免此类查询造成实例内存压力,影响其他业务。旧版本无拦截。

    • 控制(A OR B) AND (C OR D)类条件展开后的组合项小于2000为佳。

    • 如果确实存在多字段、多维度的检索需求,建议结合搜索引擎、计算引擎等能力设计查询链路,并在上线前验证执行计划和资源消耗。

  • Q:如何诊断和解决特定查询触发的内存限制报错?

    A:当 SQL 查询报错 The estimated memory used by the query exceeds the maximum limit 时,按照以下步骤诊断根本原因并选择对应的解决方案。

    第一步:查看执行计划

    对报错的 SQL 语句执行 EXPLAIN 命令查看执行计划,详细信息参见解读执行计划

    在执行计划中,确认聚合、排序、去重等高内存消耗算子是否在 SQL 引擎中执行,或已下推到存储引擎执行。在 SQL 引擎中执行的算子会消耗单查询的内存配额,而下推到存储引擎执行的算子则使用存储引擎的资源。

    • 如果高内存消耗算子在 SQL 引擎中执行,优化查询是正确的解决方向(参见第二步)。

    • 如果算子已下推且查询已充分优化,可考虑调大内存限制(参见第三步)。

    第二步:优化查询语句(推荐)

    如果高内存消耗算子在 SQL 引擎中执行,可尝试以下方式降低内存消耗:

    • 针对聚合算子(GROUP BY、COUNT、SUM、AVG 等):在聚合列或分组列上创建二级索引或搜索索引,将聚合算子下推到存储引擎执行。

    • 针对排序算子(ORDER BY):确保排序列与现有索引对齐,避免在内存中排序。或通过增加更严格的 WHERE 过滤条件,在排序前减少数据集的大小。

    • 针对去重算子(DISTINCT):对高基数列上的查询使用搜索索引,或添加选择性过滤条件,减少处理的行数。

    第三步:调大内存限制阈值(请谨慎评估)

    如果所有算子已下推且查询无法进一步优化,可通过ALTER SYSTEM语句调整 QUERY_MAX_MEM 配置项:

    ALTER SYSTEM SET QUERY_MAX_MEM = <新的内存限制值(字节)>;

    例如 ALTER SYSTEM SET QUERY_MAX_MEM = 8388608;

    如果使用的SQL版本说明低于 2.9.6.0,可联系 Lindorm 技术支持(钉钉号:s0s3eg3)调大内存限制阈值。

    QUERY_MAX_MEM 当前的生效值可通过SHOW VARIABLES语句查询。

    警告

    当在线应用的查询并发度较高时,直接调大该内存限制的阈值可能会加重 Lindorm 的内存使用负载,进而引发强制的 Full GC 等行为,从而降低整个集群的响应能力。调大该内存限制前请务必充分评估。

索引与 Schema 问题

  • Q:为什么创建二级索引时报错 Executing job number exceed, max job number = 8

    A:一个实例中仅允许同时存在 8 个二级索引的构建任务。如果某一时刻二级索引的构建任务数已达 8 个,再次创建新的二级索引时,创建语句将会报错。

    解决方案:建议避免同时创建大量二级索引。如果有大量创建的需求,联系 Lindorm 技术支持(钉钉号:s0s3eg3)。

  • Q:在宽表引擎上已执行删除列操作,为什么重新添加同名列时会报错 column is under deleting

    A:为避免由于数据类型等因素引发的脏数据问题,执行删除列的操作后,宽表引擎还需要异步清理内存、热存储和冷存储上该列的数据。在数据全部清理完之前,系统不允许重新添加同名的新列。

    解决方案:由于数据清理由系统自行完成,可能会消耗很长时间。建议通过以下方式加速数据的清理过程,待数据清理完成之后重新添加同名列。

    假设执行了删除列操作的表名为 dt

    -- 执行 FLUSH 操作,强制将内存中残留的数据刷到存储媒介上
    ALTER TABLE dt FLUSH;
    
    -- 执行 COMPACTION 操作,执行数据的合并和删除
    ALTER TABLE dt COMPACT;
    • FLUSH 语法从 SQL 引擎 2.7.1 版本开始支持。查看 SQL 引擎版本的方法参见SQL版本说明

    • FLUSH 操作和 COMPACT 操作是异步操作。语句执行成功并不代表数据清理完成,需要等待一段时间才能彻底清理完成。

    重要

    对数据量大的表执行 COMPACT 操作期间会占用较多系统资源,不建议在业务高峰期执行。

  • Q:创建二级索引后,写入数据时为什么会报错 Performing put operations with User-Defined-Timestamp in indexed column on MULTABLE_LATEST table is unsupported

    A:如果写入时显式指定了自定义时间戳(例如使用 UPSERT 语句写入数据时,通过 /*+ _l_ts */ 指定了自定义时间戳),此时要求主表与二级索引表之间的可变性(Mutability)必须为 MUTABLE_ALL。但出于性能考虑,Lindorm 系统默认将主表与索引表的可变性配置为 MUTABLE_LATEST,在这种配置下创建二级索引并启用会触发可变性约束限制,导致报错。

    解决方案:由于创建索引表后 MUTABILITY 参数的值不支持修改,因此需要先删除原有的二级索引。

    • 删除主表中原有的二级索引。

    -- 禁用原有二级索引
    ALTER INDEX IF EXISTS <原有二级索引名> ON <主表名> DISABLED;
    
    -- 删除原有二级索引
    DROP INDEX IF EXISTS <原有二级索引名> ON <主表名>;

    DROP INDEX 语法的详细说明参见删除二级索引

    • 将主表的 MUTABILITY 属性的值修改为 MUTABLE_ALL

    ALTER TABLE IF EXISTS <主表名> SET MUTABILITY='MUTABLE_ALL';

批量操作