MySQL 瓶頸及應對措施和實踐

大綱 

序言

一  MySQL 瓶頸及應對措施

二  mysql  具體優化參數

三  mysql 優化實踐

四 總結

參考文獻

 

序言 

1  首先給出性能瓶頸定位Show命令

我們可以通過show命令查看MySQL狀態及變量,找到系統的瓶頸:

Mysql> show status ——顯示狀態信息(擴展show status like ‘XXX’)

Mysql> show variables ——顯示系統變量(擴展show variables like ‘XXX’)

Mysql> show innodb status ——顯示InnoDB存儲引擎的狀態

Mysql> show processlist ——查看當前SQL執行,包括執行狀態、是否鎖表等

Shell> mysqladmin variables -u username -p password——顯示系統變量

Shell> mysqladmin extended-status -u username -p password——顯示狀態信息

查看狀態變量及幫助:

Shell> mysqld –verbose –help 

2  開啓 慢查詢日誌 定位慢 sql 有那些

3  其實最好的辦法就是感覺響應慢老闆接受不了,必須優化。
 

一 MySQL 瓶頸及應對措施

MySQL 是存在瓶頸的。 當 MySQL 單表數據量達到千萬級別以上時,無論如何對 MySQL 進行優化,查詢如何簡單,MySQL 的性能都會顯著降低。 採取措施:

1)增加 MySQL 配置中的 buffer 和 Cache 的數值,增加服務器 CPU 數量和內存的大小,這樣能很大程度上應對 MySQL 的性能瓶頸。 
    性能優化中,效果最顯著、成本最低的當屬硬件和服務器優化,所以這是應該首先考慮的。 
2)使用第三方引擎或衍生版本。
    如 Percona 在功能和性能上較 MySQL 有着顯著的提升;由 MySQL 創始人開發的免費 MariaDB 在 InnoDB 引擎上的性能也比 MySQL優秀;
    據官網借號,另一款改進的產品 TokuDB,性能是 MySQL 的 10 倍以上。以上這些衍生版或改進版的 MySQL 主要都是針對 MySQL 的 InnoDB
    引擎。 InnoDB 每次提交事物時,爲了保證數據已經持久化到磁盤(Durable),需要調用一次 fsync 告知文件系統,將可能在緩存中的數據
    刷新到次哦按。而 fsync 操作本身是非常『昂貴』的(消耗較多的 I/O 資源,響應較慢),如果每次事物提交都單獨做 fsync 操作,那麼這裏
    將是系統 TPS 的一個瓶頸,所以就有了 Group Commit 的概念。 MySQL 5.0 後,處於分佈式架構的考慮,爲了保證 InnoDB 內部 Commit 
    和 MySQL 日誌的順序一致,InnoDB 被迫放棄支持 Group Commit。之後,就一直沒有好的解決方案了。直到出現 MariaDB,才比較完美的解決
    了這個問題。
3)遷移到其它數據庫。
    如開源的 Post供熱SQL 和商業的 Oracle。 與 Oracle 和 PostgreSQL 相比, MySQL 屬於線程模式,並且採用了插件引擎的架構。這種實現
    的確有自己的優勢:輕巧快速、系統資源消耗小、支持更多併發連接,但進程模式能更充分的應用 CPU 資源,在應付複雜業務查詢上更有優勢。比如,
    在常規優化的前提下,Oracle 的但別性能瓶頸經驗值在 2 億數據量的級別,遠遠優於 MySQL。 不僅如此,在關聯查詢和內置函數等功能上,
    Oracle 都是完勝 MySQL 數據庫的。 再比如,PostgreSQL 數據庫相比 MySQL,擁有更強大的查詢優化器,不會頻繁重建索引,支持物化視圖等
    優勢。當然,遷移到其他數據庫還要看應用的類型,比如是偏向 OLTP( On-Line Transaction Processing,聯機事物處理)的應用還是偏向
    OLAP(On-Line Analytical Processing,聯機分析處理)應用。 
4)對數據庫進行分區、分表,減少單表體積。
5)使用NoSQL 等輔助解決方案,如 Memcached、Redis。
6)使用中間件做數據拆分和分佈式部署,這方面的典型案例有阿里巴巴對外開源的數據庫中間件 Cobar。
7)使用數據庫連接池技術。 
    在第 3 點,我們講過,MySQL 是線程模式,可以支持更多的併發連接數。MySQL 能支持遠比 Oracle 和 PostgreSQL 更多的連接。所以 Oracle
    和 PostgreSQL 應用中通常用數據庫連接池技術來複用連接。那麼 MySQL 爲什麼還需要用這些數據庫應用中常見的數據庫連接池技術呢? 原因在於
    MySQL 的所機制還不夠完善,同時程序中的一些問題都有可能導致 MySQL 數據庫連接阻塞,在併發大的情況下,就會浪費很多連接資源和反覆連接
    的消耗。使用數據庫連接池,讓連接進行排隊和複用,一定程度上可以緩解高併發下的連接壓力。
