面試題:在平常工做中怎麼作MySQL優化的?

MySQL常見的優化手段分爲下面幾個方面:html

SQL優化、設計優化,硬件優化等,其中每一個大的方向中又包含多個小的優化點git

下面咱們具體來看看github

文章首發在公衆號(月伴飛魚),以後同步到掘金和我的網站:xiaoflyfish.cn/算法

以爲有收穫,但願幫忙點贊,轉發下哈,謝謝,謝謝sql

SQL優化

此優化方案指的是經過優化 SQL 語句以及索引來提升 MySQL 數據庫的運行效率,具體內容以下:數據庫

分頁優化

例如:緩存

select * from table where type = 2 and level = 9 order by id asc limit 190289,10;
複製代碼

優化方案:服務器

  • 延遲關聯網絡

    先經過where條件提取出主鍵,在將該表與原數據表關聯,經過主鍵id提取數據行,而不是經過原來的二級索引提取數據行oop

    例如:

select a.* from table a, (select id from table where type = 2 and level = 9 order by id asc limit 190289,10 ) b where a.id = b.id
複製代碼
  • 書籤方式

    書籤方式說白了就是找到limit第一個參數對應的主鍵值,再根據這個主鍵值再去過濾並limit

    例如:

select * from table where id > (select * from table where type = 2 and level = 9 order by id asc limit 190289, 1) limit 10;
複製代碼

索引優化

正確使用索引

假如咱們沒有添加索引,那麼在查詢時就會觸發全表掃描,所以查詢的數據就會不少,而且查詢效率會很低,爲了提升查詢的性能,咱們就須要給最常使用的查詢字段上,添加相應的索引,這樣才能提升查詢的性能

創建覆蓋索引

InnoDB使用輔助索引查詢數據時會回表,可是若是索引的葉節點中已經包含要查詢的字段,那它沒有必要再回表查詢了,這就叫覆蓋索引

例如對於以下查詢:

select name from test where city='上海'
複製代碼

咱們將被查詢的字段創建到聯合索引中,這樣查詢結果就能夠直接從索引中獲取

alter table test add index idx_city_name (city, name);
複製代碼

在 MySQL 5.0 以前的版本儘可能避免使用or查詢

在 MySQL 5.0 以前的版本要儘可能避免使用 or 查詢,可使用 union 或者子查詢來替代,由於早期的 MySQL 版本使用 or 查詢可能會致使索引失效,在 MySQL 5.0 以後的版本中引入了索引合併

索引合併簡單來講就是把多條件查詢,好比or或and查詢對多個索引分別進行條件掃描,而後將它們各自的結果進行合併,所以就不會致使索引失效的問題了

若是從Explain執行計劃的type列的值是index_merge能夠看出MySQL使用索引合併的方式來執行對錶的查詢

避免在 where 查詢條件中使用 != 或者 <> 操做符

SQL中,不等於操做符會致使查詢引擎放棄索引索引,引發全表掃描,即便比較的字段上有索引

解決方法:經過把不等於操做符改爲or,可使用索引,避免全表掃描

例如,把column<>’aaa’,改爲column>’aaa’ or column<’aaa’ ,就可使用索引了

適當使用前綴索引

MySQL 是支持前綴索引的,也就是說咱們能夠定義字符串的一部分來做爲索引

咱們知道索引越長佔用的磁盤空間就越大,那麼在相同數據頁中能放下的索引值也就越少,這就意味着搜索索引須要的查詢時間也就越長,進而查詢的效率就會下降,因此咱們能夠適當的選擇使用前綴索引,以減小空間的佔用和提升查詢效率

好比,郵箱的後綴都是固定的「@xxx.com」,那麼相似這種後面幾位爲固定值的字段就很是適合定義爲前綴索引

alter table test add index index2(email(6));
複製代碼

使用前綴索引,定義好長度,就能夠作到既節省空間,又不用額外增長太多的查詢成本

須要注意的是,前綴索引也存在缺點,MySQL沒法利用前綴索引作order by和group by 操做,也沒法做爲覆蓋索引

查詢具體的字段而非所有字段

要儘可能避免使用 select *,而是查詢須要的字段,這樣能夠提高速度,以及減小網絡傳輸的帶寬壓力

優化子查詢

儘可能使用 Join 語句來替代子查詢,由於子查詢是嵌套查詢,而嵌套查詢會新建立一張臨時表,而臨時表的建立與銷燬會佔用必定的系統資源以及花費必定的時間,同時對於返回結果集比較大的子查詢,其對查詢性能的影響更大

小表驅動大表

咱們要儘可能使用小表驅動大表的方式進行查詢,也就是若是 B 表的數據小於 A 表的數據,那執行的順序就是先查 B 表再查 A 表,具體查詢語句以下:

