際際滷

際際滷Share a Scribd company logo
10 things you might
not know about
MySQL
Jorge Bernal <jbernal@warp.es>


                                 Version 0.1
1
Query cache
Before
mysql>SELECTAVG(Population)FROMCity_huge;
++
|AVG(Population)|
++
|354359.9948|
++
1rowinset(0.58sec)

mysql>SELECTAVG(Population)FROMCity_huge;
++
|AVG(Population)|
++
|354359.9948|
++
1rowinset(0.56sec)
After
mysql>SELECTAVG(Population)FROMCity_huge;
++
|AVG(Population)|
++
|354359.9948|
++
1rowinset(0.56sec)

mysql>SELECTAVG(Population)FROMCity_huge;
++
|AVG(Population)|
++
|354359.9948|
++
1rowinset(0.00sec)
The magic
SETglobalquery_cache_size=8*1024*1024;
mysql>SHOWGLOBALVARIABLESLIKE'query_cache%';
+++
|Variable_name|Value|
+++
|query_cache_limit|1048576|
|query_cache_min_res_unit|4096|
|query_cache_size|8388608|
|query_cache_type|ON|
|query_cache_wlock_invalidate|OFF|
+++
5rowsinset(0.00sec)

mysql>SHOWGLOBALSTATUSLIKE'Qc%';
+++
|Variable_name|Value|
+++
|Qcache_free_blocks|1|
|Qcache_free_memory|8378312|
|Qcache_hits|1|
|Qcache_inserts|1|
|Qcache_lowmem_prunes|0|
|Qcache_not_cached|0|
|Qcache_queries_in_cache|1|
|Qcache_total_blocks|4|
+++
8rowsinset(0.00sec)
2
Life beyond MyISAM/
       InnoDB
Archive
mysql>SELECTENGINE,
(INDEX_LENGTH+DATA_LENGTH)ASSize
FROMINFORMATION_SCHEMA.TABLES
WHERETABLE_NAME='City_huge';
+++
|ENGINE|Size|
+++
|MyISAM|63203585|
+++

mysql>SELECTENGINE,
(INDEX_LENGTH+DATA_LENGTH)ASSize
FROMINFORMATION_SCHEMA.TABLES
WHERETABLE_NAME='City_huge';
+++
|ENGINE|Size|
+++
|ARCHIVE|13520399|
+++
ARCHIVE   MyISAM




80% compression
But...


 Only INSERT and SELECT
 No indexing
CSV
mysql>ALTERTABLECityENGINE=CSV;
QueryOK,0rowsaffected(0.05sec)
Records:0Duplicates:0Warnings:0

root@warhol:/usr/local/mysql/data/world$headCity.CSV
1,quot;Kabulquot;,quot;AFGquot;,quot;Kabolquot;,1780000
2,quot;Qandaharquot;,quot;AFGquot;,quot;Qandaharquot;,237500
3,quot;Heratquot;,quot;AFGquot;,quot;Heratquot;,186800
4,quot;MazareSharifquot;,quot;AFGquot;,quot;Balkhquot;,127800
5,quot;Amsterdamquot;,quot;NLDquot;,quot;NoordHollandquot;,731200
6,quot;Rotterdamquot;,quot;NLDquot;,quot;ZuidHollandquot;,593321
7,quot;Haagquot;,quot;NLDquot;,quot;ZuidHollandquot;,440900
8,quot;Utrechtquot;,quot;NLDquot;,quot;Utrechtquot;,234323
9,quot;Eindhovenquot;,quot;NLDquot;,quot;NoordBrabantquot;,201843
10,quot;Tilburgquot;,quot;NLDquot;,quot;NoordBrabantquot;,193238
Merge
mysql>SHOWCREATETABLEallweekG
[...]
CreateTable:CREATETABLE`allweek`(
`ID`int(11)NOTNULLDEFAULT'0',
`Name`char(35)NOTNULLDEFAULT'',
`CountryCode`char(3)NOTNULLDEFAULT'',
`District`char(20)NOTNULLDEFAULT'',
`Population`int(11)NOTNULLDEFAULT'0',
`modtime`datetimeDEFAULTNULL,
KEY`modtime`(`modtime`)
)ENGINE=MRG_MyISAMDEFAULTCHARSET=latin1
UNION=(`monday`,`tuesday`,`wednesday`,`thursday`,`city
date`)
1rowinset(0.00sec)
3
AUTO_INCREMENT
     woes
