Mysql學習(八)性能優化

服務器層面的性能優化(瞭解)

這一部分了解就可以了,一般程序員更多的是在表的設計上以及SQL語句編寫上進行優化

服務器硬件優化

提升硬件設備,例如選擇儘量高頻率的內存(頻率不能高於主板的支持)、提升網絡帶寬、使用SSD高
速磁盤、提升CPU性能等。
CPU的選擇:

  • 對於數據庫併發比較高的場景,CPU的數量比頻率重要。
  • 對於CPU密集型場景和頻繁執行復雜SQL的場景,CPU的頻率越高越好。

CentOS系統針對mysql的參數優化

內核相關參數(/etc/sysctl.conf)

以下參數可以直接放到sysctl.conf文件的末尾。

  1. 增加監聽隊列上限:
    net.core.somaxconn = 65535
    net.core.netdev_max_backlog = 65535
    net.ipv4.tcp_max_syn_backlog = 65535
  2. 加快TCP連接的回收:
    net.ipv4.tcp_fin_timeout = 10
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1
  3. TCP連接接收和發送緩衝區大小的默認值和最大值:
    net.core.wmem_default = 87380
    net.core.wmem_max = 16777216
    net.core.rmem_default = 87380
    net.core.rmem_max = 16777216
  4. 減少失效連接所佔用的TCP資源的數量,加快資源回收的效率:
    net.ipv4.tcp_keepalive_time = 120
    net.ipv4.tcp_keepalive_intvl = 30
    net.ipv4.tcp_keepalive_probes = 3
  5. 單個共享內存段的最大值:
    kernel.shmmax = 4294967295
  1. 這個參數應該設置的足夠大,以便能在一個共享內存段下容納整個的Innodb緩衝池的大小。
  2. 這個值的大小對於64位linux系統,可取的最大值爲(物理內存值-1)byte,建議值爲大於物理內存的一半,一般取值大於Innodb緩衝池的大小即可。
  1. 控制換出運行時內存的相對權重:
    vm.swappiness = 0
    這個參數當內存不足時會對性能產生比較明顯的影響。(設置爲0,表示Linux內核虛擬內存完全被佔
    用,纔會要使用交換區。)

Linux系統內存交換區: 在Linux系統安裝時都會有一個特殊的磁盤分區,稱之爲系統交換分區。 使用 free -m 命令可以看到swap就是內存交換區。 作用:當操作系統沒有足夠的內存時,就會將部分虛擬內存寫到磁盤的交換區中,這樣就會發生 內存交換。
如果Linux系統上完全禁用交換分區,帶來的風險:

  1. 降低操作系統的性能
  2. 容易造成內存溢出,崩潰,或都被操作系統kill掉

增加資源限制(/etc/security/limit.conf)

開文件數的限制(以下參數可以直接放到limit.conf文件的末尾):

  • soft nofile 65535
  • hard nofile 65535

*:表示對所有用戶有效
soft:表示當前系統生效的設置(soft不能大於hard )
hard:表明系統中所能設定的最大值
nofile:表示所限制的資源是打開文件的最大數目
65535:限制的數量
以上兩行配置將可打開的文件數量增加到65535個,以保證可以打開足夠多的文件句柄。
注意:這個文件的修改需要重啓系統才能生效。

磁盤調度策略

  1. cfq (完全公平隊列策略,Linux2.6.18之後內核的系統默認策略)
    該模式按進程創建多個隊列,各個進程發來的IO請求會被cfq以輪循方式處理,對每個IO請求都是公平
    的。該策略適合離散讀的應用。
  2. deadline (截止時間調度策略)
    deadline,包含讀和寫兩個隊列,確保在一個截止時間內服務請求(截止時間是可調整的),而默認讀
    期限短於寫期限。這樣就防止了寫操作因爲不能被讀取而餓死的現象,deadline對數據庫類應用是最好
    的選擇。
  3. noop (電梯式調度策略)
    noop只實現一個簡單的FIFO隊列,傾向餓死讀而利於寫,因此noop對於閃存設備、RAM及嵌入式系統
    是最好的選擇。
  4. anticipatory (預料I/O調度策略)
    本質上與deadline策略一樣,但在最後一次讀操作之後,要等待6ms,才能繼續進行對其它I/O請求進
    行調度。它會在每個6ms中插入新的I/O操作,合併寫入流,用寫入延時換取最大的寫入吞吐量。
    anticipatory適合於寫入較多的環境,比如文件服務器。該策略對數據庫環境表現很差。