MySQL 瓶頸是真實存在的,但是不少大型互聯網公司仍然在使用 MySQL,並且能使用的很好,這一方面是因爲這些公司的技術實力足以對 MySQL 進行二次開發和改進,另一方面則得益於其成熟的架構。所以,一個工具能不能用好,人的因素佔很大的比重。
 

二  mysql  具體優化實踐

先普及以下 GB和Gb區別

0.1342 GB (gigabytes)
1.074 Gb (gigabits)
內存一般不會說Gb,只有GB。
這裏,其實就是著名的大B和小b的區別。
一般,通訊上說的,是小b,即bit,意思是位。
8個位是一個字節,叫Byte,就是大B。
我們一般說的寬帶,4M,就是4Mb,要除8,纔是電腦上一般用的B,下載速度也就是400KB左右。
內存的容量不說Gb,帶寬纔會涉及到Gb。
基本上,b就是個傳輸速率纔會涉及到的概念。

總結 容量大小度量GB,通訊傳輸速率大小Gb;

1  transaction_isolation


解讀:事務隔離級別,Oracle, SQL Server等商業知名數據庫默認級別爲READ-COMMITTED,而MySQL默認爲REPEATABLE-READ,它利用自身獨有的Gap Lock解決了"幻讀"。但也因爲Gap Lock的緣故,相比於READ-COMMITTED級別的Record Lock,REPEATABLE-READ的事務併發插入性能受到很大的限制。
設置:隔離級別的選擇取決於實際的業務需求(安全與性能的權衡),如果不是金融、電信等事務級別要求很高的業務,完全可以設置成transaction_isolation=READ-COMMITTED。

2 innodb_buffer_pool_size


解讀:InnoDB緩衝池大小,它決定了MySQL可以在內存中緩存多少數據和索引,而不是每次都從磁盤上讀取。我們都知道Redis的讀寫很快,其最重要的原因是它的所有數據都緩存在內存中。試想一下如果innodb_buffer_pool_size足夠大,MySQL所有表數據和索引都能被緩存,那將是一種什麼體驗?
設置:如果是專用的MySQL服務器,一般設置爲操作系統內存的75%左右,但至少保留2G內存用於操作系統維護和MySQL異常事件處理。

3 innodb_buffer_pool_instances


解讀:InnoDB緩衝池實例個數,InnoDB緩衝池是通過一整個鏈表的方式來管理頁面(段、簇、頁)的,由於互斥鎖的存在(保護鏈表中的頁面),高併發事務下,頁面的讀取和寫入就需要鎖的競爭和等待。通過設置innodb_buffer_pool_instances,將一整個鏈表劃分爲多個,每個緩衝池實例管理自己的頁面和互斥,從而提高效率。
設置:如果緩衝池比較大(8G以上),可以按照innodb_buffer_pool_size / innodb_buffer_pool_instances = 1G進行設置,但如果緩衝池特別大(32G以上),可以按照每個實例2~3G進行劃分,實例數不是越多越好,多實例代表多線程,線程的開銷(CPU、MEM)也得考慮。

4 innodb_log_file_size


解讀:InnoDB日誌文件大小(Redo Log),它將事務對InnoDB表的修改記錄保存在ib_logfile0、ib_logfile1中。innodb_log_file_size越大,緩衝池中的髒數據需要檢查點(checkpoint)進行刷盤的頻率就越少,從而減少磁盤IO來降低高併發負載造成的峯值。但日誌文件也不是越大約好,由於內存中髒數據刷盤的頻率減少,一旦數據庫發生異常崩潰,數據庫重啓時從innodb_log_file中讀取數據進行恢復的時間越長。

設置:一般選取業務高峯期一個小時的日誌量作爲標準,計算過程如下:

# 命令
mysql -uroot -h172.26.96.146 -p -e 'show engine innodb status\G'|grep 'Log sequence number' \
&& sleep 60 \
&& mysql -uroot  -h172.26.96.146 -p -e 'show engine innodb status\G'|grep 'Log sequence number'
# 輸出
Log sequence number 149949388055
Log sequence number 149959622102
 
