Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
I've been looking at a large Drupal 6 site running notifications, comment posting is very slow due to the following query:
INSERT INTO notifications_queue (uid, destination, sid, module, eid, send_interval, send_method, cron, created, conditions) SELECT DISTINCT s.uid, s.destination, s.sid, s.module, 3467363, s.send_interval, s.send_method, s.cron, 1298441289, s.conditions FROM notifications s LEFT JOIN notifications_fields f ON s.sid = f.sid WHERE (s.status = 1) AND (s.event_type = 'node') AND (s.send_interval >= 0) AND (s.uid <> 1) AND ((f.field = 'tid' AND f.intval IN (16651,16661,16671,16681,4431,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,1761,711,16651,16661,4431,2161,16681,16671,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161)) OR (f.field = 'nid' AND f.intval = 1496241) OR (f.field = 'type' AND f.value = 'blog') OR (f.field = 'author' AND f.intval = 268241)) GROUP BY s.uid, s.destination, s.sid, s.module, s.send_interval, s.send_method, s.cron, s.conditions HAVING s.conditions = count(f.sid)
EXPLAIN (of the same query except just the SELECT):
+----+-------------+-------+------+-----------------------------+---------+---------+---------------+--------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------------------+---------+---------+---------------+--------+----------------------------------------------+
| 1 | SIMPLE | s | ALL | PRIMARY,uid_type_conditions | NULL | NULL | NULL | 206910 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | f | ref | PRIMARY | PRIMARY | 4 | connect.s.sid | 1 | Using where |
+----+-------------+-------+-----
This takes around 1.5 seconds on average.
Can half the number of rows examined by added an additional index to {notifications}. Late here so no patch - will sort that tomorrow, but:
CREATE INDEX queue ON notifications (status, event_type, send_interval, uid);
Now the explain looks like:
MariaDB [connect]> EXPLAIN SELECT DISTINCT s.uid, s.destination, s.sid, s.module, 3467363, s.send_interval, s.send_method, s.cron, 1298441289, s.conditions FROM notifications s LEFT JOIN notifications_fields f ON s.sid = f.sid WHERE (s.status = 1) AND (s.event_type = 'node') AND (s.send_interval >= 0) AND (s.uid <> 1) AND ((f.field = 'tid' AND f.intval IN (16651,16661,16671,16681,4431,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,1761,711,16651,16661,4431,2161,16681,16671,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161,16651,16661,4431,16681,16671,1761,711,2161)) OR (f.field = 'nid' AND f.intval = 1496241) OR (f.field = 'type' AND f.value = 'blog') OR (f.field = 'author' AND f.intval = 268241)) GROUP BY s.uid, s.destination, s.sid, s.module, s.send_interval, s.send_method, s.cron, s.conditions HAVING s.conditions = count(f.sid);
+----+-------------+-------+------+-----------------------------------+---------+---------+---------------+--------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------------------------+---------+---------+---------------+--------+----------------------------------------------+
| 1 | SIMPLE | s | ref | PRIMARY,uid_type_conditions,queue | queue | 772 | const,const | 107625 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | f | ref | PRIMARY | PRIMARY | 4 | connect.s.sid | 1 | Using where |
+----+-------------+-------+------+-----------------------------------+---------+---------+---------------+--------+----------------------------------------------+
2 rows in set (0.00 sec)
Counts from the two tables:
MariaDB [connect]> SELECT COUNT(*) FROM notifications;
+----------+
| COUNT(*) |
+----------+
| 214620 |
+----------+
1 row in set (0.05 sec)
MariaDB [connect]> SELECT COUNT(*) FROM notifications_fields;
+----------+
| COUNT(*) |
+----------+
| 398802 |
+----------+
1 row in set (0.09 sec)
Comment | File | Size | Author |
---|---|---|---|
#9 | notifications-add_indices-1070762-9.patch | 2.13 KB | pdrake |
#8 | notifications_add_indices-1070762-8-D6.patch | 1.83 KB | pdrake |
#4 | notifications_added-indicies-1070762.patch | 1.35 KB | mstef |
#1 | index_1070762.patch | 573 bytes | catch |
Comments
Comment #1
catchHere's the patch. I'm not sure what the status of the different versions is, so against 2.x-dev for now.
Comment #2
catchHad an idea to optimize the hook_node_*() of this further (not the actual query, just take the hard work out of the node/comment posting) - posted an issue at #1075862: Notifications queuing adds a lot of time to node/comment inserts, doesn't affect the patch here.
Comment #3
mstef CreditAttribution: mstef commentedIsn't this patch forgetting to alter hook_schema() as well?
Comment #4
mstef CreditAttribution: mstef commentedAttached patch added 3 indicies to improve performance. Also added the changes to hook_schema().
Comment #5
rjbrown99 CreditAttribution: rjbrown99 commentedThis seems to impact the 6.x-4.x branch as well. I have similar queries showing up.
Comment #6
rjbrown99 CreditAttribution: rjbrown99 commentedFWIW, the patch in #4 still needs to add the indexes to the schema. IE, something like
Comment #7
mstef CreditAttribution: mstef commentedOh, I think I screwed that up..
Comment #8
pdrake CreditAttribution: pdrake commentedI fixed the schema issues in #4 and added one additional index that reduces the number of rows examined in the query below by more than 99.9%, and reduces the execution time from ~4.06s to ~0.00s:
SELECT n.sid FROM notifications n INNER JOIN notifications_fields nf? ON n.sid = nf?.sid WHERE (n.event_type = ?) AND (nf?.field = ?) AND (nf?.intval = ?)
Comment #9
pdrake CreditAttribution: pdrake commentedHere's a followup that adds another beneficial index. Specifically, when inserting items into the notifications_queue table, a select is performed which uses both notifications_fields.value and notifications_fields.intval. This index reduces query execution time from ~4.7s to ~0.3s for queries like:
INSERT INTO notifications_queue (uid, destination, sid, module, eid, send_interval, send_method, cron, created, conditions) SELECT DISTINCT s.uid, s.destination, s.sid, s.module, ?, s.send_interval, s.send_method, s.cron, ?, s.conditions FROM notifications s LEFT JOIN notifications_fields f USE INDEX (field_intval) ON s.sid = f.sid WHERE (s.status = 1) AND (s.event_type = 'node') AND (s.send_interval >= 0) AND ((f.field = 'tid' AND f.intval IN (?,?)) OR (f.field = 'nid' AND f.intval = ?) OR (f.field = 'type' AND f.value = '?') OR (f.field = 'author' AND f.intval = ?)) GROUP BY s.uid, s.destination, s.sid, s.module, s.send_interval, s.send_method, s.cron, s.conditions HAVING s.conditions = count(f.sid);
Comment #10
rjbrown99 CreditAttribution: rjbrown99 commentedCross-reference with two other indexing issues:
#868446: Improved indexes for Notifications during: Notifications cancelling/User blocking/User delete operations
#1512416: Slow query - Index for notifications.uid
Comment #11
catchI tried #9 on a site that's having problems with this query, but didn't see an improvement, here's the EXPLAIN:
And forcing the index field_intval or field_value_intval to be used actually increases the number of rows evaluated:
I don't have any good ideas to improve this yet, just updating the issue for now.
Comment #12
pdrake CreditAttribution: pdrake commented@catch note that the query addressed by the index in #9 is a bit different than the original query and the query in #11 (specifically, the original query has a WHERE clause like "AND (s.uid <> ?)."
Comment #13
catchHmm OK it looks like that's due to this variable:
However removing that condition the EXPLAIN still doesn't look improved: