1. 程式人生 > >優化SQL語句的一般步驟詳情

優化SQL語句的一般步驟詳情

1.通過show status 命令瞭解各種SQL的執行頻率。

格式:show [session|global]status;

其中:session(預設)表示當前連線,

     global表示自資料庫啟動至今

show status;
show global status;
show status like 'com_%';
show global status like 'com_%';

引數說明:

com_xxx表示每個xxx語句執行的次數如:

com_select執行select操作的次數,一次查詢只累計加1

com_update執行update操作的次數

com_insert執行insert操作的次數,對批量插入只算一次。

com_delete執行delete操作的次數

只針對InnoDB儲存引擎的。

InnoDB_rows_read執行select操作的次數

InnoDB_rows_updated執行update操作的次數

InnoDB_rows_inserted執行insert操作的次數

InnoDB_rows_deleted執行delete操作的次數

其他:

connections連線mysql的數量

Uptime伺服器已經工作的秒數

Slow_queries慢查詢的次數

2.定位執行效率較低的SQL語句

1)explain select * from table where id=1000;

2)desc select * from table where id=1000;

每一列的簡單解釋

id:1

select_type:SIMPLE表示select的型別,常見的取值有SIMPLE(簡單表,即不使用表連線或者子查詢)、PRIMARY(主查詢,即外層的查詢)、UNION(UNION中的第二個或者後面的查詢語句)、SUBQUERY(子查詢中的第一個SELECT)等。

table:stu 輸出結果集的表

type:range 表示表的連線型別,效能由好到差:system(表僅一行)、const

(只一行匹配)、eq_ref(對於前面的每一行使用主鍵和唯一)、ref(同eq_ref,但沒有使用主鍵和唯一)、ref_or_null(同前面對null查詢)、index_merge(索引合併優化)、unique_subquery(主鍵子查詢)、index_subquery(非主鍵子查詢)、range(表單中的範圍查詢)、index(都通過查詢索引來得到資料)、all(通過全表掃描得到的資料)

possible_keys:name,ind_stu_name 表示查詢可能使用的索引。

key:name 表示實際使用的索引。

key_len:50 索引欄位的長度

ref:NULL

rows:8 掃描行的數量

Extra:Using where;Using index執行情況的說明和描述

索引問題

索引是資料庫優化中最常見也是最重要的手段之一,通過索引通常可以幫助使用者解決大多數的SQL效能問題。

1.索引的儲存分類

  MyISAM儲存引擎的表的資料和索引是自動分開儲存的,各自是獨一的一個檔案;InnoDB儲存引擎的表的資料和索引是儲存在同一個表空間裡面,但可以有多個檔案組成。

  MySQL目前不支援函式索引,但是能對列的前面某一部分進行索引,例如name欄位,可以只取name的前4個字元進行索引,這個特性可以大大縮小索引檔案的大小,使用者在設計表結構的時候也可以對文字列根據此特性進行靈活設計。

--其中company表名 ind_company_name索引名
create index ind_company_name on company(name(4));

2.MySQL如何使用索引

索引用於快速找出在某個列中有一特定值的行。對相關列使用索引是提高SELECT 操作效能的最佳途徑。

1)、使用索引

a)對於建立的多列索引,只要查詢的條件中用到最左邊的列,索引一般就會被使用。如下建立一個複合索引。

create index ind_sales2_com_mon on sales2(company_id,moneys);

然後按company_id進行查詢,發現使用到了複合索引

explain select * from sales2 where company_id = 2006\G

使用下面的查詢就沒有使用到複合索引。

explain select * from sales2 where moneys = 1\G

b)使用like的查詢,後面如果是常量並且只有%號不在第一個字元,索引才可能會被使用,如下:

複製程式碼

mysql> explain select * from t1 where name like "%z"\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4
        Extra: Using where
1 row in set (0.01 sec)
mysql> explain select * from t1 where name like "z%"\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: range
possible_keys: idx_name
          key: idx_name
      key_len: 63
          ref: NULL
         rows: 3
        Extra: Using where
1 row in set (0.00 sec)

