我有一个包含id (INT, primary key), name(VARCHAR), status(TINYINT)
列的帖子表。帖子的状态可以是1, 2, 3 or 4
。而且,比方说,我想选择状态为2的帖子。如何优化表格以进行快速查询,因为为状态字段添加索引不会有太大帮助。
我使用的是mysql 5.5。引擎可以是innodb
或myisam
。
由于
更新评论
据我所知,索引是如何工作的,当我们添加索引时,它有点创建该列的副本,但是以有序的方式 - 数字(或文本 - 按字母顺序),因此它提供了二进制的可能性搜索。因此,如果我们需要找到一些值,我们可以避免对表进行全面扫描。但在这种情况下,假设我的表中有100K行,并且大致上说 - 状态为1, 2, 3 and 4
的行数相等。如果我为该字段添加索引,毕竟它应该对其余25k行进行线性搜索,如果我们考虑到添加索引会减慢插入和更新速度,那么它可能不值得。
查询 - SELECT id, name FROM posts WHERE status = 2
答案 0 :(得分:2)
在这种情况下索引的效果取决于几个因素。以下是三种情况,其中指数运作良好(第三种情况归功于Vatev)。
第一种情况是,状态为2的记录非常少。比如,您有一个包含百万条记录的表,在任何给定时间只有100条具有状态2。索引可以帮助您找到它们。
第二个是status
是表中的主键(或至少是主键中的第一列)。这会将status = 2
的任何表扫描限制为仅限表的一部分。当然,拥有主键状态会使后续状态更新变得更加昂贵,因为数据必须根据状态值进行物理重新定位。
第三种情况是status
- 或包含status
的索引中的列 - 是覆盖索引。这意味着查询所需的所有列都在索引中,因此引擎永远不必查找原始数据页。
一般来说,我建议不要使用低基数字段的索引。这是一般规则,但在某些情况下,这样的索引可以提高性能。
答案 1 :(得分:2)
我不知道您的应用程序或SQL要求
您可以批量获取它们DONT使用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分区进行测试以获得更高的性能,但它仍然需要完整的“TABLE(分区)”扫描
有关示例,请参阅http://sqlfiddle.com/#!2/d947c/7并查看EXPLAIN PARTITIONS语句,以了解当WHERE状态= 1时,将仅使用partition_post_status_id_1。
或者通过分区
查看覆盖索引方法http://sqlfiddle.com/#!2/20b0d/1涉及聚合函数(如SUM()和COUNT()的查询也可以在分区上并行化以获得更高的性能
答案 2 :(得分: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
按顺序存储在磁盘上。