MySQL更新与子查询和愚蠢的慢

时间:2017-06-20 03:59:52

标签: mysql performance subquery between

我有一个简单的查询。我所要做的就是将地理位置数据组合到一个主表中,这样我就不需要在提取数据时使用连接。是的,加入很好,但我想把所有数据放在一个表中......

这是我的问题:

> explain UPDATE test AS l 
SET l.country_name1 = ( 
select country_name
from ip2location_db9 
    where  
        l.ip >= ip_from
        ORDER BY ip_from DESC
        LIMIT 1) 

******************** 1. row *********************
           id: 1
  select_type: UPDATE
        table: l
   partitions: 
         type: ALL
possible_keys: 
          key: 
      key_len: 
          ref: 
         rows: 10
     filtered: 100.00
        Extra: 
******************** 2. row *********************
           id: 2
  select_type: DEPENDENT SUBQUERY
        table: ip2location_db9
   partitions: 
         type: index
possible_keys: idx_ip_from,idx_ip_from_to,ip_from_to,ip_from
          key: idx_ip_from
      key_len: 5
          ref: 
         rows: 1
     filtered: 33.33
        Extra: Using where; Backward index scan
2 rows in set

简单吧?除了这10行的查询需要130多秒? 单独运行时的select语句超快(0.00秒)并且工作正常。

explain select country_name
from ip2location_db9
    where  
        ip_from <= INET_ATON('114.160.63.108')
        ORDER BY ip_from DESC
       LIMIT 1

******************** 1. row *********************
           id: 1
  select_type: SIMPLE
        table: ip2location_db9
   partitions: 
         type: range
possible_keys: idx_ip_from,idx_ip_from_to
          key: idx_ip_from
      key_len: 5
          ref: 
         rows: 1949595
     filtered: 100.00
        Extra: Using index condition
1 rows in set

但是,当我在测试表中只使用14行的update语句时,查询会花费130秒。

表格设置如下:

CREATE TABLE `test` (
  `ip` int(10) unsigned DEFAULT NULL,
  `country_name` varchar(64) COLLATE utf8_bin DEFAULT NULL,
  `region_name` varchar(128) COLLATE utf8_bin DEFAULT NULL,
  `city_name` varchar(128) COLLATE utf8_bin DEFAULT NULL,
  `latitude` double DEFAULT NULL,
  `longitude` double DEFAULT NULL,
  `zip_code` varchar(30) COLLATE utf8_bin DEFAULT NULL,
  `data` varchar(512) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

CREATE TABLE `ip2location_db9`(
    `ip_from` INT(10) UNSIGNED,
    `ip_to` INT(10) UNSIGNED,
    `country_code` CHAR(2),
    `country_name` VARCHAR(64),
    `region_name` VARCHAR(128),
    `city_name` VARCHAR(128),
    `latitude` DOUBLE,
    `longitude` DOUBLE,
    `zip_code` VARCHAR(30),
    INDEX `idx_ip_from` (`ip_from`),
    INDEX `idx_ip_to` (`ip_to`),
    INDEX `idx_ip_from_to` (`ip_from`, `ip_to`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

请注意,在运行更新查询时,硬盘驱动器活动会变得疯狂,利用率很高。 看到?没有什么花哨。表数据匹配。行正确编入索引。

解释说明使用索引就像我自己执行选择时一样:

******************** 1. row *********************
           id: 1
  select_type: UPDATE
        table: l
   partitions: 
         type: ALL
possible_keys: 
          key: 
      key_len: 
          ref: 
         rows: 14
     filtered: 100.00
        Extra: 
******************** 2. row *********************
           id: 2
  select_type: DEPENDENT SUBQUERY
        table: ip2location_db93
   partitions: 
         type: index
possible_keys: idx_ip_from,idx_ip_from_to
          key: idx_ip_from
      key_len: 5
          ref: 
         rows: 1
     filtered: 33.33
        Extra: Using where
2 rows in set

那么我可能做错了什么会导致查询花费2分钟来运行14行? 它不是硬件。 i7 6990,32gb ram,跑掉ssd。这不是世界上最快的,但我可以比这个查询更快地手动更新14行......

我花了很多时间搜索,试图找出为什么这么长时间。我假设我没有正确搜索。也许我不知道一个特定的术语或某些东西会指向我正确的方向。我不是数据库的人。只是为了工作而做这件事。

希望你们能保住我的理智......

添加更多信息......

我试图让这个查询工作很多方面。堆栈上的其他问题表示要避免子查询并使用连接。好的,这是我尝试的第一件事,但我不能让查询使用我建立的索引。

> explain UPDATE test AS l 
    JOIN 
        ip2location_db9 AS h
    ON  
        l.ip <= h.ip_from and l.ip >= h.ip_to
    SET 
        l.country_name1 = h.country_name



******************** 1. row *********************
           id: 1
  select_type: UPDATE
        table: l
   partitions: 
         type: ALL
possible_keys: ip
          key: 
      key_len: 
          ref: 
         rows: 10
     filtered: 100.00
        Extra: 
******************** 2. row *********************
           id: 1
  select_type: SIMPLE
        table: h
   partitions: 
         type: ALL
possible_keys: idx_ip_from,idx_ip_to,idx_ip_from_to,ip_from_to,ip_from,ip_to
          key: 
      key_len: 
          ref: 
         rows: 3495740
     filtered: 11.11
        Extra: Range checked for each record (index map: 0x3F)
2 rows in set

使用联接的更新是否比这更容易?
即使使用强制索引也不会使查询使用索引。

我已经尝试了很多方法

UPDATE test
JOIN (
   SELECT * FROM ip2location_db9 
) AS t1 
ON (test.ip <= t1.ip_from and test.ip >= t1.ip_to)
SET test.country_name1 = t1.country_name


UPDATE test l,
    (SELECT 
        *
    FROM
        ip2location_db9,test
    WHERE
        test.ip >= ip_from 
    ORDER BY ip_from DESC
    LIMIT 1) AS PreQualified 
SET 
    l.country_name1 = PreQualified.country_name

什么都行不通!我做错了什么?

0 个答案:

没有答案