天天看點

mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

一. 性能監控

使用show profile查詢解析工具,可以指定具體的type

此工具預設是禁用的,可以通過伺服器變量在繪畫級别動态的修改

set profiling=1;

當設定完成之後,在伺服器上執行的所有語句,都會測量其耗費的時間和其他一些查詢執行狀态變更相關的資料。

select * from emp;

在mysql的指令行模式下隻能顯示兩位小數的時間,可以使用如下指令檢視具體的執行時間

show profiles;

執行如下指令可以檢視詳細的每個步驟的時間:

show profile for query 1;

用法

1. 将profiling屬性設定為1
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)
 2. 執行查詢語句
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)
3. show profile  檢視所有的執行語句
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)
4. 檢視最後一條執行語句的執行情況
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)
5.檢視其中的某一條執行語句執行情況
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

type

1. all: 顯示所有性能資訊

show profile all for query n

mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

2. block io: 顯示塊IO操作的次數

show profile block io for query n

3. context switches: 顯示上下文切換次數,被動和主動

show profile context switches for query n

4. cpu: 顯示使用者CPU時間,系統CPU時間

show profile cpu for query n

5. IPC: 顯示發送和接受的消息數量

show profile ipc for query n

6. Memory: 暫未實作

7. page faults: 顯示頁錯誤數量

show profile page faults for query n

8. source: 顯示源碼中的函數名稱與位置

show profile source for query n

9. swaps: 實作swap的次數

show profile swaps for query n

MYSQL performance schema詳解 用于監控mysql

0、performance_schema的介紹

MySQL的performance schema 用于監控MySQL server在一個較低級别的運作過程中的資源消耗、資源等待等情況。

特點如下:

1、提供了一種在資料庫運作時實時檢查server的内部執行情況的方法。performance_schema 資料庫中的表使用performance_schema存儲引擎。該資料庫主要關注資料庫運作過程中的性能相關的資料,與information_schema不同,information_schema主要關注server運作過程中的中繼資料資訊

2、performance_schema通過監視server的事件來實作監視server内部運作情況, “事件”就是server内部活動中所做的任何事情以及對應的時間消耗,利用這些資訊來判斷server中的相關資源消耗在了哪裡?一般來說,事件可以是函數調用、作業系統的等待、SQL語句執行的階段(如sql語句執行過程中的parsing 或 sorting階段)或者整個SQL語句與SQL語句集合。事件的采集可以友善的提供server中的相關存儲引擎對磁盤檔案、表I/O、表鎖等資源的同步調用資訊。 ​ 3、performance_schema中的事件與寫入二進制日志中的事件(描述資料修改的events)、事件計劃排程程式(這是一種存儲程式)的事件不同。performance_schema中的事件記錄的是server執行某些活動對某些資源的消耗、耗時、這些活動執行的次數等情況。 ​ 4、performance_schema中的事件隻記錄在本地server的performance_schema中,其下的這些表中資料發生變化時不會被寫入binlog中,也不會通過複制機制被複制到其他server中。 ​ 5、 目前活躍事件、曆史事件和事件摘要相關的表中記錄的資訊。能提供某個事件的執行次數、使用時長。進而可用于分析某個特定線程、特定對象(如mutex或file)相關聯的活動。 ​ 6、PERFORMANCE_SCHEMA存儲引擎使用server源代碼中的“檢測點”來實作事件資料的收集。對于performance_schema實作機制本身的代碼沒有相關的單獨線程來檢測,這與其他功能(如複制或事件計劃程式)不同 ​ 7、收集的事件資料存儲在performance_schema資料庫的表中。這些表可以使用SELECT語句查詢,也可以使用SQL語句更新performance_schema資料庫中的表記錄(如動态修改performance_schema的setup_*開頭的幾個配置表,但要注意:配置表的更改會立即生效,這會影響資料收集) ​ 8、performance_schema的表中的資料不會持久化存儲在磁盤中,而是儲存在記憶體中,一旦伺服器重新開機,這些資料會丢失(包括配置表在内的整個performance_schema下的所有資料) ​ 9、MySQL支援的所有平台中事件監控功能都可用,但不同平台中用于統計事件時間開銷的計時器類型可能會有所差異。

1、performance schema入門

在mysql的5.7版本中,性能模式是預設開啟的,如果想要顯式的關閉的話需要修改配置檔案,不能直接進行修改,會報錯Variable 'performance_schema' is a read only variable。
--檢視performance_schema的屬性
mysql> SHOW VARIABLES LIKE 'performance_schema';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| performance_schema | ON    |
+--------------------+-------+
1 row in set (0.01 sec)
​
--在配置檔案中修改performance_schema的屬性值,on表示開啟,off表示關閉
[mysqld]
performance_schema=ON
​
--切換資料庫
use performance_schema;
​
--檢視目前資料庫下的所有表,會看到有很多表存儲着相關的資訊
show tables;
​
--可以通過show create table tablename來檢視建立表的時候的表結構
mysql> show create table setup_consumers;
+-----------------+---------------------------------
| Table           | Create Table                    
+-----------------+---------------------------------
| setup_consumers | CREATE TABLE `setup_consumers` (
  `NAME` varchar(64) NOT NULL,                      
  `ENABLED` enum('YES','NO') NOT NULL               
) ENGINE=PERFORMANCE_SCHEMA DEFAULT CHARSET=utf8 |  
+-----------------+---------------------------------
1 row in set (0.00 sec)                             
           

想要搞明白後續的内容,同學們需要了解兩個基本概念:

instruments: 生産者,用于采集mysql中各種各樣的操作産生的事件資訊,對應配置表中的配置項我們可以稱為監控采集配置項。

consumers:消費者,對應的消費者表用于存儲來自instruments采集的資料,對應配置表中的配置項我們可以稱為消費存儲配置項。

2、performance_schema表的分類

performance_schema庫下的表可以按照監視不同的緯度就行分組。
--語句事件記錄表,這些表記錄了語句事件資訊,目前語句事件表events_statements_current、曆史語句事件表events_statements_history和長語句曆史事件表events_statements_history_long、以及聚合後的摘要表summary,其中,summary表還可以根據帳号(account),主機(host),程式(program),線程(thread),使用者(user)和全局(global)再進行細分)
show tables like '%statement%';
​
--等待事件記錄表,與語句事件類型的相關記錄表類似:
show tables like '%wait%';
​
--階段事件記錄表,記錄語句執行的階段事件的表
show tables like '%stage%';
​
--事務事件記錄表,記錄事務相關的事件的表
show tables like '%transaction%';
​
--監控檔案系統層調用的表
show tables like '%file%';
​
--監視記憶體使用的表
show tables like '%memory%';
​
--動态對performance_schema進行配置的配置表
show tables like '%setup%';
           

3、performance_schema的簡單配置與使用

資料庫剛剛初始化并啟動時,并非所有instruments(事件采集項,在采集項的配置表中每一項都有一個開關字段,或為YES,或為NO)和consumers(與采集項類似,也有一個對應的事件類型儲存表配置項,為YES就表示對應的表儲存性能資料,為NO就表示對應的表不儲存性能資料)都啟用了,是以預設不會收集所有的事件,可能你需要檢測的事件并沒有打開,需要進行設定,可以使用如下兩個語句打開對應的instruments和consumers(行計數可能會因MySQL版本而異)。
--打開等待事件的采集器配置項開關,需要修改setup_instruments配置表中對應的采集器配置項
UPDATE setup_instruments SET ENABLED = 'YES', TIMED = 'YES'where name like 'wait%';
​
--打開等待事件的儲存表配置開關,修改setup_consumers配置表中對應的配置項
UPDATE setup_consumers SET ENABLED = 'YES'where name like '%wait%';
​
--當配置完成之後可以檢視目前server正在做什麼,可以通過查詢events_waits_current表來得知,該表中每個線程隻包含一行資料,用于顯示每個線程的最新監視事件
select * from events_waits_current\G
*************************** 1. row ***************************
            THREAD_ID: 11
             EVENT_ID: 570
         END_EVENT_ID: 570
           EVENT_NAME: wait/synch/mutex/innodb/buf_dblwr_mutex
               SOURCE: 
          TIMER_START: 4508505105239280
            TIMER_END: 4508505105270160
           TIMER_WAIT: 30880
                SPINS: NULL
        OBJECT_SCHEMA: NULL
          OBJECT_NAME: NULL
           INDEX_NAME: NULL
          OBJECT_TYPE: NULL
OBJECT_INSTANCE_BEGIN: 67918392
     NESTING_EVENT_ID: NULL
   NESTING_EVENT_TYPE: NULL
            OPERATION: lock
      NUMBER_OF_BYTES: NULL
                FLAGS: NULL
/*該資訊表示線程id為11的線程正在等待buf_dblwr_mutex鎖,等待事件為30880
屬性說明:
    id:事件來自哪個線程,事件編号是多少
    event_name:表示檢測到的具體的内容
    source:表示這個檢測代碼在哪個源檔案中以及行号
    timer_start:表示該事件的開始時間
    timer_end:表示該事件的結束時間
    timer_wait:表示該事件總的花費時間
注意:_current表中每個線程隻保留一條記錄,一旦線程完成工作,該表中不會再記錄該線程的事件資訊
*/
​
/*
_history表中記錄每個線程應該執行完成的事件資訊,但每個線程的事件資訊隻會記錄10條,再多就會被覆寫,*_history_long表中記錄所有線程的事件資訊,但總記錄數量是10000,超過就會被覆寫掉
*/
select thread_id,event_id,event_name,timer_wait from events_waits_history order by thread_id limit 21;
​
/*
summary表提供所有事件的彙總資訊,該組中的表以不同的方式彙總事件資料(如:按使用者,按主機,按線程等等)。例如:要檢視哪些instruments占用最多的時間,可以通過對events_waits_summary_global_by_event_name表的COUNT_STAR或SUM_TIMER_WAIT列進行查詢(這兩列是對事件的記錄數執行COUNT(*)、事件記錄的TIMER_WAIT列執行SUM(TIMER_WAIT)統計而來)
*/
SELECT EVENT_NAME,COUNT_STAR FROM events_waits_summary_global_by_event_name  ORDER BY COUNT_STAR DESC LIMIT 10;
​
/*
instance表記錄了哪些類型的對象會被檢測。這些對象在被server使用時,在該表中将會産生一條事件記錄,例如,file_instances表列出了檔案I/O操作及其關聯檔案名
*/
select * from file_instances limit 20; 
           

4、常用配置項的參數說明

1、啟動選項

performance_schema_consumer_events_statements_current=TRUE
是否在mysql server啟動時就開啟events_statements_current表的記錄功能(該表記錄目前的語句事件資訊),啟動之後也可以在setup_consumers表中使用UPDATE語句進行動态更新setup_consumers配置表中的events_statements_current配置項,預設值為TRUE
​
performance_schema_consumer_events_statements_history=TRUE
與performance_schema_consumer_events_statements_current選項類似,但該選項是用于配置是否記錄語句事件短曆史資訊,預設為TRUE
​
performance_schema_consumer_events_stages_history_long=FALSE
與performance_schema_consumer_events_statements_current選項類似,但該選項是用于配置是否記錄語句事件長曆史資訊,預設為FALSE
​
除了statement(語句)事件之外,還支援:wait(等待)事件、state(階段)事件、transaction(事務)事件,他們與statement事件一樣都有三個啟動項分别進行配置,但這些等待事件預設未啟用,如果需要在MySQL Server啟動時一同啟動,則通常需要寫進my.cnf配置檔案中
performance_schema_consumer_global_instrumentation=TRUE
是否在MySQL Server啟動時就開啟全局表(如:mutex_instances、rwlock_instances、cond_instances、file_instances、users、hostsaccounts、socket_summary_by_event_name、file_summary_by_instance等大部分的全局對象計數統計和事件彙總統計資訊表 )的記錄功能,啟動之後也可以在setup_consumers表中使用UPDATE語句進行動态更新全局配置項
預設值為TRUE
​
performance_schema_consumer_statements_digest=TRUE
是否在MySQL Server啟動時就開啟events_statements_summary_by_digest 表的記錄功能,啟動之後也可以在setup_consumers表中使用UPDATE語句進行動态更新digest配置項
預設值為TRUE
​
performance_schema_consumer_thread_instrumentation=TRUE
是否在MySQL Server啟動時就開啟
​
events_xxx_summary_by_yyy_by_event_name表的記錄功能,啟動之後也可以在setup_consumers表中使用UPDATE語句進行動态更新線程配置項
預設值為TRUE
​
performance_schema_instrument[=name]
是否在MySQL Server啟動時就啟用某些采集器,由于instruments配置項多達數千個,是以該配置項支援key-value模式,還支援%号進行通配等,如下:
​
# [=name]可以指定為具體的Instruments名稱(但是這樣如果有多個需要指定的時候,就需要使用該選項多次),也可以使用通配符,可以指定instruments相同的字首+通配符,也可以使用%代表所有的instruments
​
## 指定開啟單個instruments
​
--performance-schema-instrument= 'instrument_name=value'
​
## 使用通配符指定開啟多個instruments
​
--performance-schema-instrument= 'wait/synch/cond/%=COUNTED'
​
## 開關所有的instruments
​
--performance-schema-instrument= '%=ON'
​
--performance-schema-instrument= '%=OFF'
​
注意,這些啟動選項要生效的前提是,需要設定performance_schema=ON。另外,這些啟動選項雖然無法使用show variables語句檢視,但我們可以通過setup_instruments和setup_consumers表查詢這些選項指定的值。
           

