大綱
序言
一 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