查看調度策略的方法:
cat /sys/block/devname/queue/scheduler
修改調度策略的方法:
echo > /sys/block/devname/queue/scheduler

MySQL數據庫配置優化

這裏的配置指的是my.cnf(linux)或者my.ini(windows)裏面的配置

innodb_buffer_pool_size

表示緩衝池大小,該值越大,每一次讀取到內存的數據就越大,這樣可以減少磁盤交互。一般推薦設置爲物理內存的50%~80%。默認爲128M。
調優參考計算方法:
val = Innodb_buffer_pool_pages_data / Innodb_buffer_pool_pages_total * 100%
val > 95% 則考慮增大 innodb_buffer_pool_size, 建議使用物理內存的75%
val < 95% 則考慮減小 innodb_buffer_pool_size, 建議設置爲:Innodb_buffer_pool_pages_data * Innodb_page_size * 1.05 / (102410241024)
修改配置文件的調整方法,修改my.cnf配置:
innodb_buffer_pool_size = 2147483648 #設置2G
innodb_buffer_pool_size = 2G #設置2G
innodb_buffer_pool_size = 500M #設置500M

innodb_flush_log_at_trx_commit

這個參數之前講日誌落盤的時候講過默認爲1,爲0的時候效率最高,但沒辦法保證數據的安全,只要mysqld的進程崩潰就會丟失數據。但是如果是讀寫分離的話,負責讀的從機可以設定爲1

sync_binlog

可以設置提交幾次事物之後寫進bin_log,該數據最安全的是1,即每次提交數據都會寫到bin_log日誌裏面。當然這也是效率最差的。
如果是從機是不用寫操作的可以設置爲0。

innodb_max_dirty_pages_pct

髒頁佔innodb_buffer_pool_size的比例時,觸發刷髒頁到磁盤。 推薦值爲25%~50%。
後臺進程最大IO性能指標。

innodb_io_capacity

默認200,如果SSD,調整爲5000~20000

innodb_data_file_path

指定innodb共享表空間文件的大小。(分磁盤,這樣可以減輕磁盤的IO操作性能壓力)

long_qurey_time

慢查詢日誌的閾值設置,單位秒。(慢查詢日誌,不要隨意開啓,開啓的時候,要合理設置閾值)

binlog_format

mysql複製的形式,row爲MySQL8.0的默認形式。(statement,row,mixed)

max_connections

調高該參數則應降低interactive_timeout、wait_timeout的值。

innodb_log_file_size

過大,實例恢復時間長;過小,造成日誌切換頻繁。

general_log

全量日誌建議關閉。
默認關閉。
1 innodb_flush_log_at_trx_commit=1 1 sync_binlog=1 1 innodb_max_dirty_pages_pct=30 1 innodb_io_capacity=200 1 innodb_data_file_path 1 long_qurey_time=0.3 1 binlog_format=row 1 max_connections=200 1 innodb_log_file_size
將數據保存在內存中,保證從內存讀取數據
設置足夠大的 innodb_buffer_pool_size ,將數據讀取到內存中。

數據庫設計層面優化

  • 設計中間表,一般針對於統計分析功能,或者實時性不高的需求(OLTP、OLAP)
  • 爲減少關聯查詢,創建合理的冗餘字段(考慮數據庫的三範式和查詢性能的取捨,創建冗餘字段還需要注意數據一致性問題)逆範式
  • 對於字段太多的大表,考慮拆表(比如一個表有100多個字段)(查詢的時候,是以整行爲單位去加載的。)
    Blob Clob Text類型的字段,建議拆到另一張表中。(商品表10-15列,商品詳情介紹[text]—建議將商品詳情介紹拆成單獨的表)
  • 對於表中經常不被使用的字段或者存儲數據比較多的字段,考慮拆表(比如商品表中會存儲商品介紹,此時可以將商品介紹字段單獨拆解到另一個表中,使用商品ID關聯)
  • 每張表建議都要有一個主鍵(主鍵索引),而且主鍵類型最好是int類型,建議自增主鍵(不考慮分佈式系統的情況下)

