【问题标题】:Define index for sparse column为稀疏列定义索引
【发布时间】:2017-05-06 14:31:45
【问题描述】:

我有一个包含“A”和“B”列的表格。

'A' 是一个具有 90% 'null' 和 10% 不同值的列,并且大多数时候我查询具有这些不同值中的一个或两个的记录。

而“B”是一个具有 90% value='1' 和 10% 不同值的列,并且大多数时候我查询具有这些不同值中的一个或两个的记录。

在这个表中,我们大部分时间都有 DML 事务。

现在,我不知道在这些列上定义索引好不好?如果是,是哪种类型的索引?

【问题讨论】:

  • 这些 DML 是由几个会话完成还是一个会话完成?
  • Oracle 不会索引所有列都为空的值。所以x(a) 上的索引将只包含非空值。
  • @WernfriedDomscheit 由多个会话完成
  • @a_horse_with_no_name 这意味着空值的存在不会对oracle造成任何影响?

标签: oracle performance indexing sparse-matrix


【解决方案1】:

原则上位图索引在这种情况下是最好的。但是,由于多用户环境,它们不适合 - 表锁会显着降低应用程序的速度,甚至可能会出现死锁。

也许您可以通过智能分区和使用Partial Indexes(Oracle 12c 中的新功能)来优化您的应用程序

下面的 CREATE TABLE 语句应该是等效的。

CREATE TABLE YOUR_TABLE (a INTEGER, b INTEGER, ... more COLUMNS)
PARTITION BY LIST (a) SUBPARTITION BY LIST (b) (
    PARTITION part_a_NULL VALUES (NULL) (
        SUBPARTITION part_a_NULL_b_1 VALUES (1) INDEXING OFF,
        SUBPARTITION part_a_NULL_b_other VALUES (DEFAULT) INDEXING ON
    ),
    PARTITION part_a_others VALUES (DEFAULT) (
        SUBPARTITION part_a_others_b_1 VALUES (1) INDEXING OFF,
        SUBPARTITION part_a_others_b_other VALUES (DEFAULT) INDEXING ON
    )   
);

CREATE TABLE YOUR_TABLE (a INTEGER, b INTEGER, ... more COLUMNS)
PARTITION BY LIST (a) SUBPARTITION BY LIST (b) 
    SUBPARTITION TEMPLATE (
        SUBPARTITION b_1 VALUES (1) INDEXING OFF,
        SUBPARTITION b_other VALUES (DEFAULT) INDEXING ON
    )
(
    PARTITION part_a_NULL VALUES (NULL),
    PARTITION part_a_others VALUES (DEFAULT)
);

CREATE INDEX IND_A ON YOUR_TABLE (A) LOCAL INDEXING PARTIAL;
CREATE INDEX IND_B ON YOUR_TABLE (B) LOCAL INDEXING PARTIAL;

这样,您的索引将只占用整个表空间的 10%。如果您的 WHERE 条件是 WHERE A IS NULLWHERE B = 1,那么 Oracle 优化器无论如何都会跳过此类索引。

使用此查询进行验证

SELECT table_name, partition_name, subpartition_name, indexing
FROM USER_TAB_SUBPARTITIONS
WHERE table_name = 'YOUR_TABLE';

如果 INDEXING 用于所需的子分区。

更新

我只是看到实际上这是一种矫枉过正的做法,因为 A 列上的 NULL 值无论如何都不会创建任何索引条目。所以可以简化为

CREATE TABLE YOUR_TABLE (a INTEGER, b INTEGER, ... more COLUMNS)
PARTITION BY LIST (b) (
    PARTITION part_b_1 VALUES (1) INDEXING OFF,
    PARTITION part_b_other VALUES (DEFAULT) INDEXING ON
);

【讨论】:

  • 非常感谢。您的解决方案和使用过滤索引(按功能索引模拟)有什么区别?
  • 这个解决方案只在有意义的地方创建索引条目(即具有低基数,分别是许多不同的值)并保持它们的小而高效。使用像 nvl(A,'XX') 这样的基于函数的索引,你可以强制为每条记录创建索引条目——不管它是否有意义。
  • in 表示对于 A 列,如果我创建索引,虽然 oracle 没有为 null 使用索引,但使用索引的成本与所有记录都有价值一样吗?
  • 这不仅是“oracle 不使用空索引” - 索引中甚至不存在空值。假设您有一列并且所有值都是 NULL。此类列的索引大小为“0 字节”。
  • 所以,如果我在 A 列(90% null)上创建简单索引,并且在我的查询中我有 where A="1"(任何值),并且永远不想有类似 where A 的东西IS NULL 或其中 A IS NOT NULL ,创建此索引有帮助吗?
