obloader导入数据1亿多后,查询过慢

【 使用环境 】生产环境 or 测试环境
【 OB or 其他组件 】
【 使用版本 】
【问题描述】清晰明确描述问题
【复现路径】问题出现前后相关操作
【附件及日志】推荐使用OceanBase敏捷诊断工具obdiag收集诊断信息,详情参见链接(右键跳转查看):

https://ask.oceanbase.com/t/topic/35605619

【备注】基于 LLM 和开源文档 RAG 的论坛小助手已开放测试,在发帖时输入 [@论坛小助手] 即可召唤小助手,欢迎试用!

obloader导入一张销售流水数据表,目前针对1亿多数据量,半小时导入完成后,查询速度较慢,
建表语句

create table if not exists da_sale

(

change_date int comment "销售日期,

store_code varchar(40) comment "门店代码,

pdt_no varchar(40) comment "商品代码,

size varchar(40) comment "尺码,

qty int comment "销量,

price decimal(14,2) comment "实收价,

pricelist int comment "吊牌价,

is_groupbuy varchar(4) comment "是否团购,

amt decimal(14,2) comment "实收金额,

amt_list int comment "吊牌金额,

tot_qty int comment "累计销量,

change_month varchar(12)

);

create index idx_da_sale on da_sale(store_code,pdt_no,change_date)
主要针对下面2个场景
select count(*) from da_sale;
select * from da_sale limit 10;

这种情况下如何排查优化

1 个赞

根据您提供的信息,obloader 导入大量数据后查询性能下降,这可能是由于多种因素引起的。以下是一些可能的原因和优化建议:

1. 检查索引

  • 索引选择:您已经创建了一个复合索引 idx_da_sale,但这个索引是否适合您的查询场景?特别是对于 select count(*) from da_sale; 这样的全表扫描查询,索引的效果可能有限。
  • 索引维护:大量数据导入后,索引可能需要重新构建或优化。您可以尝试重建索引:
    ALTER TABLE da_sale REBUILD INDEX idx_da_sale;
    

2. 数据分布和分区

  • 数据分布:确保数据在各个节点上均匀分布。您可以使用 EXPLAIN 查看查询计划,确认数据是否均匀分布在各个节点上。
  • 分区表:对于大规模数据表,建议使用分区表来提高查询性能。例如,按 change_datechange_month 进行分区:
    CREATE TABLE da_sale (
      change_date INT COMMENT '销售日期',
      store_code VARCHAR(40) COMMENT '门店代码',
      pdt_no VARCHAR(40) COMMENT '商品代码',
      size VARCHAR(40) COMMENT '尺码',
      qty INT COMMENT '销量',
      price DECIMAL(14,2) COMMENT '实收价',
      pricelist INT COMMENT '吊牌价',
      is_groupbuy VARCHAR(4) COMMENT '是否团购',
      amt DECIMAL(14,2) COMMENT '实收金额',
      amt_list INT COMMENT '吊牌金额',
      tot_qty INT COMMENT '累计销量',
      change_month VARCHAR(12)
    ) PARTITION BY RANGE (change_date) (
      PARTITION p0 VALUES LESS THAN (20230101),
      PARTITION p1 VALUES LESS THAN (20230201),
      PARTITION p2 VALUES LESS THAN (20230301),
      -- 添加更多分区
    );
    

3. 查询优化

  • 查询计划:使用 EXPLAIN 查看查询计划,确保查询使用了合适的索引和执行计划。例如:
    EXPLAIN SELECT COUNT(*) FROM da_sale;
    EXPLAIN SELECT * FROM da_sale LIMIT 10;
    
  • 统计信息:确保统计信息是最新的,这有助于优化器生成更优的执行计划。您可以运行:
    ANALYZE TABLE da_sale;
    

4. 系统参数调整

  • 内存和缓存:检查系统参数,确保有足够的内存和缓存来支持查询。例如,调整 memory_limitmemory_limit_percentage 参数。
  • 并行度:增加查询的并行度可以提高查询性能。您可以调整 parallel_query 相关参数。

5. 合并操作

  • 自适应合并:如果您使用的是 V4.2.0 及以上版本,可以开启自适应合并功能,这有助于优化查询性能。具体步骤如下:
    ALTER SYSTEM SET enable_adaptive_merge = true;
    
  • 手动合并:如果自适应合并不可用,您可以手动触发合并操作:
    ALTER SYSTEM MAJOR FREEZE;
    

6. 日志和诊断

  • 日志分析:使用 obdiag 工具收集诊断信息,分析日志文件,查找可能的性能瓶颈。
  • 全链路诊断:如果问题仍然存在,可以使用全链路诊断机制,追踪 SQL 请求在数据库全链路过程中的执行情况,帮助快速定位问题。

7. 其他建议

  • 硬件资源:确保硬件资源(如 CPU、内存、磁盘 I/O)足够支持当前的查询负载。
  • 数据预处理:在导入数据前,可以对数据进行预处理,例如删除重复数据、优化数据格式等。

希望以上建议能帮助您优化查询性能。如果问题仍然存在,建议联系 OceanBase 的技术支持团队获取进一步的帮助。

更多信息请查看:

  1. 向量索引
  2. 使用 LOAD DATA 语句导入数据
  3. OceanBase AP 核心特性
  4. 全链路追踪概述
  5. OceanBase AP 概述
  6. 分布式执行和并行查询
  7. SQL 调优典型场景和案例
  8. obloader/obdumper
  9. 列存 FAQ
  10. 自适应合并
1 个赞

count计数查询直接超时,调整超时时间1分钟仍然失败,
分页limit10获取长达21s

1 个赞

根据您提供的信息,count 计数查询直接超时,即使调整超时时间到 1 分钟仍然失败,而 limit 10 查询也耗时较长。这表明查询性能存在严重问题。以下是一些详细的排查和优化建议:

1. 检查查询计划

  • 使用 EXPLAIN 查看查询计划
    EXPLAIN SELECT COUNT(*) FROM da_sale;
    EXPLAIN SELECT * FROM da_sale LIMIT 10;
    
  • 分析查询计划:确保查询没有使用全表扫描。如果 count 查询使用了全表扫描,可以考虑以下优化措施。

2. 优化 count 查询

  • 使用覆盖索引:如果 count 查询频繁,可以考虑创建一个覆盖索引,减少全表扫描的开销。例如:
    CREATE INDEX idx_count ON da_sale(change_date);
    
  • 使用近似计数:如果精确计数不是必须的,可以考虑使用近似计数函数,如 APPROX_COUNT_DISTINCT
    SELECT APPROX_COUNT_DISTINCT(change_date) FROM da_sale;
    

3. 优化 limit 查询

  • 使用索引:确保 limit 查询使用了合适的索引。例如,如果经常查询前 10 条记录,可以创建一个覆盖索引:
    CREATE INDEX idx_limit ON da_sale(store_code, pdt_no, change_date);
    
  • 避免大偏移量:如果 limit 查询涉及较大的偏移量,可以考虑使用子查询来优化:
    SELECT * FROM da_sale WHERE change_date IN (SELECT change_date FROM da_sale ORDER BY change_date LIMIT 10);
    

4. 调整系统参数

  • 增加并行度:增加查询的并行度可以提高查询性能。例如,调整 parallel_query 相关参数:
    SET parallel_degree_policy = 'MANUAL';
    SET parallel_servers_target = 10;
    
  • 调整内存和缓存:确保有足够的内存和缓存来支持查询。例如,调整 memory_limitmemory_limit_percentage 参数:
    SET GLOBAL memory_limit = '10G';
    SET GLOBAL memory_limit_percentage = 80;
    

5. 数据分布和分区

  • 数据分布:确保数据在各个节点上均匀分布。您可以使用 EXPLAIN 查看查询计划,确认数据是否均匀分布在各个节点上。
  • 分区表:对于大规模数据表,建议使用分区表来提高查询性能。例如,按 change_datechange_month 进行分区:
    CREATE TABLE da_sale (
      change_date INT COMMENT '销售日期',
      store_code VARCHAR(40) COMMENT '门店代码',
      pdt_no VARCHAR(40) COMMENT '商品代码',
      size VARCHAR(40) COMMENT '尺码',
      qty INT COMMENT '销量',
      price DECIMAL(14,2) COMMENT '实收价',
      pricelist INT COMMENT '吊牌价',
      is_groupbuy VARCHAR(4) COMMENT '是否团购',
      amt DECIMAL(14,2) COMMENT '实收金额',
      amt_list INT COMMENT '吊牌金额',
      tot_qty INT COMMENT '累计销量',
      change_month VARCHAR(12)
    ) PARTITION BY RANGE (change_date) (
      PARTITION p0 VALUES LESS THAN (20230101),
      PARTITION p1 VALUES LESS THAN (20230201),
      PARTITION p2 VALUES LESS THAN (20230301),
      -- 添加更多分区
    );
    

