views:

240

answers:

6

We have a loop in our PHP code which inserts rows into a table. e.g.:

while ($info = mysql_fetch_assoc($someQuery)) {
    mysql_query("INSERT INTO X (i,j) VALUES ($info['ii'],$info['jj'])");
}

This was fine a few months ago because the loop would only iterate several times. However, due to our website getting more traffic this loop now sometimes iterates 1000 or more times. The table has some overhead (4,305 KiB) and SELECTs from this table are appearing in the MySQL slow-log, probably because they are having to wait for a long list of INSERTs to release the locks?

How should I optimise the code so it can scale better?

Some things I thought I might try:

  • INSERT DELAYED - Need to look into it. Could it help?
  • Try inserting multiple rows in the same query. But what limit should I set? 50, 500, 1000?
+4  A: 

Instead of repeating mysql_query(), use prepared statements. They are a much more efficient way of repeating the same query many times with different values.

Also, I would look into those queries that are appearing in the slow log. Use EXPLAIN (<query>) to check that indexes are being used.

Ben James
Thanks for the suggestion but currently the code base doesn't use PDO or even mysqli.
Tom
I can see that, but this really is a good case for starting to use a more advanced database module. Many of the `mysql_` functions have `mysqli_` equivalents, so you may find it easy to switch to `mysqli`, and then optimise just a few queries (such as the one above) as prepared statements.
Ben James
Ok, thanks Ben - I will take that on board and try to action it with the rest of the developers.
Tom
+7  A: 

What is $someQuery? Could you use INSERT ... SELECT syntax?

Alex Barrett
Thanks - yes, this could work. I will try it.
Tom
Thanks for your suggestion - I read about INSERT...SELECT (via your link) but unfortunately, I have to do a little processing on the data (e.g. addslashes) and also insert one field that doesn't come from the SELECT.
Tom
If you are using `addslashes` to escape string values prior to generating the insert query, that wouldn't be necessary with this method. If you update your question with a little more context (specifically _$someQuery_ and any processing you are doing on the result) it will be easier to assist you.
Alex Barrett
The code is a bit verbose to add to the question. Also, I wasn't trying to get my one specific problem addressed - I was trying to learn in general to write better code :-) You have assisted quite enough and I appreciate it.
Tom
Okay Tom; I'm glad it was helpful to you. Good luck :)
Alex Barrett
+2  A: 

What Ben James said is important. Prepared statements are much faster if you execute the same SQL, and just change parameters in it.

Also, You my try to change the whole loop to something like:

INSERT INTO x (i, j) SELECT (here goes your $someQuery)

Of course you have to adapt the $someQuery, so I selects only two columns which are of the same type (or can by cast automaticly) as the columns i and j.

If you do not have very complicated mechanism in php, then this shloud be possible and will be much faster than any php loop.

SWilk
+4  A: 

You can also build up a statement like this:

INSERT INTO X (i,j)
VALUES ($info['ii'],$info['jj']),
(val, val),
...
(val, val);

You can experiment with different limits to determine where, if anywhere, your SQL gets too long. Then you can set the limit to something sane.

Phil Wallach
I tend to limit it to 100-200 value sets, depending on the amount of data per part. Wrapping it in a 'BEGIN / COMMIT' transaction can also help somewhat.
Alister Bulman
+2  A: 

One of the fastest ways to get data into MySQL is LOAD DATA INFILE. Think of it as a CSV import. You could write your data one row at a time to disk and then do a bulk load. According to this page on insert speeds LOAD DATA INFILE is 20x faster than a straight INSERT.

There may be other undesirable side-effects, though, as the table may well be locked for the whole process rather than just one row at a time. Running this in e.g. 100 row batches may make both parts of the problem manageable - I think you'll just need to experiment.

Karl B
+1  A: 

There are stored procedures and function in new versions of MySQL. You can use there a structured language with variables, loops and conditional blocks. One stored procedure should be faster than many independent inserts. However, it is a new language to learn.

You need mysqli extension in php, to use stored procedures.

You can also look at mysqli_multi_query.
http://pl.php.net/manual/en/mysqli.multi-query.php

Michas