【问题标题】:MySQL update not using indexes with WHERE IN clause after certain valueMySQL更新在特定值之后不使用带有WHERE IN子句的索引
【发布时间】:2022-11-02 07:39:10
【问题描述】:

我们有一个包含大约 1000 万条记录的表,我们正在尝试使用 where 子句中的 id(主键)更新一些列。

UPDATE  table_name SET column1=1, column2=0,column3='2022-10-30' WHERE id IN(1,2,3,4,5,6,7,......etc);

场景 1:当 IN 子句中有 3000 个或更少的 id 时,如果我尝试 EXPLAIN,则“possible_keys”和“key”显示 PRIMARY,并且查询执行得非常快。

场景 2:当 IN 子句中有 3000 个或更多 id(最多 30K)时,如果我尝试 EXPLAIN,则“possible_keys”显示 NULL,“key”显示 PRIMARY,查询将永远运行。如果我使用 FORCE INDEX(PRIMARY),那么“possible_keys”和“key”会显示 PRIMARY,并且查询执行得非常快。

场景 3:当 IN 子句中有超过 30k 个 id 时,即使我使用 FORCE INDEX(PRIMARY),“possible_keys”显示为 NULL,“key”显示为 PRIMARY,查询将永远运行。

我相信优化器会进行全表扫描而不是索引扫描。我们是否可以进行任何更改以使优化器进行索引扫描而不是表扫描?请建议是否需要更改任何参数来解决此问题。

MySQL版本是5.7

【问题讨论】:

    标签: mysql


    【解决方案1】:

    据我所知,您只需要提供一个包含所有 id 的临时表并从中加入 table_name :

    update (select 1 id union select 2 union select 3) ids
    join table_name using (id) set column1=1, column2=0, column3='2022-10-30';
    

    在 mysql 8 中,您可以使用更简洁的值表构造函数(省略 mariadb 的“行”,例如values (1),(2),(3)):

    update (select null id where 0 union all values row(1),row(2),row(3)) ids
    join table_name using (id) set column1=1, column2=0, column3='2022-10-30';
    

    fiddle

    【讨论】:

    • 谢谢,@ysth 但我试图理解为什么它会以这种方式运行,并且还想知道我们是否可以进行任何更改,以便优化器进行索引扫描而不是全表扫描。
    • 您不是唯一遇到此问题的人(在一个长值列表中,可能与表行成比例,选择完全扫描)。 IIRC 有些人在升级到 5.7 后遇到了它。我以为我曾经提到过会影响它的设置,但我搜索得相当彻底,找不到这样的东西。我不记得这是否也是 8 的问题。IMO 这是 FORCE INDEX 没有修复它的错误。
    【解决方案2】:

    UPDATEing 具有所有相同更新值的表的重要块时,我看到了一个危险信号。

    您是否总是更新同一组行?该信息可以在您加入的较小的单独表中吗?

    或者可能是其他一些结构的专注于帮助更新更快的架构更改?

    如果你必须有一个很长的 IN 列表,我建议一次做 100 个。并且不要尝试在同一笔交易中COMMIT 全部 3000+。 (分块提交违反了一些业务逻辑,所以你可能不想这样做。)

    【讨论】:

      猜你喜欢
      • 2010-10-09
      • 2015-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-03
      • 1970-01-01
      相关资源
      最近更新 更多