简体   繁体   中英

Mysql Count query optimization

I have the below query to optimize.

SELECT count(*) AS count FROM area 
INNER JOIN entity ON area.id = entity.id
INNER JOIN areacust ON area.id = areacust.id 
WHERE entity.deleted=0 
AND area.id > 0

There are indexes on deleted, ids on all the tables.

Now when i have suppose 20 Lac (2 million) of records then the query takes lots of time to give me the result. Its between 10 to 20 seconds.

How can i optimize it more. Also is there any other technique to get count.

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  vtiger_crmentity    ref PRIMARY,entity_deleted_idx  entity_deleted_idx  4      const    729726  Using where; Using index
1   SIMPLE  area    eq_ref  PRIMARY PRIMARY 4   area.id 1   Using index
1   SIMPLE  areacust    eq_ref  PRIMARY PRIMARY 4   area.id 1   Using where; Using index

New explain for composite key

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  entity  ref PRIMARY,entity_deleted_idx,comp_index   deleted_idx 4   const   928304  Using index
1   SIMPLE  area    eq_ref  PRIMARY PRIMARY 4   entity.id   1   Using index
1   SIMPLE  areacust    eq_ref  PRIMARY PRIMARY 4   entity.idid 1   Using index

As per comments, if you want to keep the query in question - you have to allocate more resources to your MySQL instance. I am assuming you use InnoDB for storage engine, otherwise this advice is useless:

Increase the value of innodb_buffer_pool variable. As much as you can. You want to allocate as much RAM as possible.

Also, get rid of index on deleted column, it's useless. Its cardinality is too low for it to be an index.

The other "technique" that you can (should) use is taking care of this count manually.

Create the table that holds the count number you are interested in. Every time you update / insert / delete the entity or area record - update the count value manually (increase, decrease).

That way all you have to do is look up a single record of a single table. Setting up triggers that will sort this out automatically should be trivial. That way you'll take care of the count at runtime instead waste I/O and CPU to constantly traverse the data set.

You could try:

SELECT count(*) AS count 
  FROM area 
  JOIN entity
    ON entity.id = area.id
   AND entity.deleted = 0 
  JOIN areacust 
    ON areacust.id = area.id 

I like to include conditions in JOINs where possible and keep the table I'm JOINing on the left of the equals in these conditions.

Also the WHERE area.id > 0 was strange.. most foreign_keys start at 1 due to auto_increment ids in other tables so this will include all rows. I have deleted this condition.

From the look of your explain, you don't really want the top row to be using entity_deleted_idx . You may get more joy with a composite index on (id, deleted) for entity

These are the indexes i'd have for this query:

  • area - (id) This is probably the PRIMARY already
  • areacust - (id) This is probably the PRIMARY already
  • entity - (id, deleted) This should be added and used.

UPDATE

Remove all unsused indexes from the table entity except for the PRIMARY and the composite index.

If that doesn't work run:

SELECT count(*) AS count 
  FROM area 
  JOIN entity USE INDEX (**composite_index_name**)
    ON entity.id = area.id
   AND entity.deleted = 0 
  JOIN areacust 
    ON areacust.id = area.id 

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