6. 合并操作

  • 自适应合并:如果您使用的是 V4.2.0 及以上版本,可以开启自适应合并功能,这有助于优化查询性能。具体步骤如下:
    ALTER SYSTEM SET enable_adaptive_merge = true;
    
  • 手动合并:如果自适应合并不可用,您可以手动触发合并操作:
    ALTER SYSTEM MAJOR FREEZE;
    

7. 日志和诊断

  • 日志分析:使用 obdiag 工具收集诊断信息,分析日志文件,查找可能的性能瓶颈。
  • 全链路诊断:如果问题仍然存在,可以使用全链路诊断机制,追踪 SQL 请求在数据库全链路过程中的执行情况,帮助快速定位问题。

8. 硬件资源

  • 确保硬件资源:确保硬件资源(如 CPU、内存、磁盘 I/O)足够支持当前的查询负载。

9. 统计信息

  • 更新统计信息:确保统计信息是最新的,这有助于优化器生成更优的执行计划。您可以运行:
    ANALYZE TABLE da_sale;
    

10. 联系技术支持

  • 技术支持:如果以上措施仍然无法解决问题,建议联系 OceanBase 的技术支持团队获取进一步的帮助。

希望以上建议能帮助您优化查询性能。如果问题仍然存在,建议联系 OceanBase 的技术支持团队获取更多帮助。

检索到的文档如下:

  1. OceanBase 术语
  2. GV$OB_SQL_AUDIT
  3. 集群管理 FAQ
  4. 分页查询
  5. 连接次序
  6. ob_trx_idle_timeout
  7. 在您开始前
  8. LIMIT
  9. ob_query_switch_leader_retry_timeout

(小助手的答复已结束,如未能解决您的问题,请继续提问并等待其他同学的回复,谢谢!)

试试使用count(1) count*无法走索引的。
是否多次执行都慢呢,第一次硬解析sql会比较慢一些,
可以把你的详细sql和执行计划发出来看看。使用obdiag收集该sql信息
obdiag收集SQL性能问题信息
obdiag gather scene run --scene=observer.perf_sql --env “{db_connect=’-hxx -Pxx -uxx -pxx -Dxx’, trace_id=‘xx’}”

:ok_hand:

1 个赞

是的,count(*)直接超时,count(1)基本在毫秒级别,谢谢

2 个赞

第二条select * 把* 改为所有字段名称试试

2 个赞

目前可以了,count(1)查询后,后续性能都正常,可能主要是硬解析,没走索引导致

2 个赞

如果是obloader导入的数据 建议先手动收集一下 统计信息 在去查询数据

2 个赞

收集完成后,单表查询效率提升,最快的时候2s,针对2张表关联后做总量sum计算,长达20s

2 个赞

1、两个表结构发一下
2、explain extended sql 保存在文本里
3、 obdiag收集下
obdiag收集SQL性能问题信息
obdiag gather scene run --scene=observer.perf_sql --env “{db_connect=’-hxx -Pxx -uxx -pxx -Dxx’, trace_id=‘xx’}”
https://www.oceanbase.com/docs/common-obdiag-cn-1000000001768178

1 个赞

is_global_index=false是不是这个有关

2 个赞

