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

Vij*_*rge 3 mysql performance group-by

我在一个mysql数据库表上运行SQL查询,该表有一整天的110Mn +唯一记录.

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

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

网站说明:

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;
Run Code Online (Sandbox Code Playgroud)

提前致谢!问候,

Bil*_*win 9

我鼓励您学习如何使用EXPLAIN来分析数据库的查询优化计划.另请参阅Baron Schwartz的演示文稿EXPLAIN Demystified(链接到幻灯片的PDF链接在该页面上).

学习如何创建索引 - 这与主键或自动增量伪代码不同.参见Yoshinori Matsunobu撰写的更多掌握索引艺术的演讲.

你的表可以在CP_FLAG和上使用索引WEB_STATUS.

CREATE INDEX CW ON RAW_LAW_20100503 (CP_FLAG, WEB_STATUS);
Run Code Online (Sandbox Code Playgroud)

这有助于根据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;
Run Code Online (Sandbox Code Playgroud)

因为您的覆盖索引包含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           |
+------+--------------+------------------+------+--------------------------+
Run Code Online (Sandbox Code Playgroud)

Using filesort最后一行只是意味着它具有排序没有索引的好处.但是对子查询产生的三行进行排序是微不足道的,MySQL会在内存中进行排序.


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

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


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

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

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

你似乎需要一个导师,因为你处在一个需要回答很多问题的阶段.你有工作的人可以帮助你吗?