查询庞大的数据库表需要在mysql中花费太多时间

时间:2010-05-04 21:18:35

标签: mysql performance group-by

我在mysql数据库表上运行sql查询,该表每天有110Mn +唯一记录。

问题:每当我使用“where”子句运行任何查询时,它至少需要30-40分钟。由于我想在第二天生成大部分数据,因此我需要访问整个数据库表。

您能指导我优化/重组部署模型吗?

网站说明:

mysql  Ver 14.12 Distrib 5.0.24, for pc-linux-gnu (i686) using readline 5.0
4 GB RAM, 
Dual Core dual CPU 3GHz
RHEL 3

my.cnf内容:

[mysqld]
datadir=/data/mysql/data/
socket=/tmp/mysql.sock

sort_buffer_size = 2000000
table_cache = 1024
key_buffer = 128M
myisam_sort_buffer_size = 64M

# Default to using old password format for compatibility with mysql 3.x
# clients (those using the mysqlclient10 compatibility package).
old_passwords=1

[mysql.server]
user=mysql
basedir=/data/mysql/data/

[mysqld_safe]
err-log=/data/mysql/data/mysqld.log
pid-file=/data/mysql/data/mysqld.pid
[root@reports root]#

数据库表详情:

CREATE TABLE `RAW_LOG_20100504` (
  `DT` date default NULL,
  `GATEWAY` varchar(15) default NULL,
  `USER` bigint(12) default NULL,
  `CACHE` varchar(12) default NULL,
  `TIMESTAMP` varchar(30) default NULL,
  `URL` varchar(60) default NULL,
  `VERSION` varchar(6) default NULL,
  `PROTOCOL` varchar(6) default NULL,
  `WEB_STATUS` int(5) default NULL,
  `BYTES_RETURNED` int(10) default NULL,
  `RTT` int(5) default NULL,
  `UA` varchar(100) default NULL,
  `REQ_SIZE` int(6) default NULL,
  `CONTENT_TYPE` varchar(50) default NULL,
  `CUST_TYPE` int(1) default NULL,
  `DEL_STATUS_DEVICE` int(1) default NULL,
  `IP` varchar(16) default NULL,
  `CP_FLAG` int(1) default NULL,
  `USER_LOCATE` bigint(15) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 MAX_ROWS=200000000;

提前致谢! 的问候,

2 个答案:

答案 0 :(得分:9)

我建议您学习如何使用EXPLAIN来分析数据库的查询优化计划。另请参阅Baron Schwartz的演讲EXPLAIN Demystified(该页面上幻灯片的PDF链接)。

了解如何创建索引 - 这与主键或自动增量伪代码不同。请参阅Yoshinori Matsunobu的演讲More Mastering the Art of Indexing

您的表格可以使用CP_FLAGWEB_STATUS上的索引。

CREATE INDEX CW ON RAW_LAW_20100503 (CP_FLAG, WEB_STATUS);

这有助于根据cp_flag条件查找行的子集。

然后你仍然遇到MySQL GROUP BY查询带来的不幸效率低下。它将临时结果集复制到磁盘上的临时文件中并在那里对其进行排序。磁盘I / O往往会破坏性能。

您可以提升sort_buffer_size配置参数,直到它足够大,以至于MySQL可以在内存而不是磁盘上对结果集进行排序。但那可能行不通。

您可能不得不预先计算所需的COUNT(),并定期更新此统计信息。


@Marcus的评论给了我另一个想法。您按Web状态进行分组,Web状态的一组不同值是一个相当短的列表,它们不会更改。因此,您可以为每个不同的值运行单独的查询,并且比使用创建临时表进行排序的GROUP BY查询更快地生成所需的结果。或者,您可以为每个状态值运行子查询,并将UNION组合在一起:

(SELECT COUNT(*), WEB_STATUS FROM RAW_LOG_20100504 WHERE CP_FLAG > 0 AND WEB_STATUS = 200)
UNION
(SELECT COUNT(*), WEB_STATUS FROM RAW_LOG_20100504 WHERE CP_FLAG > 0 AND WEB_STATUS = 404)
UNION
(SELECT COUNT(*), WEB_STATUS FROM RAW_LOG_20100504 WHERE CP_FLAG > 0 AND WEB_STATUS = 304)
UNION
...etc...
ORDER BY 1 DESC;

由于涵盖索引包括CP_FLAGWEB_STATUS,因此这些查询永远不需要读取表中的实际行。他们只读取索引中的条目,他们可以更快地访问它们,因为(a)它们在排序的树中,(b)如果你为key_buffer_size分配了足够的内容,它们可能会缓存在内存中。

我尝试的EXPLAIN报告(包含1M行测试数据)显示这很好地使用了索引,并且没有创建临时表:

+------+--------------+------------------+------+--------------------------+
| id   | select_type  | table            | key  | Extra                    |
+------+--------------+------------------+------+--------------------------+
|  1   | PRIMARY      | RAW_LOG_20100504 | CW   | Using where; Using index |
|  2   | UNION        | RAW_LOG_20100504 | CW   | Using where; Using index |
|  3   | UNION        | RAW_LOG_20100504 | CW   | Using where; Using index |
| NULL | UNION RESULT | <union1,2,3>     | NULL | Using filesort           |
+------+--------------+------------------+------+--------------------------+

最后一行的Using filesort意味着它必须在没有索引的情况下进行排序。但是对子查询产生的三行进行排序是微不足道的,MySQL会在内存中进行排序。


在设计最佳数据库解决方案时,很少有简单的答案。很大程度上取决于您如何使用数据以及哪些查询具有更高优先级来快速生成。如果有一个简单的答案适用于所有情况,那么软件默认只启用该设计,您不必做任何事情。

您确实需要阅读大量手册,书籍和博客,以了解如何充分利用您可以使用的所有功能。


是的,我仍然建议使用索引。显然它之前没有工作,当你在没有索引的好处的情况下查询1亿行时。

您必须了解必须设计有利于您要运行的特定查询的索引。我无法知道您在评论中描述的索引是否合适,因为您没有显示您正试图加快的其他查询。

索引是一个复杂的主题。如果在错误的列上定义索引,或者如果以错误的顺序获取列,则给定查询可能无法使用它。自1994年以来,我一直在支持SQL开发人员,而且我从来没有找到一条简洁的规则来解释如何设计索引。

您似乎需要一名导师,因为您处于需要回答大量问题的阶段。你有工作的人可以帮助你吗?

答案 1 :(得分:2)

为where子句中的任何字段添加索引。主键必须是唯一的;唯一索引必须是唯一的,但唯一性不是索引的先决条件。

  

错误定义或不存在的索引是性能不佳的主要原因之一,修复这些索引通常可以带来惊人的改进

快速信息: