使用 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';创建新的二级索引并写入数据,语法说明参见CREATE INDEX。
自定义时间戳的写入方式参见通过HINT设置时间戳实现多版本数据管理。
二级索引可变性约束与自定义时间戳的关系参见二级索引。
批量操作
Q:为什么不支持批量更新或报错
Update's WHERE clause can only contain PK columns?A:默认仅支持单行更新,需手动开启批量更新开关,具体操作参见批量删除某个表后导致此表的查询超时。
Q:如何开启批量删除?
A:开启和查询批量删除设置的方法参见如何开启批量删除?。