Very Slow MySQL Queries, even with indexes I have a relatively large 4-deep relational data setup like this:

client_applications: (potentially 1,000's of records)
&nbsp;&nbsp;&nbsp;- ...
&nbsp;&nbsp;&nbsp;- account_id
&nbsp;&nbsp;&nbsp;- deleted_at

client_application_versions: (potentially 10,000's of records)
&nbsp;&nbsp;&nbsp;- ...
&nbsp;&nbsp;&nbsp;- client_application_id
&nbsp;&nbsp;&nbsp;- deleted_at

cloud_logs: (potentially 1,000,000's of records)
&nbsp;&nbsp;&nbsp;- ...
&nbsp;&nbsp;&nbsp;- client_application_version_id
&nbsp;&nbsp;&nbsp;- deleted_at

logs: (potentially 1,000,000,000's of records) 
&nbsp;&nbsp;&nbsp;- ...
&nbsp;&nbsp;&nbsp;- cloud_log_id
&nbsp;&nbsp;&nbsp;- time_stamp
&nbsp;&nbsp;&nbsp;- deleted_at



I am still in development so the structure and setup is not set in stone, but I think it is setup ok. Using Rails 3.2.11 and InnoDB MySQL. The database is filled with a small (compared to the eventual db size) set of data (logs only has 700,000 rows) I have 4 queries, 3 of which are problematic, to retrieve logs.


Grab first page of logs, ordered by timestamp, limited by account_id, client_application_id, client_application_version_id (Over 100 seconds)
Grab first page of logs, ordered by timestamp, limited by account_id, client_application_id (Over 100 seconds)
Grab first page of logs, ordered by timestamp, limited by account_id (Over 100 seconds)
Grab first page of logs, ordered by timestamp (~2 seconds)



Here are the EXPLAIN statements. I have indexes on all applicable fields already. Would it be better to duplicate the various ...id fields on the logs table to prevent joins? Or is there some magic sauce I am missing when making these queries? I have never dealt with this quantity of data before so perhaps my standard way of approaching the setup and queries just doesn't scale? How can I alter my setup or statements to make these queries return in a reasonable time?


UPDATE
I have added a few combined indexes on the Logs table and shaved a tiny bit off of the time. Here are the explains for that. I have concluded that the ORDER method is the source of the delay. Removing the order by timestamp desc causes the query to return in a second or so. So the new question is why, when indexed on timestamp, does it still take over a minute to run this query?


UPDATE 2
Using a subquery to fetch the 100 id's increased performance to 14 seconds, but that is still far too long. The optimizations I have tried so far have all shortened the time a bit, but I feel that they are not getting at the root of the problem. Here is the EXPLAIN for the subquery approach.
Not a DBA or MySQL expert here, but let's try :). 
So let's take your second query - a bit smaller than the 1st one - and simplify the table names. 

We have something like : (LO = logs, CL = cloud_logs, CAV = client_application_versions, CA = client_applications)

 SELECT LO.* FROM LO 
 INNER JOIN CL      ON CL.id    = LO.cloud_log_id 
 INNER JOIN CAV     ON CAV.id   = CL.client_application_version_id 
 INNER JOIN CA      ON CA.id    = CAV.client_application_id 
 WHERE (LO.deleted_at IS NULL) 
 AND (CA.account_id = '3') 
 AND (CA.id = '5') 
 ORDER BY timestamp DESC LIMIT 100 OFFSET 0


And so you say it takes about 100 seconds, correct ?

When you say : 


  I have indexes on all applicable fields already.


Yet I believe that's where the flaw is. You don't have that much joins, and you may have 7 billion data or just 700, that should be performing well if indexing is correctly thought, and I think that's probably the order by / limit that is messing with your performance because of poor indexing.

1/ Have you tried : 

SELECT LO.* FROM LO WHERE (LO.deleted_at IS NULL)


or 

SELECT * FROM CA WHERE (CA.account_id = '3') AND (CA.id = '5')


See how these requests perform in time, if everything ok with these 2 tables ?

2/ Have you indexed timestamp as well ? Indexing the column you are making the "order by" on is crucial as well. 
In fact, you should even think about your data and how many values you're gonna have for each of the data you're querying on. This is very well explained right there : http://www.mysqlperformanceblog.com/2006/09/01/order-by-limit-performance-optimization/  and will certainly help you.

3/ From what I've read on MySQL a few mins ago you could also try a MySQLCheck see if everything ok with your tables if you think your indexing is OK  http://dev.mysql.com/doc/refman/5.0/en/mysqlcheck.html . I know that in older versions of oracle we had to compute stats after creating indexes, maybe something similar here ?

Hope this helps. 

[EDIT : 12/01/13 After comments ]

Ok, glad to see you already divided the time by 4 but indeed 25s is way too long.

1/ Have you tried to play with indexes by creating one that would make sense, like explained by Peter here (http://www.mysqlperformanceblog.com/2006/09/01/order-by-limit-performance-optimization/) ? Like an index on (CA.account_id, CA.id, timestamp) etc ? 



2/ How long does it take when you get rid of the order by / limit like below ?

SELECT LO.* FROM LO 
 INNER JOIN CL      ON CL.id    = LO.cloud_log_id 
 INNER JOIN CAV     ON CAV.id   = CL.client_application_version_id 
 INNER JOIN CA      ON CA.id    = CAV.client_application_id 
 WHERE (LO.deleted_at IS NULL) 
 AND (CA.account_id = '3') 
 AND (CA.id = '5') 


To check if this would be the order by/limit that mess up with your performance ?



3/ In case 2 is verified, you could try something like : 

SELECT LO.* FROM LO 
 INNER JOIN CL      ON CL.id    = LO.cloud_log_id 
 INNER JOIN CAV     ON CAV.id   = CL.client_application_version_id 
 INNER JOIN CA      ON CA.id    = CAV.client_application_id 
 INNER JOIN 
 (
    SELECT LO.id FROM LO 
    INNER JOIN CL      ON CL.id    = LO.cloud_log_id 
    INNER JOIN CAV     ON CAV.id   = CL.client_application_version_id 
    INNER JOIN CA      ON CA.id    = CAV.client_application_id 
    WHERE (LO.deleted_at IS NULL) 
    AND (CA.account_id = '3') 
    AND (CA.id = '5') 
    ORDER BY timestamp DESC LIMIT 0,100
  ) AS PERF  ON PERF.id = LO.id


Where you replace LO.id by the column that makes sense with Logs (I suppose you have some sort of Logs id . This is based on : http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/ 
Note you can change the LIMIT 0,100 and keep the OFFSET keyword instead in case you need it (if PostgreSQL compatibility is required).