Performance: change {system}.type: alter table system modify column type VARCHAR(32);

robertDouglass - January 28, 2008 - 14:57
Project:Drupal
Version:7.x-dev
Component:base system
Category:bug report
Priority:critical
Assigned:Unassigned
Status:closed
Issue tags:drupal.org upgrade, needs backport to D6, Novice
Description

The type column of the {system} table is currently a varchar(255) field. Yet in 99% of all Drupal installations it stores either the text 'module' or the text 'theme' and nothing else. This is already a strong case for making it an int column and defining constants for module and theme. Then, on every page, there is this query:
SELECT filename FROM system WHERE name = 'user' AND type = 'module';

This query always takes over 1 ms to run making it not a criminally slow query, but relatively slow, nonetheless. This is because the list of indexes available doesn't help the query much:

mysql> describe SELECT filename FROM system WHERE name = 'user' AND type = 'module';
+----+-------------+--------+------+-------------------+-----------+---------+-------+------+-------------+
| id | select_type | table  | type | possible_keys     | key       | key_len | ref   | rows | Extra       |
+----+-------------+--------+------+-------------------+-----------+---------+-------+------+-------------+
|  1 | SIMPLE      | system | ref  | modules,bootstrap | bootstrap | 38      | const |   54 | Using where |
+----+-------------+--------+------+-------------------+-----------+---------+-------+------+-------------+

Adding an index for these two columns won't work:

mysql> CREATE UNIQUE INDEX name_type ON system (name,type);
ERROR 1071 (42000): Specified key was too long; max key length is 1000 bytes

Shortening the potential length of type allows for making an index:

mysql> alter table system modify column type VARCHAR(32);
Query OK, 79 rows affected (0.03 sec)
Records: 79  Duplicates: 0  Warnings: 0

mysql> CREATE UNIQUE INDEX name_type ON system (name,type);
Query OK, 79 rows affected (0.02 sec)
Records: 79  Duplicates: 0  Warnings: 0

This allows the query to drop consistently below 1ms:

0.32 1 drupal_get_filename SELECT filename FROM system WHERE name = 'color' AND type = 'module'
0.24 1 drupal_get_filename SELECT filename FROM system WHERE name = 'comment' AND type = 'module'
0.25 1 drupal_get_filename SELECT filename FROM system WHERE name = 'dblog' AND type = 'module'
0.29 1 drupal_get_filename SELECT filename FROM system WHERE name = 'help' AND type = 'module'
0.37 1 drupal_get_filename SELECT filename FROM system WHERE name = 'openid' AND type = 'module'
0.34 1 drupal_get_filename SELECT filename FROM system WHERE name = 'search' AND type = 'module'
0.26 1 drupal_get_filename SELECT filename FROM system WHERE name = 'statistics' AND type = 'module'
0.27 1 drupal_get_filename SELECT filename FROM system WHERE name = 'taxonomy' AND type = 'module'
0.29 1 drupal_get_filename SELECT filename FROM system WHERE name = 'tracker' AND type = 'module'
0.22 1 drupal_get_filename SELECT filename FROM system WHERE name = 'update' AND type = 'module'

#1

jaydub - January 29, 2008 - 01:06

Rather than changing the column def you can instead change the length of the index on the name or type column. So in order to avoid the max key length error:

CREATE UNIQUE mysql> CREATE UNIQUE INDEX name_type ON system (name (12),type);

mysql> CREATE UNIQUE INDEX name_type ON system (name (12),type);
Query OK, 44 rows affected (0.05 sec)
Records: 44  Duplicates: 0  Warnings: 0

mysql> explain SELECT filename FROM system WHERE name = 'user' AND type = 'module';
+----+-------------+--------+-------+---------------+-----------+---------+-------------+------+-------+
| id | select_type | table  | type  | possible_keys | key       | key_len | ref         | rows | Extra |
+----+-------------+--------+-------+---------------+-----------+---------+-------------+------+-------+
|  1 | SIMPLE      | system | const | name_type     | name_type | 805     | const,const |    1 |       |
+----+-------------+--------+-------+---------------+-----------+---------+-------------+------+-------+
1 row in set (0.00 sec)

#2

jaydub - January 29, 2008 - 01:12

This issue: http://drupal.org/node/185126 has some system table optimizations that I believe are already in D6. The index created in that issue covers 4 or 5 fields from the system table but not the 'name' field so perhaps this issue can still be considered.

#3

robertDouglass - February 2, 2008 - 15:59

@jaydub: interesting suggestion with the index lenght. I still think varchar(255) is wasteful for the type column.

#5

robertDouglass - September 22, 2008 - 08:11

Hmm. I forgot that I had suggested this. I still think it's a good idea.

#6

robertDouglass - September 22, 2008 - 08:13
Title:change {system}.type to an integer column and create constants for 'module' and 'theme'.» Performance: change {system}.type: alter table system modify column type VARCHAR(32);

In particular I think we should shorten the field, so I'm changing the title.

#7

nicholasThompson - January 19, 2009 - 12:09

This sounds like a sensible fix...

#8

webchick - February 9, 2009 - 04:29

#9

drifter - February 9, 2009 - 08:29
Assigned to:Anonymous» drifter
Status:active» needs review

Here's my first attempt at a novice patch. Tested both installing and upgrading.

AttachmentSizeStatusTest resultOperations
system-type-varchar32.patch1.48 KBIdleFailed: 9659 passes, 0 fails, 1 exceptionView details | Re-test

#10

Dave Reid - February 9, 2009 - 14:58
Status:needs review» needs work

A good start. I think we should actually put the type field at varchar(12). Do we really have any need for a type larger than 12 chars (profile, theme, engine, module, etc)? See {registry}.type - it's a varchar(9).

In system_schema(), you'll want to add another index 'type_name' => array('type', 'name'),. We can probably change array('type', 12) to simply just 'type'. Make sure you make the same index addition/change in the system_update_7019.

The first part of a PHP doc block of a function should fit to 80 characters. For system_update_7019, I'd recommend "Shorten the {system}.type column and add an index on type and name."

It's preferred in core to actually just do:

<?php
db_drop_index($ret, 'system', 'modules');
db_change_field($ret, 'system', 'type', 'type', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => ''));
db_add_index($ret, 'system', 'modules', array('type', 'status', 'weight', 'filename'));
db_add_index($ret, 'system', 'type_name', array('type', 'name'));
?>

instead of:
<?php
$spec = array(
+   
'description' => 'The type of the item, either module, theme, or theme_engine.',
+   
'type' => 'varchar',
+   
'length' => 32,
+   
'not null' => TRUE,
+   
'default' => '',
+  );

$indexes = array(
+   
'indexes' => array(
+     
'modules' => array(array('type', 12), 'status', 'weight', 'filename'),
+    ),
+  );

db_drop_index($ret, 'system', 'modules');
db_change_field($ret, 'system', 'type', 'type', $spec, $indexes);
?>

#11

drifter - February 9, 2009 - 15:19
Status:needs work» needs review

Applied the changes suggested in #10 - type is now VARCHAR(12) - just enough to hold "theme_engine".

AttachmentSizeStatusTest resultOperations
system-type-varchar12.patch1.61 KBIdleFailed: Failed to apply patch.View details | Re-test

#12

Dave Reid - February 9, 2009 - 15:28

Wow, yes that update function looks a whole lot better now. :) Now I'm just waiting for the testing bot to come back green.

#13

Dave Reid - February 9, 2009 - 15:44
Issue tags:+needs backport to D6

Marked #371504: Missing index in system table as a duplicate of this.

#14

Dave Reid - February 9, 2009 - 15:49

If we want to backport this to 6.x, we should rename the update function system_update_6048() so that we won't be out of sync when we backport.

#15

drifter - February 11, 2009 - 08:47

I don't know, it's just a minor performance enhancement, should it be backported?

#16

catch - February 11, 2009 - 10:38

We can commit as is, then if goba decides it's worth backporting, move the function back to 6048() in HEAD once that's done. This issue comes up every time with backported schema changes, dunno what a good solution is.

#17

Dave Reid - February 11, 2009 - 15:16
Status:needs review» reviewed & tested by the community

Ok then, looks good to me. Applies cleanly, tested manually and testing bot gives the green.

#18

robertDouglass - February 14, 2009 - 22:19

Nice work people. Every performance improvement that can be backported should be.

#19

Gábor Hojtsy - February 17, 2009 - 14:58
Issue tags:+drupal.org upgrade