c)如果對大的文字進行搜尋,使用全文索引而不使用like" %...%".

d)如果列名是索引,使用column_name is null將使用索引。

2)、存在索引但不使用索引

a)如果MySQL估計使用索引比全表掃描更慢,則不使用索引。例如如果列key_part1均勻分佈在1到100之間,查詢時使用索引就不是很好

select * from table_name where key_part1 >1 and key_part <90;

b)如果使用MEMORY/HEAP表並且where條件中不使用"="進行索引列,那麼不會用到索引。Heap表只有在"="的條件下會使用索引。

c)用or分割開的條件,如果or前的條件中的列有索引,而後面的列中沒有索引,那麼涉及的索引都不會被用到。

d)如果不是索引列的第一部分,如雖然在money上面建有複合索引,但是由於money不是索引的第一列,那麼在查詢中這個索引也不會被MySQL採用。

e)如果like是以%開始,可見雖然在name上面建有索引,但是由於條件中like的值為"%"在第一位了,那麼MySQL也不會採用這個索引。

f)如果列型別是字串,但在查詢時把一個數值型常量賦值給了一個字元型的列名name,那麼雖然在name列上有索引,但是也沒有用到。

3.檢視索引使用情況

  如果索引正在工作,Handler_read_key的值將很高,這個值代表了一個行被索引值讀的次數。

  Handler_read_rnd_next的值高則意味著查詢執行低效,並且應該建立索引補救。

 

mysql> show status like 'handler_read%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Handler_read_first    | 13    |
| Handler_read_key      | 35    |
| Handler_read_next     | 4     |
| Handler_read_prev     | 0     |
| Handler_read_rnd      | 0     |
| Handler_read_rnd_next | 96    |
+-----------------------+-------+

 

兩個簡單實用的優化方法

對於大多數開發人員來說,可能只希望掌握一些簡單實用的優化方法,對於更多更復雜的優化,更傾向於交給專業DBA來做。

1.定期分析表和檢查表

ANALYZE [LOCAL|NO_WRITE_TO_BINLOG] TABLE tal_name[,tbl_name]...

本語句用於分析和儲存表的關鍵字分佈,分析的結果將可以使得系統得到準確的統計資訊,使得SQL能夠生成正確的執行計劃。

mysql> analyze table t1;
+---------+---------+----------+----------+
| Table   | Op      | Msg_type | Msg_text |
+---------+---------+----------+----------+
| test.t1 | analyze | status   | OK       |
+---------+---------+----------+----------+

分析表的語法如下:(檢查一個或多個表是否有錯誤)

 

CHECK TABLE tbl_name[,tbl_name]...[option]... option =
{QUICK|FAST|MEDIUM|EXTENDED|CHANGED}

mysql> check table t1;
+---------+-------+----------+----------+
| Table   | Op    | Msg_type | Msg_text |
+---------+-------+----------+----------+
| test.t1 | check | status   | OK       |
+---------+-------+----------+----------+

 

2.定期優化表

OPTIMIZE [LOCAL|NO_WRITE_TO_BINLOG] TABLE tbl_name[,tbl_name]

  如果已經刪除了表的一大部分,或者如果已經對含有可變長度行的表進行了很多的改動,則需要做定期優化。這個命令可以將表中的空間碎片進行合併,但是此命令只對MyISAM、BDB和InnoDB表起作用。

mysql> optimize table t1;
+---------+----------+----------+----------+
| Table   | Op       | Msg_type | Msg_text |
+---------+----------+----------+----------+
| test.t1 | optimize | status   | OK       |
+---------+----------+----------+----------+

常用SQL的優化

1.大批量匯入資料

當用load命令匯入資料的時候,適當設定可以提高匯入的速度。對於MyISAM儲存引擎的表,可以通過以下方式快速的匯入大量的資料。

ALTER TALBE tbl_name DISABLE KEYS
loading the data
ALTER TABLE tbl_name ENABLE KEYS

DISABLE KEYS 和 ENABLE KEYS用來開啟或關閉MyISAM表非唯一索引的更新,可以提高速度,注意:對InnoDB表無效。

