【问题标题】:Effective indexing for a DB with millions of rows具有数百万行的数据库的有效索引
【发布时间】:2012-05-29 04:01:04
【问题描述】:

我有一个 MISAM MySQL DB 表,其中包含数百万行,我被要求使用该表,但我需要首先加快查询速度。

以前根本没有索引!我在 'type' 列上添加了一个新索引,这很有帮助,但我想知道是否还有其他列可能也是最好的索引?

这是我的创建表:

CREATE TABLE `clicks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`companyid` int(11) DEFAULT '0',
`type` varchar(32) NOT NULL DEFAULT '',
`contextid` int(11) NOT NULL DEFAULT '0',
`period` varchar(16) NOT NULL DEFAULT '',
`timestamp` int(11) NOT NULL DEFAULT '0',
`location` varchar(32) NOT NULL DEFAULT '',
`ip` varchar(32) DEFAULT NULL,
`useragent` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `companyid` (`companyid`,`type`,`period`),
KEY `type` (`type`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

典型的 SELECT 语句通常会按 companyidtypecontextid 列进行过滤。

例如:

SELECT period, count(period) as count FROM clicks WHERE contextid in (123) AND timestamp > 123123123 GROUP BY period ORDER BY timestamp ASC

SELECT period, count(period) as count FROM clicks WHERE contextid in (123) AND type IN('direct') AND timestamp > 123123123 GROUP BY period ORDER BY timestamp ASC

我的问题的最后一部分是这样的:当我在 type 添加索引时,大约需要 1 小时 - 如果我要添加或删除多个索引,您可以在一个查询中完成还是必须这样做他们一个接一个地等待每个完成?

感谢您的意见。

【问题讨论】:

    标签: mysql database-design indexing


    【解决方案1】:

    您可以在单个查询中添加多个索引。总体而言,这将节省一些时间,但在您等待整个查询完成时,该表将无法访问:

    ALTER TABLE table1 ADD INDEX `Index1`('col1'),
     ADD INDEX `Index2`('col2')
    

    关于indexes,这是一个复杂的主题。但是,在 WHERE 子句中包含的具有高基数的单个列上添加索引是一个不错的起点。 MySQL 将尝试为查询选择最佳索引并使用它。

    为了进一步调整性能,您应该考虑multi-column indexes,我看到您已经使用您的“companyid”索引实现了它。

    要能够使用索引一直到 GROUP BYORDER BY 子句,您可能需要了解很多条件。

    为了最好地利用索引,您的数据库服务器必须有足够的 RAM 来将索引完全存储在内存中,并且必须正确配置服务器以实际利用内存。

    【讨论】:

      【解决方案2】:

      在我看来,timestampperiod 可以在 WHERE 子句中使用时被索引。

      也不要使用contextid in (123),而是使用contextid = 123,而不是type IN('direct'),使用type = 'direct'

      【讨论】:

      • 同意“使用 = 而不是 IN”。不确定时间戳索引,因为每一行都会不同,并且为了匹配值,必须扫描整个索引 - 这并不比表扫描更有效吗? (这是我的理解,可能有误……)
      • 谢谢,我将尝试为 period、contextid 和 companyid 添加索引,看看是否可以更快!
      • @Brian:当您为表建立索引时,您需要具有高度差异化的列,例如具有不同值的每一行。至于 SQL 查询优化器是否足够聪明,可以知道在值之后读取时间戳索引,这取决于我们正在谈论的数据库。知道的唯一方法是解释查询。
      【解决方案3】:

      添加哪些索引取决于您的查询。您正在排序 (GROUP BY) 或选择 (WHERE) 的任何内容都适合用作索引。

      您可能还想看看how Mysql uses indexes

      关于添加索引的时间,如果你确定要添加多个索引,你可以做mysqldump,手动编辑.sql文件中的表结构,然后重新导入。这可能需要一段时间,但至少您可以一次完成所有更改。然而,这并不真正符合随手测试的想法......所以请谨慎使用这种方法。 (我在修改多个具有相同结构的表时已经这样做了,并希望为所有表添加一些索引。)

      另外,我不是 100% 确定,但我认为当您添加索引时,Mysql 会使用索引创建表的副本,然后删除原始表 - 所以请确保您的表的当前大小和一些边距的服务器/分区。

      【讨论】:

        【解决方案4】:

        索引确实很强大,但并不像您想象的那么黑。了解 MySQL 的 EXPLAIN PLAN 功能,这将帮助您系统地找到可以改进的地方:

        http://dev.mysql.com/doc/refman/5.5/en/execution-plan-information.html

        【讨论】:

          【解决方案5】:

          这是您的一个查询,分为多行,以便于阅读。

          SELECT period, count(period) as count 
          FROM clicks 
          WHERE contextid in (123) 
          AND timestamp > 123123123 
          GROUP BY period 
          ORDER BY timestamp ASC
          

          我什至不确定这是一个有效的查询。我认为您的 GROUP BY 和 ORDER BY 必须在 SQL 中匹配。我认为您必须在 count 上订购,因为 GROUP BY 将在 period 上订购。

          优化查询的重要部分是 WHERE 子句。在这种情况下,contextidtimestamp 上的索引会加快查询速度。

          显然,您不能索引每个 WHERE 子句。您为最常见的 WHERE 子句编制索引。

          我会一次向现有表添加一个索引。是的,它很慢。但是您应该只需要添加一次索引。

          【讨论】:

          • 谢谢,我正在考虑向 contextid 和 companyid 添加索引,因为我认为索引时间戳(采用 UNIX 时间戳格式)不会那么有效,因为数据中没有重复(除非我错了!)
          • @atomicguava:您可能是正确的,索引时间戳不会大大加快您的查询速度。但是,一般来说,您希望索引具有高差异性或缺乏重复性的列。
          猜你喜欢
          • 2018-12-21
          • 2011-04-22
          • 2021-12-27
          • 1970-01-01
          • 2011-01-04
          • 2011-06-19
          • 1970-01-01
          • 2023-04-03
          • 1970-01-01
          相关资源
          最近更新 更多