Oracle SQL:选择超大表的子集的最佳方法是啥

Posted

技术标签:

【中文标题】Oracle SQL:选择超大表的子集的最佳方法是啥【英文标题】:Oracle SQL: What is the best way to select a subset of a very large tableOracle SQL:选择超大表的子集的最佳方法是什么 【发布时间】:2018-02-02 07:57:54 【问题描述】:

我已经在这些论坛上漫游了几年,我总是发现我的问题已经被问到了,并且已经给出了合适的答案。

我现在有一个非常通用(也许很简单)的问题,但我还没有找到一个线程问同样的问题。

情况:

我有一个支付表,每天有 10-50M 条记录,有 10 天的历史记录和数百列。大约 10-20 列被索引。其中一个索引是 batch_id。 我有一个批处理表,其中的记录和列要少得多,比如每天 10k 和 30 列。

如果我想选择一个特定发件人的所有付款,我可以这样做:

Select * from payments p 
where p.sender_id = 'SenderA'

这会运行一段时间,即使 sender_id 也已编入索引。所以我想,最好先选择批次,然后用 batch_id 进入支付表:

select * from payments p
where p.batch_id in 
(select b.batch_id from batches where b.sender_id = 'SenderA')
--and p.sender_id = 'SenderA'

现在,我的问题是:

在第二个脚本中,我是否应该在支付表的 where 子句中取消对 Sender_id 的注释?过滤 sender_id 两次感觉不是很有效,即使它在不同的表中。 如果我将其设为内部联接而不是嵌套查询会更好吗? 如果我将其设为公用表表达式而不是嵌套查询或内连接会更好吗?

我想这可以归结为一个问题:查询此问题的最佳方法是什么?

【问题讨论】:

docs.oracle.com/cd/B10500_01/server.920/a96533/ex_plan.htm 查询返回多少行? 对问题的简短回答: 1) 如果两列(batch_id 和 sender_id)的索引方式相同,则第一个简单查询更有效。 2) 视情况而定。在大多数情况下,Oracle 会创建相同的解释计划,所以没关系。 3)与第二个答案相同。一些可能会提高性能的事情:保持表和索引统计信息是最新的对于这种查询,单列索引将是最有效的 谢谢。返回的行数通常为 4M 左右。 两张表是否分区?这将有很大帮助...此外,您可以尝试使用with 构造实现两个子集之一(paymentsbatchessender_id)...无论如何,您必须比较执行计划选择查询的形式。 【参考方案1】:

在最坏的情况下,两个查询应该同时运行,而在最好的情况下,我希望第一个查询运行得更快。如果运行速度较慢,则其他地方存在问题。第二个查询中不需要附加条件。

第一个查询将检索单个值的索引条目,因此与第二个查询相比,它访问的块更少,第二个查询必须查找多个批次的索引条目(以及执行子查询,但这可能并不重要)。

但是,Oracle 一如既往的危险在于,决定优化器选择哪个查询计划的因素有很多。我会立即验证您的索引列上的统计信息是否是最新的。如果不是,这可能是您的问题,您无需进一步阅读。

下一步是获取查询执行计划。我的猜测是,这会告诉您您的查询正在运行全表扫描。

Oracle 是否选择对这样的查询执行全表扫描取决于返回的行数以及 Oracle 是否认为使用索引或简单地读取整个表更有效。两者之间翻转的阈值不是一个固定的数字:它取决于很多东西,其中之一是一个名为DB_FILE_MULTIBLOCK_READ_COUNT的参数。

这是由 Orale 设置的,理论上它应该被配置为索引和全表扫描查询之间的转换应该是平滑的。换句话说,在您的查询返回足够多的行以使全表扫描更有效的过渡点,索引扫描和表扫描的时间应该大致相同。

不幸的是,我看到过这样的系统,Oracle 转而进行全表扫描的速度太快了,一旦行数超过某个阈值,就会导致查询时间过长。

正如我之前所说,首先检查您的统计数据。如果这不起作用,请获取 QEP 并开始调整您的 Oracle 实例。

Tuning Oracle 是一个非常复杂的主题,这里无法完整回答,所以我不得不推荐链接。这是有关参数的有用页面:减少它可能会有所帮助:Why Change the Oracle DB_FILE_MULTIBLOCK_READ_COUNT。

除此之外,一般的 Oracle 性能调优指南在这里:(Oracle) Configuring a Database for Performance。

如果您仍有问题,您需要进一步调查,然后提出更具体的问题。

编辑: 根据您所说的查询在表中返回 10M-50M 中的 4M 行的评论。如果它是 10M 中的 4M,那么索引就没有任何用处。即使 50M 中有 4M,仍然可以肯定全表扫描将是最有效的方法。

你说你有很多列,所以这个 4M 行获取可能会返回大量数据。

您或许可以考虑拆分一些不需要的列并将它们放入子表中。特别是,如果您的列包含大量数据(例如,一些文本 cmets 或其他),最好将它们保留在主表之外。

记住 - 小就是快,不仅在行数方面,而且在每行的大小方面。

【讨论】:

谢谢,我认为这个答案解释了问题和下一步要采取的措施。我在从数据库中获取所需信息方面做得越来越好,但我对统计和执行计划知之甚少;以及如何改进这些。我总是被告知我应该问 DBA,但这种人很少见! 回复您的编辑:确实,表中有很多数据。在我的选择中,我只询问发送者、接收者和状态。我还计算了记录并总结了金额。我想全表扫描是不可避免的。话虽如此,我仍然会看看统计数据。可悲的是,我认为我没有足够的拉力将一些列移动到子表中。【参考方案2】:
    SQL 是一种声明性语言。这意味着,你指定你喜欢什么而不是如何。 检查您的主要索引和“正常”索引...

【讨论】:

这个问题值得更广泛的回答,因为 OP 显然存在真正的性能问题。 问题是:“查询这个的最好方法是什么?”答案是: 1. SQL 是一种声明性语言。这意味着,你指定你喜欢什么而不是如何。

以上是关于Oracle SQL:选择超大表的子集的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

Oracle中 多表连接到底有哪几种方式

使用 Sqoop 并行导入 Oracle 表的最佳方法是啥?

创建返回具有复杂 SQL 的表的 Oracle 视图或过程,是不是可能以及如何?

Oracle PL/SQL - 根据条件对不同列进行选择、分组、排序、where-clause 的最佳方法?

寻找在 S3 存储桶中复制 oracle 表的最佳方法

Oracle 表三种连接方式(sql优化)