tinyint 列优化 mysql

Posted

技术标签:

【中文标题】tinyint 列优化 mysql【英文标题】:tinyint column optimization mysql 【发布时间】:2013-08-27 12:06:22 【问题描述】:

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

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

谢谢

更新到 cmets

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

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

【问题讨论】:

为什么为状态字段添加索引没有那么大的帮助? 为什么添加索引不能加快查询速度?你在做什么类型的查询?请举个例子。 究竟有哪些查询需要优化? 在不了解表结构和查询的情况下尝试提供优化查询的建议是没有意义的。 @Andrius Naruševičius 坏主意,这被称为 SQL 反模式。因为您将元数据放入表名中.. 【参考方案1】:

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

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

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

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

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

【讨论】:

第二种情况也适用于使用以status开头的覆盖索引的查询。 @Vatev 。 . .好点。我修改了答案以包含它。 True nice explain +1 as a note note,“一般来说,我建议不要在低基数字段上使用索引。这是一般规则,但在某些情况下,这样的索引可以提高性能。”聚合函数是一个示例,如果您以可以使用松散索引扫描的方式对状态进行索引,将会受益。【参考方案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 分区进行测试以获得更高的性能,但它仍然需要完整的“TABLE(分区)”扫描

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

或者查看覆盖索引的方法http://sqlfiddle.com/#!2/20b0d/1 with partitioning

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

【讨论】:

【参考方案3】:

如果您的要求是真正经常运行这种类型的查询并获取整个表的 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 顺序存储在磁盘上.

【讨论】:

以上是关于tinyint 列优化 mysql的主要内容,如果未能解决你的问题,请参考以下文章

MySQL TINYINT(1) 到 BOOL

我可以在 MySQL tinyInt 上建立索引吗?

tinyint 列优化 mysql

MySQL TinyInt 值不会更新

mysql tinyint(2) 的错误映射为 boolean 与学说

引导切换以使用 AJAX 在不刷新页面的情况下更改 MySQL TINYINT