deleted_at
上没有索引。
- 在大表上会很慢吗?(或者
IS NULL
在不需要索引的情况下进行了优化) - 我应该在
deleted_at
列上添加索引吗?
所以,Laravel的“soft_delete”deleted_at
列在MySQL中是否需要索引?
说明: Laravel 在deleted_at
列中存储时间戳,以表示记录何时进行软删除,而不是布尔值。
deleted_at
上没有索引。
IS NULL
在不需要索引的情况下进行了优化)deleted_at
列上添加索引吗?所以,Laravel的“soft_delete”deleted_at
列在MySQL中是否需要索引?
说明: Laravel 在deleted_at
列中存储时间戳,以表示记录何时进行软删除,而不是布尔值。
deleted_at
列不适合作为索引候选列。相比于评论,我将尝试更好地解释:仅当其基数相对较高时,索引才有用。 基数是描述数据集中索引唯一性的数字。这意味着它是总记录数除以总唯一记录数得到的数量。
例如,主键的基数为1。每个记录都包含唯一的主键值。 1也是最高的数字。你可以将其视为“100%”。
但是,像deleted_at这样的列没有这样的价值。 Laravel对deleted_at的处理方式是检查它是否为空。 这意味着它只有两个可能的值。 包含两个值的列的基数极低,并随着记录数的增加而降低。
你可以为这样的列创建索引,但这并没有帮助。实际上,它可能会减缓速度并占用空间。
简而言之:不需要为该列创建索引,索引对性能没有任何有益影响。
tinyint
类型的列。 - N.B.WHERE x = true
这样的查询将会很快,并且你可能会得出在布尔列上创建索引很棒的结论。那么对于WHERE x = false
呢?2除以x,其中x > 0且 < 无限大
告诉你,随着数据增长,你将浪费空间。 - N.B.deleted_at
是一个有很多不同值且很少为空的日期时间类型,对其进行索引将是一个好选择。 - Rick Jamesdeleted_at
上。而且 GROUP BY
因素也很重要。请展示 SHOW CREATE TABLE
和 EXPLAIN SELECT ...
两者。 - Rick Jamesinnodb_buffer_pool
包含数据吗?你在这里做出了完全错误的假设 - 第一个查询,即32秒的查询,在缓冲池中没有任何数据。一旦执行,它就会填充它。你的第二个查询现在使用内存中的数据。你错误地认为这是因为索引。 - MjhEXISTS
并摆脱GROUP BY
可以更有效地完成此操作。 - Rick Jamesinnodb_buffer_pool
不是空的,我认为有一些优化,因为有轻微的增加?在这个查询中,有3个表上的deleted_at
IS NULL检查,如果我删除IS NULL检查的where子句,则查询在没有deleted_at索引的情况下以27.5毫秒的速度运行。因此,很明显这些索引在条件中带来了巨大的好处。我不知道添加太多的deleted_at索引会有什么后果,但在这种情况下,我认为我不在意。 - danrichards简短回答:可能。
详细回答:
如果deleted_at
中有非常少的不同值,MySQL将不会使用INDEX(deleted_at)
。
如果deleted_at
中有许多不同的非空日期,则MySQL将使用INDEX(deleted_at)
。
到目前为止,大部分讨论都没有考虑到这个单列索引的基数。
注意:这与类似于is_deleted
的2个值标志不同。在这种情况下,对于单列索引是无用的。
更多讨论(从MySQL的角度)
https://laravel.com/docs/5.2/eloquent#soft-deleting中提到
现在,当您在模型上调用delete方法时,deleted_at列将设置为当前日期和时间。并且,在查询使用软删除的模型时,软删除模型将自动从所有查询结果中排除。
据此,我假设这是表定义中发生的事情:
deleted_at DATETIME NULL -- (or TIMESTAMP NULL)
而且这个值被初始化(显式或隐式)为NULL
。
情况1:有很多新的行,但没有“删除”:所有deleted_at
的值都是NULL
。在这种情况下,优化器会避开INDEX(deleted_at)
因为它没有帮助。实际上,使用索引会浪费更多时间去遍历整个索引和数据。忽略索引并假定所有行都是可能被SELECTed
的候选行,这样更便宜。
情况2:少数行(其中之一)已被删除:现在deleted_at
有多个值。尽管Laravel只关心IS NULL
与IS NOT NULL
,但MySQL将其视为一个多值列。但是,由于测试是用于IS NULL
,而大多数行仍然是NULL
,因此优化器的反应与情况1相同。
情况3:被软删除的行比仍处于活动状态的行要多得多:现在索引突然变得有用了,因为表中只有小部分IS NULL
。
情况2和情况3之间没有确切的分界线。20%是一个方便的经验法则。
现在,从执行的角度来看。
INDEX(deleted_at)
用于deleted_at IS NULL
:
NULL
的行。IS NULL
失败为止。INDEX(deleted_at)
未使用:
deleted_at IS NULL
,否则过滤掉该行。复合索引:
拥有以deleted_at
开头的“复合”(多列)索引可能非常有益。例如:
INDEX(deleted_at, foo)
WHERE deleted_at IS NULL
AND foo BETWEEN 111 AND 222
无论表中有多少百分比的内容被删除,都很可能有效地使用索引。
NULL
和foo >= 111
的索引BTree。IS NULL
或foo <= 222
失败。请注意,在INDEX
中,NULL
非常类似于任何其他单个值。 (并且NULLs
存储在其他值之前。)
deleted_at
实际上具有 NULL
或大量不同的 TIMESTAMPs
,那么 MySQL 就无法将其视为布尔值。相反,它(错误地)假定不同值的数量均匀分布。 - Rick James
where
子句都需要建立索引。如果您愿意,可以将您的评论发布为答案。 - rap-2-h