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),哪列優先放在多列索引的前面
覆蓋索引是理性的索引 (explain 里extra的信息時using index)

覆蓋索引包括所有需要的列,但是不需要讀取單獨的數據頁,實際意味着不需要讀取數據存儲,只利用索引數據就可以檢索到實際想要的查詢的數據
在myisam表里,意味着只要讀入索引就可以得到問題的記錄,在innodb中 索引和數據是位於同一個文件中的,但仍然會高效些,因為只需要讀入索引
優化部分索引的性能

select type from tb where sid=1

建立(sid,type)的索引 就是覆蓋索引,比單獨在sid,type上建索引要快


與其在長字符的列上定義索引,還不如只在左邊的一小部分上建立索引

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 |
+----------------------+----------+-----------------------+----------------------+-------------+
mysql> set profiling=1;
Query OK, 0 rows affected

mysql> select * from a;
+----+-----+-------+
| id | uid | phone |
+----+-----+-------+
|  1 |   1 | 22    |
|  2 |   2 | 33    |
|  3 |   3 | 33    |
|  4 |   4 | 22    |
|  5 |   5 | 22    |
+----+-----+-------+
5 rows in set

mysql> select * from b;
+-------+
| phone |
+-------+
| 1111  |
| 2222  |
| 3333  |
| 4444  |
+-------+
4 rows in set

mysql> show profiles;
+----------+------------+-----------------+
| Query_ID | Duration   | Query           |
+----------+------------+-----------------+
|        1 | 0.00025225 | select * from a |
|        2 |  0.0009805 | select * from b |
+----------+------------+-----------------+
2 rows in set

mysql> show profile for query 2;
+----------------------+----------+
| Status               | Duration |
+----------------------+----------+
| starting             | 0.000106 |
| checking permissions | 1.4E-5   |
| Opening tables       | 3.3E-5   |
| init                 | 3E-5     |
| System lock          | 2E-5     |
| optimizing           | 0.000259 |
| statistics           | 4.5E-5   |
| preparing            | 2.5E-5   |
| executing            | 4E-6     |
| Sending data         | 0.000358 |
| end                  | 7E-6     |
| query end            | 6E-6     |
| closing tables       | 8E-6     |
| freeing items        | 5.4E-5   |
| cleaning up          | 1.4E-5   |
+----------------------+----------+
15 rows in set
View Code

 

優化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   |
+----+------+------+

11.優化group by  在sakila

explain select actor.first_name,actor.last_name,count(*) from film_actor
INNER JOIN actor USING(actor_id) GROUP BY film_actor.actor_id;

explain SELECT actor.first_name,actor.last_name,c.cnt from actor INNER JOIN
(SELECT actor_id ,count(actor_id) as cnt from film_actor GROUP BY actor_id)
as c USING(actor_id);

 

小表 全表掃描效率更高
主鍵可以與外鍵構成參照完整性約束,防止數據不一致,唯一索引不行
覆蓋索引

復合索引前綴規則
like %不能在前面
column is null可以使用索引
如果mysql估計使用索引比全表掃描慢,會放棄使用索引 (比如100條數據,查 where id >1 and id <100)
如果or前的的條件的列有索引,后面的沒有,索引都不會用到 (where a=1 or b=2 a有索引,b沒有,則都不會用到索引)

列類型是字符串類型,查詢時一定要給值加引號,否則索引會失效 (name varchar(10)  存個100  where name=100 會索引失效)

關聯更新

tb1 (id,sex,par,c1,c2)
tb2 (id ,age,c1,c2)

update A,B set tb1.c1=tb2.c1,tb1.c2=tb2.c2 where tb1.id=tb2.id and tb2.age>50

update tb1 inner join tb2 on tb1.id=tb2.id
set tb1.c1=tb2.c1,tb1.c2=tb2.c2
where tb2.age>50

show status
返回一些計數器,show global status查看服務器級別的所有計數
show processlist
觀察是否有大量的線程處於不正常狀態

mysql> show processlist;
+----+------+-----------------+------+---------+------+----------+------------------+
| Id | User | Host            | db   | Command | Time | State    | Info             |
+----+------+-----------------+------+---------+------+----------+------------------+
|  2 | root | localhost:50043 | NULL | Sleep   | 1019 |          | NULL             |
|  3 | root | localhost:50044 | yii2 | Sleep   | 1019 |          | NULL             |
|  8 | root | localhost:50317 | yii2 | Query   |    0 | starting | show processlist |
+----+------+-----------------+------+---------+------+----------+------------------+

其它需要注意的小細節

范式修改
優化長難的查詢語句

Mysql內部每秒可掃描內存中上百萬行數據,相比之下,相應數據給客戶端就要慢得多
使用盡可能少的查詢
有時將一個大查詢分解為多個小的查詢時有必要的(方便緩存)

切分查詢
將一個大查詢分解為多個小的相同查詢
一次性刪除10000萬的數據比一次刪除1萬暫停一會的方案更加損耗服務器開銷

分解關聯查詢
將一條關聯語句分解成多條sql語句來執行
讓緩存效率更高
執行單個查詢可以減少鎖的競爭
在應用層做關聯查詢可以更容易對數據庫進行拆分


優化特定類型查詢語句
count(*) 會忽略所有列,直接統計所有列數,因此不要使用count(列名)
在myisam中,沒有任何where條件的count(*)非常快
有where的話就不一定比其它的引擎快
可以使用explain查詢近似值,用近似值代替count(*)
增加匯總表,緩存


優化關聯查詢
確定on或者using子句列上有索引
確保group by 和order by中只有一個表中的列,mysql才可能使用到索引
使用標識列更快


優化子查詢
使用關聯查詢替代

優化group by和distinct

如果不需要order by進行group by時使用order by null,mysql不再進行文件排序
with rollup超級聚合,可以挪到應用程序處理
優化limit分頁(加條件 比如 id>上次最后一個id)

優化union
union all效率高於union

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM