1

我有一个带有列的帖子表id (INT, primary key), name(VARCHAR), status(TINYINT)。帖子的状态可以是1, 2, 3 or 4。并且,比如说,我想选择状态为 2 的帖子。如何优化表以进行快速查询,因为为状态字段添加索引并没有太大帮助。

我使用 mysql 5.5。引擎可以是innodbmyisam

谢谢

更新评论

据我了解索引是如何工作的,当我们添加索引时,它有点创建该列的副本,但是以有序的方式 - 按数字(或对于文本 - 按字母顺序),因此它提供了二进制搜索的可能性。因此,如果我们需要找到某个值,我们可以避免对表进行全扫描。但是在这种情况下,假设我的表中有 100K 行,并且大致说 - 具有 statuses 的行数相等1, 2, 3 and 4。如果我为该字段添加索引,毕竟它应该对剩余的 25k 行进行线性搜索,并且如果我们考虑到添加索引会减慢插入和更新速度,那么它可能不值得。

询问 -SELECT id, name FROM posts WHERE status = 2

4

3 回答 3

2

在这种情况下,索引的效果取决于几个因素。以下是索引可以正常工作的三种情况(第三种情况要感谢 Vatev)。

第一种是状态为 2 的记录非常少。假设您有一个包含一百万条记录的表,而在任何给定时间只有 100 条记录为状态 2。索引将帮助您找到它们。

第二个是何时status是表上的主键(或至少是主键中的第一列)。这将任何表扫描限制status = 2在表的一部分。当然,在状态上有一个主键会使后续更新状态的成本更高,因为必须根据状态的值对数据进行物理重新定位。

第三种情况是status-- 或索引中包含status-- 的列是覆盖索引。这意味着查询所需的所有列都在索引中,因此引擎永远不必查找原始数据页。

一般来说,我建议不要使用低基数字段的索引。这是一般规则,但在某些情况下,这样的索引可以提高性能。

于 2013-08-27T12:24:25.587 回答
2

我不知道您的应用程序或 SQL 要求

您可以批量获取它们 不要使用 LIMIT 因为在具有数百万条记录的表上效率不高,因为如果您像这样查询它会创建一个基于(磁盘/内存)的临时表。

SELECT id, name FROM posts WHERE status = 2 LIMIT 1000000, 1000000

如果您解释上面的查询,您将看到它将扫描 2000000 行并将使用(磁盘/内存)临时表,最坏的情况是如果内存已满,则需要基于磁盘

更好的方法是使用基于您的状态的位置(确保索引)

SELECT id, name FROM posts WHERE status = 2 and position >= 1 and <= 1000000 
SELECT id, name FROM posts WHERE status = 2 and position >= 1000001  and <= 2000000 
...
... 

真正的低基数字段确实不应该被索引一个更好的方法可能是你可以使用 LIST 分区进行测试以获得更高的性能,但它仍然需要完整的“表(分区)”扫描

有关示例,请参见http://sqlfiddle.com/#!2/d947c/7并查看 EXPLAIN PARTITIONS 语句以查看当您的 WHERE status = 1 时,仅会使用 partition_post_status_id_1。

或者查看带有分区的覆盖索引方法http://sqlfiddle.com/#!2/20b0d/1

此外,涉及聚合函数(例如 SUM() 和 COUNT())的查询可以在分区上并行运行以获得更高的性能

于 2013-08-27T20:16:53.780 回答
0

如果您的要求是真正经常运行这种类型的查询并获取整个表的 1/4,我建议您将表更改为使用 InnoDB 引擎和主键(status, id)

CREATE TABLE posts
( id INT, 
  name VARCHAR (whatever), 
  status TINYINT,
  PRIMARY KEY (status, id),
  UNIQUE INDEX (id)
) ENGINE = InnoDB ; 

这样,您仍然具有(id)唯一性,但首先基于聚集索引(InnoDB 默认选择该索引作为主键),status因此您想要使用WHERE status = @X的数据按顺序存储在磁盘上。

于 2013-10-04T20:52:46.990 回答