【问题标题】:MySQL very big table - creating sort index for over 24 hoursMySQL 非常大的表 - 创建超过 24 小时的排序索引
【发布时间】:2015-02-01 02:56:51
【问题描述】:

我有一个非常大的 MySQL 表(6800 万行),

我正在尝试使用以下查询每分钟保持一行:

    delete bt     
    from table1 bt 
    join          (select date, min(time) as time
       from table1           
    group by date, hour(time), minute(time)          
    ) 
    btt          
    on btt.date = bt.date 
and hour(bt.time) = hour(btt.time) 
and minute(bt.time) = minute(btt.time) 
and btt.time <> bt.time

我的表有以下列

+----------------+-------------+------+-----+---------+----------------+
| Field          | Type        | Null | Key | Default | Extra          |
+----------------+-------------+------+-----+---------+----------------+
| id             | int(11)     | NO   | PRI | NULL    | auto_increment |
| date           | varchar(11) | NO   |     | NULL    |                |
| time           | varchar(12) | NO   |     | NULL    |                |
| gmt_offset     | varchar(2)  | YES  |     | NULL    |                |
| type           | varchar(10) | YES  |     | NULL    |                |
| yield_b        | varchar(10) | YES  |     | NULL    |                |
| yield_d        | varchar(10) | YES  |     | NULL    |                |
+----------------+-------------+------+-----+---------+----------------+

查询运行超过 24 小时,当我运行时

SHOW FULL PROCESSLIST;

国家说

Creating sort index

这样的查询需要这么长时间是正常的吗?有什么方法可以加快速度吗?谢谢!

编辑:

Gordon 的回答是对的,只有一行小错误。这是正确的查询,确实比前一个查询快得多:

create table temp_table1 as
    select t.*
    from (select t1.*,
                 (@rn := if(@prevd = date and minute(time) = @prevm, @rn + 1,
                            if(@prevd := date, if(@prevm := minute(time), 1, 1), 1)
                           )
                 ) as seqnum
          from table1 t1 cross join
               (select @rn := 0, @prevd := 0, @prevm := 0) vars
          order by date, time
         ) t
     where seqnum = 1;

【问题讨论】:

  • 请澄清。从您的查询看来,您正试图在每个不同的分钟内删除除第一行(time 的最小值)之外的所有行。那是对的吗?请出示您的索引。有什么理由必须将datetime 分开吗?如果您将它们组合成一个列,给它一个 DATETIME 数据类型,并为它建立索引,那么您将顺利找到一个有效的解决方案。
  • 还有一个问题:id 值是否随日期+时间单调增加?也就是说,如果一个id 大于另一个,是否保证其日期+时间与另一个相同或晚于另一个?

标签: mysql sorting indexing


【解决方案1】:

戈登的回答很好。这是另一种方法,如果您的 id 随时间单调增加,这将起作用。

首先,获取每个不同分钟内第一次观察的id 值。

SELECT MIN(id) As first_id_in_minute
  FROM table1 
 GROUP BY date, HOUR(time), MINUTE(time)

这些是您要保留的行的id 值。

然后删除其余的行。使用LEFT JOIN ... IS NULL 获取不匹配的行。这可能比IS NOT IN(...) 快。

 DELETE a 
   FROM table1 AS a
   LEFT JOIN  (
                   SELECT MIN(id) As first_id_in_minute
                     FROM table1 
                 GROUP BY date, HOUR(time), MINUTE(time)
             ) AS b ON a.id = b.first_id_in_minute
 WHERE b.first_id_in_minute IS NULL
 LIMIT 1000

我设置了 LIMIT 1000 以减小每个 DELETE 操作的大小。您应该重复此查询,直到它声明没有行受到影响。

尝试在 (date, time, id) 上放置一个复合索引以加速其中的 MIN() ... GROUP BY 部分。

就像 Gordon 建议的那样,在桌子的副本上试试这个,嗯?

【讨论】:

  • 嗨奥利。事实上,在我的数据中,时间随着 ID 的增加而增加,所以你所说的是一个非常好的主意。谢谢
【解决方案2】:

与其删除一堆行,不如用你想要的数据创建一个临时表,然后截断原始表并将其重新插入:

create table temp_table1 as
    select t.*
    from (select t1.*,
                 (@rn := if(@prevd <> date or minute(time) <> @prevm, 1,
                            if(@prevd := date, if(@prevm := minute(time), 1, 1), 1)
                           )
                 ) as seqnum
          from table1 t1 cross join
               (select @rn := 0, @prevd := 0, @prevm := 0) vars
          order by date, time
         ) t
     where seqnum = 1;


truncate table table1;

insert into table1(col1, . . ., coln)
    select col1, . . . , coln
    from temp_table1;

第一个查询有一个子查询,它在一分钟内枚举行。然后,只保留第一个。然后将其插入到表的空版本中。当然,在截断原始表之前测试第一个查询的结果(我会将数据复制到其他地方,以防万一)。

【讨论】:

  • 太棒了。多谢!我也不需要插入 seqnum ,对吗?这个变量是什么意思?某种索引?
  • @adrCoder 。 . . seqnum 枚举原始表中同一分钟内出现的行。逻辑取第一个。
  • 嗨,戈登。您的解决方案不起作用。它基本上将 seqnum = 1 放入每一行并且不删除任何行..你能看一下吗?谢谢
猜你喜欢
  • 2016-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-24
相关资源
最近更新 更多