【问题标题】:Indexing & alternatives for low-selectivity columns低选择性色谱柱的索引和替代品
【发布时间】:2011-05-10 06:43:56
【问题描述】:

有哪些策略可用于在低选择性列上选择记录?

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

关于索引的类似问题
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 不支持上面列出的前三个选项,其余选项似乎有问题 - 还有其他常用的方法吗?

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

【问题讨论】:

  • 我通常建议此时进行分区。为什么这看起来有问题?
  • 我想我正在考虑为每个状态值分区到一个单独的表中,因此最终需要维护大量表以及将记录从表移动到表的复杂代码。但是我想您可以将数据划分为 status=final 和“其余”。即便如此,有时您可能想要选择所有记录而不考虑状态(例如某种月度销售报告),但我没想到需要做多少额外的工作。
  • @littlegreen。我并不是真的在寻找特定于 DBMS 的建议或升级指导。我想到的 DBMS 是 Informix SE,但请不要专注于此。
  • @RedGrittyBrick,至于分区,一些数据库引擎可以自动按列值分区(并且有两个级别 - 从逻辑上讲它可能仍然是单个表,例如只是物理存储在不同的 HDD 上)。如果您实际上创建单独的表,将它们连接回来需要维护一个 UNION ALL 视图(就是这样)。
  • 您是否真的遇到了性能打击,或者这是一个智力锻炼?只是好奇。

标签: sql database-design indexing relational-database


【解决方案1】:

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

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

忘记你所说的是否基于选择性索引,因为将索引放在快速变化的列上通常也是一个坏主意。您的索引将有热块,其中所有第 1 步都被删除,另一个所有第 2 步都被插入,哦顺便说一句,一些第 2 步同时被删除到第 3 步。这不会很好地扩展。

我建议将您的状态垂直分区到一个单独的表中。

您的发票表将包含一个 PK 和除状态之外的所有列。

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

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

您还有其他选择 你可以写付费状态也可以不写。如果它是一个分区表,您可以在发票状态表移动到已付款时将其从发票状态表中删除。 (当然,您会在奖金材料中提到的历史记录表中写入付费记录)。然后,您将对状态表进行外部连接,空值表示已付费。如果您几乎从不查询付费状态,那么就没有理由进行快速查询。

奖励材料

在任何一种情况下,您都需要在报表中跟踪这些移动。每次更新状态时,您都希望将其写入历史记录表。最终你会想要分析我所说的运输时间。从填写到付款的平均时间是多少,按月计算?由于经济不景气,这种情况会增加吗?从放置到填充的运输时间是多少,按月计算。夏季会因为假期失踪而需要更长的时间吗?你明白了。通过更新该列,您将丢失这些答案,因此您需要将该历史记录嵌入到您的程序中。

【讨论】:

    【解决方案2】:

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

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

    索引可以用来做

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

    否则它不是很有用。

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

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

    但是,我不相信您理解在您的示例中您具有低选择性。正如您所说,大多数参赛作品将被支付,并且将分配很少的参赛作品。这些(分配的)条目将具有高选择性。特别是如果有附加条件并且如果存在包含这些附加条件的复合索引。

    所以,你可能会遇到一个无关紧要的问题。

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

    【讨论】:

    • 感谢您对具体数值和综合指数的澄清。
    【解决方案3】:

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

    我认为它非常适合所描述的问题 - 您可以在 Informix 上找到更多关于它的信息:http://www.dbmag.intelligententerprise.com/blog/main/archives/2008/09/data_partitioni.html

    【讨论】:

    • 谢谢马克,看来我用错了这个词。有趣的文章
    • 实际上,如果你想准确一点,no 你是不对的——它可能对用户是透明的,也可能不是——在这两种情况下,它都被称为分区。 (还有水平分区的概念,不只是垂直)
    • @Unreason,要点——我应该用我的回答来反映这是 Informix 支持的 a 分区方式(我在我的较早的评论),而不是 only 可能的方法。
    • @Unreason,我想你的意思是说还有一个垂直的概念,因为到目前为止的整个讨论都是关于水平分区(分隔行)。
    【解决方案4】:

    如果您可以放松数据库规范化,并且可能的状态数量很少(例如:

    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)
    

    您可以将这些信息放在单独的表中,并且可能由触发器驱动,或者至少使用约束进行检查。

    这不是一个通用的解决方案,实际上可扩展性很差,但允许您对更有意义的列使用分区,例如发票日期。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多