select name from A where id in (select id from B);
複製代碼

不要在列上進行運算操做

不要在列字段上進行算術運算或其餘表達式運算,不然可能會致使查詢引擎沒法正確使用索引,從而影響了查詢的效率

select * from test where id + 1 = 50;
select * from test where month(updateTime) = 7;
複製代碼

一個很容易踩的坑:隱式類型轉換:

select * from test where skuId=123456
複製代碼

skuId這個字段上有索引,可是explain的結果卻顯示這條語句會全表掃描

緣由在於skuId的字符類型是varchar(32),比較值倒是整型,故須要作類型轉換

適當增長冗餘字段

增長冗餘字段能夠減小大量的連表查詢,由於多張表的連表查詢性能很低,全部能夠適當的增長冗餘字段,以減小多張表的關聯查詢,這是以空間換時間的優化策略

正確使用聯合索引

使用了 B+ 樹的 MySQL 數據庫引擎,好比 InnoDB 引擎,在每次查詢複合字段時是從左往右匹配數據的,所以在建立聯合索引的時候須要注意索引建立的順序

例如,咱們建立了一個聯合索引是 idx(name,age,sex),那麼當咱們使用,姓名+年齡+性別、姓名+年齡、姓名等這種最左前綴查詢條件時,就會觸發聯合索引進行查詢;然而若是非最左匹配的查詢條件,例如,性別+姓名這種查詢條件就不會觸發聯合索引

Join優化

MySQL的join語句鏈接表使用的是nested-loop join算法,這個過程相似於嵌套循環,簡單來講,就是遍歷驅動表(外層表),每讀出一行數據,取出鏈接字段到被驅動表(內層表)裏查找知足條件的行,組成結果行

要提高join語句的性能,就要儘量減小嵌套循環的循環次數

一個顯著優化方式是對被驅動表的join字段創建索引,利用索引能快速匹配到對應的行,避免與內層表每一行記錄作比較,極大地減小總循環次數。另外一個優化點,就是鏈接時用小結果集驅動大結果集,在索引優化的基礎上能進一步減小嵌套循環的次數

若是難以判斷哪一個是大表,哪一個是小表,能夠用inner join鏈接,MySQL會自動選擇小表去驅動大表

避免使用JOIN關聯太多的表

對於 MySQL 來講,是存在關聯緩存的,緩存的大小能夠由join_buffer_size參數進行設置

在 MySQL 中,對於同一個 SQL 多關聯(join)一個表,就會多分配一個關聯緩存,若是在一個 SQL 中關聯的表越多,所佔用的內存也就越大

若是程序中大量的使用了多表關聯的操做,同時join_buffer_size設置的也不合理的狀況下,就容易形成服務器內存溢出的狀況,就會影響到服務器數據庫性能的穩定性

排序優化

利用索引掃描作排序

MySQL有兩種方式生成有序結果:其一是對結果集進行排序的操做,其二是按照索引順序掃描得出的結果天然是有序的

可是若是索引不能覆蓋查詢所需列,就不得不每掃描一條記錄回表查詢一次,這個讀操做是隨機IO,一般會比順序全表掃描還慢

所以,在設計索引時,儘量使用同一個索引既知足排序又用於查找行

例如:

--創建索引(date,staff_id,customer_id)
select staff_id, customer_id from test where date = '2010-01-01' order by staff_id,customer_id;
複製代碼

只有當索引的列順序和ORDER BY子句的順序徹底一致,而且全部列的排序方向都同樣時,纔可以使用索引來對結果作排序

UNION優化

MySQL處理union的策略是先建立臨時表,而後將各個查詢結果填充到臨時表中最後再來作查詢,不少優化策略在union查詢中都會失效,由於它沒法利用索引

最好手工將where、limit等子句下推到union的各個子查詢中,以便優化器能夠充分利用這些條件進行優化

此外,除非確實須要服務器去重,必定要使用union all,若是不加all關鍵字,MySQL會給臨時表加上distinct選項,這會致使對整個臨時表作惟一性檢查,代價很高

慢查詢日誌

出現慢查詢一般的排查手段是先使用慢查詢日誌功能,查詢出比較慢的 SQL 語句,而後再經過 Explain 來查詢 SQL 語句的執行計劃,最後分析並定位出問題的根源,再進行處理

慢查詢日誌指的是在 MySQL 中能夠經過配置來開啓慢查詢日誌的記錄功能,超過long_query_time值的 SQL 將會被記錄在日誌中

咱們能夠經過設置「slow_query_log=1」來開啓慢查詢