# 計算
( 149959622102 - 149949388055 ) / 1024 / 1024 = 10M
10 / 60 * 3600 = 600M
600 / 2 = 300M
 
# 解釋
Log sequence number代表InnoDB運行至今寫入日誌的總字節數,兩次打印之間線程休眠60秒
得到一分鐘之內事務日誌記錄的總量10M,再轉換成一個小時的總量600M
因爲`ib_logfile0、ib_logfile1`兩個文件循環寫入,一個文件爲300M
最終,innodb_log_file_size=300M

5 innodb_flush_log_at_trx_commit


解讀:InnoDB事務日誌刷盤時機

當0時,事務提交到日誌緩衝區,後臺Write線程每隔一秒將緩衝區的日誌寫入系統緩衝區,實際寫入物理日誌文件的時機取決於操作系統。

當1時,事務提交到日誌緩衝區,Master線程同步將緩衝區的日誌直接寫入物理日誌文件,這完全符合InnoDB ACID事務標準,數據不會丟失。

當2時,事務提交到系統緩衝區,Master線程每隔一秒將系統緩衝區的日誌寫入物理日誌文件。
設置:安全1 > 2 > 0,速度0 > 2 > 1,根據實際業務需求(安全與速度權衡)選擇合理的刷盤時機。


6 innodb_file_per_table


解讀:InnoDB獨立表空間,innodb_file_per_table = ON表示每張表在獨立的物理文件中(.ibd)存儲數據和索引,innodb_file_per_table = OFF表示所有表都共享表空間即一個物理文件(ibdata1)。如果通過drop/truncate table操作,獨立表空間的物理存儲會立即被回收(刪除/初始化),而共享表空間不會被回收且只會一直增大。
設置:innodb_file_per_table = ON,但需要注意的是,獨立表空間只存儲數據和索引,如回滾日誌緩衝(Undo Log)、插入索引緩衝(Insert Buffer)、二次寫緩衝(Doublewrite Buffer)等還是放在共享表空間。

query_cache_size
解讀:查詢緩存大小,它是爲了在追蹤表的數據未發生變化時,本次查詢命中之前的查詢語句,從而跳過解析、優化、執行階段,直接返回緩衝池中的數據。但實際在OLTP系統中,極少能命中查詢緩存(前提是數據庫中的數據變化頻率很小),因爲一旦數據有變則緩存失效。且因爲查詢緩存會跟蹤所有表的變化,它也會成爲整個數據庫的瓶頸(資源競爭點)。
設置:query_cache_size = 0,同時配合設置query_cache_type = 0,MySQL5.7.20以上、MySQL8.0會直接棄用所有查詢緩存配置項。

max_connections
解讀:最大連接數,當max_connections設置太小時(默認151),MySQL可能會報錯Too many connections。當max_connections設置太大時(1000以上),操作系統可能忙於線程間的切換而失去響應。
設置:每個連接都會消耗一定內存,計算過程如下:

 

三  接着說mysql 5.7 性能優化實戰

調整Innodb_Buffer_Pool_size大小必須重啓mysql進程纔可以生效,如今在MySQL5.7裏,可以直接動態設置,方便了很多。

這個功能應用的場景:

  • A 全部數據查詢數據量大,需要調優。
  • B 機器增加內存,DBA粗心大意忘記調大Innodb_Buffer_Pool_size了。
  • C 工作交接,新來的DBA發現前任DBA設置的Innodb_Buffer_Pool_size不合理。

需要注意的地方,在調整Buffer_Pool期間,用戶的請求將會阻塞,直到調整完畢,所以請勿在白天調整,在凌晨3-4點低峯期調整。

調整時,內部把數據頁移動到一個新的位置,單位是塊。如果想增加移動的速度,需要調整innodb_buffer_pool_chunk_size參數的大小,默認是128M。

例(把BP 128M增大爲384M):

mysql> SELECT @@innodb_buffer_pool_size;

+---------------------------+

| @@innodb_buffer_pool_size |

+---------------------------+

|                134217728 |

+---------------------------+

1 row in set (0.00 sec)

mysql> SELECT @@innodb_buffer_pool_chunk_size;

+---------------------------------+

| @@innodb_buffer_pool_chunk_size |

+---------------------------------+

|                      134217728 |

+---------------------------------+

1 row in set (0.00 sec)

mysql> SET GLOBAL innodb_buffer_pool_size=402653184;