Schema modifications would not be good for D6, but a short index can always be added. Gerhard suggested in an issue marked duplicate of this one to add an index on name in D6: #371504: Missing index in system table

#20

webchick - February 18, 2009 - 15:14
Status:reviewed & tested by the community» fixed

Committed to HEAD. :) Thanks!

#21

Dave Reid - February 19, 2009 - 03:05
Version:7.x-dev» 6.x-dev
Assigned to:drifter» Anonymous
Status:fixed» needs review

Here's the backport of just the index for type_name for D6. Not sure why we need to have a separate issue just for the D6 version. Why not reuse this thread?

AttachmentSizeStatusTest resultOperations
215080-system-index-type-name-D6.patch1013 bytesIgnoredNoneNone

#22

andypost - April 21, 2009 - 21:23

Here the new patch against DRUPAL-6

changed system_update_6050 because of system_update_6049 was for #363262: Missing index on url_alias table

So what with new D7 patch? - if update in d6 so it dowsn't needed in 7

#23

andypost - April 21, 2009 - 21:22

Patch attached

AttachmentSizeStatusTest resultOperations
system-index-type-name-D6.patch984 bytesIgnoredNoneNone

#24

andypost - August 12, 2009 - 01:31
Status:needs review» reviewed & tested by the community

Why this patch still not commited to 6?

#25

Gábor Hojtsy - September 14, 2009 - 12:23
Version:6.x-dev» 7.x-dev
Priority:normal» critical
Status:reviewed & tested by the community» needs work

I've modified the patch to add the system_update_6053() function, but apart from that, committed the 6.x patch as-is. Thanks! Now back to 7.x to fix the upgrade path!

#26

andypost - September 26, 2009 - 18:15

Just one-line patch, drop old index before adding new one

AttachmentSizeStatusTest resultOperations
215080_index_update.patch823 bytesIdlePassed: 13461 passes, 0 fails, 0 exceptionsView details | Re-test

#27

andypost - September 26, 2009 - 18:15
Status:needs work» needs review

change status

#28

catch - September 28, 2009 - 02:00
Status:needs review» needs work

This doesn't look right to me - if system_update_6050() changes the column definition, we need to remove that from 7019() (and probably the entire function - in which case it should be emptied except for an inline comment explaining that it was moved to 6050().

#29

andypost - September 28, 2009 - 12:38
Status:needs work» needs review

@catch updates are different, D6 have only index definition but D7 changes column, so we need only drop a previous index because it already exists from D6

<?php
function system_update_6053() {
 
$ret = array();
 
db_add_index($ret, 'system', 'type_name', array(array('type', 12), 'name'));
  return
$ret;
}

function
system_update_7018() {
 
$ret = array();
 
db_drop_index($ret, 'system', 'modules');
 
db_change_field($ret, 'system', 'type', 'type', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => ''));
 
db_add_index($ret, 'system', 'modules', array('type', 'status', 'weight', 'name'));
db_drop_index($ret, 'system', 'type_name');
 
db_add_index($ret, 'system', 'type_name', array('type', 'name'));
  return
$ret;
}
?>

#30

catch - September 28, 2009 - 12:49
Status:needs review» reviewed & tested by the community

Sorry andy, that make complete sense. RTBC then.

#31

andypost - September 28, 2009 - 15:24

Just reorder for more readablity

AttachmentSizeStatusTest resultOperations
215080_index_update.patch856 bytesIdleInvalid patch format in 215080_index_update_0.patch.View details | Re-test

#32

Dries - September 28, 2009 - 15:38
Status:reviewed & tested by the community» fixed

Committed to CVS HEAD. Thanks!

#33

Dave Reid - October 5, 2009 - 19:52
Status:fixed» needs work

No, this path is broken now. Whenever we add an update function to D6, we need to add the same exact update function back into D7. That way people that are going to upgrade from D6.10 should still hit all the update functions in the latest D6 version. This current upgrade path is going to drop an index that does not exist for people that are not using the latest versions of D6, causing an error.

#34

andypost - October 16, 2009 - 17:22
Status:needs work» fixed

Suppose this issue is fixed, Let's back extra section in follow-ups at #278592: Sync 6.x extra updates with HEAD

#35

System Message - October 30, 2009 - 17:30
Status:fixed» closed

Automatically closed -- issue fixed for 2 weeks with no activity.

 
 

Drupal is a registered trademark of Dries Buytaert.