Merge
mysql> SHOW CREATE TABLE allweek\\G
[...]
Create Table: CREATE TABLE `allweek` (
`ID` int(11) NOT NULL DEFAULT '0',
`Name` char(35) NOT NULL DEFAULT '',
`CountryCode` char(3) NOT NULL DEFAULT '',
`District` char(20) NOT NULL DEFAULT '',
`Population` int(11) NOT NULL DEFAULT '0',
`modtime` datetime DEFAULT NULL,
KEY `modtime` (`modtime`)
) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1
UNION=(`monday`,`tuesday`,`wednesday`,`thursday`,`city
date`)
1 row in set (0.00 sec)
3
AUTO_INCREMENT
woes
mysql> CREATE TABLE t (
‐> id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
‐> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t (id)
‐> VALUES (NULL);
Query OK, 1 row affected (0.04 sec)
mysql> SELECT *
‐> FROM t
‐> WHERE id IS NULL;
+‐‐‐‐+
| id |
+‐‐‐‐+
| 1 |
+‐‐‐‐+
But...
• id is AUTO_INCREMENT, so it’s 1
• let’s run that again
mysql> CREATE TABLE t (
‐> id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
‐> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t (id)
‐> VALUES (NULL);
Query OK, 1 row affected (0.04 sec)
mysql> SELECT *
‐> FROM t
‐> WHERE id IS NULL;
+‐‐‐‐+
| id |
+‐‐‐‐+
| 1 |
+‐‐‐‐+
mysql> CREATE TABLE t (
‐> id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
‐> );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t (id)
‐> VALUES (NULL);
Query OK, 1 row affected (0.04 sec)
mysql> SELECT *
‐> FROM t
‐> WHERE id IS NULL;
+‐‐‐‐+
| id |
+‐‐‐‐+
| 1 |
+‐‐‐‐+
mysql> SELECT *
‐> FROM t
‐> WHERE id IS NULL;
Empty set (0.00 sec)
mysql> SELECT * FROM t;
+‐‐‐‐+
| id |
+‐‐‐‐+
| 1 |
+‐‐‐‐+
1 row in set (0.00 sec)
WTF???
• WHERE ID IS NULL acts as
WHERE ID = LAST_ISERT_ID()
• But only the first time
• Brought to you by some weird ODBC
compatibility decision
4
How to get random
data
SELECT name
FROM random
ORDER BY RAND()
LIMIT 1
X
SELECT name
FROM random
ORDER BY RAND()
LIMIT 1
Wrong!
There is a better way
SELECT name
FROM random JOIN
(
SELECT CEIL(
RAND() *
(SELECT MAX(id) FROM random))
AS id
) AS r2
USING (id);
Really?
Really?
ORDER BY RAND() Subquery
10.000,00
1.000,00
100,00
Seconds
10,00
1,00
0,10
100 1000 10000 100000
Table Size
So...
Every time you use ORDER BY RAND()...
God kills a kitten
Please, think of the kittens
5
Prefix indexes
There’s no need to
index the whole
column
Name is CHAR(52)
mysql> SHOW CREATE TABLE Country\\G
*************************** 1. row ***************************
Table: Country
Create Table: CREATE TABLE `Country` (
`Code` char(3) NOT NULL DEFAULT '',
`Name` char(52) NOT NULL DEFAULT '',
`Continent` enum('Asia','Europe','North
America','Africa','Oceania','Antarctica','South America') NOT NULL
DEFAULT 'Asia',
`Region` char(26) NOT NULL DEFAULT '',
`SurfaceArea` float(10,2) NOT NULL DEFAULT '0.00',
`IndepYear` smallint(6) DEFAULT NULL,
`Population` int(11) NOT NULL DEFAULT '0',
`LifeExpectancy` float(3,1) DEFAULT NULL,
`GNP` float(10,2) DEFAULT NULL,
`GNPOld` float(10,2) DEFAULT NULL,
`LocalName` char(45) NOT NULL DEFAULT '',
`GovernmentForm` char(45) NOT NULL DEFAULT '',
`HeadOfState` char(60) DEFAULT NULL,
`Capital` int(11) DEFAULT NULL,
`Code2` char(2) NOT NULL DEFAULT '',
PRIMARY KEY (`Code`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
1 row in set (0.02 sec)
mysql> SELECT COUNT(*) AS Total, COUNT(DISTINCT Name) AS Diff, COUNT(*)
‐ COUNT(DISTINCT Name) AS Dupes FROM Country;
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Total | Diff | Dupes |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| 239 | 239 | 0 |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (1.04 sec)
mysql> SELECT COUNT(*) AS Total, COUNT(DISTINCT LEFT(Name,50)) AS Diff,
COUNT(*) ‐ COUNT(DISTINCT LEFT(Name,50)) AS Dupes FROM Country;
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Total | Diff | Dupes |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| 239 | 239 | 0 |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (2.02 sec)
mysql> SELECT COUNT(*) AS Total, COUNT(DISTINCT LEFT(Name,20)) AS Diff,
COUNT(*) ‐ COUNT(DISTINCT LEFT(Name,20)) AS Dupes FROM Country;
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Total | Diff | Dupes |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| 239 | 239 | 0 |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (0.93 sec)
mysql> SELECT COUNT(*) AS Total, COUNT(DISTINCT Name) AS Diff, COUNT(*)
‐ COUNT(DISTINCT Name) AS Dupes FROM Country;
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Total | Diff | Dupes |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| 239 | 239 | 0 |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (1.04 sec)
mysql> SELECT COUNT(*) AS Total, COUNT(DISTINCT LEFT(Name,50)) AS Diff,
COUNT(*) ‐ COUNT(DISTINCT LEFT(Name,50)) AS Dupes FROM Country;
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Total | Diff | Dupes |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| 239 | 239 | 0 |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (2.02 sec)
mysql> SELECT COUNT(*) AS Total, COUNT(DISTINCT LEFT(Name,20)) AS Diff,
COUNT(*) ‐ COUNT(DISTINCT LEFT(Name,20)) AS Dupes FROM Country;
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Total | Diff | Dupes |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
Got it? Cool! Now...
| 239 | 239 | 0 |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (0.93 sec)
There is a better way
CREATE DEFINER=`root`@`localhost` PROCEDURE `pref_index`(t_name CHAR(255), c_name CHAR(255))
BEGIN
DECLARE plength INT DEFAULT 1;
SET @q = CONCAT('SELECT CHARACTER_MAXIMUM_LENGTH INTO @maxlen
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = \"world2\" AND
TABLE_NAME = \"',
t_name,'\" AND
COLUMN_NAME = \"',
c_name, '\"');
PREPARE q FROM @q;
EXECUTE q;
DEALLOCATE PREPARE q;
REPEAT
SET @qq = CONCAT('SELECT COUNT(*) ‐ COUNT(DISTINCT ',c_name,')
into @dupe FROM ',t_name);
SET @pq = CONCAT('SELECT COUNT(*) ‐ COUNT(DISTINCT LEFT(',c_name,',',plength,'))
into @pdupe FROM ',t_name);
PREPARE qs FROM @qq;
EXECUTE qs;
DEALLOCATE PREPARE qs;
PREPARE ps FROM @pq;
EXECUTE ps;
DEALLOCATE PREPARE ps;
SET plength = plength + 1;
UNTIL plength >= @maxlen OR @pdupe = @dupe
END REPEAT;
SELECT plength, @pdupe, @dupe;
END
mysql> CALL pref_index('Country', 'Name');
+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| plength | @pdupe | @dupe |
+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| 18 | 0 | 0 |
+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (1.18 sec)
Query OK, 0 rows affected (1.18 sec)
mysql> SELECT COUNT(*) AS Total, COUNT(DISTINCT LEFT(Name,18)) AS
Diff, COUNT(*) ‐ COUNT(DISTINCT LEFT(Name,18)) AS Dupes FROM Country;
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Total | Diff | Dupes |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| 239 | 239 | 0 |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (0.00 sec)
mysql> ALTER TABLE Country ADD KEY (Name(18));
Query OK, 239 rows affected (1.48 sec)
Records: 239 Duplicates: 0 Warnings: 0
mysql> CALL pref_index('Country', 'Name');
+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| plength | @pdupe | @dupe |
+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| 18 | 0 | 0 |
+‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (1.18 sec)
Query OK, 0 rows affected (1.18 sec)
mysql> SELECT COUNT(*) AS Total, COUNT(DISTINCT LEFT(Name,18)) AS
Diff, COUNT(*) ‐ COUNT(DISTINCT LEFT(Name,18)) AS Dupes FROM Country;
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Total | Diff | Dupes |
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| 239 | 239 | 0 |
We just saved 34 bytes per row!
+‐‐‐‐‐‐‐+‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (0.00 sec)
mysql> ALTER TABLE Country ADD KEY (Name(18));
Query OK, 239 rows affected (1.48 sec)
Records: 239 Duplicates: 0 Warnings: 0
6
InnoDB clustered index
Data stored in PK order
Primary key Index
Leaf nodes
Indexes point to PK instead of actual data
So what?
Choose your PK
wisely
Load data in order
mysql> load data infile '/tmp/city_order.txt' into
table City_huge;
Query OK, 818027 rows affected (15.86 sec)
Records: 818027 Deleted: 0 Skipped: 0 Warnings: 0
mysql> load data infile '/tmp/city_rand.txt' into
table City_huge;
Query OK, 818027 rows affected (33 min 23.57 sec)
Records: 818027 Deleted: 0 Skipped: 0 Warnings: 0
But...
• Only in 5.0
• 5.1 implementation under review
• http://forge.mysql.com/wiki/
Testing_Show_Profiles_5_1
8
Usage statistics
Know what’s going on
SHOW STATUS
mysql> SHOW STATUS LIKE 'Com_select';
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Variable_name | Value |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Com_select | 13 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (1.65 sec)
mysql> SELECT 1;
+‐‐‐+
| 1 |
+‐‐‐+
| 1 |
+‐‐‐+
1 row in set (0.00 sec)
mysql> SHOW STATUS LIKE 'Com_select';
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Variable_name | Value |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Com_select | 14 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (0.00 sec)
SHOW STATUS
mysql> SHOW STATUS LIKE 'Com_select';
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Variable_name | Value |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Com_select | 14 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (0.00 sec)
mysql> FLUSH STATUS;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW STATUS LIKE 'Com_select';
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Variable_name | Value |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
| Com_select | 0 |
+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐+
1 row in set (0.00 sec)
External tools
• mysqlsla
http://hackmysql.com/mysqlsla
mysqlsla parses, filters, analyzes and
sorts MySQL slow, general, binary and
microslow patched logs in order to
create a customizable report of the
queries and their meta-property values.
External tools
• mysqlidxchk
http://hackmysql.com/mysqlidxchk
mysqlidxchk (MySQL Index Checker)
checks MySQL databases/tables for
unused indexes. Given one or more
slow, general, or \"raw\" log files,
mysqlidxchk reports which indexes in
the database schema are not used by
the queries in the log files.
9
Understanding
REPLACE
mysql> CREATE TABLE fk_relations (
‐> key1 INT NOT NULL PRIMARY KEY,
‐> key2 INT NOT NULL UNIQUE
‐> );
Query OK, 0 rows affected (0.02 sec)
mysql> INSERT INTO fk_relations VALUES (1,1), (2,2);
Query OK, 2 rows affected (0.00 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM fk_relations;
+‐‐‐‐‐‐+‐‐‐‐‐‐+
| key1 | key2 |
+‐‐‐‐‐‐+‐‐‐‐‐‐+
| 1 | 1 |
| 2 | 2 |
+‐‐‐‐‐‐+‐‐‐‐‐‐+
2 rows in set (0.00 sec)
Performance
• Be careful with complex queries
• Extremely slow on large datasets
• Use summary tables instead
Too complex
select d.dept_name, SUM(salary) from departments d
LEFT JOIN dept_emp de USING (dept_no) LEFT JOIN
salaries s USING (emp_no) where s.from_date >=
'2000‐01‐01' and s.to_date < '2001‐01‐01' group by
dept_no;
Instead, split in two
select d.dept_name, SUM(salary) from departments d
LEFT JOIN dept_emp de USING (dept_no) LEFT JOIN
salaries s USING (emp_no) where s.from_date >=
'2000‐01‐01' and s.to_date < '2001‐01‐01' group by
dept_no;
CREATE TEMPORARY TABLE salaries2000
SELECT * FROM salaries s
WHERE s.from_date >= '2000‐01‐01'
AND s.to_date < '2001‐01‐01';
SELECT d.dept_name, SUM(salary)
FROM departments d
LEFT JOIN dept_emp de USING (dept_no)
LEFT JOIN salaries2000 s USING (emp_no)
GROUP BY dept_no;
Big performance gain
(If indexes are in their place)
Big performance gain
(If indexes are in their place)
I’ve seen 10X!
one more thing...
one more thing...
I had to do it ;-)
Shit happens
mysql> select * from t;
+‐‐‐‐‐‐+
| id |
+‐‐‐‐‐‐+
| 2 |
+‐‐‐‐‐‐+
1 row in set (0.00 sec)
mysql> drop table t;
Query OK, 0 rows affected (0.04 sec)
WTF?!?
mysql> drop table t;
Query OK, 0 rows affected (0.00 sec)
mysql> drop table t;
ERROR 1051 (42S02): Unknown table 't'
Some seconds ago...
mysql> select * from t;
+‐‐‐‐+
| id |
+‐‐‐‐+
| 1 |
+‐‐‐‐+
1 row in set (0.00 sec)
WTF?!?
mysql> create temporary table t(id int);
...again
Query OK, 0 rows affected (0.08 sec)
mysql> select * from t;
Empty set (0.00 sec)
mysql> insert into t values (2);
Query OK, 1 row affected (0.05 sec)
mysql> select * from t;
+‐‐‐‐‐‐+
| id |
+‐‐‐‐‐‐+
| 2 |
+‐‐‐‐‐‐+
1 row in set (0.00 sec)
• Temporary tables exist per session
• Be careful using connection pools
• They overlap current tables
There’s a reason for everything
mysql> select * from t;
+‐‐‐‐‐‐+
| id |
+‐‐‐‐‐‐+
| 2 |
+‐‐‐‐‐‐+
1 row in set (0.00 sec)
temporary
mysql> drop table t;
Query OK, 0 rows affected (0.04 sec)
mysql> select * from t;
+‐‐‐‐+
| id |
+‐‐‐‐+
| 1 |
+‐‐‐‐+
regular
1 row in set (0.00 sec)
mysql> drop table t;
Query OK, 0 rows affected (0.00 sec)
mysql> drop table t;
ERROR 1051 (42S02): Unknown table 't'
What else?
• MySQL Proxy
https://launchpad.net/mysql-proxy
• MySQL Sandbox
https://launchpad.net/mysql-sandbox
• MySQL Random Query Generator
https://launchpad.net/randgen
Questions?
Probably out of time at this point, but it’s
the standard
0 comments
Post a comment