Laravel“软删除”是不是需要 MySQL 上的索引?

Posted

技术标签:

【中文标题】Laravel“软删除”是不是需要 MySQL 上的索引?【英文标题】:Does Laravel's "soft_delete" need index on MySQL?Laravel“软删除”是否需要 MySQL 上的索引? 【发布时间】:2014-12-18 22:37:35 【问题描述】:

如果我在 laravel 4.2 中使用软删除(数据库是 mysql),每个 eloquent 查询都有WHERE deleted_at IS NULLdeleted_at 上没有索引。

在大桌子上会很慢吗? (或者也许 IS NULL 不需要索引就可以优化) 我应该在deleted_at 列上添加索引吗?

那么,Laravel 的“soft_delete”deleted_at 列在 MySQL 中是否需要索引?

澄清:Laravel 在deleted_at 列中存储一个时间戳来表示记录何时被软删除,而不是布尔值。

【问题讨论】:

它不需要索引。如果为空,则不会被删除。如果它有值,则将其删除。这意味着它有两个可能的值才能工作。具有两个可用值的列不是很好的索引候选 - 它们的基数收敛到 0。因此,deleted_at 不需要索引。 好的,谢谢。我认为几乎每个where 子句都需要编入索引。如果需要,您可以发表评论作为答案。 【参考方案1】:

deleted_at 列不是一个好的索引候选。与评论相比,我将尝试更好地解释:索引仅在其基数相对较高时才有用。基数是描述数据集中索引唯一性的数字。这意味着它是记录总数除以唯一记录总数。

例如,主键的基数是1。每条记录都包含唯一的主键值。 1 也是最高的数字。您可以将其视为“100%”。

但是,deleted_at 之类的列没有这样的值。 Laravel 对 deleted_at 所做的是检查它是否为空。这意味着它有两个可能的值。包含两个值的列的基数极低,随着记录数的增加而降低。

您可以为这样的列建立索引,但它没有任何帮助。将会发生的事情是它可能会减慢速度并占用空间。

TL;DR:不,您不必为该列建立索引,索引不会对性能产生有益影响。

【讨论】:

这是否意味着没有理由为布尔列建立索引,还是我误解了? @Cabloo - 你没有误解,任何具有小范围唯一值的列都是错误的索引候选,即使你有一个 tinyint 其值为 0 和 @987654325 @. @N.B.从what I have been reading 开始,它不是关于唯一值的数量,而是关于值的分布。所以如果 50% 的记录为真,则不会影响查询时间。但是如果 5% 的记录是真的,那么它会减少查询时间。换句话说,它是关于“选择性”的。为了使索引有用,对该索引的搜索在数据集中必须相对较少。 @Cabloo - 有问题的人提到了只有 400 万行的表。我观察到 5000 万行表在没有索引的情况下表现良好。他们通过索引布尔列获得性能的一种情况并不足以证明这一点。想象一个有 1 亿条记录的表。只有一条记录包含true 值。所以是的,执行WHERE x = true 之类的查询会很快,并且您会推断为布尔列建立索引很棒。 WHERE x = false 呢? 2 除以 x,其中x > 0 and < infinity 告诉您,随着数据的增长,您将浪费空间。 @N.B. - 如果deleted_at 是一个包含许多不同值且很少为 NULL 的日期时间,那么索引它会很好。【参考方案2】:

我不知道为什么以上@N.B.有这么多的赞成票,在我的上下文中,我认为这完全不正确。

我在一些键表的deleted_at 时间戳中添加了索引,并享受了一些从32 秒缩短到5.4 毫秒以下的查询。这实际上取决于您应用的性质。

在我的场景中,我有 3 个带有软删除的表,一些简单的连接(都带有索引),但是由于 Laravel 处理软删除的默认性质,我的查询受到了影响。

我强烈建议为这些列编制索引,这样当记录数量增加时,您的应用程序就会阻塞。

【讨论】:

这两个例子都是可疑的——你没有显示有什么索引,尤其是那些将用于JOINing的索引——它不会出现在deleted_at上。 GROUP BY 也很重要。请同时显示SHOW CREATE TABLEEXPLAIN SELECT ... 丹,您是否阅读了答案并试图理解它?似乎您或 Rick James 都不知道 b-tree 是如何工作的。另外,您在运行查询之前是否清除了所有缓存?您确定innodb_buffer_pool 包含数据吗?您在这里做了一个完全错误的假设 - 第一个查询,即 32 秒的查询,在缓冲池中没有任何数据。一旦执行,它就会填满它。您的第二个查询现在使用内存中的数据。你错误地认为这是因为索引。 这些是可疑的查询——它们测试 any 变体是否为 NULL。使用EXISTS 并摆脱GROUP BY 可以更有效地完成此操作。 @Mjh - 核心点是优化器是否会使用 BTree 组织的索引。其次,如果它必须加载整个索引(或表),那么查看数百万条缓存记录可能需要 5 秒。 我删除了在 2.7 毫秒运行查询的索引并多次重新运行查询。我得到了 167、142 和 151。 innodb_buffer_pool 不是空的,我认为有一些优化,因为有轻微的增加?在此查询中有一个deleted_at IS NULL 检查,如果我删除 IS NULL 检查的 where 子句,则查询会在 27.5 毫秒内触发,而没有 deleted_at 索引。很明显,这些指数在标准中带来了巨大的好处。我不知道添加太多deleted_at 索引的后果,但我认为我不会在意这次经历。【参考方案3】:

简短回答:也许。

长答案:

如果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 NULLIS NOT NULL,但 MySQL 将其视为多值列。但是,由于测试是针对IS NULL 并且大多数行仍然是NULL,因此优化器的反应与案例1 相同。

案例 3:软删除的行多于仍然处于活动状态的行:现在索引突然变得有用,因为只有一小部分表 IS NULL

案例 2 和案例 3 之间没有确切的分界线。20% 是一个方便的经验法则。

现在,从执行的角度来看。

INDEX(deleted_at) 用于deleted_at IS NULL

    使用NULL 向下钻取第一行的索引 BTree。 扫描直到IS NULL失败。 对于每个匹配的行,进入 data BTree 以获取该行。

INDEX(deleted_at) 未使用:

    扫描数据 BTree(或使用其他索引) 对于每个 data 行,检查 deleted_at IS NULL,否则过滤掉该行。

复合索引:

可能有一个以deleted_at 开头的“复合”(多列)索引。示例:

INDEX(deleted_at, foo)

WHERE deleted_at IS NULL
  AND foo BETWEEN 111 AND 222

这很有可能有效地使用索引无论表中有多少百分比有deleted_at IS NULL

    使用NULLfoo >= 111 向下钻取第一行的索引BTree。 扫描直到IS NULLfoo <= 222 失败。 对于每个匹配的行,进入 data BTree 以获取该行。

请注意,在INDEX 中,NULL 的行为与任何其他单个值非常相似。 (并且NULLs 存储在其他值之前。)

【讨论】:

@Mjh - MySQL 优化的效果取决于 MySQL 明显的基数,而不是 Laravel 提供的 intent。如果deleted_at 实际上有NULL 或许多不同的TIMESTAMPs,那么MySQL 会将其视为布尔值。相反,它(错误地)假设不同值的数量是均匀分布的。

以上是关于Laravel“软删除”是不是需要 MySQL 上的索引?的主要内容,如果未能解决你的问题,请参考以下文章

软删除是不是适用于流明框架?流明的限制?

Laravel 5.5 模型上的自定义软删除

laravel软删除相关问题

如何在laravel背包中启用软删除

laravel软删除和恢复删除用户

Laravel 查询包括软删除的记录