hdparm -m16 -d1
on the disks on boot to enable
reading/writing of multiple sectors at a time, and DMA. This may
increase the response time by 5-50 %.
async
(default) and noatime
.
ulimit -n #
in the safe_mysqld
script).
SELECT * FROM table_name...
LOCK TABLES
if you do a lot of changes in a batch; For example group
multiple UPDATES
or DELETES
together.
EXPLAIN SELECT
, SHOW VARIABLES
, SHOW STATUS
and SHOW PROCESSLIST
.
myisamchk
, CHECK TABLE
, OPTIMIZE TABLE
).
GRANT
on table level or column level if you don't really need it.
--with-mysqld-ldflags=-all-static
) and strip the final executable
with strip sql/mysqld
.
OPTIMIZE table
once in a while. This is especially
important on variable size rows that are updated a lot.
myisamchk -a
; Remember to take down MySQL before doing this!
CHECK table
.
mysqladmin -i10 processlist extended-status
mysqladmin debug
to get information about locks and performance.
WHERE
clause.
JOIN
tables
GROUP BY
ORDER BY
DISTINCT
GROUP BY
s on a big table, create
summary tables of the big table and query this instead.
UPDATE table set count=count+1 where key_column=constant
is very fast!
INSERT
.
Reading 2000000 rows by key: | NT | Linux | |
mysql | 367 | 249 | |
mysql_odbc | 464 | † | |
db2_odbc | 1206 | † | |
informix_odbc | 121126 | † | |
ms-sql_odbc | 1634 | † | |
oracle_odbc | 20800 | † | |
solid_odbc | 877 | † | |
sybase_odbc | 17614 | † | |
† | |||
Inserting (350768) rows: | NT | Linux | |
mysql | 381 | 206 | |
mysql_odbc | 619 | † | |
db2_odbc | 3460 | † | |
informix_odbc | 2692 | † | |
ms-sql_odbc | 4012 | † | |
oracle_odbc | 11291 | † | |
solid_odbc | 1801 | † | |
sybase_odbc | 4802 | † |
In the above test, MySQL was run with a 8M cache; the other databases
were run with installations defaults.
back_log | Change if you do a lot of new connections. |
thread_cache_size | Change if you do a lot of new connections. |
key_buffer_size | Pool for index pages; Can be made very big |
bdb_cache_size | Record and key cache used by BDB tables. |
table_cache | Change if you have many tables or simultaneous connections |
delay_key_write | Set if you need to buffer all key writes |
log_slow_queries | Find queries that takes a lot of time |
max_heap_table_size | Used with GROUP BY |
sort_buffer | Used with ORDER BY and GROUP BY |
myisam_sort_buffer_size | Used with REPAIR TABLE |
join_buffer_size | When doing a join without keys |
ANALYSE
procedure can help you find the optimal types for a table:
SELECT * FROM table_name PROCEDURE ANALYSE()
NOT NULL
for columns which will not store null values. This is
particularly important for columns which you index.
INDEX (a,b)
, you don't need an index on (a)
.
CHAR
/VARCHAR
column, index just a prefix
of the column to save space.
CREATE TABLE table_name (hostname CHAR(255) not null, index(hostname(10)))
.sym
files.
BEGIN WORK
/ COMMIT
| ROLLBACK
).
VARCHAR
, BLOB
or TEXT
columns).
If not, the table is created in dynamic-size format.
VARCHAR
, BLOB
and TEXT
columns
to another table just to get more speed on the main table.
myisampack
(pack_isam
for ISAM) one can create a read-only, packed
table. This minimizes disk usage which is very nice when using slow disks.
The packed tables are perfect to use on log tables which one will not
update anymore.
ORDER BY
/ GROUP BY
REPAIR TABLE
SELECT HIGH_PRIORITY
, INSERT LOW_PRIORITY
)
Auto_increment
REPLACE
(REPLACE INTO table_name VALUES (...)
)
INSERT DELAYED
LOAD DATA INFILE
/ LOAD_FILE()
INSERT
to insert many rows at a time.
SELECT INTO OUTFILE
LEFT JOIN
, STRAIGHT JOIN
LEFT JOIN
combined with IS NULL
ORDER BY
can use keys in some cases.
LIMIT
SELECT * from table1 WHERE a > 10 LIMIT 10,20
DELETE * from table1 WHERE a > 10 LIMIT 10
foo IN (
list of constants)
is very optimized.
GET_LOCK()
/RELEASE_LOCK()
LOCK TABLES
INSERT
and SELECT
can run concurrently.
CREATE TEMPORARY TABLE
CREATE TABLE .. SELECT
Delayed_keys
>, >=, =, <, <=, IF NULL and BETWEEN on a key.
SELECT * FROM table_name WHERE key_part1=1 and key_part2 >
5;
SELECT * FROM table_name WHERE key_part1 IS NULL;
- When you use a LIKE that doesn't start with a wildcard.
SELECT * FROM table_name WHERE key_part1 LIKE 'jani%'
- Retrieving rows from other tables when performing joins.
SELECT * from t1,t2 where t1.col=t2.key_part
- Find the
MAX()
or MIN()
value for a specific index.
SELECT MIN(key_part2),MAX(key_part2) FROM table_name where key_part1=10
ORDER BY
or GROUP BY
on a prefix of a key.
SELECT * FROM foo ORDER BY key_part1,key_part2,key_part3
- When all columns used in the query are part of one key.
SELECT key_part3 FROM table_name WHERE key_part1=1
key_part1
is evenly
distributed between 1 and 100, it's not good to use an index in the
following query:SELECT * FROM table_name where key_part1 > 1 and key_part1 < 90
ORDER BY
on a HEAP table
SELECT * FROM table_name WHERE key_part2=1
LIKE
that starts with a wildcardSELECT * FROM table_name WHERE key_part1 LIKE '%jani%'
SELECT * from table_name WHERE key_part1 = # ORDER BY key2
EXPLAIN
on every query that you think is too slow!
mysql> explain select t3.DateOfAction, t1.TransactionID -> from t1 join t2 join t3 -> where t2.ID = t1.TransactionID and t3.ID = t2.GroupID -> order by t3.DateOfAction, t1.TransactionID; +-------+--------+---------------+---------+---------+------------------+------+---------------------------------+ | table | type | possible_keys | key | key_len | ref | rows | Extra | +-------+--------+---------------+---------+---------+------------------+------+---------------------------------+ | t1 | ALL | NULL | NULL | NULL | NULL | 11 | Using temporary; Using filesort | | t2 | ref | ID | ID | 4 | t1.TransactionID | 13 | | | t3 | eq_ref | PRIMARY | PRIMARY | 4 | t2.GroupID | 1 | | +-------+--------+---------------+---------+---------+------------------+------+---------------------------------+Types
ALL
and range
signal a potential problem.
SHOW processlist
to find out what is going on:
+----+-------+-----------+----+---------+------+--------------+-------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-------+-----------+----+---------+------+--------------+-------------------------------------+ | 6 | monty | localhost | bp | Query | 15 | Sending data | select * from station,station as s1 | | 8 | monty | localhost | | Query | 0 | | show processlist | +----+-------+-----------+----+---------+------+--------------+-------------------------------------+Use
KILL
in mysql
or mysqladmin
to kill off runaway threads.
SHOW VARIABLES;
SHOW COLUMNS FROM ...\G
EXPLAIN SELECT ...\G
FLUSH STATUS;
SELECT ...;
SHOW STATUS;
SELECT
and INSERT
at the same time.
INSERT
on a table with deleted rows,
combined with SELECTS
that take a long time.
HAVING
on things you can have in a WHERE
clause.
JOINS
without using keys or keys which are not unique enough.
JOINS
on columns that have different column types.
=
WHERE
clause with UPDATE
or DELETE
in the MySQL
monitor. If you tend to do this, use the --i-am-a-dummy
option to the mysq
client.
LOCK TABLES
(Works on all table types)
GET_LOCK()
/RELEASE_LOCK()
ALTER TABLE
also does a table lock on BDB tables.
LOCK TABLES
gives you multiple readers on a table or one writer.
WRITE
lock has higher priority than a READ
lock to avoid
starving the writers. For writers that are not important one can use
the LOW_PRIORITY
keyword to let the lock handler prefer readers.
UPDATE LOW_PRIORITY SET value=10 WHERE id=10;
SELECT /*! SQL_BUFFER_RESULTS */ ...
SELECT SQL_BUFFER_RESULTS ...
SELECT SQL_SMALL_RESULT ... GROUP BY ...
SELECT SQL_BIG_RESULT ... GROUP BY ...
SELECT STRAIGHT_JOIN ...
FROM
clause.
SELECT ... FROM
table_name [USE INDEX (index_list) | IGNORE INDEX (index_list)] table_name2
mysql> LOCK TABLES trans READ, customer WRITE; mysql> select sum(value) from trans where customer_id=some_id; mysql> update customer set total_value=sum_from_previous_statement where customer_id=some_id; mysql> UNLOCK TABLES;
mysql> BEGIN WORK; mysql> select sum(value) from trans where customer_id=some_id; mysql> update customer set total_value=sum_from_previous_statement where customer_id=some_id; mysql> COMMIT;
UPDATE customer SET value=value+new_value WHERE customer_id=some_id;
REPLACE
works exactly like INSERT
, except that if an old record in
the table has the same value as a new record on a unique index, the
old record is deleted before the new record is inserted.
Instead of using
SELECT 1 FROM t1 WHERE key=# IF found-row LOCK TABLES t1 DELETE FROM t1 WHERE key1=# INSERT INTO t1 VALUES (...) UNLOCK TABLES t1; ENDIFDo
REPLACE INTO t1 VALUES (...)
localhost
when connecting to the MySQL server.
--skip-locking
(default on some OSes) if possible. This will turn off
external locking and will give better performance.
SELECT * FROM table_name WHERE hash=MD5(concat(col1,col2)) AND col_1='constant' AND col_2='constant'
STATUS
variables.
CHECK
and REPAIR
table.
GROUP BY
and DISTINCT
LEFT JOIN ... IF NULL
optimization.
CREATE TABLE ... SELECT
CREATE TEMPORARY table_name (...)
RENAME
(RENAME TABLE foo as foo_old, foo_new as foo
)
MERGE TABLES