我有一个 54k 行的表,包含 10G 的数据

我正在对其运行此更新查询:

UPDATE my_table SET blog_object_version='19'

运行需要1个多小时
如何提高性能?

附加信息:

我在亚马逊 rds 上运行, db.m5.4xlarge
这是我的例子: 在此处输入图片说明

这是我在aws 性能洞察中看到的:
wait/io/file/innodb/innodb_data_file

在此处输入图片说明

我的数据库上没有运行任何其他查询:

mysql> show processlist;
+----+----------+---------------------+----------+---------+------+----------+----------------------------------------------+
| Id | User     | Host                | db       | Command | Time | State    | Info                                         |
+----+----------+---------------------+----------+---------+------+----------+----------------------------------------------+
|  3 | rdsadmin | localhost:65182     | NULL     | Sleep   |    0 |          | NULL                                         |
|  4 | rdsadmin | localhost           | NULL     | Sleep   |    1 |          | NULL                                         |
|  6 | admin    | 123.45.67.890:6170  | my_table | Query   | 3901 | updating | UPDATE my_table SET blog_object_version='19' |
| 12 | admin    | 123.45.67.890:6360  | NULL     | Sleep   | 2981 |          | NULL                                         |
| 18 | admin    | 123.45.67.890:7001  | NULL     | Query   |    0 | starting | show processlist                             |
+----+----------+---------------------+----------+---------+------+----------+----------------------------------------------+

这是我的桌子:

mysql> show create table my_table\G;
*************************** 1. row ***************************
       Table: my_table
Create Table: CREATE TABLE `my_table` (
  `index` int(11) NOT NULL AUTO_INCREMENT,
  `id` varchar(100) DEFAULT NULL,
  `user_id` varchar(50) NOT NULL,
  `associate_object_id` varchar(50) NOT NULL,
  `type` varchar(50) DEFAULT NULL,
  `creation_date` datetime DEFAULT NULL,
  `version_id` varchar(50) NOT NULL,
  `blog_object` longtext,
  `blog_object_version` varchar(100) DEFAULT NULL,
  `last_update` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`index`),
  UNIQUE KEY `id_user_id_version_id` (`id`,`user_id`,`version_id`) USING BTREE,
  KEY `user_id_associate_object_id` (`user_id`,`associate_object_id`),
  KEY `user_id_associate_object_id_version_id` (`user_id`,`associate_object_id`,`version_id`)
) ENGINE=InnoDB AUTO_INCREMENT=54563151 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

这些是我的索引:

mysql> SHOW INDEX FROM my_table;
+----------+------------+----------------------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name                               | Seq_in_index | Column_name         | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------------------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| my_table |          0 | PRIMARY                                |            1 | index               | A         |       43915 |     NULL | NULL   |      | BTREE      |         |               |
| my_table |          0 | id_user_id_version_id                  |            1 | id                  | A         |        3659 |     NULL | NULL   | YES  | BTREE      |         |               |
| my_table |          0 | id_user_id_version_id                  |            2 | user_id             | A         |        8783 |     NULL | NULL   |      | BTREE      |         |               |
| my_table |          0 | id_user_id_version_id                  |            3 | version_id          | A         |       43915 |     NULL | NULL   |      | BTREE      |         |               |
| my_table |          1 | user_id_associate_object_id            |            1 | user_id             | A         |         378 |     NULL | NULL   |      | BTREE      |         |               |
| my_table |          1 | user_id_associate_object_id            |            2 | associate_object_id | A         |        4391 |     NULL | NULL   |      | BTREE      |         |               |
| my_table |          1 | user_id_associate_object_id_version_id |            1 | user_id             | A         |         385 |     NULL | NULL   |      | BTREE      |         |               |
| my_table |          1 | user_id_associate_object_id_version_id |            2 | associate_object_id | A         |        6273 |     NULL | NULL   |      | BTREE      |         |               |
| my_table |          1 | user_id_associate_object_id_version_id |            3 | version_id          | A         |       43915 |     NULL | NULL   |      | BTREE      |         |               |
+----------+------------+----------------------------------------+--------------+---------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

#1楼 票数:0

有了这个声明:

UPDATE my_table SET blog_object_version='19'

需要获取、检查和更新所有记录。 因为没有 WHERE 子句。

