由于高负载重新设计查询/数据库

Posted

技术标签:

【中文标题】由于高负载重新设计查询/数据库【英文标题】:Redesign query/database because of high load 【发布时间】:2017-10-29 17:34:31 【问题描述】:

我在查询或重新设计我的数据库方面需要帮助。我不是数据库管理员,也不是 mysql 专家。

我有以下表格:

CREATE TABLE IF NOT EXISTS `pop_contor` (
  `key` varchar(50) 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,
  `page` varchar(500) DEFAULT NULL,
  `share` float DEFAULT NULL,
  `cadv` float NOT NULL,
  `os` varchar(50) NOT NULL,
  `browsershort` varchar(50) NOT NULL,
  `browser` varchar(50) DEFAULT NULL,
  `country` varchar(10) DEFAULT NULL,
  `date` date DEFAULT NULL,
  `hour` int(2) NOT NULL,
  `tstamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`key`),
  KEY `contor_IX1` (`uniqueLink`,`ip`),
  KEY `owner` (`owner`,`share`,`hour`),
  KEY `uniqueUser` (`uniqueUser`,`share`,`hour`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

和:

   CREATE TABLE IF NOT EXISTS `pop_links` (
  `unique` varchar(8) NOT NULL DEFAULT '',
  `uniqueUser` varchar(30) DEFAULT NULL,
  `uniqueCategories` varchar(150) DEFAULT NULL,
  `browser` varchar(60) NOT NULL,
  `os` varchar(50) NOT NULL,
  `country` varchar(100) NOT NULL,
  `name` varchar(50) DEFAULT NULL,
  `url` varchar(500) CHARACTER SET ascii DEFAULT NULL,
  `description` varchar(150) DEFAULT NULL,
  `bid` varchar(6) DEFAULT NULL,
  `amount` varchar(5) DEFAULT NULL,
  `remain` varchar(20) DEFAULT NULL,
  `rtoday` varchar(20) NOT NULL DEFAULT '0',
  `frequency` varchar(2) DEFAULT NULL,
  `dlimit` varchar(6) NOT NULL DEFAULT '0',
  `hours` varchar(100) NOT NULL DEFAULT 'all',
  `block` varchar(500) CHARACTER SET ascii NOT NULL,
  `valid` int(1) DEFAULT NULL,
  `payed` int(1) DEFAULT NULL,
  `startDate` date DEFAULT NULL,
  `endDate` date DEFAULT NULL,
  `date` date DEFAULT NULL,
  PRIMARY KEY (`unique`),
  KEY `unique` (`unique`,`uniqueCategories`,`browser`,`os`,`country`,`url`,`bid`,`remain`,`rtoday`,`frequency`,`hours`,`block`,`valid`,`startDate`,`endDate`,`uniqueUser`    )
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我要做的是根据几个标准向每个访问者(基于 ip)显示最适合的链接。我尝试了几个查询,但由于服务器仍然超载而失败。它主要发生在表 pop_contor 获得大约 200k 个条目时。表格链接中有大约 30 行。

第一次尝试是选择>where>select>where>select>where,但它吃了我的午餐,包括我的午餐盒。

第二次尝试是(查询大约需要 5 秒才能完成):

SELECT l. * FROM pop_links AS l 
LEFT JOIN ( SELECT uniqueLink, SUM( ip = '".$ip."' ) AS ip_visits 
FROM pop_contor 
GROUP BY uniqueLink ) AS c 
ON c.uniqueLink = l.unique AND ip_visits <= frequency 
WHERE (`uniqueCategories` LIKE '%,".$cat.",%'OR `uniqueCategories` = '1') 
AND (`hours` LIKE '%,".date("H").",%' OR `hours` = 'all') 
AND (`browser` LIKE '%".$user_browser[name]."%' OR `browser` = '1') 
AND (`country` LIKE '%".$ccode."%' OR `country` = '1') 
AND (`os` LIKE '%".$user_browser[platform]."%' OR `os` = '1') 
AND (`remain` > '0') 
AND (`rtoday` > '0') 
AND `valid` = '1' 
AND (`block` NOT LIKE '%".$unique."%') 
ORDER BY `bid` DESC, `remain` DESC 
LIMIT 1

解释:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     l   ALL     NULL    NULL    NULL    NULL    16  Using where; Using temporary; Using filesort
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    16  
2   DERIVED     pop_contor  index   NULL    contor_IX1  141     NULL    299128  Using index

第三次尝试:

SELECT pop_links.unique, pop_links.uniqueUser, pop_links.uniqueCategories, pop_links.browser, pop_links.os, pop_links.country, pop_links.url, pop_links.bid, pop_links.remain, pop_links.rtoday, pop_links.frequency, pop_links.hours, pop_links.block, pop_links.valid, pop_links.startDate, pop_links.endDate, COUNT( IF( pop_contor.ip = '".$ip."', 1, NULL ) ) < pop_links.frequency AS toto 
FROM pop_links 
LEFT JOIN pop_contor 
ON pop_links.unique = pop_contor.uniqueLink 
WHERE (`uniqueCategories` LIKE '%,".$cat.",%' OR `uniqueCategories` = '1') 
AND (`hours` LIKE '%,".date("H").",%' OR `hours` = 'all') 
AND (pop_links.browser LIKE '%".$user_browser[name]."%' OR pop_links.browser = '1') 
AND (pop_links.country LIKE '%".$ccode."%' OR pop_links.country = '1')
AND (pop_links.os LIKE '%".$user_browser[platform]."%' OR pop_links.os = '1')
AND (`remain` > '0') 
AND `rtoday` > '0' 
AND `valid` = '1' 
AND (`block` NOT LIKE '%".$unique."%') 
GROUP BY pop_links.unique, pop_contor.uniqueLink 
ORDER BY `toto` DESC , pop_links.bid DESC , pop_links.remain DESC 
LIMIT 1

解释:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  pop_links   index   NULL    unique  2683    NULL    16  Using where; Using index; Using temporary; Using f...
1   SIMPLE  pop_contor  ref     contor_IX1  contor_IX1  93  admin_pops.pop_links.unique     13277   Using index

查询在大约 0.3 秒内完成,但服务器上的负载仍然很高。 SHOW PROCESSLIST 向我展示了很多“复制到 tmp 表”状态。

【问题讨论】:

发布到dba.stackexchange.com 和Check this out 谢谢@AbdullaNilam 在 dba.stack 上我得到“您每 40 分钟只能发帖一次。” 您有许多类似于column LIKE '%something'WHERE 过滤条件。这种情况是臭名昭著的 SQL 性能反模式。用更好的 MySQL 索引来优化是不可能的。您没有在问题中解释您的数据是什么样的,因此很难就如何将column LIKE '%something' 重构为column = 'something'column LIKE 'something%'(尾随%)提出建议。但这就是您必须做的,以使这项工作更有效。 这是pop_links imgur.com/lZpFne4中的数据屏幕 在短期内,删除您为用户提供的搜索选项之一可能是值得的,以减少字符串开头带有通配符的LIKE 子句的数量.也许仅仅删除其中一个过滤器就可以将您的性能提高到可接受的水平? 【参考方案1】:

已更换 hours LIKE '%,".date("H").",%' OR hours = 'all'paused = 0(在链接中添加了一个暂停的列)。

负载从 ~4 下降到 ~0.20。我无法相信column LIKE %something 会对查询性能产生多大影响。

【讨论】:

以上是关于由于高负载重新设计查询/数据库的主要内容,如果未能解决你的问题,请参考以下文章

Envoy的负载均衡与限流设计

可扩展高可用负载均衡网站架构设计方案

java架构师大型分布式综合项目实战,高并发,集群,高可用,程序设计,性能优化,架构设计,负载均衡,大数据量

大型网站的架构设计问题----大型高并发高负载网站的系统架构

美团面试题:如何设计负载均衡架构支撑千万级用户的高并发访问?

面试90%都会翻车的高可用+高并发+负载均衡架构设计 !