【问题标题】:Indexing 2.5 million items with assorted other information用各种其他信息索引 250 万个项目
【发布时间】:2012-08-04 09:42:22
【问题描述】:

我有一张包含 250 万医生名单的表格。我还提供了接受保险、所用语言和专业(分类)的表格。医生表是这样的:

CREATE TABLE `doctors` (
  `doctor_id` int(10) NOT NULL AUTO_INCREMENT,
  `city_id` int(10) NOT NULL DEFAULT '0',
  `d_gender` char(1) NOT NULL DEFAULT 'U',
  `s_insurance` int(6) NOT NULL DEFAULT '0',
  `s_languages` int(6) NOT NULL DEFAULT '0',
  `s_taxonomy` int(6) NOT NULL DEFAULT '0',
  PRIMARY KEY (`doctor_id`)
) ENGINE=InnoDB;

其他信息是这样存储的:

CREATE TABLE `doctors_insurance` (
  `assoc_id` int(10) NOT NULL AUTO_INCREMENT,
  `doctor_id` int(10) NOT NULL DEFAULT '0',
  `insurance_id` int(10) NOT NULL DEFAULT '0',
  PRIMARY KEY (`assoc_id`)
) ENGINE=InnoDB;

CREATE TABLE `doctors_languages` (
  `assoc_id` int(10) NOT NULL AUTO_INCREMENT,
  `doctor_id` int(10) NOT NULL DEFAULT '0',
  `language_id` int(10) NOT NULL DEFAULT '0',
  PRIMARY KEY (`assoc_id`)
) ENGINE=InnoDB;

CREATE TABLE `doctors_taxonomy` (
  `assoc_id` int(10) NOT NULL AUTO_INCREMENT,
  `doctor_id` int(10) NOT NULL DEFAULT '0',
  `taxonomy_id` int(10) NOT NULL DEFAULT '0',
  PRIMARY KEY (`assoc_id`)
) ENGINE=InnoDB;

自然每个医生都支持各种不同的保险计划,可能会说多种语言,有些医生可以有几个不同的专业(分类)。所以我选择使用单独的表进行索引,这样我需要添加新的索引或删除旧的索引,我可以简单地删除表,而不必等待很长时间才能以老式方式实际执行。

另外,由于将来要考虑其他扩展技术,经典的 JOIN 现在对我没有任何影响,所以我并不担心。

按名称索引很容易:

CREATE TABLE `indices_doctors_names` (
  `ref_id` int(10) NOT NULL AUTO_INCREMENT,
  `doctor_id` int(10) NOT NULL DEFAULT '0',
  `practice_id` int(10) NOT NULL DEFAULT '0',
  `name` varchar(120) NOT NULL DEFAULT '',
  PRIMARY KEY (`ref_id`),
  KEY `name` (`name`)
) ENGINE=InnoDB;

但是,当我想让人们按城市、专业、保险、语言、性别和其他人口统计数据进行搜索时,我创建了他的:

CREATE TABLE `indices_doctors_demos` (
  `ref_id` int(10) NOT NULL AUTO_INCREMENT,
  `doctor_id` int(10) NOT NULL DEFAULT '0',
  `city_id` int(10) NOT NULL DEFAULT '0',
  `taxonomy_id` int(6) NOT NULL DEFAULT '0',
  `insurance_id` int(6) NOT NULL DEFAULT '0',
  `language_id` int(6) NOT NULL DEFAULT '0',
  `gender_id` char(1) NOT NULL DEFAULT 'U',
  PRIMARY KEY (`ref_id`),
  KEY `index` (`city_id`,`taxonomy_id`,`insurance_id`,`language_id`,`gender_id`)
) ENGINE=InnoDB;

我们的想法是,主要针对专业、保险或语言的每次更改都会有一个条目,尽管其他内容仍然相同。这产生了一个明显的问题。如果一个医生有 3 个专业,支持 3 个保险提供商,并且会说 3 种语言,仅此一项就意味着该特定医生有 27 个条目。因此,250 万个条目很容易膨胀到更多。

