【问题标题】:How to optimize oracle query for repeated full table sorts?如何针对重复的全表排序优化 oracle 查询?
【发布时间】:2009-07-07 23:38:20
【问题描述】:

我有一个数据库基础架构,我们定期(至少每天一次)将表的全部内容从源数据库复制到大约 20 个目标数据库。由于使用了复制代码(我们必须使用常规的 oracle 查询,无法控制或直接访问源数据库)——这导致源表有 20 种全表排序。

有没有办法在查询中对此进行优化?我正在寻找基本上可以告诉oracle“我将重复对整个表进行排序”的东西? MySQL 有一个带有 myisamchk 的选项,您可以告诉它对表进行排序并保持排序顺序,但显然由于多种原因这不适用于此处。

目前,还涉及一些中间表(从 A 同步到 B,然后从 B 同步到 C。)我们确实可以控制中间表,所以如果那里有调整选项,那也会很有用。

一般来说,查询几乎都是非常简单的形式:

select a, b, c, d, e, ... z from tbl1 order by a, b, c, d, e, ... z;

我知道流,但如上所述,主要源表不在我们的控制范围内,因此我们将无法在那里使用流。 (此外,这些源表每天都会从快照中完全重建,因此流无论如何都不会真正起作用。)

【问题讨论】:

  • 为了进一步澄清 - 复制代码在源数据库和目标数据库之间进行并行行级比较,并执行一整套单独的行更新 (I/U/D) 以目标表使其与源同步。同样的全表选择和排序也会针对目标表运行。
  • 如果目标是将数据从一个表/数据库复制到另一个,我不明白你为什么要排序,你能解释一下吗?

标签: database performance oracle replication


【解决方案1】:

您可以查看multi-table INSERT 功能。它应该执行一次 FULL SCAN 并将插入到多个表中。考虑(10gR2):

SQL> CREATE TABLE t1 (ID NUMBER);

Table created
SQL> CREATE TABLE t2 (ID NUMBER);

Table created

SQL> INSERT ALL
  2     INTO t1 VALUES (d_id)
  3     INTO t2 VALUES (d_id)
  4  /* your select goes here */
  5  SELECT ROWNUM d_id FROM dual d CONNECT BY LEVEL <= 5;

10 rows inserted
SQL> SELECT COUNT(*) FROM t1;

  COUNT(*)
----------
         5
SQL> SELECT COUNT(*) FROM t2;

  COUNT(*)
----------
         5

您必须检查它是否适用于数据库链接。

【讨论】:

  • +1 好建议。我已经在数据库链接 (10.2.0.4) 上使用此构造实现了生产代码。
  • 不幸的是,这是为了避免编写代码...首先清空表,这要么意味着大量的重做,要么意味着在重新加载期间使用不安全并且可能完全不可用的表。 (基本上,您通过每次都插入所有数据来避免排序。)
【解决方案2】:

一些有助于排序问题的事情是在您正在排序的列上设置索引(如果它们还没有的话,还可以加入表)。您还可以创建已排序的物化视图,Oracle 会将排序结果缓存起来。

【讨论】:

  • 您可以在单个表上执行聚集索引,这会将其按排序顺序排列。它有优点和缺点。 Asktom.oracle.tom 对此有很好的建议。但我认为当您将一堆表连接在一起时,这不会有太大影响。
  • 索引组织表按主键“排序”(或更正确地结构化)。在索引集群中,Oracle 数据库将具有相同集群键值的行存储在一起。它们不一定按排序顺序存储。也就是说,如果集群在部门 id 上,则 DEPT 10 中的所有员工都会在一起,DEPT 20 中的所有员工都会在一起,但在全表扫描中,DEPT 20 可能会在 DEPT 10 之前出现。
  • 这可能适用于中间表,但除了“添加索引”之外,不太可能对源表完成任何操作。 (peoplesoft 安装是主要数据源。)这是一个直接的 1Table:1Table 同步,不涉及连接。
【解决方案3】:

您没有确切说明复制是如何完成的或涉及的数据量(或者您为什么要对数据进行排序)。

如果目标是尽量减少对源数据库的影响,最好的办法可能是提取到中间文件并将文件加载到目标数据库中。排序可以在中间文件(如果是纯文本)上完成,也可以作为导出或导入目标数据库的一部分。

In source database :
create table export_emp_info
 organization external
 ( type oracle_datapump
   default directory DATA_PUMP_DIR
   location ('emp.dmp')
) as select emp_id, emp_name, dept_id from emp order by dept_id
/


Copy file then, import in dest database:
create table import_emp_info
(EMP_ID                    NUMBER(12),
EMP_NAME                   VARCHAR2(100),
DEPT_ID                    NUMBER)
 organization external
 ( type oracle_datapump
   default directory DATA_PUMP_DIR
   location ('emp.dmp')
)
/
insert into emp_info select * from import_emp_info;

如果您不想或不能在源数据库上拥有外部表,则可以使用 emp 表的直接 expdp(如果您对源数据库目录结构的访问权限有限,则可能使用 NETWORK_LINK)和QUERY 进行订购。

【讨论】:

  • 基本上,我从两侧(源和目标)依次拉出行并执行“合并排序样式同步”,依次比较来自源或目标的每一行以了解我是否需要从源插入新行或从目标删除行。它保证我可以在源/目标表的任何状态下运行该进程,并最终使它们与在回滚的情况下开始时相同或相同。我没有考虑过“外部文件+排序”的方法,会考虑的。
【解决方案4】:

您可以将数据从源表 A 加载到中间表 B,然后在 B 和目标表 C 之间进行分区交换。精确复制,不涉及排序。

【讨论】:

    【解决方案5】:

    这种 I/U/D 形式的复制就是 MERGE 命令的用途。需要昂贵的排序合并是非常值得怀疑的,我希望看到散列连接。只要哈希表可以存储在内存中,哈希连接几乎不会比扫描表更昂贵。

    一个方便的优化是根据非键属性存储哈希值,这样您就可以在键列上连接源表和目标表,并比较小的哈希值而不是完整的列集 -更改检测变得容易。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-12
      • 2010-09-13
      • 2017-06-15
      • 1970-01-01
      • 2011-10-28
      • 2018-06-21
      • 2015-01-31
      • 1970-01-01
      相关资源
      最近更新 更多