1、把da_sale_imp2表结构 也发一下
2、select count(store_code) from da_store where region =‘A089’;你统计这个大概多少数据
3、explain extended select sum(qty) from da_sale_imp2 where store_code
in (select store_code from da_store where region =‘A089’); --保存在文本里
4、explain extended select sum(qty) from da_sale_imp2 dsi inner join (select store_code from da_store where region =‘A089’) ds on dsi.store_code=ds.store_code; 保存在文本里
5、select sum(qty) from da_sale_imp2 dsi inner join (select store_code from da_store where region =‘A089’) ds on dsi.store_code=ds.store_code;–执行一下这个语句 看看多长时间
6、obdiag尽量收集一下
obdiag收集SQL性能问题信息
obdiag gather scene run --scene=observer.perf_sql --env “{db_connect=’-hxx -Pxx -uxx -pxx -Dxx’, trace_id=‘xx’}”
https://www.oceanbase.com/docs/common-obdiag-cn-1000000001768178

CREATE TABLE da_sale_imp2 (
change_date int(11) DEFAULT NULL COMMENT ‘销售日期’,
store_code varchar(40) DEFAULT NULL COMMENT ‘门店代码’,
pdt_no varchar(40) DEFAULT NULL COMMENT ‘商品代码’,
size varchar(40) DEFAULT NULL COMMENT ‘尺码’,
qty int(11) DEFAULT NULL COMMENT ‘销量’,
price decimal(14,2) DEFAULT NULL COMMENT ‘实收价’,
pricelist int(11) DEFAULT NULL COMMENT ‘吊牌价’,
is_groupbuy varchar(4) DEFAULT NULL COMMENT ‘是否团购’,
amt decimal(14,2) DEFAULT NULL COMMENT ‘实收金额’,
amt_list int(11) DEFAULT NULL COMMENT ‘吊牌金额’,
tot_qty int(11) DEFAULT NULL COMMENT ‘累计销量’,
change_month varchar(12) DEFAULT NULL
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = ‘zstd_1.3.8’ REPLICA_NUM = 3 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0 WITH COLUMN GROUP(each column)

explain extended select sum(qty) from da_sale_imp2 where store_code
in (select store_code from da_store where region =‘A089’);

|ID|OPERATOR |NAME |EST.ROWS |EST.TIME(us)|

|0 |SCALAR GROUP BY | |1 |45447656 |
|1 |└─HASH RIGHT SEMI JOIN | |16761789 |45143864 |
|2 | ├─SUBPLAN SCAN |VIEW1 |978 |2600 |
|3 | │ └─TABLE RANGE SCAN |da_store(idx_da_store_01)|978 |2598 |
|4 | └─COLUMN TABLE FULL SCAN|da_sale_imp2 |159999999|2037933 |

Outputs & filters:

0 - output([T_FUN_SUM(da_sale_imp2.qty(0x7fbb8a039520))(0x7fbb8a038b90)]), filter(nil), rowset=256
group(nil), agg_func([T_FUN_SUM(da_sale_imp2.qty(0x7fbb8a039520))(0x7fbb8a038b90)])
1 - output([da_sale_imp2.qty(0x7fbb8a039520)]), filter(nil), rowset=256
equal_conds([da_sale_imp2.store_code(0x7fbb8a0384d0) = VIEW1.store_code(0x7fbb8a03b960)(0x7fbb8a03bda0)]), other_conds(nil)
2 - output([VIEW1.store_code(0x7fbb8a03b960)]), filter(nil), rowset=256
access([VIEW1.store_code(0x7fbb8a03b960)])
3 - output([da_store.STORE_CODE(0x7fbb8a037fd0)]), filter(nil), rowset=256
access([da_store.__pk_increment(0x7fbb8a039ec0)], [da_store.STORE_CODE(0x7fbb8a037fd0)]), partitions(p0)
is_index_back=true, is_global_index=false,
range_key([da_store.REGION(0x7fbb8a0374d0)], [da_store.__pk_increment(0x7fbb8a039ec0)]), range(A089,MIN ; A089,MAX),
range_cond([da_store.REGION(0x7fbb8a0374d0) = ‘A089’(0x7fbb8a183cf0)])
4 - output([da_sale_imp2.store_code(0x7fbb8a0384d0)], [da_sale_imp2.qty(0x7fbb8a039520)]), filter(nil), rowset=256
access([da_sale_imp2.store_code(0x7fbb8a0384d0)], [da_sale_imp2.qty(0x7fbb8a039520)]), partitions(p0)
is_index_back=false, is_global_index=false,
range_key([da_sale_imp2.__pk_increment(0x7fbb8a03a500)]), range(MIN ; MAX)always true
Used Hint:

/*+

*/
Qb name trace:

stmt_id:0, stmt_type:T_EXPLAIN
stmt_id:1, SEL$1 > SEL$6FCAE2AA
stmt_id:2, SEL$2
Outline Data:

/*+
BEGIN_OUTLINE_DATA
LEADING(@“SEL$6FCAE2AA” (“VIEW1”@“SEL$1” “ob”.“da_sale_imp2”@“SEL$1”))
USE_HASH(@“SEL$6FCAE2AA” “ob”.“da_sale_imp2”@“SEL$1”)
INDEX(@“SEL$2” “ob”.“da_store”@“SEL$2” “idx_da_store_01”)
FULL(@“SEL$6FCAE2AA” “ob”.“da_sale_imp2”@“SEL$1”)
USE_COLUMN_TABLE(@“SEL$6FCAE2AA” “ob”.“da_sale_imp2”@“SEL$1”)
UNNEST(@“SEL$2”)
OPTIMIZER_FEATURES_ENABLE(‘4.3.4.0’)
END_OUTLINE_DATA
*/
Optimization Info:

da_store:
table_rows:17833
physical_range_rows:978
logical_range_rows:978
index_back_rows:978
output_rows:978
table_dop:1
dop_method:Table DOP
avaiable_index_name:[idx_da_store, idx_da_store_01, DA_STORE]
pruned_index_name:[idx_da_store]
unstable_index_name:[DA_STORE]
stats info:[version=1734489554279688, is_locked=0, is_expired=0]
dynamic sampling level:0
estimation method:[OPTIMIZER STATISTICS, STORAGE]
da_sale_imp2:
table_rows:25929589
physical_range_rows:159999999
logical_range_rows:159999999
index_back_rows:0
output_rows:159999999
table_dop:1
dop_method:Table DOP
avaiable_index_name:[da_sale_imp2]
stats info:[version=1734487610882355, is_locked=0, is_expired=0]
dynamic sampling level:0
estimation method:[OPTIMIZER STATISTICS, STORAGE]
Plan Type:
LOCAL
Note:
Degree of Parallelisim is 1 because of table property

EXPLAIN EXTENDED SELECT
sum( qty )
FROM
da_sale_imp2 dsi
INNER JOIN ( SELECT store_code FROM da_store WHERE region =‘A089’ ) ds ON dsi.store_code = ds.store_code

================================================================================
|ID|OPERATOR |NAME |EST.ROWS |EST.TIME(us)|

|0 |SCALAR GROUP BY | |1 |45448679 |
|1 |└─HASH JOIN | |16775301 |45144643 |
|2 | ├─TABLE RANGE SCAN |da_store(idx_da_store_01)|978 |2598 |
|3 | └─COLUMN TABLE FULL SCAN|dsi |159999999|2037933 |

Outputs & filters:

0 - output([T_FUN_SUM(dsi.qty(0x7fbd3ae37830))(0x7fbd3ae36ea0)]), filter(nil), rowset=256
group(nil), agg_func([T_FUN_SUM(dsi.qty(0x7fbd3ae37830))(0x7fbd3ae36ea0)])
1 - output([dsi.qty(0x7fbd3ae37830)]), filter(nil), rowset=256
equal_conds([dsi.store_code(0x7fbd3ae363c0) = da_store.STORE_CODE(0x7fbd3ae349e0)(0x7fbd3ae35b00)]), other_conds(nil)
2 - output([da_store.STORE_CODE(0x7fbd3ae349e0)]), filter(nil), rowset=256
access([da_store.__pk_increment(0x7fbd3ae38290)], [da_store.STORE_CODE(0x7fbd3ae349e0)]), partitions(p0)
is_index_back=true, is_global_index=false,
range_key([da_store.REGION(0x7fbd3ae33ee0)], [da_store.__pk_increment(0x7fbd3ae38290)]), range(A089,MIN ; A089,MAX),
range_cond([da_store.REGION(0x7fbd3ae33ee0) = ‘A089’(0x7fbd3af13730)])
3 - output([dsi.store_code(0x7fbd3ae363c0)], [dsi.qty(0x7fbd3ae37830)]), filter(nil), rowset=256
access([dsi.store_code(0x7fbd3ae363c0)], [dsi.qty(0x7fbd3ae37830)]), partitions(p0)
is_index_back=false, is_global_index=false,
range_key([dsi.__pk_increment(0x7fbd3ae388d0)]), range(MIN ; MAX)always true
Used Hint:

/*+

*/
Qb name trace:

stmt_id:0, stmt_type:T_EXPLAIN
stmt_id:1, SEL$1 > SEL$CFEA49FE > SEL$A7A036C9
stmt_id:2, SEL$2
Outline Data:

/*+
BEGIN_OUTLINE_DATA
LEADING(@“SEL$A7A036C9” (“ob”.“da_store”@“SEL$2” “dsi”@“SEL$1”))
USE_HASH(@“SEL$A7A036C9” “dsi”@“SEL$1”)
INDEX(@“SEL$A7A036C9” “ob”.“da_store”@“SEL$2” “idx_da_store_01”)
FULL(@“SEL$A7A036C9” “dsi”@“SEL$1”)
USE_COLUMN_TABLE(@“SEL$A7A036C9” “dsi”@“SEL$1”)
MERGE(@“SEL$2” > “SEL$1”)
OUTER_TO_INNER(@“SEL$CFEA49FE”)
OPTIMIZER_FEATURES_ENABLE(‘4.3.4.0’)
END_OUTLINE_DATA
*/
Optimization Info:

da_store:
table_rows:17833
physical_range_rows:978
logical_range_rows:978
index_back_rows:978
output_rows:978
table_dop:1
dop_method:Table DOP
avaiable_index_name:[idx_da_store, idx_da_store_01, DA_STORE]
pruned_index_name:[idx_da_store]
unstable_index_name:[DA_STORE]
stats info:[version=1734489554279688, is_locked=0, is_expired=0]
dynamic sampling level:0
estimation method:[OPTIMIZER STATISTICS, STORAGE]
dsi:
table_rows:25929589
physical_range_rows:159999999
logical_range_rows:159999999
index_back_rows:0
output_rows:159999999
table_dop:1
dop_method:Table DOP
avaiable_index_name:[da_sale_imp2]
stats info:[version=1734487610882355, is_locked=0, is_expired=0]
dynamic sampling level:0
estimation method:[OPTIMIZER STATISTICS, STORAGE]
Plan Type:
LOCAL
Note:
Degree of Parallelisim is 1 because of table property

1 个赞

目前查询仍然是20S以上,obdiag目前未部署,mac上可以本地部署吗,有没有推荐的文档

1 个赞

表结构基本是这种,目前尝试了三种,未分区,分区模式,列存模式,关联查询反馈基本是一致的

1 个赞

你的ob集群搭建的什么环境上?结构什么样的
参数查询一下
show parameters where name in (‘memory_limit’,‘memory_limit_percentage’,‘system_memory’,‘log_disk_size’,‘log_disk_percentage’,‘datafile_size’,‘datafile_disk_percentage’);
集群资源配置情况
select zone,concat(SVR_IP,’:’,SVR_PORT) observer,
cpu_capacity_max cpu_total,cpu_assigned_max cpu_assigned,
cpu_capacity-cpu_assigned_max as cpu_free,
round(memory_limit/1024/1024/1024,2) as memory_total,
round((memory_limit-mem_capacity)/1024/1024/1024,2) as system_memory,
round(mem_assigned/1024/1024/1024,2) as mem_assigned,
round((mem_capacity-mem_assigned)/1024/1024/1024,2) as memory_free,
round(log_disk_capacity/1024/1024/1024,2) as log_disk_capacity,
round(log_disk_assigned/1024/1024/1024,2) as log_disk_assigned,
round((log_disk_capacity-log_disk_assigned)/1024/1024/1024,2) as log_disk_free,
round((data_disk_capacity/1024/1024/1024),2) as data_disk,
round((data_disk_in_use/1024/1024/1024),2) as data_disk_used,
round((data_disk_capacity-data_disk_in_use)/1024/1024/1024,2) as data_disk_free
from oceanbase.gv$ob_servers;
租户查询
select * from oceanbase.dba_ob_tenants;

1 个赞

1 个赞

sql_result-2.txt (1.1 MB)
这个可以帮忙分析下吗