2、系統變量

show variables like '%performance_schema%';
--重要的屬性解釋
performance_schema=ON
/*
控制performance_schema功能的開關,要使用MySQL的performance_schema,需要在mysqld啟動時啟用,以啟用事件收集功能
該參數在5.7.x之前支援performance_schema的版本中預設關閉,5.7.x版本開始預設開啟
注意:如果mysqld在初始化performance_schema時發現無法配置設定任何相關的内部緩沖區,則performance_schema将自動禁用,并将performance_schema設定為OFF
*/
​
performance_schema_digests_size=10000
/*
控制events_statements_summary_by_digest表中的最大行數。如果産生的語句摘要資訊超過此最大值,便無法繼續存入該表,此時performance_schema會增加狀态變量
*/
performance_schema_events_statements_history_long_size=10000
/*
控制events_statements_history_long表中的最大行數,該參數控制所有會話在events_statements_history_long表中能夠存放的總事件記錄數,超過這個限制之後,最早的記錄将被覆寫
全局變量,隻讀變量,整型值,5.6.3版本引入 * 5.6.x版本中,5.6.5及其之前的版本預設為10000,5.6.6及其之後的版本預設值為-1,通常情況下,自動計算的值都是10000 * 5.7.x版本中,預設值為-1,通常情況下,自動計算的值都是10000
*/
performance_schema_events_statements_history_size=10
/*
控制events_statements_history表中單個線程(會話)的最大行數,該參數控制單個會話在events_statements_history表中能夠存放的事件記錄數,超過這個限制之後,單個會話最早的記錄将被覆寫
全局變量,隻讀變量,整型值,5.6.3版本引入 * 5.6.x版本中,5.6.5及其之前的版本預設為10,5.6.6及其之後的版本預設值為-1,通常情況下,自動計算的值都是10 * 5.7.x版本中,預設值為-1,通常情況下,自動計算的值都是10
除了statement(語句)事件之外,wait(等待)事件、state(階段)事件、transaction(事務)事件,他們與statement事件一樣都有三個參數分别進行存儲限制配置,有興趣的同學自行研究,這裡不再贅述
*/
performance_schema_max_digest_length=1024
/*
用于控制标準化形式的SQL語句文本在存入performance_schema時的限制長度,該變量與max_digest_length變量相關(max_digest_length變量含義請自行查閱相關資料)
全局變量,隻讀變量,預設值1024位元組,整型值,取值範圍0~1048576
*/
performance_schema_max_sql_text_length=1024
/*
控制存入events_statements_current,events_statements_history和events_statements_history_long語句事件表中的SQL_TEXT列的最大SQL長度位元組數。 超出系統變量performance_schema_max_sql_text_length的部分将被丢棄,不會記錄,一般情況下不需要調整該參數,除非被截斷的部分與其他SQL比起來有很大差異
全局變量,隻讀變量,整型值,預設值為1024位元組,取值範圍為0~1048576,5.7.6版本引入
降低系統變量performance_schema_max_sql_text_length值可以減少記憶體使用,但如果彙總的SQL中,被截斷部分有較大差異,會導緻沒有辦法再對這些有較大差異的SQL進行區分。 增加該系統變量值會增加記憶體使用,但對于彙總SQL來講可以更精準地區分不同的部分。
*/
           

5、重要配置表的相關說明

配置表之間存在互相關聯關系,按照配置影響的先後順序,可添加為

/*
performance_timers表中記錄了server中有哪些可用的事件計時器
字段解釋:
    timer_name:表示可用計時器名稱,CYCLE是基于CPU周期計數器的定時器
    timer_frequency:表示每秒鐘對應的計時器機關的數量,CYCLE計時器的換算值與CPU的頻率相關、
    timer_resolution:計時器精度值,表示在每個計時器被調用時額外增加的值
    timer_overhead:表示在使用定時器擷取事件時開銷的最小周期值
*/
select * from performance_timers;
​
/*
setup_timers表中記錄目前使用的事件計時器資訊
字段解釋:
    name:計時器類型,對應某個事件類别
    timer_name:計時器類型名稱
*/
select * from setup_timers;
​
/*
setup_consumers表中列出了consumers可配置清單項
字段解釋:
    NAME:consumers配置名稱
    ENABLED:consumers是否啟用,有效值為YES或NO,此列可以使用UPDATE語句修改。
*/
select * from setup_consumers;
​
/*
setup_instruments 表列出了instruments 清單配置項,即代表了哪些事件支援被收集:
字段解釋:
    NAME:instruments名稱,instruments名稱可能具有多個部分并形成層次結構
    ENABLED:instrumetns是否啟用,有效值為YES或NO,此列可以使用UPDATE語句修改。如果設定為NO,則這個instruments不會被執行,不會産生任何的事件資訊
    TIMED:instruments是否收集時間資訊,有效值為YES或NO,此列可以使用UPDATE語句修改,如果設定為NO,則這個instruments不會收集時間資訊
*/
SELECT * FROM setup_instruments;
​
/*
setup_actors表的初始内容是比對任何使用者和主機,是以對于所有前台線程,預設情況下啟用監視和曆史事件收集功能
字段解釋:
    HOST:與grant語句類似的主機名,一個具體的字元串名字,或使用“%”表示“任何主機”
    USER:一個具體的字元串名稱,或使用“%”表示“任何使用者”
    ROLE:目前未使用,MySQL 8.0中才啟用角色功能
    ENABLED:是否啟用與HOST,USER,ROLE比對的前台線程的監控功能,有效值為:YES或NO
    HISTORY:是否啟用與HOST, USER,ROLE比對的前台線程的曆史事件記錄功能,有效值為:YES或NO
*/
SELECT * FROM setup_actors;
​
/*
setup_objects表控制performance_schema是否監視特定對象。預設情況下,此表的最大行數為100行。
字段解釋:
    OBJECT_TYPE:instruments類型,有效值為:“EVENT”(事件排程器事件)、“FUNCTION”(存儲函數)、“PROCEDURE”(存儲過程)、“TABLE”(基表)、“TRIGGER”(觸發器),TABLE對象類型的配置會影響表I/O事件(wait/io/table/sql/handler instrument)和表鎖事件(wait/lock/table/sql/handler instrument)的收集
    OBJECT_SCHEMA:某個監視類型對象涵蓋的資料庫名稱,一個字元串名稱,或“%”(表示“任何資料庫”)
    OBJECT_NAME:某個監視類型對象涵蓋的表名,一個字元串名稱,或“%”(表示“任何資料庫内的對象”)
    ENABLED:是否開啟對某個類型對象的監視功能,有效值為:YES或NO。此列可以修改
    TIMED:是否開啟對某個類型對象的時間收集功能,有效值為:YES或NO,此列可以修改
*/
SELECT * FROM setup_objects;
​
/*
threads表對于每個server線程生成一行包含線程相關的資訊,
字段解釋:
    THREAD_ID:線程的唯一辨別符(ID)
    NAME:與server中的線程檢測代碼相關聯的名稱(注意,這裡不是instruments名稱)
    TYPE:線程類型,有效值為:FOREGROUND、BACKGROUND。分别表示前台線程和背景線程
    PROCESSLIST_ID:對應INFORMATION_SCHEMA.PROCESSLIST表中的ID列。
    PROCESSLIST_USER:與前台線程相關聯的使用者名,對于背景線程為NULL。
    PROCESSLIST_HOST:與前台線程關聯的用戶端的主機名,對于背景線程為NULL。
    PROCESSLIST_DB:線程的預設資料庫,如果沒有,則為NULL。
    PROCESSLIST_COMMAND:對于前台線程,該值代表着目前用戶端正在執行的command類型,如果是sleep則表示目前會話處于空閑狀态
    PROCESSLIST_TIME:目前線程已處于目前線程狀态的持續時間(秒)
    PROCESSLIST_STATE:表示線程正在做什麼事情。
    PROCESSLIST_INFO:線程正在執行的語句,如果沒有執行任何語句,則為NULL。
    PARENT_THREAD_ID:如果這個線程是一個子線程(由另一個線程生成),那麼該字段顯示其父線程ID
    ROLE:暫未使用
    INSTRUMENTED:線程執行的事件是否被檢測。有效值:YES、NO 
    HISTORY:是否記錄線程的曆史事件。有效值:YES、NO * 
    THREAD_OS_ID:由作業系統層定義的線程或任務辨別符(ID):
*/
select * from threads
           

注意:在performance_schema庫中還包含了很多其他的庫和表,能對資料庫的性能做完整的監控,大家需要參考官網詳細了解。

6、performance_schema實踐操作

基本了解了表的相關資訊之後,可以通過這些表進行實際的查詢操作來進行實際的分析。

--1、哪類的SQL執行最多?
SELECT DIGEST_TEXT,COUNT_STAR,FIRST_SEEN,LAST_SEEN FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--2、哪類SQL的平均響應時間最多?
SELECT DIGEST_TEXT,AVG_TIMER_WAIT FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--3、哪類SQL排序記錄數最多?
SELECT DIGEST_TEXT,SUM_SORT_ROWS FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--4、哪類SQL掃描記錄數最多?
SELECT DIGEST_TEXT,SUM_ROWS_EXAMINED FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--5、哪類SQL使用臨時表最多?
SELECT DIGEST_TEXT,SUM_CREATED_TMP_TABLES,SUM_CREATED_TMP_DISK_TABLES FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--6、哪類SQL傳回結果集最多?
SELECT DIGEST_TEXT,SUM_ROWS_SENT FROM events_statements_summary_by_digest ORDER BY COUNT_STAR DESC
--7、哪個表實體IO最多?
SELECT file_name,event_name,SUM_NUMBER_OF_BYTES_READ,SUM_NUMBER_OF_BYTES_WRITE FROM file_summary_by_instance ORDER BY SUM_NUMBER_OF_BYTES_READ + SUM_NUMBER_OF_BYTES_WRITE DESC
--8、哪個表邏輯IO最多?
SELECT object_name,COUNT_READ,COUNT_WRITE,COUNT_FETCH,SUM_TIMER_WAIT FROM table_io_waits_summary_by_table ORDER BY sum_timer_wait DESC
--9、哪個索引通路最多?
SELECT OBJECT_NAME,INDEX_NAME,COUNT_FETCH,COUNT_INSERT,COUNT_UPDATE,COUNT_DELETE FROM table_io_waits_summary_by_index_usage ORDER BY SUM_TIMER_WAIT DESC
--10、哪個索引從來沒有用過?
SELECT OBJECT_SCHEMA,OBJECT_NAME,INDEX_NAME FROM table_io_waits_summary_by_index_usage WHERE INDEX_NAME IS NOT NULL AND COUNT_STAR = 0 AND OBJECT_SCHEMA <> 'mysql' ORDER BY OBJECT_SCHEMA,OBJECT_NAME;
--11、哪個等待事件消耗時間最多?
SELECT EVENT_NAME,COUNT_STAR,SUM_TIMER_WAIT,AVG_TIMER_WAIT FROM events_waits_summary_global_by_event_name WHERE event_name != 'idle' ORDER BY SUM_TIMER_WAIT DESC
--12-1、剖析某條SQL的執行情況,包括statement資訊,stege資訊,wait資訊
SELECT EVENT_ID,sql_text FROM events_statements_history WHERE sql_text LIKE '%count(*)%';
--12-2、檢視每個階段的時間消耗
SELECT event_id,EVENT_NAME,SOURCE,TIMER_END - TIMER_START FROM events_stages_history_long WHERE NESTING_EVENT_ID = 1553;
--12-3、檢視每個階段的鎖等待情況
SELECT event_id,event_name,source,timer_wait,object_name,index_name,operation,nesting_event_id FROM events_waits_history_longWHERE nesting_event_id = 1553;
           