須要注意的是,在開啓慢日誌功能以後,會對 MySQL 的性能形成必定的影響,所以在生產環境中要慎用此功能

設計優化

儘可能避免使用NULL

NULL在MySQL中很差處理,存儲須要額外空間,運算也須要特殊的運算符,含有NULL的列很難進行查詢優化

應當指定列爲not null,用0、空串或其餘特殊的值代替空值,好比定義爲int not null default 0

最小數據長度

越小的數據類型長度一般在磁盤、內存和CPU緩存中都須要更少的空間,處理起來更快

使用最簡單數據類型

簡單的數據類型操做代價更低,好比:能使用 int 類型就不要使用 varchar 類型,由於 int 類型比 varchar 類型的查詢效率更高

儘可能少定義 text 類型

text 類型的查詢效率很低,若是必需要使用 text 定義字段,能夠把此字段分離成子表,須要查詢此字段時使用聯合查詢,這樣能夠提升主表的查詢效率

適當分表、分庫策略

分表是指當一張表中的字段更多時,能夠嘗試將一張大表拆分爲多張子表,把使用比較高頻的主信息放入主表中,其餘的放入子表,這樣咱們大部分查詢只須要查詢字段更少的主表就能夠完成了,從而有效的提升了查詢的效率

分庫是指將一個數據庫分爲多個數據庫。好比咱們把一個數據庫拆分爲了多個數據庫,一個主數據庫用於寫入和修改數據,其餘的用於同步主數據並提供給客戶端查詢,這樣就把一個庫的讀和寫的壓力,分攤給了多個庫,從而提升了數據庫總體的運行效率

常見類型選擇

整數類型寬度設置

MySQL能夠爲整數類型指定寬度,例如int(11),實際上並無意義,它並不會限制值的範圍,對於存儲和計算來講,int(1)和int(20)是相同的

VARCHAR和CHAR類型

char類型是定長的,而varchar存儲可變字符串,比定長更省空間,可是varchar須要額外1或2個字節記錄字符串長度,更新時也容易產生碎片

須要結合使用場景來選擇:若是字符串列最大長度比平均長度大不少,或者列的更新不多,選擇varchar較合適;若是要存很短的字符串,或者字符串值長度都相同,好比MD5值,或者列數據常常變動,選擇使用char類型

DATETIME和TIMESTAMP類型

datetime的範圍更大,能表示從1001到9999年,timestamp只能表示從1970年到2038年。datetime與時區無關,timestamp顯示值依賴於時區。在大多數場景下,這兩種類型都能良好地工做,可是建議使用timestamp,由於datetime佔用8個字節,timestamp只佔用了4個字節,timestamp空間效率更高

BLOB和TEXT類型

blob和text都是爲存儲很大數據而設計的字符串數據類型,分別採用二進制和字符方式存儲

在實際使用中,要慎用這兩種類型,它們的查詢效率很低,若是字段必需要使用這兩種類型,能夠把此字段分離成子表,須要查詢此字段時使用聯合查詢,這樣能夠提升主表的查詢效率

範式化

當數據較好範式化時,修改的數據更少,並且範式化的表一般要小,能夠有更多的數據緩存在內存中,因此執行操做會更快

缺點則是查詢時須要更多的關聯

第一範式:字段不可分割,數據庫默認支持

第二範式:消除對主鍵的部分依賴,能夠在表中加上一個與業務邏輯無關的字段做爲主鍵,好比用自增id

第三範式:消除對主鍵的傳遞依賴,能夠將表拆分,減小數據冗餘

硬件優化

MySQL 對硬件的要求主要體如今三個方面:磁盤、網絡和內存

磁盤

磁盤應該儘可能使用有高性能讀寫能力的磁盤,好比固態硬盤,這樣就能夠減小 I/O 運行的時間,從而提升了 MySQL 總體的運行效率

磁盤也能夠儘可能使用多個小磁盤而不是一個大磁盤,由於磁盤的轉速是固定的,有多個小磁盤就至關於擁有多個並行運行的磁盤同樣

網絡

保證網絡帶寬的通暢(低延遲)以及夠大的網絡帶寬是 MySQL 正常運行的基本條件,若是條件容許的話也能夠設置多個網卡,以提升網絡高峯期 MySQL 服務器的運行效率

內存

MySQL 服務器的內存越大,那麼存儲和緩存的信息也就越多,而內存的性能是很是高的,從而提升了整個 MySQL 的運行效率

若是以上文章對您有幫助,請給咱們的開源項目點點star: http://github.crmeb.net/u/defu 不勝感激!  

來自 「開源世界 」 ,連接:https://ym.baisou.ltd/post/726.html,如需轉載,請註明出處,不然將追究法律責任。

相關文章
相關標籤/搜索