提高 MYSQL 涉及联合的查询的性能

Increase Performance of MYSQL QUERY involving unions

有一个 Golang 实用程序,它能够每天减少 table 历史数据中的数据点。

记录范围为每天 20 到 400 条记录。 总共至少有1亿条记录。

该实用程序能够 trim 在给定日期之前每天减少到 n 条记录。 (n 的范围为每天 1 到 300 条记录)

我使用的方法如下:

第 1 步:

CREATE TABLE main_table_tmp LIKE main_table;

第 2 步:

ALTER TABLE main_table_tmp ADD COLUMN timekey INT;

第 3 步:

INSERT INTO main_table_tmp 
SELECT * FROM (
  SELECT *,FLOOR(UNIX_TIMESTAMP(column_name)/((1440/2)*60)) AS timekey 
  FROM main_table
  WHERE column_name <= '2018-01-01' 
  GROUP BY timekey
) m 
UNION ALL 
(SELECT * ,0 As timekey FROM main_table where column_name > 'date') ;

第 4 步:

ALTER TABLE main_table_tmp DROP COLUMN timekey;

DROP TABLE maintable;

RENAME TABLE maintable_tmp TO maintable;

我正在使用 golang 实现上述目标。

func somefuncname(){

  ---- 
  ----
  ----
  q := "CREATE TABLE " + *tablename + "_tmp LIKE " + *tablename + ";"
  rows, err := db.Query(q)
  if err != nil {
  fmt.Println(err)
  }
//--ALTER ADD timekey
//--INSERT INTO SELECT *....
//--ALTER DROP timekey ,DROP table and rename

}

这个查询的当前响应时间很慢

部分结果: 总记录: 200 万条
执行时间: 180 秒

这是在 16Gb RAM 上 CPU 部署在低等级系统上很慢

我为解决此问题采取的步骤:

  1. 查看了所有 table 的索引。尝试删除索引并 运行ning 实用程序。删除索引使实用程序快了 5 秒,这也不算多。

  2. 分阶段执行实用程序:如果总记录超过 100 万条,则 运行 实用程序一次 100 万条

但经过所有这些努力,主要问题似乎出在查询本身。

只是不够快。我只是需要一种提高查询效率的方法

感谢任何帮助, 谢谢你们!!

为什么我们添加 timekey 然后删除它?将它添加到一个空的 table 中很快,但是在它被填充后从 table 中删除它,就像 table 的一个额外副本。如果我们不需要,那是不必要的工作。

我们可以对一个表达式做一个GROUP BY;该表达式不必出现在 SELECT 列表中。例如:

SELECT t.*
  FROM main_table t
 WHERE t.column_name <= '2018-01-01'
 GROUP 
    BY FLOOR(UNIX_TIMESTAMP(t.column_name)/((1440/2)*60))

(请注意,如果 sql_mode 中包含 ONLY_FULL_GROUP_BY,此查询将导致错误;这会禁用允许查询 运行 的 MySQL-specific 扩展。)

没有一些 table 定义(包括存储引擎、列数据类型、索引)并且没有 EXPLAIN 输出,我们只是猜测。

但是一些建议:

在正在填充的空 table 上删除二级索引,并在 table 加载后添加它们。

我会避免 UNION。鉴于其中一个 SELECT 语句在 column_name 上有一个谓词,而另一个在完全不同的列 date 上有一个谓词,我们确实想要分开 SELECT 语句。

CREATE TABLE main_table_tmp LIKE main_table
;

-- for performance, remove secondary indexes, leave just the cluster index
ALTER TABLE main_table_tmp 
    DROP INDEX noncluster_index_1
  , DROP INDEX noncluster_index_2
  , ...
;

-- for performance, have a suitable index available on main_table 
-- with `column_name` as the leading column
INSERT INTO main_table_tmp 
SELECT h.*
  FROM main_table h
 WHERE h.column_name <= '2018-01-01'
 GROUP 
    BY FLOOR(UNIX_TIMESTAMP(h.column_name)/((1440/2)*60))
;

-- for performance, have a suitable index available on main_table
-- with `date` as the leading column
INSERT INTO main_table_tmp
SELECT c.*
  FROM main_table
 WHERE c.date > '????-??-??'
;

-- add secondary indexes 
ALTER TABLE maint_table_tmp
    ADD UNIQUE INDEX noncluster_index_1 (fee,fi,fo)
  , ADD INDEX noncluster_index_2 (fum)
  , ...
;