使用show processlist檢視連接配接的線程個數

來觀察是否有大量線程處于不正常的狀态或者其他不正常的特征
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

屬性說明

id: session id

user: 操作的使用者

host: 操作的主機

db: 操作的資料庫

command: 指令類型

    sleep:線程正在等待用戶端發送新的請求

    query:線程正在執行查詢或正在将結果發送給用戶端

    locked:在mysql的服務層,該線程正在等待表鎖

    analyzing and statistics:線程正在收集存儲引擎的統計資訊,并生成查詢的執行計劃

    Copying to tmp table:線程正在執行查詢,并且将其結果集都複制到一個臨時表中

    sorting result:線程正在對結果集進行排序

    sending data:線程可能在多個狀态之間傳送資料,或者在生成結果集或者向用戶端傳回資料

info: 詳細的SQL語句

time: 相應指令執行時間

state: 指令執行狀态

二. schema與資料類型優化

資料類型的優化

1. 更小的通常更好

應該盡量使用可以正确存儲資料的最小資料類型,更小的資料類型通常更快,因為它們占用更少的磁盤,記憶體和CPU緩存,并且處理時需要的CPU周期更少,但是要確定沒有低估需要存儲的值的範圍,如果無法确認那種資料類型,就選擇你認為不會超過範圍的最小類型

例如: 設計兩張表,設計不同的資料類型,檢視表的容量

2. 簡單就好

簡單資料類型的操作通常需要更少的CPU周期,例如,

1、整型比字元操作代價更低,因為字元集和校對規則是字元比較比整型比較更複雜,

2、使用mysql自建類型而不是字元串來存儲日期和時間

3、用整型存儲IP位址

案例:

建立兩張相同的表,改變日期的資料類型,檢視SQL語句執行的速度

3. 盡量避免null

如果查詢中包含可為NULL的列,對mysql來說很難優化,因為可為null的列使得索引、索引統計和值比較都更加複雜,坦白來說,通常情況下null的列改為not null帶來的性能提升比較小,所有沒有必要将所有的表的schema進行修改,但是應該盡量避免設計成可為null的列

4. 實際細則

1) 整形類型

可以使用的幾種整數類型:TINYINT,SMALLINT,MEDIUMINT,INT,BIGINT分别使用8,16,24,32,64位存儲空間。

盡量使用滿足需求的最小資料類型

2) 字元和字元串類型

1、char長度固定,即每條資料占用等長位元組空間;最大長度是255個字元,适合用在身份證号、手機号等定長字元串

2、varchar可變程度,可以設定最大長度;最大空間是65535個位元組,适合用在長度可變的屬性

3、text不設定長度,當不知道屬性的最大長度時,适合用text

按照查詢速度:char>varchar>text

varchar根據實際内容長度儲存資料

    1、使用最小的符合需求的長度。

    2、varchar(n) n小于等于255使用額外一個位元組儲存長度,n>255使用額外兩個位元組儲存長度。

    3、varchar(5)與varchar(255)儲存同樣的内容,硬碟存儲空間相同,但記憶體空間占用不同,是指定的大小 。

    4、varchar在mysql5.6之前變更長度,或者從255一下變更到255以上時時,都會導緻鎖表。

    應用場景

        1、存儲長度波動較大的資料,如:文章,有的會很短有的會很長

        2、字元串很少更新的場景,每次更新後都會重算并使用額外存儲空間儲存長度

        3、适合儲存多位元組字元,如:漢字,特殊字元等

char固定長度的字元串

    1、最大長度:255

    2、會自動删除末尾的空格

    3、檢索效率、寫效率 會比varchar高,以空間換時間

    應用場景

        1、存儲長度波動不大的資料,如:md5摘要

        2、存儲短字元串、經常更新的字元串

3) BLOB和TEXT類型

MySQL 把每個 BLOB 和 TEXT 值當作一個獨立的對象處理。

兩者都是為了存儲很大資料而設計的字元串類型,分别采用二進制和字元方式存儲。

4) datetime和timestamp

1、不要使用字元串類型來存儲日期時間資料

2、日期時間類型通常比字元串占用的存儲空間小

3、日期時間類型在進行查找過濾時可以利用日期來進行比對

4、日期時間類型還有着豐富的處理函數,可以友善的對時間類型進行日期計算

5、使用int存儲日期時間不如使用timestamp類型

datetime

    占用8個位元組

    與時區無關,資料庫底層時區配置,對datetime無效

    可儲存到毫秒

    可儲存時間範圍大

    不要使用字元串存儲日期類型,占用空間大,損失日期類型函數的便捷性

timestamp

    占用4個位元組

    時間範圍:1970-01-01到2038-01-19

    精确到秒

    采用整形存儲

    依賴資料庫設定的時區

    自動更新timestamp列的值

date

    占用的位元組數比使用字元串、datetime、int存儲要少,使用date類型隻需要3個位元組

    使用date類型還可以利用日期時間函數進行日期之間的計算

    date類型用于儲存1000-01-01到9999-12-31之間的日期

5) 使用枚舉代替字元串類型

有時可以使用枚舉類代替常用的字元串類型,mysql存儲枚舉類型會非常緊湊,會根據清單值的資料壓縮到一個或兩個位元組中,mysql在内部會将每個值在清單中的位置儲存為整數,并且在表的.frm檔案中儲存“數字-字元串”映射關系的查找表

 create table enum_test(e enum('fish','apple','dog') not null);

 insert into enum_test(e) values('fish'),('dog'),('apple');

 select e+0 from enum_test;

6) 特殊類型資料

人們經常使用varchar(15)來存儲ip位址,然而,它的本質是32位無符号整數不是字元串,可以使用INET_ATON()和INET_NTOA函數在這兩種表示方法之間轉換

案例:

select inet_aton('1.1.1.1')

select inet_ntoa(16843009)

合理使用範式和反範式

1. 範式

        優點

            範式化的更新通常比反範式要快

            當資料較好的範式化後,很少或者沒有重複的資料

            範式化的資料比較小,可以放在記憶體中,操作比較快

        缺點

            通常需要進行關聯

2. 反範式

        優點

            所有的資料都在同一張表中,可以避免關聯

            可以設計有效的索引;

        缺點

            表格内的備援較多,删除資料時候會造成表有些有用的資訊丢失

3. 注意

1) 在企業中很好能做到嚴格意義上的範式或者反範式,一般需要混合使用

        在一個網站執行個體中,這個網站,允許使用者發送消息,并且一些使用者是付費使用者。現在想檢視付費使用者最近的10條資訊。  在user表和message表中都存儲使用者類型(account_type)而不用完全的反範式化。這避免了完全反範式化的插入和删除問題,因為即使沒有消息的時候也絕不會丢失使用者的資訊。這樣也不會把user_message表搞得太大,有利于高效地擷取資料。

        另一個從父表備援一些資料到子表的理由是排序的需要。

        緩存衍生值也是有用的。如果需要顯示每個使用者發了多少消息(類似論壇的),可以每次執行一個昂貴的自查詢來計算并顯示它;也可以在user表中建一個num_messages列,每當使用者發新消息時更新這個值。

2) 案例

範式設計
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)
反範式設計
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

主鍵的選擇

代理主鍵

    與業務無關的,無意義的數字序列

自然主鍵

    事物屬性中的自然唯一辨別

推薦使用代理主鍵

    它們不與業務耦合,是以更容易維護

    一個大多數表,最好是全部表,通用的鍵政策能夠減少需要編寫的源碼數量,減少系統的總體擁有成本

字元集的選擇

1.純拉丁字元能表示的内容,沒必要選擇 latin1 之外的其他字元編碼,因為這會節省大量的存儲空間。

2.如果我們可以确定不需要存放多種語言,就沒必要非得使用UTF8或者其他UNICODE字元類型,這回造成大量的存儲空間浪費。

3.MySQL的資料類型可以精确到字段,是以當我們需要大型資料庫中存放多位元組資料的時候,可以通過對不同表不同字段使用不同的資料類型來較大程度減小資料存儲量,進而降低 IO 操作次數并提高緩存命中率。

存儲引擎的選擇

存儲引擎的對比

mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

适當的資料備援

1.被頻繁引用且隻能通過 Join 2張(或者更多)大表的方式才能得到的獨立小字段。

2.這樣的場景由于每次Join僅僅隻是為了取得某個小字段的值,Join到的記錄又大,會造成大量不必要的 IO,完全可以通過空間換取時間的方式來優化。不過,備援的同時需要確定資料的一緻性不會遭到破壞,確定更新的同時備援字段也被更新。

适當拆分

當我們的表中存在類似于 TEXT 或者是很大的 VARCHAR類型的大字段的時候,如果我們大部分通路這張表的時候都不需要這個字段,我們就該義無反顧的将其拆分到另外的獨立表中,以減少常用資料所占用的存儲空間。這樣做的一個明顯好處就是每個資料塊中可以存儲的資料條數可以大大增加,既減少實體 IO 次數,也能大大提高記憶體中的緩存命中率。

三. 執行計劃

在企業的應用場景中,為了知道優化SQL語句的執行,需要檢視SQL語句的具體執行過程,以加快SQL語句的執行效率。

可以使用explain+SQL語句來模拟優化器執行SQL查詢語句,進而知道mysql是如何處理sql語句的。

官網位址: https://dev.mysql.com/doc/refman/5.5/en/explain-output.html

1、執行計劃中包含的資訊

Column Meaning
id The

SELECT

identifier
select_type The

SELECT

type
table The table for the output row
partitions The matching partitions
type The join type
possible_keys The possible indexes to choose
key The index actually chosen
key_len The length of the chosen key
ref The columns compared to the index
rows Estimate of rows to be examined
filtered Percentage of rows filtered by table condition
extra Additional information

id

select查詢的序列号,包含一組數字,表示查詢中執行select子句或者操作表的順序

id号分為三種情況:

1、如果id相同,那麼執行順序從上到下

explain select * from emp e join dept d on e.deptno = d.deptno join salgrade sg on e.sal between sg.losal and sg.hisal;
           

2、如果id不同,如果是子查詢,id的序号會遞增,id值越大優先級越高,越先被執行

explain select * from emp e where e.deptno in (select d.deptno from dept d where d.dname = 'SALES');
           

3、id相同和不同的,同時存在:相同的可以認為是一組,從上往下順序執行,在所有組中,id值越大,優先級越高,越先執行

explain select * from emp e join dept d on e.deptno = d.deptno join salgrade sg on e.sal between sg.losal and sg.hisal where e.deptno in (select d.deptno from dept d where d.dname = 'SALES');
           

select_type

主要用來分辨查詢的類型,是普通查詢還是聯合查詢還是子查詢

select_type

Value
Meaning
SIMPLE Simple SELECT (not using UNION or subqueries)
PRIMARY Outermost SELECT
UNION Second or later SELECT statement in a UNION
DEPENDENT UNION Second or later SELECT statement in a UNION, dependent on outer query
UNION RESULT Result of a UNION.
SUBQUERY First SELECT in subquery
DEPENDENT SUBQUERY First SELECT in subquery, dependent on outer query
DERIVED Derived table
UNCACHEABLE SUBQUERY A subquery for which the result cannot be cached and must be re-evaluated for each row of the outer query
UNCACHEABLE UNION The second or later select in a UNION that belongs to an uncacheable subquery (see UNCACHEABLE SUBQUERY)
--sample:簡單的查詢,不包含子查詢和union
explain select * from emp;
​
--primary:查詢中若包含任何複雜的子查詢,最外層查詢則被标記為Primary
explain select staname,ename supname from (select ename staname,mgr from emp) t join emp on t.mgr=emp.empno ;
​
--union:若第二個select出現在union之後,則被标記為union
explain select * from emp where deptno = 10 union select * from emp where sal >2000;
​
--dependent union:跟union類似,此處的depentent表示union或union all聯合而成的結果會受外部表影響
explain select * from emp e where e.empno  in ( select empno from emp where deptno = 10 union select empno from emp where sal >2000)
​
--union result:從union表擷取結果的select
explain select * from emp where deptno = 10 union select * from emp where sal >2000;
​
--subquery:在select或者where清單中包含子查詢
explain select * from emp where sal > (select avg(sal) from emp) ;
​
--dependent subquery:subquery的子查詢要受到外部表查詢的影響
explain select * from emp e where e.deptno in (select distinct deptno from dept);
​
--DERIVED: from子句中出現的子查詢,也叫做派生類,
explain select staname,ename supname from (select ename staname,mgr from emp) t join emp on t.mgr=emp.empno ;
​
--UNCACHEABLE SUBQUERY:表示使用子查詢的結果不能被緩存
 explain select * from emp where empno = (select empno from emp where [email protected]@sort_buffer_size);
 
