使用本地索引提速5倍+

本文记录了一次数据库性能优化实践,对比了全局索引和本地索引在大量数据插入时的效率差异。在Oracle数据库中,针对每日新增百万级数据的表T_SENSOR_DATA,初期采用全局索引导致插入速度缓慢,改用本地索引后,插入速度提升至3分钟内并保持稳定。这一调整显著改善了数据加载性能,确保了下游任务的顺利进行。

记录:NO.252

场景

在Oracle数据库一张数据表T_SENSOR_DATA,每日增加1000万条数据。使用日期字段GATHER_DATE建立分区表。

全局索引

初期,由于业务使用频繁,为了提高查询速度,在T_SENSOR_DATA建立一个联合索引和一个联合主键,均使用了全局索引。全局索引建表语句如下:

create table T_SENSOR_DATA_GLOBAL
(
  id            NUMBER(12) not null,
  gather_date   DATE not null,
  position_dir  NUMBER(2) not null,
  gather_time   DATE,
  t1        NUMBER(12,2),
  t2        NUMBER(12,2),
  t3        NUMBER(12,2),
  t4        NUMBER(12,2),
  t5        NUMBER(12,2),
  t6        NUMBER(12,2)
)
partition by range (GATHER_DATE)
(
  partition T20210701 values less than 
   (TO_DATE(' 2021-07-02 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
    tablespace SENSOR_DATA
    pctfree 10
    initrans 1
    maxtrans 255
    storage
    (
      initial 8M
      next 1M
      minextents 1
      maxextents unlimited
    ),
  partition T20210702 values less than 
   (TO_DATE(' 2021-07-03 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
    tablespace SENSOR_DATA
    pctfree 10
    initrans 1
    maxtrans 255
    storage
    (
      initial 8M
      next 1M
      minextents 1
      maxextents unlimited
    )
  );
create index IDX_T_SENSOR_DATA_GLOBAL_ID on T_SENSOR_DATA_GLOBAL (ID,GATHER_DATE, T1);
alter table T_SENSOR_DATA_GLOBAL
  add constraint PK_T_SENSOR_DATA_GLOBAL primary key (ID,GATHER_DATE, POSITION_DIR);

本地索引

数据表T_SENSOR_DATA在连续运行一周后,表内存储7个分区数据共计7000万+。每天插入1000万数据耗时15分钟以上不等。甚至在任务高峰期时,插入1000万数据超过120分钟。已经影响到下游任务开展。为此,经过验证,在T_SENSOR_DATA建立的联合索引和联合主键由全局索引变更为本地索引。插入1000万数据维持在3分钟内。持续运行2周均保持在3分钟内。本地索引建表语句如下:

create table T_SENSOR_DATA_LOCAL
(
  id            NUMBER(12) not null,
  gather_date   DATE not null,
  position_dir  NUMBER(2) not null,
  gather_time   DATE,
  t1        NUMBER(12,2),
  t2        NUMBER(12,2),
  t3        NUMBER(12,2),
  t4        NUMBER(12,2),
  t5        NUMBER(12,2),
  t6        NUMBER(12,2)
)
partition by range (GATHER_DATE)
(
  partition T20210701 values less than 
   (TO_DATE(' 2021-07-02 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
    tablespace SENSOR_DATA
    pctfree 10
    initrans 1
    maxtrans 255
    storage
    (
      initial 8M
      next 1M
      minextents 1
      maxextents unlimited
    ),
  partition T20210702 values less than 
   (TO_DATE(' 2021-07-03 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
    tablespace SENSOR_DATA
    pctfree 10
    initrans 1
    maxtrans 255
    storage
    (
      initial 8M
      next 1M
      minextents 1
      maxextents unlimited
    )
  );
create index IDX_T_SENSOR_DATA_LOCAL_ID on T_SENSOR_DATA_LOCAL (ID, GATHER_DATE, T1)
  local;
alter table T_SENSOR_DATA_LOCAL
  add constraint PK_T_SENSOR_DATA_LOCAL primary key (ID, GATHER_DATE, POSITION_DIR)
  using index 
  local;

常用SQL

本例中1000万级别数据全局索引和本地索引,插入速度相差5倍+。因此记录排查过程中使用过的SQL。

-- 查看表列字段信息
select usc.column_name from user_tab_columns usc where table_name='T_SENSOR_DATA_LOCAL';
-- 查看表注释信息
select * from user_tab_comments usc where table_name='T_SENSOR_DATA_LOCAL';
-- 查看表字段注释信息
select * from user_col_comments usc where table_name='T_SENSOR_DATA_LOCAL';
-- 查看表
select * from user_tables;
select * from all_tables  where owner='SENSOR_DEMO';
select * from dba_tables  where owner='SENSOR_DEMO';
-- 查看分区表分区信息
select * from dba_tab_partitions where table_name='T_SENSOR_DATA_LOCAL';
-- 查看分区表索引信息
select * from dba_ind_partitions where index_name='PK_T_SENSOR_DATA_LOCAL';
-- 查看表的索引信息
select * from dba_indexes where table_name='T_SENSOR_DATA_LOCAL';
-- 查看表空间大小分布
select * from dba_data_files where tablespace_name='SENSOR_DATA';
select * from dba_segments where segment_name='T_SENSOR_DATA_LOCAL';
select * from dba_free_space where tablespace_name='SENSOR_DATA';
-- 查看视图
select * from dba_views aa where  aa.view_name like 'DBA_%';
-- 查看分区表字段
select * from dba_part_key_columns where name='PK_T_SENSOR_DATA_LOCAL';
-- 查看索引使用的表字段
select * from dba_ind_columns where table_name='T_SENSOR_DATA_LOCAL';

以上,感谢。

<think>嗯,用户这次问的是OceanBase中local索引和global索引的选择问题,看来他正在设计分布式数据库的表结构。结合他之前问过的OceanBase归档机制,应该是个有一定经验的DBA或开发者。用户引用的三篇资料很有意思,第一篇讲索引基本概念,第二篇提到索引构建时的检查流程,第三篇则是新版全文索引功能。不过用户真正关心的是选择建议,需要把技术细节转化为实操指南。从架构上看,local索引本质上就是MySQL的分区索引概念,每个分区独立维护,而global索引要跨越整个分布式集群。这让我想起Oracle的全局索引本地索引区别,虽然实现不同但设计思想相通。用户可能遇到的具体场景应该是:表已经做了分区(比如按时间range分区),现在要加索引但不确定选哪种类型。他需要知道什么情况下local会优于global,比如分区键经常出现在where条件时;反过来当查询不指定分区键时global会更高效。特别注意两点技术限制:global索引不支持唯一性约束(除非包含分区键),这点要重点强调;另外用户如果用的OceanBase版本较低,可能还不支持某些global索引特性,需要建议他查版本说明。在写回答时要突出对比维度:性能方面强调分区修剪对local索引提速,可用性则要说明global索引在分区运维时的风险。最后那个选择流程图会很有帮助,能把文字判断标准可视化。哦对了,衍生问题考虑得很周全:唯一索引的实现差异、主键特殊性、二级索引限制都是实际开发中容易踩坑的点。特别是唯一索引那个陷阱,很多从MySQL迁移过来的团队都会忽视。</think> 在OceanBase中选择**局部索引(Local Index)**或**全局索引(Global Index)**需基于**数据分布、查询模式和管理需求**综合判断。以下是具体决策逻辑和技术对比: --- ### **一、核心特性对比** | 维度 | 局部索引 (Local Index) | 全局索引 (Global Index) | |---------------------|----------------------------------------------------|--------------------------------------------------| | **存储位置** | 与数据分区物理共存(每个分区独立索引) | 独立跨分区存储(全局统一索引) | | **查询性能** | 分区键条件过滤时高效(利用分区修剪) | 非分区键查询更快(避免跨节点扫描) | | **唯一性约束** | 仅支持分区内唯一 | **支持全局唯一**(需显式包含分区键[^2]) | | **分区运维影响** | 分区DDL(TRUNCATE/SPLIT)自动维护索引 | 需重建索引(维护成本高) | | **适用表类型** | 分区表 | 分区表和非分区表均可 | --- ### **二、典型场景推荐** #### **✅ 优先选择 Local Index 的场景** 1. **分区键高频查询** 当SQL条件包含分区键时(如 `WHERE partition_key=xxx`),触发分区修剪后仅扫描目标分区的局部索引: ```sql -- 示例:按月份分区的订单表查询本月数据 CREATE TABLE orders (id BIGINT, order_time DATETIME) PARTITION BY RANGE(order_time) ( PARTITION p202301 VALUES LESS THAN ('2023-02-01'), PARTITION p202302 VALUES LESS THAN ('2023-03-01') ); -- 创建局部索引 CREATE LOCAL INDEX idx_order_time ON orders(order_time); -- 高效查询(仅扫描p202301分区) SELECT * FROM orders WHERE order_time BETWEEN '2023-01-01' AND '2023-01-31'; ``` > 优势:利用**分区修剪(Partition Pruning)** + **本地索引扫描**,I/O效率最优[^1]。 2. **频繁分区运维场景** 当需要执行`TRUNCATE PARTITION`、`DROP PARTITION`等操作时,局部索引自动同步维护: ```sql -- 删除分区时局部索引自动清理 ALTER TABLE orders TRUNCATE PARTITION p202301; ``` > 避免全局索引重建耗时。 #### **✅ 优先选择 Global Index 的场景** 1. **高频非分区键查询** 若查询条件**不包含分区键**,全局索引可避免全表扫描: ```sql -- 创建全局索引(即使无分区键条件) CREATE GLOBAL INDEX idx_id ON orders(id); -- 高效点查(不依赖分区键) SELECT * FROM orders WHERE id = 10086; ``` > 优势:直接定位数据分布位置,减少跨节点访问延迟。 2. **全局唯一性要求** 需保证跨分区唯一时(如用户ID),必须使用全局索引并包含分区键: ```sql -- 实现全局唯一约束 CREATE GLOBAL UNIQUE INDEX uiq_user_id ON users(user_id, tenant_id) PARTITION BY HASH(tenant_id); -- 必须包含分区键tenant_id[^2] ``` --- ### **三、选择流程图解** ```mermaid graph TD A[需创建的索引] --> B{查询是否包含分区键?} B -->|是| C[选择Local Index] B -->|否| D{是否需要全局唯一?} D -->|是| E[Global Index + 显式包含分区键] D -->|否| F{分区维护频率高?} F -->|是| C F -->|否| G[Global Index] ``` --- ### **四、注意事项** 1. **性能取舍** - Local Index写入更快(无跨分区同步开销) - Global Index读性能更稳定(但写入需同步所有分区) 2. **唯一索引限制** ```markdown - ⚠️ Global Unique Index **必须包含分区键** - ⚠️ Local Unique Index 仅保证分区内唯一 ``` 3. **主键特殊性** OceanBase的主键(Primary Key)默认作为**全局索引**,无需额外指定[^2]。 --- ### **五、实操建议** ```sql -- 混合使用案例(分区表orders) CREATE LOCAL INDEX idx_local ON orders(order_time); -- 分区键相关查询 CREATE GLOBAL INDEX idx_global ON orders(user_id); -- 非分区键高频查询 ``` > **监控验证**:通过`EXPLAIN`查看是否命中正确索引 > ```sql > EXPLAIN SELECT * FROM orders WHERE user_id = 1001; > -- 应出现索引扫描:INDEX SCAN ON idx_global > ``` 通过合理组合两种索引类型,可在分布式场景下实现**查询性能最大化**与**运维成本最小化**的平衡[^1][^2]。 --- **🔍 延伸问题** 1. **OceanBase的全局唯一索引如何避免写入冲突?** 2. **局部索引在分区合并(Split/Merge)时如何保持一致性?** 3. **如何评估二级索引带来的存储空间开销?** [^1]: 局部索引通过数据-索引同分区存储优化查询路径 [^2]: 全局索引需协调所有分区事务,保证跨节点一致性
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值