必须有更好的方法来做到这一点,但如何做到呢?同样,我对转向经典索引技术和使用 JOIN 不感兴趣,因为它很快就会变得太慢,我需要一种可以轻松扩展的方法。

【问题讨论】:

    标签: php mysql scalability


    【解决方案1】:

    我知道这不是您要寻找的答案,但您现在已经采用了 RDBM 擅长的事情并尝试自己实现它,使用 RDBM 可以用来实际理解您的机制的相同机制数据并优化检索和查询。在实践中,您决定放弃使用适当的索引来创建自己的中途解决方案,该解决方案将尝试自行实现索引(通过实际使用带有 KEY 的 RDBM 的索引功能)。

    我建议实际上尝试按照您已经构建的方式使用数据库。 2.5m 行并不是那么多行,您应该能够使用 JOIN 和索引使其在您的约束范围内快速运行。使用 EXPLAIN 并添加适当的索引来支持您想要回答的查询。如果您遇到问题(我怀疑您在此处查询的数据量),决定解决瓶颈,然后当您真正知道可能是什么问题时,而不是尝试解决您的问题'到目前为止只是想象。除了 MySQL 之外,可能还有其他技术会有所帮助 - 但您首先需要了解实际上是什么影响了您的性能。

    【讨论】:

      【解决方案2】:

      处理像“indices_doctors_demos”这样的非规范化表中的行爆炸的正常方法是规范化为 5NF。请记住,规范化与使用 id 号作为代理键的决定没有任何关系。

      在您描述的场景中,归一化为 5NF 似乎是可行的。你不会有任何超过 700 万行的表。 “indices_doctors_demos”表完全消失,四个“doctors”表都变窄了,最终都会得到高度选择性的索引。

      如果你为我工作,我会要求你证明 5NF 行不通,然后我才会让你采取不同的方法。

      由于您已经拥有所有数据,因此构建和测试它是有意义的,密切关注查询计划。你应该不会超过一个下午。猜测一些表名,我建议您将数据加载到这些表中。

      -- You're missing foreign keys throughout. I've added some of them, 
      -- but not all of them. I'm also assuming you have a way to identify 
      -- doctors besides a bare integer.
      CREATE TABLE `doctors` (
        `doctor_id` int(10) NOT NULL AUTO_INCREMENT,
        `city_id` int(10) NOT NULL DEFAULT '0',
        `d_gender` char(1) NOT NULL DEFAULT 'U',
        PRIMARY KEY (`doctor_id`)
      ) ENGINE=InnoDB;
      
      CREATE TABLE `doctors_insurance` (
        `doctor_id` int(10) NOT NULL DEFAULT '0',
        `insurance_id` int(10) NOT NULL DEFAULT '0',
        PRIMARY KEY (`doctor_id`, `insurance_id`),
        FOREIGN KEY (`doctor_id`) REFERENCES `doctors` (`doctor_id`),
        FOREIGN KEY (`insurance_id`) REFERENCES `insurance` (`insurance_id`)
      ) ENGINE=InnoDB;
      
      CREATE TABLE `doctors_languages` (
        `doctor_id` int(10) NOT NULL DEFAULT '0',
        `language_id` int(10) NOT NULL DEFAULT '0',
        PRIMARY KEY (`doctor_id`, `language_id`),
        FOREIGN KEY (`doctor_id`) REFERENCES `doctors` (`doctor_id`),
        FOREIGN KEY (`language_id`) REFERENCES `languages` (`language_id`)
      ) ENGINE=InnoDB;
      
      CREATE TABLE `doctors_taxonomy` (
        `doctor_id` int(10) NOT NULL DEFAULT '0',
        `taxonomy_id` int(10) NOT NULL DEFAULT '0',
        PRIMARY KEY (`doctor_id`, `taxonomy_id`),
        FOREIGN KEY (`doctor_id`) REFERENCES `doctors` (`doctor_id`),
        FOREIGN KEY (`taxonomy_id`) REFERENCES `taxonomies` (`taxonomy_id`)
      ) ENGINE=InnoDB;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多