如果只需要更新一些记录(因为其他记录已经有blog_object_version='19'那么如果您这样做,您可能会看到(小的)改进:

UPDATE my_table SET blog_object_version='19' WHERE blog_object_version != '19'

因为他的语句只更新需要更改的记录,但仍然需要获取所有记录。

如果并非所有记录的blog_object_version都不等于“19”,那么在该字段上添加索引可能会有所改善,因为只有这样才能使用不等于“19”的blob_object_version获取这些记录。

如果所有记录都需要更新,那么这不会有任何改善......

#2楼 票数:0

非常基本的问题,有一个非常基本的解决方案:

INDEX(blog_object_version)

为什么? 如果没有这个索引, UPDATE必须读取54K中的一个(或54M?)行以检查'19'

使用该索引,只需读取相关行。

提示:

许多VARCHAR列听起来应该是INT (或者更小的东西,比如SMALLINT )? (更改类型不太可能加快查询速度。)

折腾user_id_associate_object_id ; 索引user_id_associate_object_id_version_id处理相同的事情。

更新所有行

更新多达 1K 行是合理的。 如果合适,更新少于 20% 的表可能会使用索引。

但是...如果您需要更新所有 54K 行,则有几个问题。

这将需要很长时间,并且可能需要大量磁盘空间,因为旧副本和新副本都会保留到更新完成。 (这是为了它可以原子地提交或回滚整个更新。)

通常,需要更新整个表的所有行中的列是“糟糕的设计”。 有时,可以将另一表中的列放在一行中。 然后是更新blog_object_version的一行查询。 但这意味着在SELECT需要时执行JOIN (这可能不是问题。)如果您不更改所有行,则情况会更加混乱。

所以,...如果您决定更新“很多”(或全部)大表,我建议以每块 100-1000 行的方式进行更新。 更多详情: http : //mysql.rjweb.org/doc.php/deletebig#deleting_in_chunks

更改缓冲区

另一个问题(不太重要)是更新非唯一索引列时,索引需要更新。 这需要修改表示INDEX的 BTree 。 对于非唯一索引,这是在后台完成的,主要是在提交查询之后。

在完成 BTree 更新之前,不会有索引错误的风险。 这是因为“更改缓冲区”。 该构造保持挂起的索引更新,以便以后持久化到磁盘。

  ask by dina translate from so

未解决问题?本站智能推荐:

1回复

最初,在大型MySQL表上进行简单查询需要很长时间,后来又要快得多

我们正努力应对缓慢的查询,这种查询仅在首次调用时才会发生。 之后查询要快得多。 第一次执行查询时,大约需要15-20秒。 后续通话时间不到1.5秒。 但是,如果几个小时没有再次调用,查询将再次花费15-20秒。 该表是一个称为系统(外键)的实体的每日读数表,其中包含系统ID,日期,
1回复

长时间运行mysql“清理”事务

我一直在尝试调试MySQL(AWS RDS)v5.6.19a中的“锁定等待超时超时”错误,当我尝试使用主ID选择行进行更新时偶尔会抛出该错误,即: 经过几个小时的调试后,我已经排除了我的应用程序的另一部分“直接”锁定同一行(这是明显的罪魁祸首)。 因此我开始深入研究mysql锁定的兔子
1回复

MyISAM-> InnoDB之后,mysql查询变慢并移至AWS

我计划从共享的托管服务器切换到AWS设置(针对Apache / php的EC2,针对MySQL的RDS)。 我在共享服务器上运行了超过一年的PHP / mysql网站测试版。 尤其是一页在浏览器中总是运行很快(完全加载可能少于3秒)。 在准备搬迁时,我将数据库和所有表从MyISAM
1回复

具有复合索引和日期范围的 AWS RDS MySql 简单查询在大约 800 万个数据中执行时间太长

查询非常简单,即 但第一次运行时,最终返回结果需要 30 到 60 秒。 然后它在 10 秒内返回结果。 另一个问题是,当我更改 device_id 时,它再次需要很长时间。 除了使用正确的索引之外,我无法理解为什么会发生这种情况。 我们知道,由于我们的 API 遇到超时,API Gatewa
1回复

MySQL:binlogs保留了多长时间?

我有一个mysql slave,我试图复制一个主mysql实例。 我从生产主实例迁移了一周左右的数据。 当时我在master上调用了SHOW MASTER STATUS并获得了binlog名称和位置。 现在,当我运行SHOW MASTER STATUS我得到: 那个binlog
2回复

如何在Amazon RDS上为MySQL禁用innodb_doublewrite?

请任何人帮我禁用我在Amazon RDS上托管的MySQL数据库的“innodb_doublewrite”。 我需要这个,因为我们需要快速更新大约1500万行。 我知道有一个启动选项: 但是怎么用呢? 除此之外,Amazon RDS参数组不会显示“innodb_double
1回复

MySQL:InnoDB与MyISAM:如何以及为什么要改变(Amazon RDS)?

我没有设置我的数据库并注意到我的一些表是innoDB而其他表是MyISAM。 我有兴趣改变它们的原因是因为Jurg van Vliet (在AWS上写O'Reilly书籍) 说 Amazon RDS的自动数据库备份能力取决于所有表是innoDB表。 将数据库中的所有表转换为InnoDB的
1回复

“mysql - 模式的默认字符集:utf8”中的模式“mysql”是什么意思? 是innodb吗?

在步骤 5.7 -> 8.0.23 中从 5.6 -> 5.7 -> 8.0.23 升级 mysql 时,我收到警告: “mysql - 模式的默认字符集:utf8”中的模式“mysql”是什么意思? 是innodb吗?