mysql - INSERT INTO SELECT 在集群上需要很长时间

标签 mysql query-performance mysql-cluster percona-xtradb-cluster

我的 mysql 集群:Ver 5.6.30-76.3-56,适用于 x86_64 上的 debian-linux-gnu(Percona XtraDB Cluster (GPL),版本 rel76.3,修订版 aa929cb,WSREP 版本 25.16,wsrep_25.16)

我有一个复杂的 SQL 查询,它使用以下语法将大约 36k 行插入到表中:

INSERT INTO `sometable` (SELECT ...);

选择有点复杂,但并不慢(0.0023s),但插入大约需要40-50s。当我插入行时,该表并未使用。

我的问题是:

  • 我可以以某种方式加快速度吗?
  • 缓慢的插入会导致其他表出现锁定问题(因为 select)
  • 此工作流程是好还是坏做法?还有更好的吗?

谢谢

更新:

表架构:

CREATE TABLE `sometable` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) unsigned DEFAULT NULL,
  `a` varchar(255) DEFAULT NULL,
  `b` smallint(6) unsigned DEFAULT NULL,
  `c` smallint(6) unsigned DEFAULT NULL,
  `d` smallint(6) unsigned DEFAULT NULL,
  `e` smallint(6) unsigned DEFAULT NULL,
  `f` varchar(255) DEFAULT '',
  `country_id` int(10) unsigned DEFAULT NULL,
  `city_id` int(10) unsigned DEFAULT NULL,
  `g` smallint(6) unsigned DEFAULT NULL,
  `h` smallint(6) unsigned DEFAULT NULL,
  `i` smallint(6) unsigned DEFAULT NULL,
  `j` smallint(6) unsigned DEFAULT NULL,
  `k` smallint(6) unsigned DEFAULT NULL,
  `l` varchar(3) DEFAULT NULL,
  `m` varchar(3) DEFAULT NULL,
  `n` text,
  `o` varchar(255) DEFAULT NULL,
  `p` varchar(32) DEFAULT NULL,
  `q` varchar(32) DEFAULT NULL,
  `r` varchar(32) DEFAULT NULL,
  `s` time DEFAULT NULL,
  `t` time DEFAULT NULL,
  `u` text,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `country_id` (`country_id`),
  KEY `city_id` (`city_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

更新2:

当我尝试运行查询时,在某些情况下会出现错误:

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

我的解决方案:

如果有人感兴趣,这是我的最终解决方案: gist

主要问题是,当我填充 mytable 时,其他查询被卡住,并且集群出现严重的性能问题。在此解决方案中,我创建一个临时表并以“脏读”模式填充数据,然后将这些数据分块复制到 mytable 中,因此需要更多时间,但没有性能问题,并且没有卡住查询。

最佳答案

SELECT 操作每 64 纳秒返回一次您描述的长度的行,速度非常快。这就是 2.3 毫秒内 36 公里的结果。您的 SELECT 查询计时似乎没有考虑到将结果集传输到 MySQL 客户端。无论如何,使用该性能与 INSERT 操作进行比较会让您的期望过高。

您可以在开始操作之前尝试发出此命令。它将允许您的 SELECT 操作继续进行,同时减少 SELECT 源表上应用程序流量的争用。看这里https://dev.mysql.com/doc/refman/5.7/en/set-transaction.html

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

您可以尝试一个包含临时表的两步过程。这样做的优点是不必在执行 SELECT 操作的同时更新 some_table 中的所有索引。该操作将如下所示。

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
CREATE TEMPORARY TABLE insert_batch AS SELECT ... ;
INSERT INTO some_table SELECT * FROM insert_batch;
DROP TEMPORARY TABLE insert_batch;

您应该了解,InnoDB 将批量插入作为单个事务发布到表中。如果您能够以一次处理大约 500 行而不是 36K 的方式执行此操作,您将拥有更多事务,但它们会更小。这通常是获得更高吞吐量的一种方法。

关于mysql - INSERT INTO SELECT 在集群上需要很长时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45347955/

相关文章:

mysql - 使用限制为 1 的 order by 时查询速度慢

MySQL 数据库集群与大型爬虫索引的复制设置

php - MySQL 服务器消失了 haProxy 不断出现错误

python - 尝试将 Flask-mysql 与 python 一起使用

python - 带参数的 GET 请求 FlasK/Rest API

sql-server - sql性能问题

mysql - 当在普通表和从索引表中进行选择之间执行内连接时,索引是否有用

MySQL集群配置

php - MYSQL SUM 行和 GROUP by

python - 优化 Python 代码以进行数据库访问