简体   繁体   中英

How do you optimize this MySQL slow query?

Table:

CREATE TABLE `deal_keyword` (
  `deal_id` int(11) NOT NULL default '0',
  `keyword_id` int(11) NOT NULL default '0',
  `area_id` int(11) NOT NULL default '0',
  PRIMARY KEY  (`deal_id`,`keyword_id`),
  KEY `area_id` (`area_id`,`keyword_id`,`deal_id`)
) TYPE=MyISAM

Slow Query Log:

# Time: 111115  0:18:11
# Query_time: 2  Lock_time: 0  Rows_sent: 1  Rows_examined: 3629
select count(deal_id) from deal_keyword where area_id=101 && keyword_id=115;

# Time: 111115  0:34:55
# Query_time: 2  Lock_time: 0  Rows_sent: 1  Rows_examined: 5778
select count(deal_id) from deal_keyword where area_id=101 && keyword_id=142;

# Time: 111115  0:36:05
# Query_time: 3  Lock_time: 0  Rows_sent: 1  Rows_examined: 4433
select count(deal_id) from deal_keyword where area_id=101 && keyword_id=112;

# Time: 111115  0:36:06
# Query_time: 2  Lock_time: 0  Rows_sent: 1  Rows_examined: 1955
select count(deal_id) from deal_keyword where area_id=101 && keyword_id=533;

...

Explain:

mysql> explain select count(deal_id) from deal_keyword where area_id=101 && keyword_id=115;
+--------------+------+---------------+---------+---------+-------------+------+--------------------------+
| table        | type | possible_keys | key     | key_len | ref         | rows | Extra                    |
+--------------+------+---------------+---------+---------+-------------+------+--------------------------+
| deal_keyword | ref  | area_id       | area_id |       8 | const,const | 4632 | Using where; Using index |
+--------------+------+---------------+---------+---------+-------------+------+--------------------------+
1 row in set (0.00 sec)

Config:

key_buffer=512M
max_allowed_packet=8M
table_cache=512
sort_buffer=8M
record_buffer=8M
thread_cache=8
thread_concurrency=4
myisam_sort_buffer_size=128M
interactive_timeout=28800
wait_timeout=7200

With your covering index already in place, it simply may not be possible. Here is why:

Run these queries:

SELECT COUNT(1) INTO @rowcount from deal_keyword;
SELECT FLOOR(COUNT(1) * 0.05) into @fivepct from deal_keyword;
SELECT @rowcount,@fivepct;
SELECT area_id,keyword_id,COUNT(1) paircount
FROM deal_keyword GROUP BY area_id,keyword_id
HAVING COUNT(1) >= @fivepct;

These queries will show you which paircounts exceed 5% of the total rowcount of the deal_keyword table. Any paircounts exceeding 5% of the rowcount will dismiss the use of the index in the MySQL Query Optimizer and revert to a full table scan.

Keep in mind that since the table is MyISAM, any DML (INSERT, UPDATE or DELETE) will perform a full table lock. This will cause any SELECT count on the deal_keyword table following DML to wait. Also, any burst of multiple SELECTs that come before any DML will make the DML wait.

RECOMMEDATION : Convert the table to InnoDB

ALTER TABLE deal_keyword ENGINE=InnoDB;

Counts against an InnoDB would happen as a transaction and would not block other DB Connections performing counts.

I doubt it could be optimized. One ugly way - you could probably store the current count in a separate table with cols: area_id | keyword_id | count

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM