mysql常見的優化需要註意的點
1.explain分析
explian引用
索引基數
show indexes from table_name;
主鍵索引具有最好的基數
測試時
不走緩存
SELECT SQL_NO_CACHE id from test_null;
2.更好的索引類型
索引列盡可能的為not null ,避免在可空的列索引上進行二次掃描
要盡量避免 NULL ,關於索引列為Null的是否走索引,見測試 索引列的值為null查詢時走索引的情況
3.使用unique index
與常規索引比不需要進行索引範圍掃描
4.使用primary key
主鍵是uniquekey的一種特殊形式 。在innodb中,一個uniquekey是一個聚集索引(即對磁盤上數據排列的索引),當數據按照主鍵的次序進行檢索時會極大改進性能
5.索引太多是有害的
例如,如果possible_keys 列表中有超過3個的索引,mysql優化器有太多信息而無法確定最好使用哪個索引,也就意味著有些是低效或者無用的索引
6.索引列使用最小可能的數據類型
比如在一個varchar(100)甚至更大的列上建立索引,一種改進方法是建立一個額外的列,並在包含較大的varchar(100)列的md5值的額外varchar(32)列上創建索引。
更好的方法是使用bigint來存儲md5值的數字表示,數字索引更加高效
CONV(N,from_base,to_base)
mysql> select conv(‘a‘,16,10); +-----------------+| conv(‘a‘,16,10) | +-----------------+ | 10 | +-----------------+ mysql> select conv(substr(md5(‘abc‘),1,16),16,10); +-------------------------------------+ | conv(substr(md5(‘abc‘),1,16),16,10) | +-------------------------------------+ | 10376663631224000432 | +-------------------------------------+
7.建立索引時
如果使用到多個列,定義多列索引
哪列的唯一性更高(基數大 show indexes from table_name),哪列優先放在多列索引的前面
覆蓋索引是理性的索引
覆蓋索引包括所有需要的列,但是不需要讀取單獨的數據頁,實際意味著不需要讀取數據存儲,只利用索引數據就可以檢索到實際想要的查詢的數據
在myisam表裏,意味著只要讀入索引就可以得到問題的記錄,在innodb中 索引和數據是位於同一個文件中的,但仍然會高效些,因為只需要讀入索引
優化部分索引的性能
與其在長字符的列上定義索引,還不如只在左邊的一小部分上建立索引
8.一些常見的不使用索引的情況
開始字符是通配符是,或者 在索引列上使用標量函數
like "%123",upper()
9.覆蓋索引的左前綴原則
10.更詳細的分析
set profiling=1;
select * from table;
show profile;
show profile source ;
mysql> select * from test_null where mark like ‘aaa9999%‘; +------+---------+ | id | mark | +------+---------+ | 9999 | aaa9999 | +------+---------+ 1 row in set mysql> show profile; +----------------------+----------+ | Status | Duration | +----------------------+----------+ | starting | 5.5E-5 | | checking permissions | 1.1E-5 | | Opening tables | 2E-5 | | init | 2.4E-5 | | System lock | 7E-6 | | optimizing | 8E-6 | | statistics | 1.4E-5 | | preparing | 7E-6 | | executing | 2E-6 | | Sending data | 0.006271 | | end | 5.7E-5 | | query end | 3.6E-5 | | closing tables | 5.1E-5 | | freeing items | 0.000348 | | cleaning up | 0.00011 | +----------------------+----------+ mysql> show profile source; +----------------------+----------+-----------------------+----------------------+-------------+ | Status | Duration | Source_function | Source_file | Source_line | +----------------------+----------+-----------------------+----------------------+-------------+ | starting | 5.5E-5 | NULL | NULL | NULL | | checking permissions | 1.1E-5 | check_access | sql_authorization.cc | 835 | | Opening tables | 2E-5 | open_tables | sql_base.cc | 5648 | | init | 2.4E-5 | handle_query | sql_select.cc | 121 | | System lock | 7E-6 | mysql_lock_tables | lock.cc | 321 | | optimizing | 8E-6 | JOIN::optimize | sql_optimizer.cc | 151 | | statistics | 1.4E-5 | JOIN::optimize | sql_optimizer.cc | 367 | | preparing | 7E-6 | JOIN::optimize | sql_optimizer.cc | 475 | | executing | 2E-6 | JOIN::exec | sql_executor.cc | 119 | | Sending data | 0.006271 | JOIN::exec | sql_executor.cc | 195 | | end | 5.7E-5 | handle_query | sql_select.cc | 199 | | query end | 3.6E-5 | mysql_execute_command | sql_parse.cc | 4952 | | closing tables | 5.1E-5 | mysql_execute_command | sql_parse.cc | 5004 | | freeing items | 0.000348 | mysql_parse | sql_parse.cc | 5578 | | cleaning up | 0.00011 | dispatch_command | sql_parse.cc | 1864 | +----------------------+----------+-----------------------+----------------------+-------------+
優化update
換成select使用explain
優化delete
mysql> select * from parent; +----+------+ | id | name | +----+------+ | 1 | pa | | 2 | pb | | 3 | pc | | 4 | pd | +----+------+ 4 rows in set mysql> select * from child; +-----------+----------+ | parent_id | child_id | +-----------+----------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | | 1 | 4 | | 1 | 5 | | 2 | 6 | | 0 | 7 | | 5 | 8 | | 6 | 9 | | 5 | 10 | +-----------+----------+
刪除child中parent_id不在parent表的記錄
一般的寫法是
delete from child where parent_id not in(select id from parent);
更加高效的是使用連接查詢
通過以下來驗證
set profiling=1;
select * from child where parent_id not in(select id from parent);
select child.* from child left join parent on child.parent_id=parent.id where parent.id is null;
select query_id,count(*) as ‘#ops‘ ,sum(duration) from information_schema.profiling group by query_id;
select * from information_schema.profiling ;
演示結果 mysql> set profiling=1; Query OK, 0 rows affected mysql> select * from child where parent_id not in(select id from parent); +-----------+----------+ | parent_id | child_id | +-----------+----------+ | 0 | 7 | | 5 | 8 | | 6 | 9 | | 5 | 10 | +-----------+----------+ 4 rows in set mysql> select child.* from child left join parent on child.parent_id=parent.id where parent.id is null; +-----------+----------+ | parent_id | child_id | +-----------+----------+ | 0 | 7 | | 5 | 8 | | 6 | 9 | | 5 | 10 | +-----------+----------+ 4 rows in set mysql> select query_id,count(*) as ‘#ops‘ ,sum(duration) from information_schema.profiling group by query_id; +----------+------+---------------+ | query_id | #ops | sum(duration) | +----------+------+---------------+ | 1 | 23 | 0.000749 | | 2 | 16 | 0.000388 | +----------+------+---------------+
優化器顯示第二個用了更少的操作
優化Insert,同一表的多條類似的多個insert改寫成1條減少數據庫的網絡往返
例外一個好處是mysql只需為insert語句產生一次執行計劃,可以在多個值上利用同一個執行計劃
當批量插入時,如果單個插入失敗,多個value子句說明的記錄都無法插入成功
優化insert ...on duplicate key update
replace在內部是使用delete和insert來實現的,因而其效率並不高
使用insert ...on duplicate key update
如果存在同樣主鍵值的記錄,而其它列與現在存指定的記錄有所不同,就更新該記錄,如果記錄不存在就插入該記錄,如果記錄存在而且沒有任何值發生改變
就不做任何操作,優於replace
mysql> desc a; +-------+----------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+----------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | sid | int(11) | YES | | NULL | | | type | char(10) | NO | | NULL | | +-------+----------+------+-----+---------+----------------+ mysql> select * from a; +----+-----+------+ | id | sid | type | +----+-----+------+ | 1 | 11 | aa | | 2 | 1 | b | | 3 | 2 | c | | 4 | 3 | d | +----+-----+------+ mysql> insert into a(`id`,`type`) values(1,‘a1‘); 1062 - Duplicate entry ‘1‘ for key ‘PRIMARY‘ mysql> insert into a(`id`,`type`) values(1,‘a1‘) on duplicate key update type=‘a1‘; Query OK, 2 rows affected 註意改變的是2行 mysql> insert into a(`id`,`type`) values(5,‘a5‘) on duplicate key update type=‘a5‘; Query OK, 1 row affected mysql> select * from a; +----+------+------+ | id | sid | type | +----+------+------+ | 1 | 11 | a1 | | 2 | 1 | b | | 3 | 2 | c | | 4 | 3 | d | | 5 | NULL | a5 | +----+------+------+
待續...
mysql常見的優化需要註意的點