1)針對於InnoDB型別表資料匯入的優化

  因為InnoDB表是按照主鍵順序儲存的,所以將匯入的資料主鍵的順序排列,可以有效地提高匯入資料的效率。

load data infile '/home/mysql/film_test3.txt' into table film_test4;

2)關閉唯一性校驗可以提高匯入效率

  在匯入資料前先執行set unique_checks=0,關閉唯一性校驗,在匯入結束後執行set unique_checks=1,恢復唯一性校驗,可以提高匯入效率。

3)關閉自動提交可以提高匯入效率

  在匯入資料前先執行set autocommit=0,關閉自動提交事務,在匯入結束後執行set autocommit=1,恢復自動提交,可以提高匯入效率。

2.優化insert語句

儘量使用多個值表的insert語句,這樣可以大大縮短客戶與資料庫的連線、關閉等損耗。

可以使用insert delayed(馬上執行)語句得到更高的效率。

將索引檔案和資料檔案分別存放不同的磁碟上。

可以增加bulk_insert_buffer_size變數值的方法來提高速度但是隻對MyISAM表使用

當從一個檔案中裝載一個表時,使用LOAD DATA INFILE,這個通常比使用很多insert語句要快20倍。

3.優化group by 語句

  如果查詢包含group by但使用者想要避免排序結果的損耗,則可以使用order by null來禁止排序。

4.優化巢狀查詢

巢狀查詢(裡面的子句可以用到索引,外面的用不到索引)可以使用更有效的連結查詢(Join替代)

資料庫優化

1.優化表的型別

2.通過拆分提高表的訪問效率

3.使用中間表或檢視提高統計查詢速度

MySQL伺服器優化

四種字符集問題

 

Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8

# vi /etc/my.cnf
[client]
default-charset-set=utf8  --客戶端字符集
[mysqld]
character-set-server = utf8  --伺服器字符集
collation-server=utf8_general_ci  --校驗字符集

binary log日誌問題

# vi /etc/my.conf
log-bin=mysql-bin  --開啟binlog日誌

slow log慢查詢日誌問題

mysql> show variables like '%slow%';
+---------------------+-----------------------------------------+
| Variable_name       | Value                                   |
+---------------------+-----------------------------------------+
| log_slow_queries    | OFF                                     |
| slow_launch_time    | 2                                       |
| slow_query_log      | OFF                                     |
| slow_query_log_file | /usr/local/mysql/var/localhost-slow.log |
+---------------------+-----------------------------------------+
mysql> show variables like '%long%';
+-----------------+-----------+
| Variable_name   | Value     |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
--開啟慢查詢日誌
# vi /etc/my.cnf 
log_slow_queries = slow.log
long_query_time = 5
--重啟mysql伺服器
mysql> show variables like '%slow%';
+---------------------+----------+
| Variable_name       | Value    |
+---------------------+----------+
| log_slow_queries    | ON       |
| slow_launch_time    | 2        |
| slow_query_log      | ON       |
| slow_query_log_file | slow.log |
+---------------------+----------+
mysql> show variables like '%long%';
+-----------------+----------+
| Variable_name   | Value    |
+-----------------+----------+
| long_query_time | 5.000000 |
+-----------------+----------+

socket問題

1.有時候登入mysql時提示不能用socket登入,此時可以換成tcp方式去登入,但是可以測試時可以這樣用,但是必須要在php去用之前把這個事情解決了

# /usr/local/mysql/bin/mysql -uroot -proot --protocol tcp -hlocalhost
--這樣就可以登入,可以不用mysql.sock來登入,而mysql.sock是啟動mysqld
--服務時產生的,所以重啟mysqld程序又會產生mysql.sock

root密碼丟失

1.service mysqld stop
2.mysqld_safe --skip-grant-tables --user=mysql &
--跳過授權表mysql.user和mysql.db這些表
3.mysql -uroot
4.set password = password("root");
--用這一條語句結果報錯,就是因為加了 --skip-grant-tables
5.update user set password=password("root") where user='root' and host='localhost';
6.set password for [email protected] = password("root");
7.set password = password("root");
--和第五步一樣,都可以成功修改密碼。