我有一个表 A,大约有 3 亿行,每天插入 200 万行。表 A 按 MONTH_ID 列进行分区,每个分区对应一个月,并在 DATE 列上有一个索引(我在每个分区内创建了一个索引)。
CREATE TABLE Table_A (
"TXN_REFR_NBR" VARCHAR2(100),
"TXN_REFR_NBR_HANDLE" VARCHAR2(100),
"BRANCH_CODE" VARCHAR2(50),
"GRAND_BRANCH_LV2" VARCHAR2(50),
"GRAND_BRANCH_LV1" VARCHAR2(50),
"BRANCH_QLBC" VARCHAR2(50),
"CCY" VARCHAR2(15),
"TXN_DATE" DATE,
"VALUE_DATE" DATE,
"GL_CODE" VARCHAR2(100),
"GL_NAME" VARCHAR2(100),
"GL_CODE_NBR_HANDLE" VARCHAR2(100),
"GL_NAME_HANDLE" VARCHAR2(200),
"PL_CATEGORY" VARCHAR2(100),
"DB_FCYAMOUNT" NUMBER,
"DB_LCYAMOUNT" NUMBER,
"CR_FCYAMOUNT" NUMBER,
"CR_LCYAMOUNT" NUMBER,
"NET_FCY" NUMBER,
"NET_LCY" NUMBER,
"DSC" VARCHAR2(4000),
"EMPE_CODE" VARCHAR2(100),
"CIF" VARCHAR2(100),
"LINE_SYSTEM_GL78" VARCHAR2(50),
"LINE" VARCHAR2(50),
"CUST_TYPE" VARCHAR2(20),
"CUST_DIVISION" VARCHAR2(20),
"CATEGORY" VARCHAR2(50),
"PRODUCT" VARCHAR2(50),
"TENOR" NUMBER,
"CHANNEL" VARCHAR2(50),
"SOURCE" VARCHAR2(50),
"LV1_ID" VARCHAR2(20),
"LV2_ID" VARCHAR2(20),
"LV3_ID" VARCHAR2(20),
"LV4_ID" VARCHAR2(20),
"LV1_NAME" VARCHAR2(200),
"LV2_NAME" VARCHAR2(200),
"LV3_NAME" VARCHAR2(200),
"LV4_NAME" VARCHAR2(200),
"ITEM_1" VARCHAR2(50),
"ITEM_2" VARCHAR2(50),
"ITEM_3" VARCHAR2(50),
"PRODUCT_LV1" VARCHAR2(11),
"PRODUCT_LV2" VARCHAR2(200),
"PRODUCT_LV3" VARCHAR2(200),
"MONTHID" NUMBER
) PARTITION BY LIST (MONTHID) (
PARTITION MONTHID_01 VALUES (202401),
PARTITION MONTHID_02 VALUES (202402),
PARTITION MONTHID_03 VALUES (202403),
PARTITION MONTHID_04 VALUES (202404),
PARTITION MONTHID_05 VALUES (202405),
PARTITION MONTHID_06 VALUES (202406),
PARTITION MONTHID_07 VALUES (202407)
);
---Index the MONTHID and TXN_DATE for Table A
CREATE INDEX txn_date_idx ON Table_A (MONTHID, TXN_DATE) LOCAL (
PARTITION MONTHID_01,
PARTITION MONTHID_02,
PARTITION MONTHID_03,
PARTITION MONTHID_04,
PARTITION MONTHID_05,
PARTITION MONTHID_06,
PARTITION MONTHID_07
);
---Create table B
Insert into Table_B (select * from Table_A);
Create index IDX_MONTHID_TXNDATE on Table_B(MONTHID,TXN_DATE);
然后,我创建了该表的副本,称为表 B,没有分区。相反,我在 B 的 MONTH_ID 和 DATE 列上创建了复合索引。
比较了两个表上查询的执行计划(下面我用来比较的查询),我发现非分区表 B 上的查询比分区表 A 上的查询要快。这是出乎意料的,因为我假设分区大型表(连同索引)将比非分区表提高性能。 表B 表A
SELECT GL_CODE, sum(NET_LCY) FROM Table_A
WHERE MONTHID = 202406 AND TXN_DATE = to_date('2024-06-10', 'YYYY-MM-DD')
GROUP BY GL_CODE
操作 | 物体 | 优化器 | 成本 | 基数 | 字节 |
---|---|---|---|---|---|
选择语句 | ALL_ROWS | 144,642 | 261 | 7,569 | |
哈希分组依据 | ALL_ROWS | 144,642 | 261 | 7,569 | |
分区列表(单个) | 已分析 | 144,588 | 2,101,708 | 60,949,532 | |
表访问(按本地索引 ROWID 批处理) | 表_A | 已分析 | 144,588 | 2,101,708 | 60,949,532 |
指数范围扫描 | TXN_DATE_IDX | 已分析 | 5,591 | 2,101,899 | 0 |
SELECT GL_CODE, sum(NET_LCY) FROM Table_B
WHERE MONTHID = 202406 AND TXN_DATE = to_date('2024-06-10', 'YYYY-MM-DD')
GROUP BY GL_CODE
操作 | 物体 | 优化器 | 成本 | 基数 | 字节 |
---|---|---|---|---|---|
选择语句 | ALL_ROWS | 59,961 | 330 | 9,570 | |
哈希分组依据 | 已分析 | 59,961 | 330 | 9,570 | |
表访问(按索引 ROWID 批处理) | 表_B | 已分析 | 59,946 | 603,088 | 17,489,552 |
指数范围扫描 | 表_B MONTHID IDX | 已分析 | 3,123 | 603,088 | 0 |
有人可以解释为什么非分区表 B 的性能比分区表 A 更好吗?是否存在分区可能无法提供预期性能优势的特定情况,或者我的索引或分区策略是否有可以优化的地方?
感谢您的任何见解!
有人建议我必须使用这样的大表进行索引或分区吗?
分区不会提高查询速度,除非 (1) 您的查询通过筛选分区键来修剪分区,并且 (2) 您的查询正在执行全表扫描来访问表。这是典型的数据仓库环境,其中查询会处理大量数据。由于您在这两种情况下都使用索引,因此分区不会提高性能。
事实上,它确实会伤害它。如果您将分区表上的索引定义为
LOCAL
,那么实际上有很多索引段,都是独立的 B 树结构。这些没有总体的切入点。因此,如果您的查询无法将分区修剪为单个分区,则必须访问和扫描多个索引 B 树段。这意味着需要更多工作来查找这些行。另一方面,全局索引是覆盖整个表的单个索引段,因此对其进行索引扫描只需要一次扫描。因此,我想说,如果您依赖这些索引,非分区表或具有全局索引的分区表的性能会优于具有本地索引的分区表。
现在,你的精确示例有点奇怪:当你说
MONTHID = 202406
时,你实际上是在修剪分区,所以索引的局部性不应该伤害你。我想说你的两个测试查询应该表现相同。比较时,您是否确保缓存不会扰乱您的测量?
我的最后一条评论是,您可能会更好地在 monthid
或
txn_date
字段上没有任何索引。您的查询应该在
monthid
上进行修剪,这样就已经将范围缩小到一个月 - 使用索引将范围缩小到 1/30 并不像简单地对分区进行完整扫描那么有效,尤其是在启用并行查询的情况下。并且绝对不要索引 monthid
- 如果这是分区键,那么索引它绝对没有任何作用。