views:

285

answers:

5

We are running a stats site for a bunch of games, but after the log has tipped over 100 megs, things are starting to slow down, so we need to optimize our queries.

However, we've found out that what we thought was a simple query, takes about 1.5 secs:

SELECT handle,
(
    SELECT COUNT(*)
    FROM kills
    WHERE killer=p.handle
    AND k_team != c_team
    AND gid=p.gid
) AS kills
FROM players as p
WHERE  gid="3245"
AND team="axis"
ORDER BY kills DESC LIMIT 0, 10;

This produces a result list for one of the teams.

Table kills and players consists of 36000 and 4000 rows respectfully.

Why is that query taking so long and how can it be optimize? Should we perhaps look into JOIN?

Best regards, Laka

+1  A: 

Have you tried putting an index on the kills table killer column?

Edit Info on indexes. http://www.w3schools.com/Sql/sql_create_index.asp

Daniel A. White
With CREATE INDEX you mean? What type of index? Can you be more specific?
Lasse A Karlsen
A: 

Yes, you should definitely look into joins. It's hard to tell from the snippet you posted, but whenever you can use a join over a subquery, it will be beneficial to do so.

You may also want to consider caching kill count somewhere else in your database; especially if you're using InnoDB, a COUNT() operation takes more time than simply SELECTing a [relatively] recent value from the database. You can probably implement this easily in the code by incrementing the kill count on the appropriate record, or something like that.

cookiecaper
most databases will try to optimize a query before running it. This applies to queries with subqueries as well.
Marius
Are you saying that there's no need to optimize your queries at all?
Calvin
One shouldn't rely upon his database engine to refactor improper code; optimizations are conservative by necessity and the database cannot always ascertain your intent. Optimizations are there to help fill in the blanks, not to rewrite your whole program for sanity and performance; that's your job.
cookiecaper
A: 

Try:

SELECT handle, count(*) as kills
FROM players as p
JOIN Kills as k ON k.gid = p.gid
WHERE  gid="3245"
AND team="axis"
ORDER BY kills DESC LIMIT 0, 10;
ck
"ERROR 1052 (23000): Column 'gid' in where clause is ambiguous". There is no "kills" in players. The players handle is stored in "players" and kills are stored in "kills".
Lasse A Karlsen
change the where clause to p.gid and p.team
Darryl E. Clarke
+3  A: 

In general, MySQL performs joins faster than subselects. To learn how to optimize queries, I suggest reading up on the EXPLAIN syntax.

First, ensure your kills table has a compound index on killer and gid, then try this join:

SELECT p.handle, COUNT(*) AS n_kills
FROM players p 
    JOIN kills k 
    ON p.handle = k.killer
        AND p.gid = k.gid
WHERE p.gid = 3245 
    AND p.team = "axis"
    AND k.k_team != k.c_team
GROUP BY p.handle
ORDER BY n_kills DESC LIMIT 0,10

Seeing the CREATE TABLE statement for those two tables would help determine any issues with your indexes.

John Douthat
i.e. ALTER TABLE kills ADD INDEX (`killer`, `gid`);ALTER TABLE players ADD INDEX (`handle`, `gid`);
John Douthat
Nice. But I get an error on your query - "near 'AND p.team = "axis" AND k.k_team != k.c_team GROUP BY p.handle ORDER BY n_ki' at line 1"
Lasse A Karlsen
Btw, should I use index on every column?
Lasse A Karlsen
Think I made it work, btw..
Lasse A Karlsen
sorry, I had two ANDS in there
John Douthat
Indexes are a trade-off. They slow down writes, but can speed up reads. You should have indexes on the columns or groups of columns you query or join against. A decent tip-off that something should have an index is if you're using it in a JOIN or a WHERE clause.
John Douthat
Okay, thanks for the tip!
Lasse A Karlsen
A: 

For my experience the offset in the limitation (LIMIT 0,10) is the performance killer. If you do not limit and loop thru the resource only extracting the first ten record rows, then it will fasten the query dramatically. Why? You do not fetch the full resource, just shifting the resource pointer to the end of the resource. Only the first ten rows are affected, the others are thrown away. Does not matter how big a resource is. Just try it. You 'll see!

i.e. in PHP

$res=mysql_query("SELECT handle,
(
    SELECT COUNT(*)
    FROM kills
    WHERE killer=p.handle
    AND k_team != c_team
    AND gid=p.gid
) AS kills
FROM players as p
WHERE  gid="3245"
AND team="axis"
ORDER BY kills DESC;");

$i=0;
$results = array();
while($row=mysql_fetch_array($res)){
if($i<10){
  $results[] = $row;
}
$i++;
}

Only LINUX!

WINDOWS:

$i=0;
$results = array();
while($row=mysql_fetch_array($res)){
    if($i<10) {      
      $results[] = $row;
    } else {        
        mysql_close($db);
        break;
    }
     $i++;

}

Assuming indeces are setted well.

Tom Schaefer