索引&低选择性色谱柱的替代品

时间:2010-11-15 14:40:16

标签: sql database-design indexing relational-database

在低选择性色谱柱上选择记录的策略有哪些?

一个示例可能是订单表,多年来,您构建了大量已完成的订单,但通常需要选择活动订单。订单可能会经历生命周期,例如放置,库存分配,从仓库中挑选,发送给客户,开具发票和付款。订单可能会被取消,保留等等。大多数记录最终将处于最终状态(例如付费),但您可能经常需要选择分配的订单。在这种情况下,顺序读取会很慢。

关于索引的类似问题
MySQL: low cardinality/selectivity columns = how to index?
Do indexes suck in SQL?
What are indexes and how can I use them to optimize queries in my database?
Defining indexes: Which Columns, and Performance Impact?
和许多其他人相关的减少。

我读过的方法(在stackoverflow和其他地方)包括

  • 使用位图索引
  • 使用部分索引(create index x on t(c2) where c1='a'
  • 使用聚集索引?
  • 不要索引低选择性列,请使用顺序读取
  • 将数据分区(例如,分成几个具有相同架构的表)
  • 使用补充表格(例如active_customers(customer_id)

我目前的DBMS不支持上面列出的前三个选项,其余的似乎有问题 - 还有其他常用的方法吗?

更新:我见过    - 索引低选择性列,但只选择高选择性值。

4 个答案:

答案 0 :(得分:3)

在您列出的所有方法中,只有一种(使用顺序读取)是与低选择性有关的方法(好的,群集也可以限定)。

如果您对列的选择性较低,则意味着扫描的性能优于查找。

索引可以用来做

  • 索引查找 - 检查索引指针,检索记录,重复
  • 索引扫描 - 扫描索引并直接从索引
  • 获取值

否则它不是很有用。

如果选择性低,则意味着将读取大部分索引,如果使用查找,则将以某种随机顺序读取大部分数据。如果覆盖基础表的很大一部分,这是低效的,因此更好的方法是执行顺序读取(这也很慢)。

因此,如果选择性很低,那么你无能为力(聚类可以提供帮助)。

然而,我不相信您明白在您的示例中选择性较低。正如你所说,大多数参赛作品都会被支付,而且会分配很少的参赛作品。这些(已分配)条目将具有高选择性。特别是如果存在其他条件,则存在包含这些附加条件的复合索引。

所以,你可能会碰到一个没问题的人。

现在,您可以通过分区数据或使用补充表(如果需要)来进一步提高性能。

答案 1 :(得分:3)

我同意Unreason的分支。但是有一些事情需要了解这个案例。

这称为歪斜和歪斜杀戮。这是部分索引的完美用途,您可以排除95%的付费发票,并仅索引更有趣和有选择性的统计数据。但你没有那个。您可以将所有行水平分区为单独的表/分区,但是您需要考虑行迁移(从一个状态移动到另一个状态)并且这很昂贵。 DBMS必须执行更新,删除和插入以更改状态。如果你是一个会伤害的高容量系统。

忘记关于是否根据选择性进行索引的说法,因为在快速变化的列上放置索引通常也是一个坏主意。你的索引将有热块,其中所有的步骤1都被移除,而另一个步骤2的所有步骤都被插入,哦顺便说一下,某些步骤2同时被移除到步骤3中。这不会很好地扩展。

我建议将状态垂直分区为单独的表格。

您的发票表将包含PK以及除状态以外的所有列。

您的状态可以通过两种方式处理。该表将PK值作为FK返回到发票表,状态和您输入该状态时的时间戳。最好的是状态上的水平分区表。您将为每种状态分配一个分区。因此,找到所有或一个“已放置”状态将分区修剪并只读取它所需的分区 - 这是一个非常少的块。由于行非常狭窄,因此您可能会在一个块上获得400个发票状态。查看任何一张发票的状态很容易,因为PK上有一个全局索引。

如果您的RDBMS不支持使用行迁移进行分区,则需要将这些分区作为表进行管理,然后从一个分区中删除并插入另一个分区。您将这些移动封装在过程中的事务中,因此您可以保持数据清洁。每张发票都在一个且只有一个状态表中。更难的部分是按发票ID查询,您必须检查每张表以查看它的位置。

您还有其他选择 您可以编写付费状态。如果它是分区表,则只需在发票状态表中移至付款时即可将其删除。 (当然,您会将付费记录写入奖励材料中提到的历史记录表)。然后你将对状态表进行外连接,并且null表示已付费。如果您几乎从不查询付费状态,那么实际上没有理由进行快速查询。

奖金材料

在任何一种情况下,您都希望在报告表中跟踪这些变动。每次更新状态时,您都希望将其写入历史记录表。最终你会想要分析我称之为运输时间的东西。按月填写到付费的平均时间是多少?由于经济不景气,这种情况会增加吗?从月到月的运输时间是多少。由于度假时身体缺失,夏季月份需要更长时间吗?你明白了。通过更新该列,您将丢失这些答案,因此您需要将该历史记录日志嵌入到您的过程中。

答案 2 :(得分:1)

分区是一种将相同的表存储在基于数据的不同区域中的方法 - SQL开发人员不必访问单独的表。

我认为这是所描述问题的理想选择 - 您可以在此处找到有关Informix的更多信息:http://www.dbmag.intelligententerprise.com/blog/main/archives/2008/09/data_partitioni.html

答案 3 :(得分:1)

如果您可以放松数据库规范化,并且可能的状态数量很少(例如:<5),则可以为每个状态添加一个可空列,并将索引放在这些列上。许多引擎(例如MongoDB)将跳过具有空值的行,而仅对具有实际数据的行建立索引(稀疏索引)。例如:

Invoice#     Date     State       IsPlaced   IsPaid   IsFulfilled
    1       Apr-20    Fulfilled     (null)    (null)      yes
    2       Apr-20    Fulfilled     (null)    (null)      yes
    3       Apr-20    Fulfilled     (null)    (null)      yes
    4       Apr-21    Fulfilled     (null)    (null)      yes
    5       Apr-21    Fulfilled     (null)    (null)      yes
    6       Apr-21    Paid          (null)     yes       (null)
    7       Apr-21    Placed         yes      (null)     (null)
    8       Apr-22    Placed         yes      (null)     (null)
    9       Apr-22    Paid          (null)     yes       (null)
   10       Apr-22    Placed         yes      (null)     (null)

您可以在单独的表中获取此信息,并且可能由触发器驱动,或者至少在约束条件下进行检查。

这不是通用解决方案,实际上可伸缩性较差,但是将允许您在更合理的列(例如发票日期)上使用分区。

这种技巧通常用于数据仓库设计中,在这种设计中,处理大型数据集的效率比数据规范化更为重要。