Mysql 具有多个选择的查询导致高 CPU 负载

Mysql query with multiple selects results in high CPU load

我正在尝试做一个 link 交换脚本和 运行 有点麻烦。 每个 link 可以被一个 IP 地址访问 x 次(频率 link 秒 table)。每次访问都会花费一定数量的积分(花费限制在 link 秒 table 中给出)

我有以下 tables:

CREATE TABLE IF NOT EXISTS `contor` (
`key` varchar(25) NOT NULL,
`uniqueHandler` varchar(30) DEFAULT NULL,
`uniqueLink` varchar(30) DEFAULT NULL,
`uniqueUser` varchar(30) DEFAULT NULL,
`owner` varchar(50) NOT NULL,
`ip` varchar(15) DEFAULT NULL,
`credits` float NOT NULL,
`tstamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`key`),
KEY `uniqueLink` (`uniqueLink`),
KEY `uniqueHandler` (`uniqueHandler`),
KEY `uniqueUser` (`uniqueUser`),
KEY `owner` (`owner`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `links` (
`unique` varchar(30) NOT NULL DEFAULT '',
`url` varchar(1000) DEFAULT NULL,
`frequency` varchar(5) DEFAULT NULL,
`limit` float NOT NULL DEFAULT '0',
PRIMARY KEY (`unique`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我有以下查询:

$link = MYSQL_QUERY("
    SELECT * 
    FROM `links` 
    WHERE (SELECT count(key) FROM contor WHERE ip = '$ip' AND contor.uniqueLink = links.unique) <= `frequency` 
    AND (SELECT sum(credits) as cost FROM contor WHERE contor.uniqueLink = links.unique) <= `limit`")

table link 中有 20 行。

问题是,只要 table contor 中有大约 200k 行,CPU 负载就会很大。

应用@Barmar 提供的解决方案后: 在 (uniqueLink, ip) 上添加了复合索引并删除了除 PRIMARY 之外的所有其他索引,EXPLAIN 给了我这个:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     l   ALL     NULL    NULL    NULL    NULL    18  
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    15  
2   DERIVED     pop_contor  index   NULL    contor_IX1  141     NULL    206122

尝试使用连接而不是相关子查询。

SELECT l.*
FROM links AS l
LEFT JOIN (
    SELECT uniqueLink, SUM(ip = '$ip') AS ip_visits, SUM(credits) AS total_credits
    FROM contor
    GROUP BY uniqueLink
) AS c
ON c.uniqueLink = l.unique AND ip_visits <= frequency AND total_credits <= limit

如果这没有帮助,请尝试在 contor.ip 上添加索引。

当前查询的格式为:

 SELECT l.* 
   FROM `links` l
  WHERE l.frequency >= ( SELECT COUNT(ck.key)
                           FROM contor ck
                          WHERE ck.uniqueLink = l.unique
                            AND ck.ip = '$ip' 
                       )
    AND l.limit     >= ( SELECT SUM(sc.credits) 
                           FROM contor sc
                          WHERE sc.uniqueLink = l.unique 
                       )

那些相关的子查询将用于您的午餐。还有你的饭盒。

我建议测试一个内联视图,该视图一次执行来自 contor 的两个聚合,然后将结果连接到 links table。 像这样:

 SELECT l.*
   FROM ( SELECT c.uniqueLink
               , SUM(c.ip = '$ip' AND c.key IS NOT NULL) AS count_key
               , SUM(c.credits)                          AS sum_credits
            FROM `contor` c
           GROUP
              BY c.uniqueLink
        ) d
   JOIN `links` l
     ON l.unique     = d.uniqueLink
    AND l.frequency >= d.count_key
    AND l.limit     >= d.sum_credits

为了聚合内联视图查询的最佳性能,提供一个覆盖索引,MySQL 可以使用它来优化 GROUP BY(避免使用文件排序操作)

  CREATE INDEX `contor_IX1` ON `contor` (`uniqueLink`, `credits`, `ip`) ;

添加该索引会使 uniqueLink 索引变得多余,因此...

  DROP INDEX `uniqueLink` ON `contor` ;

编辑

因为我们保证 contor.key 列是非 NULL(即 NOT NULL 约束),上面的这部分查询是不需要的 AND c.key IS NOT NULL,可以删除. (我还从上面的覆盖索引定义中删除了 key 列。)

 SELECT l.*
   FROM ( SELECT c.uniqueLink
               , SUM(c.ip = '$ip')  AS count_key
               , SUM(c.credits)     AS sum_credits
            FROM `contor` c
           GROUP
              BY c.uniqueLink
        ) d
   JOIN `links` l
     ON l.unique     = d.uniqueLink
    AND l.frequency >= d.count_key
    AND l.limit     >= d.sum_credits