--uncacheable union:表示union的查詢結果不能被緩存:sql語句未驗證
           

table

對應行正在通路哪一個表,表名或者别名,可能是臨時表或者union合并結果集 1、如果是具體的表名,則表明從實際的實體表中擷取資料,當然也可以是表的别名

2、表名是derivedN的形式,表示使用了id為N的查詢産生的衍生表

3、當有union result的時候,表名是union n1,n2等的形式,n1,n2表示參與union的id

type

type顯示的是通路類型,通路類型表示我是以何種方式去通路我們的資料,最容易想的是全表掃描,直接暴力的周遊一張表去尋找需要的資料,效率非常低下,通路的類型有很多,效率從最好到最壞依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般情況下,得保證查詢至少達到range級别,最好能達到ref

--all:全表掃描,一般情況下出現這樣的sql語句而且資料量比較大的話那麼就需要進行優化。
explain select * from emp;
​
--index:全索引掃描這個比all的效率要好,主要有兩種情況,一種是目前的查詢時覆寫索引,即我們需要的資料在索引中就可以索取,或者是使用了索引進行排序,這樣就避免資料的重排序
explain  select empno from emp;
​
--range:表示利用索引查詢的時候限制了範圍,在指定範圍内進行查詢,這樣避免了index的全索引掃描,适用的操作符: =, <>, >, >=, <, <=, IS NULL, BETWEEN, LIKE, or IN() 
explain select * from emp where empno between 7000 and 7500;
​
--index_subquery:利用索引來關聯子查詢,不再掃描全表
explain select * from emp where emp.job in (select job from t_job);
​
--unique_subquery:該連接配接類型類似與index_subquery,使用的是唯一索引
 explain select * from emp e where e.deptno in (select distinct deptno from dept);
 
--index_merge:在查詢過程中需要多個索引組合使用,沒有模拟出來
​
--ref_or_null:對于某個字段即需要關聯條件,也需要null值的情況下,查詢優化器會選擇這種通路方式
explain select * from emp e where  e.mgr is null or e.mgr=7369;
​
--ref:使用了非唯一性索引進行資料的查找
 create index idx_3 on emp(deptno);
 explain select * from emp e,dept d where e.deptno =d.deptno;
​
--eq_ref :使用唯一性索引進行資料查找
explain select * from emp,emp2 where emp.empno = emp2.empno;
​
--const:這個表至多有一個比對行,
explain select * from emp where empno = 7369;
 
--system:表隻有一行記錄(等于系統表),這是const類型的特例,平時不會出現
           

possible_keys

顯示可能應用在這張表中的索引,一個或多個,查詢涉及到的字段上若存在索引,則該索引将被列出,但不一定被查詢實際使用

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;
           

key

實際使用的索引,如果為null,則沒有使用索引,查詢中若使用了覆寫索引,則該索引和查詢的select字段重疊。

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;
           

key_len

表示索引中使用的位元組數,可以通過key_len計算查詢中使用的索引長度,在不損失精度的情況下長度越短越好。

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;
           

ref

顯示索引的哪一列被使用了,如果可能的話,是一個常數

explain select * from emp,dept where emp.deptno = dept.deptno and emp.deptno = 10;
           

rows

根據表的統計資訊及索引使用情況,大緻估算出找出所需記錄需要讀取的行數,此參數很重要,直接反應的sql找了多少資料,在完成目的的情況下越少越好

explain select * from emp;
           

extra

包含額外的資訊。

--using filesort:說明mysql無法利用索引進行排序,隻能利用排序算法進行排序,會消耗額外的位置
explain select * from emp order by sal;
​
--using temporary:建立臨時表來儲存中間結果,查詢完成之後把臨時表删除
explain select ename,count(*) from emp where deptno = 10 group by ename;
​
--using index:這個表示目前的查詢時覆寫索引的,直接從索引中讀取資料,而不用通路資料表。如果同時出現using where 表名索引被用來執行索引鍵值的查找,如果沒有,表面索引被用來讀取資料,而不是真的查找
explain select deptno,count(*) from emp group by deptno limit 10;
​
--using where:使用where進行條件過濾
explain select * from t_user where id = 1;
​
--using join buffer:使用連接配接緩存,情況沒有模拟出來
​
--impossible where:where語句的結果總是false
explain select * from emp where empno = 7469;
           

四. 通過索引進行優化

索引基本知識

索引的優點

1、大大減少了伺服器需要掃描的資料量

2、幫助伺服器避免排序和臨時表

3、将随機io變成順序io

索引的用處

1、快速查找比對WHERE子句的行

2、從consideration中消除行,如果可以在多個索引之間進行選擇,mysql通常會使用找到最少行的索引

3、如果表具有多列索引,則優化器可以使用索引的任何最左字首來查找行

4、當有表連接配接的時候,從其他表檢索行資料

5、查找特定索引列的min或max值

6、如果排序或分組時在可用索引的最左字首上完成的,則對表進行排序和分組

7、在某些情況下,可以優化查詢以檢索值而無需查詢資料行

索引的分類

主鍵索引

唯一索引

普通索引

全文索引

組合索引

面試技術名詞

回表

覆寫索引

最左比對

索引下推

索引采用的資料結構

哈希表

B+樹

索引比對方式

1. 全值比對

    全值比對指的是和索引中的所有列進行比對

        explain select * from staffs where name = 'July' and age = '23' and pos = 'dev';

2. 比對最左字首

    隻比對前面的幾列

        explain select * from staffs where name = 'July' and age = '23';

        explain select * from staffs where name = 'July';

3. 比對列字首

    可以比對某一列的值的開頭部分

        explain select * from staffs where name like 'J%';

        explain select * from staffs where name like '%y';

4. 比對範圍值

    可以查找某一個範圍的資料

        explain select * from staffs where name > 'Mary';

5. 精确比對某一列并範圍比對另外一列

    可以查詢第一列的全部和第二列的部分

        explain select * from staffs where name = 'July' and age > 25;

6. 隻通路索引的查詢

    查詢的時候隻需要通路索引,不需要通路資料行,本質上就是覆寫索引

        explain select name,age,pos from staffs where name = 'July' and age = 25 and pos = 'dev';

哈希索引

基于哈希表的實作,隻有精确比對索引所有列的查詢才有效

在mysql中,隻有memory的存儲引擎顯式支援哈希索引

哈希索引自身隻需存儲對應的hash值,是以索引的結構十分緊湊,這讓哈希索引查找的速度非常快

哈希索引的限制

1、哈希索引隻包含哈希值和行指針,而不存儲字段值,索引不能使用索引中的值來避免讀取行

2、哈希索引資料并不是按照索引值順序存儲的,是以無法進行排序

3、哈希索引不支援部分列比對查找,哈希索引是使用索引列的全部内容來計算哈希值

4、哈希索引支援等值比較查詢,也不支援任何範圍查詢

5、通路哈希索引的資料非常快,除非有很多哈希沖突,當出現哈希沖突的時候,存儲引擎必須周遊連結清單中的所有行指針,逐行進行比較,直到找到所有符合條件的行

6、哈希沖突比較多的話,維護的代價也會很高

案例

當需要存儲大量的URL,并且根據URL進行搜尋查找,如果使用B+樹,存儲的内容就會很大

select id from url where url=""

也可以利用将url使用CRC32做哈希,可以使用以下查詢方式:

select id fom url where url="" and url_crc=CRC32("")

此查詢性能較高原因是使用體積很小的索引來完成查找

組合索引

當包含多個列作為索引,需要注意的是正确的順序依賴于該索引的查詢,同時需要考慮如何更好的滿足排序和分組的需要

案例,建立組合索引a,b,c

mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)
不同SQL語句使用索引情況

聚簇索引與非聚簇索引

聚簇索引

不是單獨的索引類型,而是一種資料存儲方式,指的是資料行跟相鄰的鍵值緊湊的存儲在一起

    優點

        1、可以把相關資料儲存在一起

        2、資料通路更快,因為索引和資料儲存在同一個樹中

        3、使用覆寫索引掃描的查詢可以直接使用頁節點中的主鍵值

    缺點

        1、聚簇資料最大限度地提高了IO密集型應用的性能,如果資料全部在記憶體,那麼聚簇索引就沒有什麼優勢

        2、插入速度嚴重依賴于插入順序,按照主鍵的順序插入是最快的方式

        3、更新聚簇索引列的代價很高,因為會強制将每個被更新的行移動到新的位置

        4、基于聚簇索引的表在插入新行,或者主鍵被更新導緻需要移動行的時候,可能面臨頁分裂的問題

        5、聚簇索引可能導緻全表掃描變慢,尤其是行比較稀疏,或者由于頁分裂導緻資料存儲不連續的時候

非聚簇索引

資料檔案跟索引檔案分開存放

覆寫索引

基本介紹

1、如果一個索引包含所有需要查詢的字段的值,我們稱之為覆寫索引

2、不是所有類型的索引都可以稱為覆寫索引,覆寫索引必須要存儲索引列的值

3、不同的存儲實作覆寫索引的方式不同,不是所有的引擎都支援覆寫索引,memory不支援覆寫索引

優勢

1、索引條目通常遠小于資料行大小,如果隻需要讀取索引,那麼mysql就會極大的較少資料通路量

2、因為索引是按照列值順序存儲的,是以對于IO密集型的範圍查詢會比随機從磁盤讀取每一行資料的IO要少的多

3、一些存儲引擎如MYISAM在記憶體中隻緩存索引,資料則依賴于作業系統來緩存,是以要通路資料需要一次系統調用,這可能會導緻嚴重的性能問題

4、由于INNODB的聚簇索引,覆寫索引對INNODB表特别有用

案例示範

1、當發起一個被索引覆寫的查詢時,在explain的extra列可以看到using index的資訊,此時就使用了覆寫索引
mysql> explain select store_id,film_id from inventory\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: inventory
   partitions: NULL
         type: index
possible_keys: NULL
          key: idx_store_id_film_id
      key_len: 3
          ref: NULL
         rows: 4581
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.01 sec)
           

2、在大多數存儲引擎中,覆寫索引隻能覆寫那些隻通路索引中部分列的查詢。不過,可以進一步的進行優化,可以使用innodb的二級索引來覆寫查詢。

例如:actor使用innodb存儲引擎,并在last_name字段又二級索引,雖然該索引的列不包括主鍵actor_id,但也能夠用于對actor_id做覆寫查詢

mysql> explain select actor_id,last_name from actor where last_name='HOPPER'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: actor
   partitions: NULL
         type: ref
possible_keys: idx_actor_last_name
          key: idx_actor_last_name
      key_len: 137
          ref: const
         rows: 2
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.00 sec)
           

優化小細節

當使用索引列進行查詢的時候盡量不要使用表達式,把計算放到業務層而不是資料庫層

            select actor_id from actor where actor_id=4;

            select actor_id from actor where actor_id+1=5;

盡量使用主鍵查詢,而不是其他索引,是以主鍵查詢不會觸發回表查詢

使用字首索引

有時候需要索引很長的字元串,這會讓索引變的大且慢,通常情況下可以使用某個列開始的部分字元串,這樣大大的節約索引空間,進而提高索引效率,但這會降低索引的選擇性,索引的選擇性是指不重複的索引值和資料表記錄總數的比值,範圍從1/#T到1之間。索引的選擇性越高則查詢效率越高,因為選擇性更高的索引可以讓mysql在查找的時候過濾掉更多的行。

一般情況下某個列字首的選擇性也是足夠高的,足以滿足查詢的性能,但是對應BLOB,TEXT,VARCHAR類型的列,必須要使用字首索引,因為mysql不允許索引這些列的完整長度,使用該方法的訣竅在于要選擇足夠長的字首以保證較高的選擇性,通過又不能太長。

案例示範:

--建立資料表
create table citydemo(city varchar(50) not null);
insert into citydemo(city) select city from city;
​
--重複執行5次下面的sql語句
insert into citydemo(city) select city from citydemo;
​
--更新城市表的名稱
update citydemo set city=(select city from city order by rand() limit 1);
​
--查找最常見的城市清單,發現每個值都出現45-65次,
select count(*) as cnt,city from citydemo group by city order by cnt desc limit 10;
​
--查找最頻繁出現的城市字首,先從3個字首字母開始,發現比原來出現的次數更多,可以分别截取多個字元檢視城市出現的次數
select count(*) as cnt,left(city,3) as pref from citydemo group by pref order by cnt desc limit 10;
select count(*) as cnt,left(city,7) as pref from citydemo group by pref order by cnt desc limit 10;
--此時字首的選擇性接近于完整列的選擇性
​
--還可以通過另外一種方式來計算完整列的選擇性,可以看到當字首長度到達7之後,再增加字首長度,選擇性提升的幅度已經很小了
select count(distinct left(city,3))/count(*) as sel3,
count(distinct left(city,4))/count(*) as sel4,
count(distinct left(city,5))/count(*) as sel5,
count(distinct left(city,6))/count(*) as sel6,
count(distinct left(city,7))/count(*) as sel7,
count(distinct left(city,8))/count(*) as sel8 
from citydemo;
​
--計算完成之後可以建立字首索引
alter table citydemo add key(city(7));
​
--注意:字首索引是一種能使索引更小更快的有效方法,但是也包含缺點:mysql無法使用字首索引做order by 和 group by。 
           

使用索引掃描來排序

mysql有兩種方式可以生成有序的結果:通過排序操作或者按索引順序掃描,如果explain出來的type列的值為index,則說明mysql使用了索引掃描來做排序

掃描索引本身是很快的,因為隻需要從一條索引記錄移動到緊接着的下一條記錄。但如果索引不能覆寫查詢所需的全部列,那麼就不得不每掃描一條索引記錄就得回表查詢一次對應的行,這基本都是随機IO,是以按索引順序讀取資料的速度通常要比順序地全表掃描慢

mysql可以使用同一個索引即滿足排序,又用于查找行,如果可能的話,設計索引時應該盡可能地同時滿足這兩種任務。

隻有當索引的列順序和order by子句的順序完全一緻,并且所有列的排序方式都一樣時,mysql才能夠使用索引來對結果進行排序,如果查詢需要關聯多張表,則隻有當orderby子句引用的字段全部為第一張表時,才能使用索引做排序。order by子句和查找型查詢的限制是一樣的,需要滿足索引的最左字首的要求,否則,mysql都需要執行順序操作,而無法利用索引排序

--sakila資料庫中rental表在rental_date,inventory_id,customer_id上有rental_date的索引
--使用rental_date索引為下面的查詢做排序
explain select rental_id,staff_id from rental where rental_date='2005-05-25' order by inventory_id,customer_id\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
   partitions: NULL
         type: ref
possible_keys: rental_date
          key: rental_date
      key_len: 5
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using index condition
1 row in set, 1 warning (0.00 sec)
--order by子句不滿足索引的最左字首的要求,也可以用于查詢排序,這是因為是以你的第一列被指定為一個常數
​
--該查詢為索引的第一列提供了常量條件,而使用第二列進行排序,将兩個列組合在一起,就形成了索引的最左字首
explain select rental_id,staff_id from rental where rental_date='2005-05-25' order by inventory_id desc\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
   partitions: NULL
         type: ref
possible_keys: rental_date
          key: rental_date
      key_len: 5
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)
​
--下面的查詢不會利用索引
explain select rental_id,staff_id from rental where rental_date>'2005-05-25' order by rental_date,inventory_id\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
   partitions: NULL
         type: ALL
possible_keys: rental_date
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 16005
     filtered: 50.00
        Extra: Using where; Using filesort
​
--該查詢使用了兩中不同的排序方向,但是索引列都是正序排序的
explain select rental_id,staff_id from rental where rental_date>'2005-05-25' order by inventory_id desc,customer_id asc\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
   partitions: NULL
         type: ALL
possible_keys: rental_date
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 16005
     filtered: 50.00
        Extra: Using where; Using filesort
1 row in set, 1 warning (0.00 sec)
​
--該查詢中引用了一個不再索引中的列
explain select rental_id,staff_id from rental where rental_date>'2005-05-25' order by inventory_id,staff_id\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: rental
   partitions: NULL
         type: ALL
possible_keys: rental_date
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 16005
     filtered: 50.00
        Extra: Using where; Using filesort
1 row in set, 1 warning (0.00 sec)
           

union all,in,or都能夠使用索引,但是推薦使用in

            explain select * from actor where actor_id = 1 union all select * from actor where actor_id = 2;

            explain select * from actor where actor_id in (1,2);

             explain select * from actor where actor_id = 1 or actor_id =2;

範圍列可以用到索引

            範圍條件是:<、>

            範圍列可以用到索引,但是範圍列後面的列無法用到索引,索引最多用于一個範圍列

強制類型轉換會全表掃描

create table user(id int,name varchar(10),phone varchar(11));

alter table user add index idx_1(phone);            

explain select * from user where phone=13800001234;

    不會觸發索引

explain select * from user where phone='13800001234';

    觸發索引

更新十分頻繁,資料區分度不高的字段上不宜建立索引

更新會變更B+樹,更新頻繁的字段建議索引會大大降低資料庫性能

類似于性别這類區分不大的屬性,建立索引是沒有意義的,不能有效的過濾資料,

一般區分度在80%以上的時候就可以建立索引,區分度可以使用 count(distinct(列名))/count(*) 來計算

建立索引的列,不允許為null,可能會得到不符合預期的結果

當需要進行表連接配接的時候,最好不要超過三張表,因為需要join的字段,資料類型必須一緻

能使用limit的時候盡量使用limit

單表索引建議控制在5個以内

單索引字段數不允許超過5個(組合索引)

建立索引的時候應該避免以下錯誤概念

            索引越多越好

            過早優化,在不了解系統的情況下進行優化

join的多種方式

Simple Nested_Loop Join(簡單嵌套循環)

mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

R為驅動表,S為比對表,可以看到從R中分别取出每一個記錄去比對S表的列,然後再合并資料,對S表進行R表的行數次通路,對資料庫的開銷比較大

Index Nested-Loop Join(索引嵌套循環)

mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

這個要求非驅動表(S)上有索引,可以通過索引來減少比較,加速查詢.

在查詢時,驅動表(R)會根據關聯字段的索引進行查詢,當在索引上找到符合的值,再回表進行查詢,也就是隻有當比對到索引以後才會進行回表查詢.

如果非驅動表(S)的關聯鍵是主鍵的話,性能會非常高;如果不是主鍵,要進行多次回表查詢,先關聯索引,然後根據二級索引的主鍵ID進行回表操作,性能上要比索引是主鍵要慢

Block Nested-Loop Join(帶緩沖區)

mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

如果有索引,會選擇第二種方式進行join,但如果join列沒有索引,就會采用Block Nested-Loop Join.

可以看到中間有個join buffer緩沖區,是将驅動表的所有join相關的列都先緩存到join-buffer中,然後批量與比對表進行比對,将第一種多次比較合并為一次,降低了非驅動表(S)的通路頻率.

預設情況下join_buffer_size=256K,在查找的時候mysql會将所有需要的列緩存到join buffer當中,包括select的列,而不是僅僅隻緩存關聯列,在一個有N個join關聯的SQL當中會在執行的時候配置設定N-1個join buffer

索引監控

show status like 'Handler_read%';

mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

參數解釋

Handler_read_first:讀取索引第一個條目的次數

Handler_read_key:通過index擷取資料的次數

Handler_read_last:讀取索引最後一個條目的次數

Handler_read_next:通過索引讀取下一條資料的次數

Handler_read_prev:通過索引讀取上一條資料的次數

Handler_read_rnd:從固定位置讀取資料的次數

Handler_read_rnd_next:從資料節點讀取下一條資料的次數

索引優化分析案例

預先準備好資料

SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `itdragon_order_list`;
CREATE TABLE `itdragon_order_list` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id,預設自增長',
  `transaction_id` varchar(150) DEFAULT NULL COMMENT '交易号',
  `gross` double DEFAULT NULL COMMENT '毛收入(RMB)',
  `net` double DEFAULT NULL COMMENT '淨收入(RMB)',
  `stock_id` int(11) DEFAULT NULL COMMENT '發貨倉庫',
  `order_status` int(11) DEFAULT NULL COMMENT '訂單狀态',
  `descript` varchar(255) DEFAULT NULL COMMENT '客服備注',
  `finance_descript` varchar(255) DEFAULT NULL COMMENT '财務備注',
  `create_type` varchar(100) DEFAULT NULL COMMENT '建立類型',
  `order_level` int(11) DEFAULT NULL COMMENT '訂單級别',
  `input_user` varchar(20) DEFAULT NULL COMMENT '錄入人',
  `input_date` varchar(20) DEFAULT NULL COMMENT '錄入時間',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10003 DEFAULT CHARSET=utf8;
​
INSERT INTO itdragon_order_list VALUES ('10000', '81X97310V32236260E', '6.6', '6.13', '1', '10', 'ok', 'ok', 'auto', '1', 'itdragon', '2017-08-28 17:01:49');
INSERT INTO itdragon_order_list VALUES ('10001', '61525478BB371361Q', '18.88', '18.79', '1', '10', 'ok', 'ok', 'auto', '1', 'itdragon', '2017-08-18 17:01:50');
INSERT INTO itdragon_order_list VALUES ('10002', '5RT64180WE555861V', '20.18', '20.17', '1', '10', 'ok', 'ok', 'auto', '1', 'itdragon', '2017-09-08 17:01:49');
​
           

逐漸開始進行優化:

第一個案例:

select * from itdragon_order_list where transaction_id = "81X97310V32236260E";
--通過檢視執行計劃發現type=all,需要進行全表掃描
explain select * from itdragon_order_list where transaction_id = "81X97310V32236260E";
​
--優化一、為transaction_id建立唯一索引
 create unique index idx_order_transaID on itdragon_order_list (transaction_id);
--當建立索引之後,唯一索引對應的type是const,通過索引一次就可以找到結果,普通索引對應的type是ref,表示非唯一性索引賽秒,找到值還要進行掃描,直到将索引檔案掃描完為止,顯而易見,const的性能要高于ref
 explain select * from itdragon_order_list where transaction_id = "81X97310V32236260E";
 
 --優化二、使用覆寫索引,查詢的結果變成 transaction_id,當extra出現using index,表示使用了覆寫索引
 explain select transaction_id from itdragon_order_list where transaction_id = "81X97310V32236260E";
           

第二個案例

--建立複合索引
create index idx_order_levelDate on itdragon_order_list (order_level,input_date);
​
--建立索引之後發現跟沒有建立索引一樣,都是全表掃描,都是檔案排序
explain select * from itdragon_order_list order by order_level,input_date;
​
--可以使用force index強制指定索引
explain select * from itdragon_order_list force index(idx_order_levelDate) order by order_level,input_date;
--其實給訂單排序意義不大,給訂單級别添加索引意義也不大,是以可以先确定order_level的值,然後再給input_date排序
explain select * from itdragon_order_list where order_level=3 order by input_date;
           

五. 查詢優化

在編寫快速的查詢之前,需要清楚一點,真正重要的是響應時間,而且要知道在整個SQL語句的執行過程中每個步驟都花費了多長時間,要知道哪些步驟是拖垮執行效率的關鍵步驟,想要做到這點,必須要知道查詢的生命周期,然後進行優化,不同的應用場景有不同的優化方式,不要一概而論,具體情況具體分析,

查詢慢的原因

網絡

CPU

IO

上下文切換

系統調用

生成統計資訊

鎖等待時間

優化資料通路

減少通路量

查詢性能低下的主要原因是通路的資料太多,某些查詢不可避免的需要篩選大量的資料,我們可以通過減少通路資料量的方式進行優化

    确認應用程式是否在檢索大量超過需要的資料

    确認mysql伺服器層是否在分析大量超過需要的資料行