mysql>CREATETABLEt(
>idINTNOTNULLPRIMARYKEYAUTO_INCREMENT
>);
QueryOK,0rowsaffected(0.00sec)

mysql>INSERTINTOt(id)
>VALUES(NULL);
QueryOK,1rowaffected(0.04sec)

mysql>SELECT*
>FROMt
>WHEREidISNULL;
++
|id|
++
|1|
++
But...
 id is AUTO_INCREMENT, so its 1
 lets run that again
                     mysql>CREATETABLEt(
                     >idINTNOTNULLPRIMARYKEYAUTO_INCREMENT
                     >);
                     QueryOK,0rowsaffected(0.00sec)

                     mysql>INSERTINTOt(id)
                     >VALUES(NULL);
                     QueryOK,1rowaffected(0.04sec)

                     mysql>SELECT*
                     >FROMt
                     >WHEREidISNULL;
                     ++
                     |id|
                     ++
                     |1|
                     ++
mysql>CREATETABLEt(
>idINTNOTNULLPRIMARYKEYAUTO_INCREMENT
>);
QueryOK,0rowsaffected(0.00sec)

mysql>INSERTINTOt(id)
>VALUES(NULL);
QueryOK,1rowaffected(0.04sec)

mysql>SELECT*
>FROMt
>WHEREidISNULL;
++
|id|
++
|1|
++
mysql>SELECT*
>FROMt
>WHEREidISNULL;
Emptyset(0.00sec)

mysql>SELECT*FROMt;
++
|id|
++
|1|
++
1rowinset(0.00sec)
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
SELECTname
FROMrandomJOIN
(
SELECTCEIL(
RAND()*
(SELECTMAX(id)FROMrandom))
ASid
)ASr2
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
Theres no need to
 index the whole
     column