SQL語句優化

索引優化

  • 爲搜索字段(where中的條件)、排序字段、select查詢列,創建合適的索引,不過要考慮數據的
    業務場景:查詢多還是增刪多?
  • 儘量建立組合索引並注意組合索引的創建順序,按照順序組織查詢條件、儘量將篩選粒度大的查詢
  • 條件放到最左邊。
  • 儘量使用覆蓋索引,SELECT語句中儘量不要使用*。
  • order by、group by語句要儘量使用到索引
    name,age—組合索引
    select * from user where name = “james” order by age ---- 使用到索引
    select * from user order by name,age ---- 使用到索引
  • 索引長度儘量短,短索引可以節省索引空間,使查找的速度得到提升,同時內存中也可以裝載更多的索引鍵值。太長的列,可以選擇建立前綴索引
    比如給name列加索引,但是name列長度爲300。其實最多在使用索引搜索的時候,使用到前10個字節長度。這個時候建議建立前綴索引
  • 索引更新不能頻繁,更新非常頻繁的數據不適宜建索引,因爲維護索引的成本。
  • order by的索引生效,order by排序應該遵循最佳左前綴查詢,如果是使用多個索引字段進行排序,那麼排序的規則必須相同(同是升序或者降序),否則索引同樣會失效。

LIMIT優化

  • 如果預計SELECT語句的查詢結果是一條,最好使用 LIMIT 1,可以停止全表掃描。
SELECT * FROM user WHERE username=’李梅’; -- username沒有建立唯一索引
SELECT * FROM user WHERE username=’李梅’ LIMIT 1;
  • 處理分頁會使用到 LIMIT ,當翻頁到非常靠後的頁面的時候,偏移量會非常大,這時LIMIT的效率會非常差。
    LIMIT的優化問題,其實是 OFFSET 的問題,它會導致MySql掃描大量不需要的行然後再拋棄掉。
    解決方案1:使用order by 和索引覆蓋
    原SQL(如果 film 表中的記錄有10020條):
SELECT id,name FROM user LIMIT 10000, 20;

優化的SQL:

SELECT id,name FROM user ORDER BY id desc LIMIT 20;

解決方案2:使用子查詢

SELECT id,name FROM (select id,name from user order by id limit 10000,20) t

解決方案3:單表分頁時,使用自增主鍵排序之後,先使用where條件 id > offset值,limit後面只寫rows

其他查詢優化

  • 小表驅動大表,建議使用left join時,以小表關聯大表,因爲使用join的話,第一張表是必須全掃描的,以少關聯多就可以減少這個掃描次數。
    explain—select列的信息,顯示查詢順序,可以根據這個進行優化。
  • 避免全表掃描,mysql在使用不等於(!=或者<>)的時候無法使用導致全表掃描。在查詢的時候,如果對索引使用不等於的操作將會導致索引失效,進行全表掃描
  • 避免mysql放棄索引查詢,如果mysql估計使用全表掃描要比使用索引快,則不使用索引。(最典型的場景就是數據量少的時候)
  • 儘量不使用count(*)、儘量使用count(主鍵)

COUNT(*)\COUNT(1)\COUNT(列),從索引使用情況來說,是一樣的。如果COUNT(非索引列), 那麼MySQL會選擇該表中,最小的那顆索引樹,去進行統計。
COUNT(*)以行爲統計,最終的結果是包含null值
COUNT(1),會過濾NULL值
COUNT(列),會過濾NULL

  • JOIN兩張表的關聯字段最好都建立索引,而且最好字段類型是一樣的

SELECT * FROM orders o LEFT JOIN user u on o.user_id = u.id orders表中的user_id和user表中的id,類型要一致

  • WHERE條件中儘量不要使用1=1、not in語句(建議使用not exists)、
  • 不用 MYSQL 內置的函數,因爲內置函數不會建立查詢緩存。

SQL查詢語句和查詢結果都會在第一次查詢只會存儲到MySQL的查詢緩存中,如果需要獲取到查詢緩 存中的查詢結果,查詢的SQL語句必須和第一次的查詢SQL語句一致。 SELECT * FROM user where birthday = now();

  • 合理利用慢查詢日誌、explain執行計劃查詢、show profile查看SQL執行時的資源使用情況。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章