是否向資料庫請求了不需要的資料

1. 查詢不需要的記錄

我們常常會誤以為mysql會隻傳回需要的資料,實際上mysql卻是先傳回全部結果再進行計算,在日常的開發習慣中,經常是先用select語句查詢大量的結果,然後擷取前面的N行後關閉結果集。

優化方式是在查詢後面添加limit

2. 多表關聯時傳回全部列

select * from actor inner join film_actor using(actor_id) inner join film using(film_id) where film.title='Academy Dinosaur';

select actor.* from actor...;

3. 總是取出全部列

在公司的企業需求中,禁止使用select *,雖然這種方式能夠簡化開發,但是會影響查詢的性能,是以盡量不要使用

4. 重複查詢相同的資料

如果需要不斷的重複執行相同的查詢,且每次傳回完全相同的資料,是以,基于這樣的應用場景,我們可以将這部分資料緩存起來,這樣的話能夠提高查詢效率

執行過程的優化

查詢緩存

在解析一個查詢語句之前,如果查詢緩存是打開的,那麼mysql會優先檢查這個查詢是否命中查詢緩存中的資料,如果查詢恰好命中了查詢緩存,那麼會在傳回結果之前會檢查使用者權限,如果權限沒有問題,那麼mysql會跳過所有的階段,就直接從緩存中拿到結果并傳回給用戶端

查詢優化處理

mysql查詢完緩存之後會經過以下幾個步驟:解析SQL、預處理、優化SQL執行計劃,這幾個步驟出現任何的錯誤,都可能會終止查詢

1. 文法解析器和預處理

mysql通過關鍵字将SQL語句進行解析,并生成一顆解析樹,mysql解析器将使用mysql文法規則驗證和解析查詢,例如驗證使用使用了錯誤的關鍵字或者順序是否正确等等,預處理器會進一步檢查解析樹是否合法,例如表名和列名是否存在,是否有歧義,還會驗證權限等等

2. 查詢優化器

當文法樹沒有問題之後,相應的要由優化器将其轉成執行計劃,一條查詢語句可以使用非常多的執行方式,最後都可以得到對應的結果,但是不同的執行方式帶來的效率是不同的,優化器的最主要目的就是要選擇最有效的執行計劃

mysql使用的是基于成本的優化器,在優化的時候會嘗試預測一個查詢使用某種查詢計劃時候的成本,并選擇其中成本最小的一個

1) select count(*) from film_actor;

show status like 'last_query_cost';

可以看到這條查詢語句大概需要做1104個資料頁才能找到對應的資料,這是經過一系列的統計資訊計算來的

每個表或者索引的頁面個數

索引的基數

索引和資料行的長度

索引的分布情況

2) 在很多情況下mysql會選擇錯誤的執行計劃,原因如下:

① 統計資訊不準确

InnoDB因為其mvcc的架構,并不能維護一個資料表的行數的精确統計資訊

② 執行計劃的成本估算不等同于實際執行的成本

有時候某個執行計劃雖然需要讀取更多的頁面,但是他的成本卻更小,因為如果這些頁面都是順序讀或者這些頁面都已經在記憶體中的話,那麼它的通路成本将很小,mysql層面并不知道哪些頁面在記憶體中,哪些在磁盤,是以查詢之際執行過程中到底需要多少次IO是無法得知的

③ mysql的最優可能跟你想的不一樣

mysql的優化是基于成本模型的優化,但是有可能不是最快的優化

④ mysql不考慮其他并發執行的查詢

⑤ mysql不會考慮不受其控制的操作成本

執行存儲過程或者使用者自定義函數的成本

3) 優化器的優化政策

靜态優化

    直接對解析樹進行分析,并完成優化

動态優化

    動态優化與查詢的上下文有關,也可能跟取值、索引對應的行數有關

mysql對查詢的靜态優化隻需要一次,但對動态優化在每次執行時都需要重新評估

4) 優化器的優化類型

① 重新定義關聯表的順序

資料表的關聯并不總是按照在查詢中指定的順序進行,決定關聯順序時優化器很重要的功能

② 将外連接配接轉化成内連接配接,内連接配接的效率要高于外連接配接

③ 使用等價變換規則,mysql可以使用一些等價變化來簡化并規劃表達式

④ 優化count(),min(),max()

索引和列是否可以為空通常可以幫助mysql優化這類表達式:例如,要找到某一列的最小值,隻需要查詢索引的最左端的記錄即可,不需要全文掃描比較

⑤ 預估并轉化為常數表達式,當mysql檢測到一個表達式可以轉化為常數的時候,就會一直把該表達式作為常數進行處理

explain select film.film_id,film_actor.actor_id from film inner join film_actor using(film_id) where film.film_id = 1

⑥ 索引覆寫掃描,當索引中的列包含所有查詢中需要使用的列的時候,可以使用覆寫索引

⑦ 子查詢優化

mysql在某些情況下可以将子查詢轉換一種效率更高的形式,進而減少多個查詢多次對資料進行通路,例如将經常查詢的資料放入到緩存中

⑧ 等值傳播

如果兩個列的值通過等式關聯,那麼mysql能夠把其中一個列的where條件傳遞到另一個上:

explain select film.film_id from film inner join film_actor using(film_id

) where film.film_id > 500;

這裡使用film_id字段進行等值關聯,film_id這個列不僅适用于film表而且适用于film_actor表

explain select film.film_id from film inner join film_actor using(film_id

) where film.film_id > 500 and film_actor.film_id > 500;    3

5) 關聯查詢

join的實作方式原理

    Simple Nested-Loop Join

mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)
    Index Nested-Loop Join
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)
    Block Nested-Loop Join
mysql調優一. 性能監控二. schema與資料類型優化三. 執行計劃四. 通過索引進行優化五. 查詢優化六. 分區表七. 伺服器參數設定八. mysql叢集(後續更新)

       (1)Join Buffer會緩存所有參與查詢的列而不是隻有Join的列。

       (2)可以通過調整join_buffer_size緩存大小

       (3)join_buffer_size的預設值是256K,join_buffer_size的最大值在MySQL 5.1.22版本前是4G-1,而之後的版本才能在64位作業系統下申請大于4G的Join Buffer空間。

       (4)使用Block Nested-Loop Join算法需要開啟優化器管理配置的optimizer_switch的設定block_nested_loop為on,預設為開啟。

                show variables like '%optimizer_switch%'

案例示範

檢視不同的順序執行方式對查詢性能的影響:

explain select film.film_id,film.title,film.release_year,actor.actor_id,actor.first_name,actor.last_name from film inner join f

ilm_actor using(film_id) inner join actor using(actor_id);

檢視執行的成本:

show status like 'last_query_cost'; 

按照自己預想的規定順序執行:

explain select straight_join film.film_id,film.title,film.release_year,actor.actor_id,actor.first_name,actor.last_name from fil

m inner join film_actor using(film_id) inner join actor using(actor_id);

檢視執行的成本:

show status like 'last_query_cost'; 

6) 排序優化

無論如何排序都是一個成本很高的操作,是以從性能的角度出發,應該盡可能避免排序或者盡可能避免對大量資料進行排序。

推薦使用利用索引進行排序,但是當不能使用索引的時候,mysql就需要自己進行排序,如果資料量小則再記憶體中進行,如果資料量大就需要使用磁盤,mysql中稱之為filesort。

如果需要排序的資料量小于排序緩沖區(show variables like '%sort_buffer_size%';),mysql使用記憶體進行快速排序操作,如果記憶體不夠排序,那麼mysql就會先将樹分塊,對每個獨立的塊使用快速排序進行排序,并将各個塊的排序結果存放再磁盤上,然後将各個排好序的塊進行合并,最後傳回排序結果

  排序的算法

① 兩次傳輸排序

第一次資料讀取是将需要排序的字段讀取出來,然後進行排序,第二次是将排好序的結果按照需要去讀取資料行。

這種方式效率比較低,原因是第二次讀取資料的時候因為已經排好序,需要去讀取所有記錄而此時更多的是随機IO,讀取資料成本會比較高

兩次傳輸的優勢,在排序的時候存儲盡可能少的資料,讓排序緩沖區可以盡可能多的容納行數來進行排序操作

② 單次傳輸排序

先讀取查詢所需要的所有列,然後再根據給定列進行排序,最後直接傳回排序結果,此方式隻需要一次順序IO讀取所有的資料,而無須任何的随機IO,問題在于查詢的列特别多的時候,會占用大量的存儲空間,無法存儲大量的資料

③ 當需要排序的列的總大小超過max_length_for_sort_data定義的位元組,mysql會選擇雙次排序,反之使用單次排序,當然,使用者可以設定此參數的值來選擇排序的方式

優化特定類型的查詢

優化count()查詢

count()是特殊的函數,有兩種不同的作用,一種是某個列值的數量,也可以統計行數

1. 總有人認為myisam的count函數比較快,這是有前提條件的,隻有沒有任何where條件的count(*)才是比較快的

2. 使用近似值

在某些應用場景中,不需要完全精确的值,可以參考使用近似值來代替,比如可以使用explain來擷取近似的值

其實在很多OLAP的應用中,需要計算某一個列值的基數,有一個計算近似值的算法叫hyperloglog。

3. 更複雜的優化

一般情況下,count()需要掃描大量的行才能擷取精确的資料,其實很難優化,在實際操作的時候可以考慮使用索引覆寫掃描,或者增加彙總表,或者增加外部緩存系統。

優化關聯查詢

1. 確定on或者using子句中的列上有索引,在建立索引的時候就要考慮到關聯的順序

當表A和表B使用列C關聯的時候,如果優化器的關聯順序是B、A,那麼就不需要再B表的對應列上建上索引,沒有用到的索引隻會帶來額外的負擔,一般情況下來說,隻需要在關聯順序中的第二個表的相應列上建立索引

2. 確定任何的groupby和order by中的表達式隻涉及到一個表中的列,這樣mysql才有可能使用索引來優化這個過程

優化子查詢

子查詢的優化最重要的優化建議是盡可能使用關聯查詢代替

優化limit分頁

在很多應用場景中我們需要将資料進行分頁,一般會使用limit加上偏移量的方法實作,同時加上合适的orderby 的子句,如果這種方式有索引的幫助,效率通常不錯,否則的化需要進行大量的檔案排序操作,還有一種情況,當偏移量非常大的時候,前面的大部分資料都會被抛棄,這樣的代價太高。

要優化這種查詢的話,要麼是在頁面中限制分頁的數量,要麼優化大偏移量的性能

優化此類查詢的最簡單的辦法就是盡可能地使用覆寫索引,而不是查詢所有的列

                select film_id,description from film order by title limit 50,5

                explain select film.film_id,film.description from film inner join (select film_id from film order by title limit 50,5) as lim using(film_id);

                檢視執行計劃檢視掃描的行數

優化union查詢

mysql總是通過建立并填充臨時表的方式來執行union查詢,是以很多優化政策在union查詢中都沒法很好的使用。經常需要手工的将where、limit、order by等子句下推到各個子查詢中,以便優化器可以充分利用這些條件進行優化

除非确實需要伺服器消除重複的行,否則一定要使用union all,是以沒有all關鍵字,mysql會在查詢的時候給臨時表加上distinct的關鍵字,這個操作的代價很高

推薦使用使用者自定義變量

使用者自定義變量是一個容易被遺忘的mysql特性,但是如果能夠用好,在某些場景下可以寫出非常高效的查詢語句,在查詢中混合使用過程化和關系話邏輯的時候,自定義變量會非常有用。

使用者自定義變量是一個用來存儲内容的臨時容器,在連接配接mysql的整個過程中都存在。

1. 自定義變量的使用

set @one :=1
set @min_actor :=(select min(actor_id) from actor)
set @last_week :=current_date-interval 1 week;
           

2. 自定義變量的限制

1、無法使用查詢緩存

2、不能在使用常量或者辨別符的地方使用自定義變量,例如表名、列名或者limit子句

3、使用者自定義變量的生命周期是在一個連接配接中有效,是以不能用它們來做連接配接間的通信

4、不能顯式地聲明自定義變量地類型

5、mysql優化器在某些場景下可能會将這些變量優化掉,這可能導緻代碼不按預想地方式運作

6、指派符号:=的優先級非常低,是以在使用指派表達式的時候應該明确的使用括号

