代码之家  ›  专栏  ›  技术社区  ›  RedGrittyBrick

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

  •  8
  • RedGrittyBrick  · 技术社区  · 14 年前

    在低选择性列上选择记录的策略范围是什么?

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

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

    更新:我看到了 -索引低选择性列,但只选择高选择性值。

    3 回复  |  直到 14 年前
        1
  •  3
  •   Stephanie Page    14 年前

    我同意非理性的 然而 分支。但关于这个案子还有一些事情要知道。

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

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

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

    您的发票表将有一个pk和除status之外的所有列。

    您的状态可以通过两种方式处理。该表的pk值将作为fk返回到invoice表,状态以及输入该状态时的时间戳。最好是一个关于状态的水平分区表。对于每个可能的状态,都有一个分区。因此,找到全部或一个“已放置”状态将删除分区并只读取它需要的分区——这是非常小的块数。由于行太窄,您可能在单个块上获得400个发票状态。查找任何一张发票的状态都很容易,因为pk上有一个全局索引。

    如果RDBMS不支持带行迁移的分区,则需要将这些分区作为表来管理,并从一个分区中删除,然后插入到另一个分区中。您将把这些移动封装在一个过程中的事务中,这样就可以保持数据的整洁。每个发票都在一个且只有一个状态表中。更困难的部分是按发票ID查询,您必须检查每个表以查看它在哪里。

    你还有别的选择 您可以写或不写付费状态。如果它是一个分区表,那么当它移到paid时,您可以从invoice status表中删除该发票。(当然,你会在奖金材料中提到的历史记录表中写一个付费记录)。然后您将对状态表进行外部联接,空值表示已支付。如果您几乎从未查询过付费状态,那么实际上没有理由让它成为一个快速查询。

    奖金材料

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

        2
  •  3
  •   Unreason    14 年前

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

    如果列的选择性较低,这意味着扫描将比查找执行得更好。

    索引可用于

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

    否则它不是很有用。

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

    因此,如果选择性很低,那么就没有什么可以做的了(集群可以帮助您)。

    然而 ,我不相信你理解在你的例子中 选择性低。正如您所说,大多数条目都将被支付,很少条目将被分配。这些(分配的)条目将 高选择性 . 特别是如果有附加条件 如果 有一个包含这些附加条件的复合索引。

    所以,你可能是在用头撞一个没有问题的人。

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

        3
  •  1
  •   user359040    14 年前

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

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