2

I have a table with 5 million records. Table columns are id, customer_id, status, and amount. When I run a query without Index(status),

SELECT SUM(amount) FROM `game_results` WHERE `status` = 1

the query takes 4 seconds to complete, but when I add an index to status column, the query takes 45 seconds to complete. I need Index for the status column, because I also need to make search queries on this table.

How can I solve this issue?

Enter image description here

Slow query with index - EXPLAIN

Enter image description here

Slow query with index - ANALYZE

Enter image description here

Query without index - EXPLAIN

Enter image description here

Query without index - ANALYZE

Enter image description here

customer_id, game_id, date, prize_type filterable.

amount, point columns for summary stats.

Show Create Table

CREATE TABLE `game_results`(
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`customer_id` bigint(20) NOT NULL,
`game_id` bigint(20) NOT NULL,
`amount` decimal(10,2) NOT NULL DEFAULT '0.00',
`prize_id` smallint(6) NOT NULL DEFAULT '0',
`prize_type` smallint(6) NOT NULL DEFAULT '1',
`point` decimal(10,2) NOT NULL DEFAULT '0.00',
`ext_id` bigint(20) DEFAULT NULL,
`ext_value` bigint(20) DEFAULT NULL,
`status` smallint(6) NOT NULL DEFAULT '0',
`site_user` bigint(20) NOT NULL DEFAULT '0',
`date` bigint(20) NOT NULL DEFAULT '0',
`update_date` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10009524
   DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
5
  • Please provide your index definition in your original post. Having the EXPLAIN ANALYZE for both the fast and slow query doesn't hurt too.
    – J.D.
    Commented Jan 6, 2022 at 12:49
  • 1
    @J.D. Hello , I added EXPLAIN and ANALYZE info.
    – PanelMaker
    Commented Jan 6, 2022 at 13:17
  • This may only be tangentially related, but interesting that the cardinality of your status field is 1. If I'm interpreting that correctly, does every row in your table have the same value for status currently?
    – J.D.
    Commented Jan 6, 2022 at 13:47
  • @J.D yes same value for status at the moment. I added different values nothing changes . Its not getting any slower or faster.
    – PanelMaker
    Commented Jan 6, 2022 at 14:02
  • Please always state your MySQL (or any server that you use) version number. Also, please don't post images for the reasons outlined in this link!
    – Vérace
    Commented Jan 6, 2022 at 16:38

2 Answers 2

3

Rolando's composite index is the "perfect" solution to the question.

Or is it?

Sure, it made that one query run 4 times as fast. I would argue that 1 second is still "too long". To discuss that, we need to understand "why" that query is necessary, and how often it is run.

Also, let's look at the side effects of having that index. The column names (status and amount) smell like columns that will be changed often. If so, then be aware that updating INDEX(status, amount) is like deleting a row from one spot a BTree and inserting another row in another spot. Granted, InnoDB does a good job of making those two steps reasonably efficient (cf "change buffering"), but it is not free. This overhead occurs after the UPDATE statement that modifies status and/or amount. It is probably not a big deal in the grand scheme of things.

Without further information about the app, I cannot necessarily recommend a "summary table" or a "trigger" as a way to decrease the 1 second down to under 0.1 seconds.

For "a list of all game results belongs to user 1", you probably need an INDEX starting with user_id.

For "a summary of last month's game amounts", and there is no other filtering, an index starting with date may be useful.

I see "cardinality = 1" for status. This implies that the Optimizer thinks there is only one distinct value for status. If this is the case, the Optimizer may do things differently. (And certainly it is useless to have INDEX(status) for the query you gave when you are testing for that value.)

6
  • Hello , thank you for your answer. Amount column not updating but status column does. This table is list of game results. I also have a date , game_id columns in table but didn't mention it in the post. I filter and display data (by date , customer_id , game_id etc.. ) . I also use this table for filtered summary stats ( where I use sum and count ) . I created composite index with (date,game_id,customer_id,amount). I need to display filtered list or filtered summary stats from same table. (E.g. list of all game results belongs to user 1 or summary of last months game amounts).
    – PanelMaker
    Commented Jan 6, 2022 at 19:30
  • @PanelMaker - The additional filtering is very likely to change what the optimal index is! That is, if the query in your Question is not the 'real' query, al of the Answers _may be useless to helping you.
    – Rick James
    Commented Jan 6, 2022 at 20:06
  • @PanelMaker - And I added to my answer to provide some hints at indexing for the cases you mentioned. I can be more precise if you provide both the full SELECT and SHOW CREATE TABLE.
    – Rick James
    Commented Jan 6, 2022 at 20:15
  • Yes you are right.. I tried with additional filter and indexes not working , Query not taking 45 seconds but it is act like without indexes. Takes 4,5 seconds. What should I do about this situation. I have additional filters but I dont use all of them at the same time. When I index columns one by one , filtering and listing works fine but sum,count takes minutes to run.
    – PanelMaker
    Commented Jan 6, 2022 at 20:21
  • I added more info to my post
    – PanelMaker
    Commented Jan 6, 2022 at 20:27
3

The reason the status index makes it go slower is this:

  • The value of status is read from the index
  • The value of amount must be looked up in the table

This means having to read from the index and the table separately per index entry. In your case, doing a full table scan, where both the status and amount values are, would be better without using an index. What you need is a good covering index, an index with all the columns needed for a specific query.

Drop the status index and add a compound index on status,amount:

ALTER TABLE game_results
    DROP INDEX status
   ,ADD INDEX status_amount_ndx (status,amount)
   ,ALGORITHM=INPLACE
   ,LOCK=NONE
;

The query will use this new index, status_amount_ndx, to get both status and amount values without having to read from the table.

Give it a try!!!

1
  • Thank you very much for your help and explanation. Query dropped from 45 seconds to 1 second.
    – PanelMaker
    Commented Jan 6, 2022 at 15:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.