【解决方案2】:

例如,如果您在 A、B 上有索引 a_b_idx(按此顺序):

  • a) select ... from ... where A = ... 将使用索引

  • b) select ... from ... where B = ... 不会使用索引

另一方面,如果 B、A 上有索引 b_a_idx:

  • a) select ... from ... where A = ... 不会使用索引

  • b) select ... from ... where B = ... 将使用索引

如果 Oracle 不对第一列进行过滤,则它不能在索引中使用第二列,因为在通常情况下,索引是树状结构:column1->column2->column3->etc.

如果您执行类似 a) 的查询,您需要仅在 A 列或 A、B 列上建立索引。

如果您执行 b) 之类的查询,您需要仅在 B 列或 B、A 列上建立索引。

Oracle 不会在索引中存储全空值,但如果 B 包含非空值,它可以为 A 存储空值。

有时将整个表读入内存并忽略索引会更有成效。如果可能的结果集很大并且适用于所有记录,优化器就可以做到这一点,因为索引到记录的转换比简单的记录读取成本更高。

对于没有统计信息的表,有时它也会错误地发生,因此您需要具有 alter table ... 计算统计信息 的作业或可以在没有作业的情况下计算此类统计信息的 oracle 11+。

大多数时候,另一个索引对于查询来说是好事,但对于更新/磁盘来说是坏事。每个索引都占用磁盘空间,每次更新记录都会更新每个索引。因此,对于频繁更新的表来说,拥有很多索引并不好,但对于频繁查询的表,最好拥有涵盖所有常见情况的索引。

对于大多数平面查询(没有连接/子查询/层次结构)仅使用 1 个索引,因此为每列设置索引通常只是浪费磁盘空间。您需要多列索引来优化 where A=... 和 B=...

至于索引类型,您可能需要简单的非唯一索引。

【讨论】:

  • 非常感谢。但是你的回答和我的问题有关吗?!
  • @AlexanderAnikin - 甲骨文已经有相当长一段时间的索引跳过扫描,所以答案的最初部分不再是这种情况。看看docs.oracle.com/cd/B19306_01/server.102/b14211/…
【解决方案3】:
  • A 列

假设您创建了一个名为 _columnA_index_ 的索引。通常,RDBMS 中的索引不会包含 NULL 值,这意味着 _columnA_index_ 中没有索引条目指向具有 NULL 值的记录。因此,以下查询

Q1: select * from MyTable where A is null;

将改为进行表扫描(或 DBMS 选择在另一列上使用另一个索引,如果有的话)

但是,由于有 10% 的记录具有“不同的值”,例如,_columnA_index_ 当然会有助于查询。

Q2: select * from MyTable where A = '123';

在上面的示例中,如果查询返回

  • B 列

同样,B 上的索引也无济于事

Q3: select * from MyTable where B = 1;

但它会帮助不同的值

Q4: select * from MyTable where B = '456';
  • 空值

到目前为止,我回答说任何索引都对 NULL 值没有帮助。所以,如果你大部分时间都需要查询Q1,我建议如下思路

  • 确保您的 DBMS 版本确实支持将 NULL 值包含在索引中。例如,Oracle 11g 可以,但不是之前的版本。

  • 计划再次使用 Oracle 创建基于函数的索引 here。但你至少可以接受这个想法。

  • 重新设计应用程序的逻辑/您需要对 Null 值进行查询。我更喜欢这种方法。

【讨论】:

  • create index ... on ... ( nvl(A,'XX') ); 这样的基于函数的索引将是无用的(即浪费空间),因为Oracle 优化器无论如何都会跳过这样的索引。
  • @WernfriedDomscheit,您说得非常正确,但并不完全正确。您可以强制优化器将基于函数的索引包含在查询计划中,或者在查询中明确使用基于函数的索引。
  • 这样的话,索引确实很有帮助。
  • 是的,但是扫描索引以查找占所有值的 90% 的值是浪费时间。
  • > 重新设计应用程序的逻辑/您需要对 Null 值进行查询。我更喜欢这种方法。
猜你喜欢
  • 2019-02-27
  • 1970-01-01
  • 2018-01-22
  • 2012-01-15
  • 2014-05-12
  • 2021-01-28
  • 2013-06-12
  • 1970-01-01
  • 2018-06-12
相关资源
最近更新 更多