Query OK, 0 rows affected (0.01 sec)

mysql> SELECT @@innodb_buffer_pool_size;

+---------------------------+

| @@innodb_buffer_pool_size |

+---------------------------+

|                402653184 |

+---------------------------+

1 row in set (0.00 sec)

 

innodb_buffer_pool_chunk_size的大小,計算公式是innodb_buffer_pool_size / innodb_buffer_pool_instances

比如現在初始化innodb_buffer_pool_size爲2G,innodb_buffer_pool_instances實例爲4,innodb_buffer_pool_chunk_size設置爲1G,那麼會自動把innodb_buffer_pool_chunk_size 1G調整爲512M,例:
./mysqld --innodb_buffer_pool_size=2147483648 --innodb_buffer_pool_instances=4 
--innodb_buffer_pool_chunk_size=1073741824;

mysql> SELECT @@innodb_buffer_pool_size;

+---------------------------+

| @@innodb_buffer_pool_size |

+---------------------------+

|                2147483648 |

+---------------------------+

1 row in set (0.00 sec)

 

mysql> SELECT @@innodb_buffer_pool_instances;

+--------------------------------+

| @@innodb_buffer_pool_instances |

+--------------------------------+

|                              4 |

+--------------------------------+

1 row in set (0.00 sec)

 

# Chunk size was set to 1GB (1073741824 bytes) on startup but was

# truncated to innodb_buffer_pool_size / innodb_buffer_pool_instances

mysql> SELECT @@innodb_buffer_pool_chunk_size;

+---------------------------------+

| @@innodb_buffer_pool_chunk_size |

+---------------------------------+

|                      536870912 |

+---------------------------------+

1 row in set (0.00 sec)


監控Buffer Pool調整進程

mysql> SHOW STATUS WHERE Variable_name='InnoDB_buffer_pool_resize_status';

+----------------------------------+----------------------------------+

| Variable_name                    | Value                            |

+----------------------------------+----------------------------------+

| Innodb_buffer_pool_resize_status | Resizing also other hash tables. |

+----------------------------------+----------------------------------+

1 row in set (0.00 sec)

如下命令查看 告警xing'i
mysql> show warnings;
+---------+------+------------------------------------------------------------------+
| Level   | Code | Message                                                          |
+---------+------+------------------------------------------------------------------+
| Warning | 1292 | Truncated incorrect innodb_buffer_pool_size value: '1073741824' |
+---------+------+------------------------------------------------------------------+
1 row in set (0.00 sec)

出現警告了,再看實際大小

mysql> show variables like '%innodb_buffer_pool_size%';
+-------------------------+-------------+
| Variable_name           | Value       |
+-------------------------+-------------+
| innodb_buffer_pool_size | 1073741824|
+-------------------------+-------------+
1 row in set (0.00 sec)

 

如果有警告信息,這是因爲,調整後的buffer pool大小必須滿足如下條件:
innodb_buffer_pool_chunk_size* innodb_buffer_pool_instances的倍數,即128M的倍數。

 

四 總結

1  實戰總結innodb_buffer_pool_size =2G,innodb_buffer_pool_instances=1 效果很好。

2  性能優化面廣,涉及的方面多,需要根據實際情況而定。

3  僅僅建立索引對性能優化效果有限。

 

參考文獻

https://www.cnblogs.com/liugx/p/9935749.html

https://blog.csdn.net/nanyanglu/article/details/79109838

https://www.cnblogs.com/sandea/p/11695521.html

Ubuntu 14.04下安裝MySQL http://www.linuxidc.com/Linux/2014-05/102366.htm

《MySQL權威指南(原書第2版)》清晰中文掃描版 PDF http://www.linuxidc.com/Linux/2014-03/98821.htm

Ubuntu 14.04 LTS 安裝 LNMP Nginx\PHP5 (PHP-FPM)\MySQL http://www.linuxidc.com/Linux/2014-05/102351.htm

Ubuntu 14.04下搭建MySQL主從服務器 http://www.linuxidc.com/Linux/2014-05/101599.htm

Ubuntu 12.04 LTS 構建高可用分佈式 MySQL 集羣 http://www.linuxidc.com/Linux/2013-11/93019.htm

Ubuntu 12.04下源代碼安裝MySQL5.6以及Python-MySQLdb http://www.linuxidc.com/Linux/2013-08/89270.htm

MySQL-5.5.38通用二進制安裝 http://www.linuxidc.com/Linux/2014-07/104509.htm

發佈了38 篇原創文章 · 獲贊 25 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章