服務器層面的性能優化(瞭解)
這一部分了解就可以了,一般程序員更多的是在表的設計上以及SQL語句編寫上進行優化
服務器硬件優化
提升硬件設備,例如選擇儘量高頻率的內存(頻率不能高於主板的支持)、提升網絡帶寬、使用SSD高
速磁盤、提升CPU性能等。
CPU的選擇:
- 對於數據庫併發比較高的場景,CPU的數量比頻率重要。
- 對於CPU密集型場景和頻繁執行復雜SQL的場景,CPU的頻率越高越好。
CentOS系統針對mysql的參數優化
內核相關參數(/etc/sysctl.conf)
以下參數可以直接放到sysctl.conf文件的末尾。
- 增加監聽隊列上限:
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
net.ipv4.tcp_max_syn_backlog = 65535 - 加快TCP連接的回收:
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1 - TCP連接接收和發送緩衝區大小的默認值和最大值:
net.core.wmem_default = 87380
net.core.wmem_max = 16777216
net.core.rmem_default = 87380
net.core.rmem_max = 16777216 - 減少失效連接所佔用的TCP資源的數量,加快資源回收的效率:
net.ipv4.tcp_keepalive_time = 120
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3 - 單個共享內存段的最大值:
kernel.shmmax = 4294967295
- 這個參數應該設置的足夠大,以便能在一個共享內存段下容納整個的Innodb緩衝池的大小。
- 這個值的大小對於64位linux系統,可取的最大值爲(物理內存值-1)byte,建議值爲大於物理內存的一半,一般取值大於Innodb緩衝池的大小即可。
- 控制換出運行時內存的相對權重:
vm.swappiness = 0
這個參數當內存不足時會對性能產生比較明顯的影響。(設置爲0,表示Linux內核虛擬內存完全被佔
用,纔會要使用交換區。)
Linux系統內存交換區: 在Linux系統安裝時都會有一個特殊的磁盤分區,稱之爲系統交換分區。 使用 free -m 命令可以看到swap就是內存交換區。 作用:當操作系統沒有足夠的內存時,就會將部分虛擬內存寫到磁盤的交換區中,這樣就會發生 內存交換。
如果Linux系統上完全禁用交換分區,帶來的風險:
- 降低操作系統的性能
- 容易造成內存溢出,崩潰,或都被操作系統kill掉
增加資源限制(/etc/security/limit.conf)
開文件數的限制(以下參數可以直接放到limit.conf文件的末尾):
- soft nofile 65535
- hard nofile 65535
*:表示對所有用戶有效
soft:表示當前系統生效的設置(soft不能大於hard )
hard:表明系統中所能設定的最大值
nofile:表示所限制的資源是打開文件的最大數目
65535:限制的數量
以上兩行配置將可打開的文件數量增加到65535個,以保證可以打開足夠多的文件句柄。
注意:這個文件的修改需要重啓系統才能生效。
磁盤調度策略
- cfq (完全公平隊列策略,Linux2.6.18之後內核的系統默認策略)
該模式按進程創建多個隊列,各個進程發來的IO請求會被cfq以輪循方式處理,對每個IO請求都是公平
的。該策略適合離散讀的應用。 - deadline (截止時間調度策略)
deadline,包含讀和寫兩個隊列,確保在一個截止時間內服務請求(截止時間是可調整的),而默認讀
期限短於寫期限。這樣就防止了寫操作因爲不能被讀取而餓死的現象,deadline對數據庫類應用是最好
的選擇。 - noop (電梯式調度策略)
noop只實現一個簡單的FIFO隊列,傾向餓死讀而利於寫,因此noop對於閃存設備、RAM及嵌入式系統
是最好的選擇。 - 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執行時的資源使用情況。