Name is CHAR(52)
mysql>SHOWCREATETABLECountryG
**1.row**
Table:Country
CreateTable:CREATETABLE`Country`(
`Code`char(3)NOTNULLDEFAULT'',
`Name`char(52)NOTNULLDEFAULT'',
`Continent`enum('Asia','Europe','North
America','Africa','Oceania','Antarctica','SouthAmerica')NOTNULL
DEFAULT'Asia',
`Region`char(26)NOTNULLDEFAULT'',
`SurfaceArea`float(10,2)NOTNULLDEFAULT'0.00',
`IndepYear`smallint(6)DEFAULTNULL,
`Population`int(11)NOTNULLDEFAULT'0',
`LifeExpectancy`float(3,1)DEFAULTNULL,
`GNP`float(10,2)DEFAULTNULL,
`GNPOld`float(10,2)DEFAULTNULL,
`LocalName`char(45)NOTNULLDEFAULT'',
`GovernmentForm`char(45)NOTNULLDEFAULT'',
`HeadOfState`char(60)DEFAULTNULL,
`Capital`int(11)DEFAULTNULL,
`Code2`char(2)NOTNULLDEFAULT'',
PRIMARYKEY(`Code`)
)ENGINE=MyISAMDEFAULTCHARSET=latin1
1rowinset(0.02sec)
mysql>SELECTCOUNT(*)ASTotal,COUNT(DISTINCTName)ASDiff,COUNT(*)
COUNT(DISTINCTName)ASDupesFROMCountry;
++++
|Total|Diff|Dupes|
++++
|239|239|0|
++++
1rowinset(1.04sec)
mysql>SELECTCOUNT(*)ASTotal,COUNT(DISTINCTLEFT(Name,50))ASDiff,
COUNT(*)COUNT(DISTINCTLEFT(Name,50))ASDupesFROMCountry;
++++
|Total|Diff|Dupes|
++++
|239|239|0|
++++
1rowinset(2.02sec)

mysql>SELECTCOUNT(*)ASTotal,COUNT(DISTINCTLEFT(Name,20))ASDiff,
COUNT(*)COUNT(DISTINCTLEFT(Name,20))ASDupesFROMCountry;
++++
|Total|Diff|Dupes|
++++
|239|239|0|
++++
1rowinset(0.93sec)
mysql>SELECTCOUNT(*)ASTotal,COUNT(DISTINCTName)ASDiff,COUNT(*)
COUNT(DISTINCTName)ASDupesFROMCountry;
++++
|Total|Diff|Dupes|
++++
|239|239|0|
++++
1rowinset(1.04sec)
mysql>SELECTCOUNT(*)ASTotal,COUNT(DISTINCTLEFT(Name,50))ASDiff,
COUNT(*)COUNT(DISTINCTLEFT(Name,50))ASDupesFROMCountry;
++++
|Total|Diff|Dupes|
++++
|239|239|0|
++++
1rowinset(2.02sec)

mysql>SELECTCOUNT(*)ASTotal,COUNT(DISTINCTLEFT(Name,20))ASDiff,
COUNT(*)COUNT(DISTINCTLEFT(Name,20))ASDupesFROMCountry;
++++
|Total|Diff|Dupes|
++++
                                      Got it? Cool! Now...
|239|239|0|
++++
1rowinset(0.93sec)
There is a better way
CREATEDEFINER=`root`@`localhost`PROCEDURE`pref_index`(t_nameCHAR(255),c_nameCHAR(255))
BEGIN

DECLAREplengthINTDEFAULT1;
SET@q=CONCAT('SELECTCHARACTER_MAXIMUM_LENGTHINTO@maxlen
FROMINFORMATION_SCHEMA.COLUMNS
WHERETABLE_SCHEMA=quot;world2quot;AND
TABLE_NAME=quot;',
t_name,'quot;AND
COLUMN_NAME=quot;',
c_name,'quot;');
PREPAREqFROM@q;
EXECUTEq;
DEALLOCATEPREPAREq;

REPEAT
SET@qq=CONCAT('SELECTCOUNT(*)COUNT(DISTINCT',c_name,')
into@dupeFROM',t_name);
SET@pq=CONCAT('SELECTCOUNT(*)COUNT(DISTINCTLEFT(',c_name,',',plength,'))
into@pdupeFROM',t_name);

PREPAREqsFROM@qq;
EXECUTEqs;
DEALLOCATEPREPAREqs;

PREPAREpsFROM@pq;
EXECUTEps;
DEALLOCATEPREPAREps;

SETplength=plength+1;
UNTILplength>=@maxlenOR@pdupe=@dupe
ENDREPEAT;
SELECTplength,@pdupe,@dupe;
END
mysql>CALLpref_index('Country','Name');
++++
|plength|@pdupe|@dupe|
++++
|18|0|0|
++++
1rowinset(1.18sec)

QueryOK,0rowsaffected(1.18sec)

mysql>SELECTCOUNT(*)ASTotal,COUNT(DISTINCTLEFT(Name,18))AS
Diff,COUNT(*)COUNT(DISTINCTLEFT(Name,18))ASDupesFROMCountry;
++++
|Total|Diff|Dupes|
++++
|239|239|0|
++++
1rowinset(0.00sec)

mysql>ALTERTABLECountryADDKEY(Name(18));
QueryOK,239rowsaffected(1.48sec)
Records:239Duplicates:0Warnings:0
mysql>CALLpref_index('Country','Name');
++++
|plength|@pdupe|@dupe|
++++
|18|0|0|
++++
1rowinset(1.18sec)

QueryOK,0rowsaffected(1.18sec)

mysql>SELECTCOUNT(*)ASTotal,COUNT(DISTINCTLEFT(Name,18))AS
Diff,COUNT(*)COUNT(DISTINCTLEFT(Name,18))ASDupesFROMCountry;
++++
|Total|Diff|Dupes|
++++
|239|239|0|
                                   We just saved 34 bytes per row!
++++
1rowinset(0.00sec)

mysql>ALTERTABLECountryADDKEY(Name(18));
QueryOK,239rowsaffected(1.48sec)
Records:239Duplicates:0Warnings: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>loaddatainfile'/tmp/city_order.txt'into
tableCity_huge;
QueryOK,818027rowsaffected(15.86sec)
Records:818027Deleted:0Skipped:0Warnings:0

mysql>loaddatainfile'/tmp/city_rand.txt'into
tableCity_huge;
QueryOK,818027rowsaffected(33min23.57sec)
Records:818027Deleted:0Skipped:0Warnings:0
7
Profiling
mysql>select*fromv_client_portfolio_high;
+++++
|client_id|client_first_name|client_last_name|portfolio_value|
+++++
|5|ABNER|ROSSELLETT|1252115.50|
|500|CANDICE|BARTLETT|1384877.50|
+++++
2rowsinset(4.01sec)
mysql>select*fromv_client_portfolio_high;
+++++
|client_id|client_first_name|client_last_name|portfolio_value|
+++++
|5|ABNER|ROSSELLETT|1252115.50|
|500|CANDICE|BARTLETT|1384877.50|
+++++

                          W hy?
2rowsinset(4.01sec)
mysql>setprofiling=1;
QueryOK,0rowsaffected(0.00sec)

mysql>select*fromv_client_portfolio_high;
+++++
|client_id|client_first_name|client_last_name|portfolio_value|
+++++
|5|ABNER|ROSSELLETT|1252115.50|
|500|CANDICE|BARTLETT|1384877.50|
+++++
2rowsinset(4.01sec)

mysql>showprofiles;
++++
|Query_ID|Duration|Query|
++++
|1|0.00007600|setprofiling=1|
|2|4.01965600|select*fromv_client_portfolio_high|
++++
mysql>selectmin(seq)seq,state,count(*)numb_ops,
>round(sum(duration),5)sum_dur,round(avg(duration),5)avg_dur,
>round(sum(cpu_user),5)sum_cpu,round(avg(cpu_user),5)avg_cpu
>frominformation_schema.profiling
>wherequery_id=2
>groupbystate
>orderbyseq;
++++++++
|seq|state|numb_ops|sum_dur|avg_dur|sum_cpu|avg_cpu|
++++++++
|0|(initialization)|1|0.00004|0.00004|0.00000|0.00000|
|1|Openingtables|1|0.00023|0.00023|0.00000|0.00000|
|2|Systemlock|1|0.00001|0.00001|0.00000|0.00000|
|3|Tablelock|1|0.00001|0.00001|0.00000|0.00000|
|4|checkingpermissions|1|0.00010|0.00010|0.00000|0.00000|
|5|optimizing|4|0.00004|0.00001|0.00000|0.00000|
|6|statistics|4|0.00007|0.00002|0.00100|0.00025|
|7|preparing|4|0.00005|0.00001|0.00000|0.00000|
|8|Creatingtmptable|1|0.00003|0.00003|0.00000|0.00000|
|9|executing|37352|0.16631|0.00000|0.05899|0.00000|
|10|Copyingtotmptable|1|0.00006|0.00006|0.00000|0.00000|
|15|Sendingdata|37353|3.85151|0.00010|3.72943|0.00010|
|74717|Sortingresult|1|0.00112|0.00112|0.00100|0.00100|
|74719|removingtmptable|2|0.00003|0.00001|0.00000|0.00000|
|74721|init|1|0.00002|0.00002|0.00000|0.00000|
|74727|end|1|0.00001|0.00001|0.00000|0.00000|
|74728|queryend|1|0.00000|0.00000|0.00000|0.00000|
|74729|freeingitems|1|0.00002|0.00002|0.00000|0.00000|
|74730|closingtables|2|0.00001|0.00001|0.00000|0.00000|
|74733|loggingslowquery|1|0.00000|0.00000|0.00000|0.00000|
++++++++
But...

 Only in 5.0
 5.1 implementation under review
 http://forge.mysql.com/wiki/
  Testing_Show_Profiles_5_1
8
Usage statistics
   Know whats going on
SHOW STATUS
mysql>SHOWSTATUSLIKE'Com_select';
+++
|Variable_name|Value|
+++
|Com_select|13|
+++
1rowinset(1.65sec)

mysql>SELECT1;
++
|1|
++
|1|
++
1rowinset(0.00sec)

mysql>SHOWSTATUSLIKE'Com_select';
+++
|Variable_name|Value|
+++
|Com_select|14|
+++
1rowinset(0.00sec)
SHOW STATUS
mysql>SHOWSTATUSLIKE'Com_select';
+++
|Variable_name|Value|
+++
|Com_select|14|
+++
1rowinset(0.00sec)

mysql>FLUSHSTATUS;
QueryOK,0rowsaffected(0.00sec)

mysql>SHOWSTATUSLIKE'Com_select';
+++
|Variable_name|Value|
+++
|Com_select|0|
+++
1rowinset(0.00sec)
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 quot;rawquot; log files,
  mysqlidxchk reports which indexes in
  the database schema are not used by
  the queries in the log files.
9
Understanding
  REPLACE
mysql>CREATETABLEfk_relations(
>key1INTNOTNULLPRIMARYKEY,
>key2INTNOTNULLUNIQUE
>);
QueryOK,0rowsaffected(0.02sec)

mysql>INSERTINTOfk_relationsVALUES(1,1),(2,2);
QueryOK,2rowsaffected(0.00sec)
Records:2Duplicates:0Warnings:0

mysql>SELECT*FROMfk_relations;
+++
|key1|key2|
+++
|1|1|
|2|2|
+++
2rowsinset(0.00sec)
10 things you might not know about MySQL
mysql>REPLACEINTOfk_relationsVALUES(1,3);
QueryOK,2rowsaffected(0.03sec)
mysql>REPLACEINTOfk_relationsVALUES(1,3);
QueryOK,2rowsaffected(0.03sec)

mysql>SELECT*FROMfk_relations;
+++
|key1|key2|
+++
|1|3|
|2|2|
+++
2rowsinset(0.00sec)

mysql>REPLACEINTOfk_relationsVALUES(1,2);
QueryOK,3rowsaffected(0.06sec)

???
mysql>REPLACEINTOfk_relationsVALUES(1,2);
QueryOK,3rowsaffected(0.06sec)

mysql>SELECT*FROMfk_relations;
+++
|key1|key2|
+++
|1|2|
+++
1rowinset(0.00sec)
+++
|key1|key2|
+++
|1|3|
|2|2|
+++

mysql>REPLACEINTOfk_relationsVALUES(1,2);
Equals...
+++
|key1|key2|
+++
|1|3|
|2|2|
+++

mysql>REPLACEINTOfk_relationsVALUES(1,2);
Equals...
key1isPK
mysql>DELETEFROMfk_relationsWHEREkey1=1;
+++
|key1|key2|
+++
|2|2|
+++

mysql>REPLACEINTOfk_relationsVALUES(1,2);
Equals...
key1isPK
mysql>DELETEFROMfk_relationsWHEREkey1=1;
key2isUnique
mysql>DELETEFROMfk_relationsWHEREkey2=2;
+++
|key1|key2|
+++
|1|2|
+++

mysql>REPLACEINTOfk_relationsVALUES(1,2);
Equals...
key1isPK
mysql>DELETEFROMfk_relationsWHEREkey1=1;
key2isUnique
mysql>DELETEFROMfk_relationsWHEREkey2=2;
mysql>INSERTINTOfk_relationsVALUES(1,2);
Thats why
mysql>REPLACEINTOfk_relationsVALUES(1,2);
QueryOK,3rowsaffected(0.06sec)

mysql>SELECT*FROMfk_relations;
+++
|key1|key2|
+++
|1|2|
+++
1rowinset(0.00sec)
Thats why
mysql>REPLACEINTOfk_relationsVALUES(1,2);
QueryOK,3rowsaffected(0.06sec)

mysql>SELECT*FROMfk_relations;
+++ + 1 insert
          2 deletes
|key1|key2|
+++
|1|2|
+++
1rowinset(0.00sec)
10
  The good and bad
about temporary tables
Performance


 Be careful with complex queries
 Extremely slow on large datasets
 Use summary tables instead
Too complex
selectd.dept_name,SUM(salary)fromdepartmentsd
LEFTJOINdept_empdeUSING(dept_no)LEFTJOIN
salariessUSING(emp_no)wheres.from_date>=
'20000101'ands.to_date<'20010101'groupby
dept_no;
Instead, split in two
selectd.dept_name,SUM(salary)fromdepartmentsd
LEFTJOINdept_empdeUSING(dept_no)LEFTJOIN
salariessUSING(emp_no)wheres.from_date>=
'20000101'ands.to_date<'20010101'groupby
dept_no;

CREATETEMPORARYTABLEsalaries2000
SELECT*FROMsalariess
WHEREs.from_date>='20000101'
ANDs.to_date<'20010101';

SELECTd.dept_name,SUM(salary)
FROMdepartmentsd
LEFTJOINdept_empdeUSING(dept_no)
LEFTJOINsalaries2000sUSING(emp_no)
GROUPBYdept_no;
Big performance gain
    (If indexes are in their place)
Big performance gain
    (If indexes are in their place)

     Ive seen 10X!
one more thing...
one more thing...
    I had to do it ;-)
Shit happens
mysql>select*fromt;
++
|id|
++
|2|
++
1rowinset(0.00sec)

mysql>droptablet;
QueryOK,0rowsaffected(0.04sec)
                                        WTF?!?
mysql>droptablet;
QueryOK,0rowsaffected(0.00sec)

mysql>droptablet;
ERROR1051(42S02):Unknowntable't'
Some seconds ago...
mysql>select*fromt;
++
|id|
++
|1|
++
1rowinset(0.00sec)
                                           WTF?!?
mysql>createtemporarytablet(idint);
                                           ...again
QueryOK,0rowsaffected(0.08sec)

mysql>select*fromt;
Emptyset(0.00sec)

mysql>insertintotvalues(2);
QueryOK,1rowaffected(0.05sec)

mysql>select*fromt;
++
|id|
++
|2|
++
1rowinset(0.00sec)
 Temporary tables exist per session
  Be careful using connection pools
 They overlap current tables
Theres a reason for everything
 mysql>select*fromt;
 ++
 |id|
 ++
 |2|
 ++
 1rowinset(0.00sec)
                                         temporary
 mysql>droptablet;
 QueryOK,0rowsaffected(0.04sec)

 mysql>select*fromt;
 ++
 |id|
 ++
 |1|
 ++
                                          regular
 1rowinset(0.00sec)

 mysql>droptablet;
 QueryOK,0rowsaffected(0.00sec)

 mysql>droptablet;
 ERROR1051(42S02):Unknowntable'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 its
the standard
Thanks!

More Related Content

10 things you might not know about MySQL