MySQL cleverly maps
CREATE INDEX foo_bar ON Foo(Bar);
to
LOCK TABLE Foo WRITE; CREATE TEMPORARY TABLE A-Foo ( .... INDEX foo_bar (Bar)); INSERT INTO A-Foo SELECT * FROM Foo; ALTER TABLE Foo RENAME TO B-Foo; ALTER TABLE A-Foo RENAME TO Foo; DROP TABLE B-Foo;
If you have a very large table, expect this operation to take a) a
lot of disk space, b) a very very long time and c) block any writes to the table
in the process. I don’t recommend adding indexes or altering any very
large tables that are in production on MySQL, because you won’t be in
production for quite some time.
Update: Tom Haddon asked me if this applied to
recent versions of MySQL or to PostgreSQL. Looking at the docs, it
appears to still apply to 5.1:
- http://dev.mysql.com/doc/refman/5.1/en/create-index.html
-
http://dev.mysql.com/doc/refman/5.1/en/alter-table.html
In some cases, no temporary table is necessary:
-
If you use ALTER TABLE tbl_name RENAME TO new_tbl_name without any other
options, MySQL simply renames any files that correspond to the table
tbl_name. (You can also use the RENAME TABLE statement to rename tables. See
Section 13.1.16, “RENAME TABLE Syntax”.) -
ALTER TABLE … ADD PARTITION creates no temporary table except for MySQL
Cluster. ADD or DROP operations for RANGE or LIST partitions are immediate
operations or nearly so. ADD or COALESCE operations for HASH or KEY partitions
copy data between changed partitions; unless LINEAR HASH/KEY was used, this is
much the same as creating a new table (although the operation is done partition
by partition). REORGANIZE operations copy only changed partitions and do not
touch unchanged ones.
If other cases, MySQL creates a temporary table, even if the data wouldn’t
strictly need to be copied (such as when you change the name of a column). -
- http://dev.mysql.com/doc/refman/5.1/en/alter-table-problems.html
As far as PostgreSQL is concerned, it doesn’t mention anything about
doing the same thing, but does mention that it does a full sequential
scan of the table. During this time writes are blocked. You can use the
CONCURRENTLY keyword to allow writes to happen, but it does two scans
and will take longer, but you can still use your database.
http://www.postgresql.org/docs/8.2/interactive/sql-createindex.html
on said:
I guess “ALTER TABLE A-Foo RENAME TO A-Foo;” should have read “ALTER TABLE A-Foo RENAME TO Foo;”.
Otherwise, what are you accusing? Speed of MyQQL?
4.1? 5.0? 5.1?
on said:
Can you let me know which version of MySQL you’re using and also confirm that this isn’t the case in, for instance, PostgreSQL? I’ve always suspected avoiding MySQL was a good idea for the projects I was interested in and I’d like to have concrete proof of it…
on said:
On Oracle it’s faster to insert into a table without indexes and then create an index afterwards. Does this also apply to your example?
Thus is the following faster?
CREATE TEMPORARY TABLE A-Foo AS SELECT * FROM Foo;
CREATE INDEX A-Foo-Idx ON A-Foo(Bar));
Not 100% sure on the MySQL syntax but it should be something similar.
on said:
Ehh managed to missed the point of MySQL foobar logic of the CREATE INDEX translation. That looks utter stupid. So just disregard my previous post. Now I appreciate Oracle even more:-)