7、使用未定義變量不會産生任何文法錯誤

3. 自定義變量的使用案例

1. 優化排名語句

    1、在給一個變量指派的同時使用這個變量

        select actor_id,@rownum:[email protected]+1 as rownum from actor limit 10;

    2、查詢擷取演過最多電影的前10名演員,然後根據出演電影次數做一個排名

        select actor_id,count(*) as cnt from film_actor group by actor_id order by cnt desc limit 10;

2. 避免重新查詢剛剛更新的資料

    1、當需要高效的更新一條記錄的時間戳,同時希望查詢目前記錄中存放的時間戳是什麼

update t1 set  lastUpdated=now() where id =1;
select lastUpdated from t1 where id =1;
update t1 set lastupdated = now() where id = 1 and @now:=now();
select @now;
           

3. 确定取值的順序

     在指派和讀取變量的時候可能是在查詢的不同階段

set @rownum:=0;
select actor_id,@rownum:[email protected]+1 as cnt from actor where @rownum<=1;
因為where和select在查詢的不同階段執行,是以看到查詢到兩條記錄,這不符合預期  
           
set @rownum:=0;
select actor_id,@rownum:[email protected]+1 as cnt from actor where @rownum<=1 order by first_name
當引入了orde;r by之後,發現列印出了全部結果,這是因為order by引入了檔案排序,而where條件是在檔案排序操作之前取值的  
           
解決這個問題的關鍵在于讓變量的指派和取值發生在執行查詢的同一階段:
set @rownum:=0;
select actor_id,@rownum as cnt from actor where (@rownum:[email protected]+1)<=1;
           

六. 分區表

對于使用者而言,分區表是一個獨立的邏輯表,但是底層是由多個實體子表組成。分區表對于使用者而言是一個完全封裝底層實作的黑盒子,對使用者而言是透明的,從檔案系統中可以看到多個使用#分隔命名的表檔案。

mysql在建立表時使用partition by子句定義每個分區存放的資料,在執行查詢的時候,優化器會根據分區定義過濾那些沒有我們需要資料的分區,這樣查詢就無須掃描所有分區。

分區的主要目的是将資料安好一個較粗的力度分在不同的表中,這樣可以将相關的資料存放在一起。

分區表的應用場景

表非常大以至于無法全部都放在記憶體中,或者隻在表的最後部分有熱點資料,其他均是曆史資料

分區表的資料更容易維護

            批量删除大量資料可以使用清除整個分區的方式

            對一個獨立分區進行優化、檢查、修複等操作

分區表的資料可以分布在不同的實體裝置上,進而高效地利用多個硬體裝置

可以使用分區表來避免某些特殊的瓶頸

            innodb的單個索引的互斥通路

            ext3檔案系統的inode鎖競争

可以備份和恢複獨立的分區

分區表的限制

        一個表最多隻能有1024個分區,在5.7版本的時候可以支援8196個分區

        在早期的mysql中,分區表達式必須是整數或者是傳回整數的表達式,在mysql5.5中,某些場景可以直接使用列來進行分區

        如果分區字段中有主鍵或者唯一索引的列,那麼所有主鍵列和唯一索引列都必須包含進來

        分區表無法使用外鍵限制

分區表的原理

分區表由多個相關的底層表實作,這個底層表也是由句柄對象辨別,我們可以直接通路各個分區。存儲引擎管理分區的各個底層表和管理普通表一樣(所有的底層表都必須使用相同的存儲引擎),分區表的索引知識在各個底層表上各自加上一個完全相同的索引。從存儲引擎的角度來看,底層表和普通表沒有任何不同,存儲引擎也無須知道這是一個普通表還是一個分區表的一部分。

分區表的操作按照以下的操作邏輯進行:

select查詢

當查詢一個分區表的時候,分區層先打開并鎖住所有的底層表,優化器先判斷是否可以過濾部分分區,然後再調用對應的存儲引擎接口通路各個分區的資料

insert操作

當寫入一條記錄的時候,分區層先打開并鎖住所有的底層表,然後确定哪個分區接受這條記錄,再将記錄寫入對應底層表

delete操作

當删除一條記錄時,分區層先打開并鎖住所有的底層表,然後确定資料對應的分區,最後對相應底層表進行删除操作

update操作

當更新一條記錄時,分區層先打開并鎖住所有的底層表,mysql先确定需要更新的記錄再哪個分區,然後取出資料并更新,再判斷更新後的資料應該再哪個分區,最後對底層表進行寫入操作,并對源資料所在的底層表進行删除操作

有些操作時支援過濾的,例如,當删除一條記錄時,MySQL需要先找到這條記錄,如果where條件恰好和分區表達式比對,就可以将所有不包含這條記錄的分區都過濾掉,這對update同樣有效。如果是insert操作,則本身就是隻命中一個分區,其他分區都會被過濾掉。mysql先确定這條記錄屬于哪個分區,再将記錄寫入對應得曾分區表,無須對任何其他分區進行操作

雖然每個操作都會“先打開并鎖住所有的底層表”,但這并不是說分區表在處理過程中是鎖住全表的,如果存儲引擎能夠自己實作行級鎖,例如innodb,則會在分區層釋放對應表鎖。

分區表的類型

範圍分區

根據列值在給定範圍内将行配置設定給分區

範圍分區表的分區方式是:每個分區都包含行資料且分區的表達式在給定的範圍内,分區的範圍應該是連續的且不能重疊,可以使用values less than運算符來定義。

1、建立普通的表

CREATE TABLE employees (
    id INT NOT NULL,
    fname VARCHAR(30),
    lname VARCHAR(30),
    hired DATE NOT NULL DEFAULT '1970-01-01',
    separated DATE NOT NULL DEFAULT '9999-12-31',
    job_code INT NOT NULL,
    store_id INT NOT NULL
);
           

2、建立帶分區的表,下面建表的語句是按照store_id來進行分區的,指定了4個分區

CREATE TABLE employees (
    id INT NOT NULL,
    fname VARCHAR(30),
    lname VARCHAR(30),
    hired DATE NOT NULL DEFAULT '1970-01-01',
    separated DATE NOT NULL DEFAULT '9999-12-31',
    job_code INT NOT NULL,
    store_id INT NOT NULL
)
PARTITION BY RANGE (store_id) (
    PARTITION p0 VALUES LESS THAN (6),
    PARTITION p1 VALUES LESS THAN (11),
    PARTITION p2 VALUES LESS THAN (16),
    PARTITION p3 VALUES LESS THAN (21)
);
           
--在目前的建表語句中可以看到,store_id的值在1-5的在p0分區,6-10的在p1分區,11-15的在p3分區,16-20的在p4分區,但是如果插入超過20的值就會報錯,因為mysql不知道将資料放在哪個分區
           

3、可以使用less than maxvalue來避免此種情況

CREATE TABLE employees (
    id INT NOT NULL,
    fname VARCHAR(30),
    lname VARCHAR(30),
    hired DATE NOT NULL DEFAULT '1970-01-01',
    separated DATE NOT NULL DEFAULT '9999-12-31',
    job_code INT NOT NULL,
    store_id INT NOT NULL
)
PARTITION BY RANGE (store_id) (
    PARTITION p0 VALUES LESS THAN (6),
    PARTITION p1 VALUES LESS THAN (11),
    PARTITION p2 VALUES LESS THAN (16),
    PARTITION p3 VALUES LESS THAN MAXVALUE
);
--maxvalue表示始終大于等于最大可能整數值的整數值
           

4、可以使用相同的方式根據員工的職務代碼對表進行分區

CREATE TABLE employees (
    id INT NOT NULL,
    fname VARCHAR(30),
    lname VARCHAR(30),
    hired DATE NOT NULL DEFAULT '1970-01-01',
    separated DATE NOT NULL DEFAULT '9999-12-31',
    job_code INT NOT NULL,
    store_id INT NOT NULL
)
PARTITION BY RANGE (job_code) (
    PARTITION p0 VALUES LESS THAN (100),
    PARTITION p1 VALUES LESS THAN (1000),
    PARTITION p2 VALUES LESS THAN (10000)
);
           

5、可以使用date類型進行分區:如虛妄根據每個員工離開公司的年份進行劃分,如year(separated)

CREATE TABLE employees (
    id INT NOT NULL,
    fname VARCHAR(30),
    lname VARCHAR(30),
    hired DATE NOT NULL DEFAULT '1970-01-01',
    separated DATE NOT NULL DEFAULT '9999-12-31',
    job_code INT,
    store_id INT
)
PARTITION BY RANGE ( YEAR(separated) ) (
    PARTITION p0 VALUES LESS THAN (1991),
    PARTITION p1 VALUES LESS THAN (1996),
    PARTITION p2 VALUES LESS THAN (2001),
    PARTITION p3 VALUES LESS THAN MAXVALUE
);
           

6、可以使用函數根據range的值來對表進行分區,如timestampunix_timestamp()

CREATE TABLE quarterly_report_status (
    report_id INT NOT NULL,
    report_status VARCHAR(20) NOT NULL,
    report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
    PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
    PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
    PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
    PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
    PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
    PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
    PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
    PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
    PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
    PARTITION p9 VALUES LESS THAN (MAXVALUE)
);
--timestamp不允許使用任何其他涉及值的表達式
           

基于時間間隔的分區方案,在mysql5.7中,可以基于範圍或事件間隔實作分區方案,有兩種選擇

1、基于範圍的分區,對于分區表達式,可以使用操作函數基于date、time、或者datatime列來傳回一個整數值

CREATE TABLE members (
    firstname VARCHAR(25) NOT NULL,
    lastname VARCHAR(25) NOT NULL,
    username VARCHAR(16) NOT NULL,
    email VARCHAR(35),
    joined DATE NOT NULL
)
PARTITION BY RANGE( YEAR(joined) ) (
    PARTITION p0 VALUES LESS THAN (1960),
    PARTITION p1 VALUES LESS THAN (1970),
    PARTITION p2 VALUES LESS THAN (1980),
    PARTITION p3 VALUES LESS THAN (1990),
    PARTITION p4 VALUES LESS THAN MAXVALUE
);
​
CREATE TABLE quarterly_report_status (
    report_id INT NOT NULL,
    report_status VARCHAR(20) NOT NULL,
    report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
    PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
    PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
    PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
    PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
    PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
    PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
    PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
    PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
    PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
    PARTITION p9 VALUES LESS THAN (MAXVALUE)
);
           

2、基于範圍列的分區,使用date或者datatime列作為分區列

CREATE TABLE members (
    firstname VARCHAR(25) NOT NULL,
    lastname VARCHAR(25) NOT NULL,
    username VARCHAR(16) NOT NULL,
    email VARCHAR(35),
    joined DATE NOT NULL
)
PARTITION BY RANGE COLUMNS(joined) (
    PARTITION p0 VALUES LESS THAN ('1960-01-01'),
    PARTITION p1 VALUES LESS THAN ('1970-01-01'),
    PARTITION p2 VALUES LESS THAN ('1980-01-01'),
    PARTITION p3 VALUES LESS THAN ('1990-01-01'),
    PARTITION p4 VALUES LESS THAN MAXVALUE
);
           

真實案例:

#不分區的表
CREATE TABLE no_part_tab
(id INT DEFAULT NULL,
remark VARCHAR(50) DEFAULT NULL,
d_date DATE DEFAULT NULL
)ENGINE=MYISAM;
#分區的表
CREATE TABLE part_tab
(id INT DEFAULT NULL,
remark VARCHAR(50) DEFAULT NULL,
d_date DATE DEFAULT NULL
)ENGINE=MYISAM
PARTITION BY RANGE(YEAR(d_date))(
PARTITION p0 VALUES LESS THAN(1995),
PARTITION p1 VALUES LESS THAN(1996),
PARTITION p2 VALUES LESS THAN(1997),
PARTITION p3 VALUES LESS THAN(1998),
PARTITION p4 VALUES LESS THAN(1999),
PARTITION p5 VALUES LESS THAN(2000),
PARTITION p6 VALUES LESS THAN(2001),
PARTITION p7 VALUES LESS THAN(2002),
PARTITION p8 VALUES LESS THAN(2003),
PARTITION p9 VALUES LESS THAN(2004),
PARTITION p10 VALUES LESS THAN maxvalue);
#插入未分區表記錄
DROP PROCEDURE IF EXISTS no_load_part;
 
