Opened 8 years ago
Last modified 5 days ago
#7500 assigned enhancement
Harmful bp_activity indexes
Reported by: | brandonliles | Owned by: | espellcaste |
---|---|---|---|
Milestone: | Under Consideration | Priority: | normal |
Severity: | normal | Version: | |
Component: | Performance | Keywords: | has-patch |
Cc: |
Description
The indexes on the bp_activity table hide_sitewide
and is_spam
are harmful for query performance since the columns being indexed are tinyint(1). If MySQL uses these indexes it will likely result in table scans.
A far more selective index would be:
KEY component_type_user_id_date_recorded
(component
, type
, user_id
, date_recorded
)
Attachments (3)
Change History (23)
This ticket was mentioned in Slack in #buddypress by hnla. View the logs.
8 years ago
#4
@
8 years ago
Hi @johnjamesjacoby ,
On our site we have a little over 1.6 million rows in the bp_activity table. Most of the time MySQL was using a decent query plan, but several times an hour we were seeing MySQL use the hide_sitewide and is_spam indexes for the activity loop which was about the same performance as a table scan. On our system that was resulting in a query that averaged 6 seconds. In the graph I've posted here you see the result of our index adjustment. With those indexes removed, we're no longer seeing that bad query plan.
#5
@
8 years ago
Thanks for the quick reply!
Is that just from removing the hide_sitewide
and is_spam
alone? Or is that also with your custom compound key?
component_type_user_id_date_recorded (component, type, user_id, date_recorded)
#6
@
8 years ago
The graphs/data include that compound key that we added after analyzing the output from EXPLAIN.
#7
@
7 years ago
- Component changed from Core to Performance
- Milestone changed from Awaiting Review to Under Consideration
@brandonliles - So is 01.patch
what you are recommending? Or did you keep the existing indexes on hide_sitewide
and is_spam
intact?
My SQL-fu is not that great, so I'll leave it to you guys to discuss.
#8
@
7 years ago
@r-a-y 01.patch is exactly what I'm recommending. I didn't keep the existing indexes, due to their low selectivity they are more likely to cause the optimizer to use a poor query plan.
#9
@
7 years ago
- Keywords has-patch added; 2nd-opinion removed
- Type changed from defect (bug) to enhancement
02.patch
adds an upgrade routine to drop the hide_sitewide
and is_spam
indexes and upgrades the activity DB table to add the new, proposed index.
#10
@
7 years ago
- Milestone changed from Under Consideration to 3.0
Would like to tackle this in v3.0.
This ticket was mentioned in Slack in #buddypress by djpaul. View the logs.
7 years ago
#14
@
8 months ago
- Milestone changed from Awaiting Contributions to Up Next
- Owner set to espellcaste
- Status changed from new to assigned
I'll revisit this to confirm the improvements are still necessary.
#17
@
5 days ago
- Milestone changed from Awaiting Review to Under Consideration
I'd like to offer a different take on the suggested approach/patch.
The rules for composite indexes states that there are two main rules for using composite indexes:
- Left-to-right, no skipping: MySQL can only access the index in order, starting from the leftmost column and moving to the right. It can't skip columns in the index.
- Stops at the first range: MySQL stops using the index after the first range condition encountered.
I think that if we create this composite index as is, we might create other issues for different types of queries where not all keys will be available and a table scan will happen too.
component_type_user_id_date_recorded (component, type, user_id, date_recorded)
Let's look at a practical example. A default query from https://site.test/activity/
SELECT DISTINCT a.id FROM wp_bp_activity a WHERE a.is_spam = 0 AND a.hide_sitewide = 0 AND a.type NOT IN('activity_comment', 'last_activity') ORDER BY a.date_recorded DESC, a.id DESC LIMIT 0, 21;
I see no index being used here.
{ "id": 1, "select_type": "SIMPLE", "table": "a", "partitions": null, "type": "ALL", "possible_keys": "type,is_spam,hide_sitewide", "key": null, "key_len": null, "ref": null, "rows": 115, "filtered": 97.39, "Extra": "Using where; Using filesort" }
Now, after applying the patch, the date_recorded
index was used:
{ "id": 1, "select_type": "SIMPLE", "table": "a", "partitions": null, "type": "index", "possible_keys": "type,hide_sitewide", "key": "date_recorded", "key_len": "5", "ref": null, "rows": 21, "filtered": 9.74, "Extra": "Using where; Backward index scan" }
I also removed only the is_spam
index without applying the patch, and got the same results. The composite index was not chosen by mysql.
@brandonliles I know it's been ages since you shared your feedback. But if we could see which index is being chosen by mysql in your queries, that would help confirm my findings.
---
Outcome: it seems that only removing the is_spam
index is enough to mysql to use another index with good cardinality, rather than a full table scan.
It's not clear, based on my testing, the patch here will actually avoid a table scan. On the contrary, it is technically possible it'll force a full table scan in certain situations.
This ticket was mentioned in Slack in #buddypress by espellcaste. View the logs.
5 days ago
#19
@
5 days ago
I'm afraid it's been ages since I worked on the project that used BuddyPress so I can't really provide any useful feedback at this point.
Howdy @brandonliles!
Thanks for opening this ticket and detailing your findings.
It's generally true that indexes on columns with low cardinality is ineffective, however, benchmarks to confirm that full table scans are currently a problem would be useful. That way we can also confirm removing those indexes will improve performance as anticipated.
Once we've done that, an upgrade routine will need to be introduced that comfortably locks those tables while the indexes are adjusted. Since the Activity table is highly likely to have many, many rows, even index removal is not a simple or painless process.
TL;DR - We need: