代码和表设计的mysql性能问题

时间:2011-06-06 15:55:23

标签: mysql sql

我需要一些选择。

我有一张表格如下,约有78,000,000行...

  • id INT(主键)
  • loc VARCHAR(已编入索引)
  • 日期VARCHAR(已编入索引)
  • time VARCHAR
  • ip VARCHAR
  • 查找VARCHAR

以下是我的查询示例。

SELECT lookup, date, time, count(lookup) as count FROM dnstable
WHERE STR_TO_DATE(`date`, '%d-%b-%Y') >= '$date1' AND STR_TO_DATE(`date`, '%d-%b-%Y')   <= '$date2' AND
time >= '$hour1%' AND time <= '$hour2%' AND
`loc` LIKE '%$prov%' AND
lookup REGEXP 'ca|com|org|net' AND
lookup NOT LIKE '%.arpa' AND
lookup NOT LIKE '%domain.ca' AND 
ip NOT LIKE '192.168.2.1' AND
ip NOT LIKE '192.168.2.2' AND
ip NOT LIKE '192.168.2.3'
GROUP BY lookup
ORDER BY count DESC
LIMIT 100

我的mysql服务器配置像我发现的几个高用例。硬件很好,有4个核心,8个公羊。

此查询大约需要180秒......有没有人有一些提示让这更有效?

4 个答案:

答案 0 :(得分:3)

这里有很多错误。很多东西。我会查看查询选项的其他答案(你使用了很多LIKES,NOT LIKES和函数....你在unkeyed列上做它们......)。如果我在你的情况下,我会重新设计我的整个数据库。您好像使用它来存储DNS条目 - 主机名到IP地址。

您可能无法重新设计数据库 - 可能是客户数据库或您无法控制的内容。也许他们有很多依赖于当前数据库设计的应用程序。但是,如果您可以重构数据库,我强烈建议您这样做。

以下是我要做的事情的基本概要:

  1. 将TLD(顶级域)存储在单独的列中作为ENUM。使它成为一个索引,因此它很容易搜索,而不是试图使用正则表达式.com,.arpa等。无论如何TLD都是有限的,并且它们不会经常更改,所以这是ENUM的一个很好的候选者。

  2. 将没有TLD的域存储在常规列和反向列中。您可以索引两列,但根据您的搜索,您可能只需要索引反向列。基本上,使用反向列可以搜索一个域中的所有主机(例如google),而无需每次都进行全文搜索。 MySQL可以在反向列中对字符串“elgoog”进行密钥搜索。由于DNS是一个层次结构,因此非常适合。

  3. 分别将日期和时间列从VARCHAR更改为DATE和TIME。这是一个明显的变化。没有更多的str_to_time,str_to_date等。绝对没有必要这样做。

  4. 以不同方式存储IP地址。没有理由在这里使用VARCHAR - 它效率低,没有意义。相反,为每个八位字节使用四个单独的列(这是安全的,因为所有IPv4地址都有四个八位字节,不多也不少)作为无符号TINYINT值。这将给你0-255,你需要的范围。 (无论如何,每个IP八位字节实际上都是8位。)这应该使搜索速度更快,特别是如果你键入列。

    ex:select * from table where octet1!= 10; (这将过滤掉所有10.0.0.0/8私有IP空间)

  5. 这里的基本问题是您的数据库设计存在缺陷 - 您的查询使用的是未编入索引的列,并且您的查询效率低下。

    如果你坚持使用当前的设计......我不确定我是否能真正帮助你。对不起。

答案 1 :(得分:2)

我敢打赌,这里真正的大问题是STR_TO_DATE函数。 如果可能,请尝试使用日期列来真正拥有DATE数据类型。 (DATE,DATETIME,TIMESTAMP)

将这个新的或更改的列(带有日期数据类型)编入索引会加快对该列的选择。您必须避免日期解析当前缺少列'date'的错误数据类型。这种解析/转换避免了MySQL在'date'列上使用索引。

结论:使'date'列具有Date数据类型,将此列编入索引,并且不要在语句中使用STR_TO_DATE。


我暗示这些本地IP地址在使用否定时不是很有选择性,对吧? (这取决于表中的典型数据。) 由于未对ip-column编制索引,因此对该列的选择始终会导致全表扫描。当对ip的不等(&lt;&gt;)选择非常有选择性时,则考虑在其上放置索引并将语句改为不使用'like'但是&lt;&gt;代替。但我不认为对ip的不平等选择是非常有选择性的。

结论:我认为你不能在这里取得任何重大成就。

答案 2 :(得分:0)

一些提示

  • 使用!=代替NOT LIKE
  • 避免在mysql查询中使用REGEXP
  • 避免STR_TO_DATE(date, '%d-%b-%Y') >= '$date1'尝试将MySQL格式的日期传递给查询,而不是使用STR_TO_DATE
  • 进行转换 如果您必须使用group by,则应将
  • lookup编入索引。

尝试缓存查询结果(如果可能, )。

答案 3 :(得分:0)

问题是LIKE意味着全表遍历!这就是你看到这个的原因。 我建议的第一件事就是摆脱LIKE '192.168.2.1',因为它真的与='192.168.2.1'相同 此外,您在结尾处设置LIMIT 100意味着查询将针对所有记录运行,然后仅选择前100个 - 而是如何执行SELECT,其中仅涉及所有其他操作但不是LIKE并限制这个,然后有第二个SELECT使用LIKE?