​
DELIMITER//
CREATE PROCEDURE no_load_part()
BEGIN
    DECLARE i INT;
    SET i =1;
    WHILE i<80001
    DO
    INSERT INTO no_part_tab VALUES(i,'no',ADDDATE('1995-01-01',(RAND(i)*36520) MOD 3652));
    SET i=i+1;
    END WHILE;
END//
DELIMITER ;
 
CALL no_load_part;
#插入分區表記錄
DROP PROCEDURE IF EXISTS load_part;
 
DELIMITER&& 
CREATE PROCEDURE load_part()
BEGIN
    DECLARE i INT;
    SET i=1;
    WHILE i<80001
    DO
    INSERT INTO part_tab VALUES(i,'partition',ADDDATE('1995-01-01',(RAND(i)*36520) MOD 3652));
    SET i=i+1;
    END WHILE;
END&&
DELIMITER ;
 
CALL load_part;
           

清單分區

            類似于按range分區,差別在于list分區是基于列值比對一個離散值集合中的某個值來進行選擇

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT,

    store_id INT

)

PARTITION BY LIST(store_id) (

    PARTITION pNorth VALUES IN (3,5,6,9,17),

    PARTITION pEast VALUES IN (1,2,10,11,19,20),

    PARTITION pWest VALUES IN (4,12,13,14,18),

    PARTITION pCentral VALUES IN (7,8,15,16)

);
           

列分區

            mysql從5.5開始支援column分區,可以認為i是range和list的更新版,在5.5之後,可以使用column分區替代range和list,但是column分區隻接受普通列不接受表達式

CREATE TABLE `list_c` (

 `c1` int(11) DEFAULT NULL,

 `c2` int(11) DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=latin1

/*!50500 PARTITION BY RANGE COLUMNS(c1)

(PARTITION p0 VALUES LESS THAN (5) ENGINE = InnoDB,

 PARTITION p1 VALUES LESS THAN (10) ENGINE = InnoDB) */



 CREATE TABLE `list_c` (

 `c1` int(11) DEFAULT NULL,

 `c2` int(11) DEFAULT NULL,

 `c3` char(20) DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=latin1

/*!50500 PARTITION BY RANGE COLUMNS(c1,c3)

(PARTITION p0 VALUES LESS THAN (5,'aaa') ENGINE = InnoDB,

 PARTITION p1 VALUES LESS THAN (10,'bbb') ENGINE = InnoDB) */



 CREATE TABLE `list_c` (

 `c1` int(11) DEFAULT NULL,

 `c2` int(11) DEFAULT NULL,

 `c3` char(20) DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=latin1

/*!50500 PARTITION BY LIST COLUMNS(c3)

(PARTITION p0 VALUES IN ('aaa') ENGINE = InnoDB,

 PARTITION p1 VALUES IN ('bbb') ENGINE = InnoDB) */

           

hash分區

            基于使用者定義的表達式的傳回值來進行選擇的分區,該表達式使用将要插入到表中的這些行的列值進行計算。這個函數可以包含myql中有效的、産生非負整數值的任何表達式

CREATE TABLE employees (
    id INT NOT NULL,
    fname VARCHAR(30),
    lname VARCHAR(30),
    hired DATE NOT NULL DEFAULT '1970-01-01',
    separated DATE NOT NULL DEFAULT '9999-12-31',
    job_code INT,
    store_id INT
)
PARTITION BY HASH(store_id)
PARTITIONS 4;


CREATE TABLE employees (
    id INT NOT NULL,
    fname VARCHAR(30),
    lname VARCHAR(30),
    hired DATE NOT NULL DEFAULT '1970-01-01',
    separated DATE NOT NULL DEFAULT '9999-12-31',
    job_code INT,
    store_id INT
)
PARTITION BY LINEAR HASH(YEAR(hired))
PARTITIONS 4;
           

key分區

            類似于hash分區,差別在于key分區隻支援一列或多列,且mysql伺服器提供其自身的哈希函數,必須有一列或多列包含整數值

CREATE TABLE tk (
    col1 INT NOT NULL,
    col2 CHAR(5),
    col3 DATE
)
PARTITION BY LINEAR KEY (col1)
PARTITIONS 3;
           

子分區

            在分區的基礎之上,再進行分區後存儲

CREATE TABLE `t_partition_by_subpart`
(
  `id` INT AUTO_INCREMENT,
  `sName` VARCHAR(10) NOT NULL,
  `sAge` INT(2) UNSIGNED ZEROFILL NOT NULL,
  `sAddr` VARCHAR(20) DEFAULT NULL,
  `sGrade` INT(2) NOT NULL,
  `sStuId` INT(8) DEFAULT NULL,
  `sSex` INT(1) UNSIGNED DEFAULT NULL,
  PRIMARY KEY (`id`, `sGrade`)
)  ENGINE = INNODB
PARTITION BY RANGE(id)
SUBPARTITION BY HASH(sGrade) SUBPARTITIONS 2
(
PARTITION p0 VALUES LESS THAN(5),
PARTITION p1 VALUES LESS THAN(10),
PARTITION p2 VALUES LESS THAN(15)
);
           

如何使用分區表

如果需要從非常大的表中查詢出某一段時間的記錄,而這張表中包含很多年的曆史資料,資料是按照時間排序的,此時應該如何查詢資料呢?

因為資料量巨大,肯定不能在每次查詢的時候都掃描全表。考慮到索引在空間和維護上的消耗,也不希望使用索引,即使使用索引,會發現會産生大量的碎片,還會産生大量的随機IO,但是當資料量超大的時候,索引也就無法起作用了,此時可以考慮使用分區來進行解決

全量掃描資料,不要任何索引

使用簡單的分區方式存放表,不要任何索引,根據分區規則大緻定位需要的資料為止,通過使用where條件将需要的資料限制在少數分區中,這種政策适用于以正常的方式通路大量資料

索引資料,并分離熱點

如果資料有明顯的熱點,而且除了這部分資料,其他資料很少被通路到,那麼可以将這部分熱點資料單獨放在一個分區中,讓這個分區的資料能夠有機會都緩存在記憶體中,這樣查詢就可以隻通路一個很小的分區表,能夠使用索引,也能夠有效的使用緩存

在使用分區表的時候需要注意的問題

        null值會使分區過濾無效

        分區列和索引列不比對,會導緻查詢無法進行分區過濾

        選擇分區的成本可能很高

        打開并鎖住所有底層表的成本可能很高

        維護分區的成本可能很高 

七. 伺服器參數設定

general

        datadir=/var/lib/mysql

            資料檔案存放的目錄

        socket=/var/lib/mysql/mysql.sock

            mysql.socket表示server和client在同一台伺服器,并且使用localhost進行連接配接,就會使用socket進行連接配接

        pid_file=/var/lib/mysql/mysql.pid

            存儲mysql的pid

        port=3306

            mysql服務的端口号

        default_storage_engine=InnoDB

            mysql存儲引擎

        skip-grant-tables

            當忘記mysql的使用者名密碼的時候,可以在mysql配置檔案中配置該參數,跳過權限表驗證,不需要密碼即可登入mysql

character

        character_set_client

            用戶端資料的字元集

        character_set_connection

            mysql處理用戶端發來的資訊時,會把這些資料轉換成連接配接的字元集格式

        character_set_results

            mysql發送給用戶端的結果集所用的字元集

        character_set_database

            資料庫預設的字元集

        character_set_server

            mysql server的預設字元集

connection

        max_connections

            mysql的最大連接配接數,如果資料庫的并發連接配接請求比較大,應該調高該值

        max_user_connections

            限制每個使用者的連接配接個數

        back_log

            mysql能夠暫存的連接配接數量,當mysql的線程在一個很短時間内得到非常多的連接配接請求時,就會起作用,如果mysql的連接配接數量達到max_connections時,新的請求會被存儲在堆棧中,以等待某一個連接配接釋放資源,如果等待連接配接的數量超過back_log,則不再接受連接配接資源

        wait_timeout

            mysql在關閉一個非互動的連接配接之前需要等待的時長

        interactive_timeout

            關閉一個互動連接配接之前需要等待的秒數

log

        log_error

            指定錯誤日志檔案名稱,用于記錄當mysqld啟動和停止時,以及伺服器在運作中發生任何嚴重錯誤時的相關資訊

        log_bin

            指定二進制日志檔案名稱,用于記錄對資料造成更改的所有查詢語句

        binlog_do_db

            指定将更新記錄到二進制日志的資料庫,其他所有沒有顯式指定的資料庫更新将忽略,不記錄在日志中

        binlog_ignore_db

            指定不将更新記錄到二進制日志的資料庫

        sync_binlog

            指定多少次寫日志後同步磁盤

        general_log

            是否開啟查詢日志記錄

        general_log_file

            指定查詢日志檔案名,用于記錄所有的查詢語句

        slow_query_log

            是否開啟慢查詢日志記錄

        slow_query_log_file

            指定慢查詢日志檔案名稱,用于記錄耗時比較長的查詢語句

        long_query_time

            設定慢查詢的時間,超過這個時間的查詢語句才會記錄日志

        log_slow_admin_statements

            是否将管理語句寫入慢查詢日志

cache

        key_buffer_size

            索引緩存區的大小(隻對myisam表起作用)

        query cache

            query_cache_size

                查詢緩存的大小,未來版本被删除

                    show status like '%Qcache%';檢視緩存的相關屬性

                    Qcache_free_blocks:緩存中相鄰記憶體塊的個數,如果值比較大,那麼查詢緩存中碎片比較多

                    Qcache_free_memory:查詢緩存中剩餘的記憶體大小

                    Qcache_hits:表示有多少此命中緩存

                    Qcache_inserts:表示多少次未命中而插入

                    Qcache_lowmen_prunes:多少條query因為記憶體不足而被移除cache

                    Qcache_queries_in_cache:目前cache中緩存的query數量

                    Qcache_total_blocks:目前cache中block的數量

            query_cache_limit

                超出此大小的查詢将不被緩存

            query_cache_min_res_unit

                緩存塊最小大小

            query_cache_type

                緩存類型,決定緩存什麼樣的查詢

                    0表示禁用

                    1表示将緩存所有結果,除非sql語句中使用sql_no_cache禁用查詢緩存

                    2表示隻緩存select語句中通過sql_cache指定需要緩存的查詢

        sort_buffer_size

            每個需要排序的線程分派該大小的緩沖區

        max_allowed_packet=32M

            限制server接受的資料包大小

        join_buffer_size=2M

            表示關聯緩存的大小

        thread_cache_size

伺服器線程緩存,這個值表示可以重新利用儲存再緩存中的線程數量,當斷開連接配接時,那麼用戶端的線程将被放到緩存中以響應下一個客戶而不是銷毀,如果線程重新被請求,那麼請求将從緩存中讀取,如果緩存中是空的或者是新的請求,這個線程将被重新請求,那麼這個線程将被重新建立,如果有很多新的線程,增加這個值即可

            Threads_cached:代表目前此時此刻線程緩存中有多少空閑線程

            Threads_connected:代表目前已建立連接配接的數量

            Threads_created:代表最近一次服務啟動,已建立現成的數量,如果該值比較大,那麼伺服器會一直再建立線程

            Threads_running:代表目前激活的線程數

INNODB

        innodb_buffer_pool_size=

            該參數指定大小的記憶體來緩沖資料和索引,最大可以設定為實體記憶體的80%

        innodb_flush_log_at_trx_commit

            主要控制innodb将log buffer中的資料寫入日志檔案并flush磁盤的時間點,值分别為0,1,2

        innodb_thread_concurrency

            設定innodb線程的并發數,預設為0表示不受限制,如果要設定建議跟伺服器的cpu核心數一緻或者是cpu核心數的兩倍

        innodb_log_buffer_size

            此參數确定日志檔案所用的記憶體大小,以M為機關

        innodb_log_file_size

            此參數确定資料日志檔案的大小,以M為機關

        innodb_log_files_in_group

            以循環方式将日志檔案寫到多個檔案中

        read_buffer_size

            mysql讀入緩沖區大小,對表進行順序掃描的請求将配置設定到一個讀入緩沖區

        read_rnd_buffer_size

            mysql随機讀的緩沖區大小

        innodb_file_per_table

            此參數确定為每張表配置設定一個新的檔案

八. mysql叢集(後續更新)

    主從複制

    讀寫分離

    分庫分表