DB調優(不解釋連招) :表設計和SQL方向下

一、從表設計方向上:

  在設計表時,遵循設計範式,儘量使用一對一、一對多,當出現多對多時,儘量使用中間表來存儲(在同一個項目中,如果有多種多對多的場景,可以考慮將所有多對多的中間關係存儲在一張表中,達到減少表數量的目的)。

三範式與主鍵設計參考


二、從 SQL 語句方向上:

1. 慢查詢日誌:

  慢查詢日誌中,會記錄所有時間大於設定值的操作;包括DML、DQL、向binlog記錄的SQL信息(binlog可用於數據恢復和主從同步用),都會被記載

  查看慢日誌是否開啓,記錄文件名或表名。(文件位置一般在 /var/lib/mysql/… )

mysql> show variables like 'slow_query%';
+---------------------+------------------+
| Variable_name       | Value            |
+---------------------+------------------+
| slow_query_log      | ON               |
| slow_query_log_file | centos7-slow.log |
+---------------------+------------------+
2 rows in set (0.04 sec)

查看慢查詢時間界限,   秒:

mysql> select @@long_query_time;
+-------------------+
| @@long_query_time |
+-------------------+
|          3.000000 |
+-------------------+
1 row in set (0.03 sec)
  • 開啓慢查詢日誌功能:set global slow_query_log=ON;

  • 設置慢查詢時間界限:set global long_query_time=3;

注意:設置完成後需要斷開當前會話,重新連接一次,纔會查詢到更新

2. explain分析sql執行計劃:

2.1 explain 中的字段及對應的含義

在這裏插入圖片描述
key:表明這次查找中所用到的索引

rows:指這次查找數據所掃描的大概行數,並不是一個準確的值(可以分析count(*)的語句來查看)。

type:效率升序:all < index < range < ref < eq_ref < const < system

  • all:全表掃描(需要優化)

  • index:按照索引順序的全表掃描(需要優化)

  • range:有範圍的索引掃描

  • ref:觸發聯合索引最左原則,該列不爲主鍵和unique

  • eq_ref:對已經建立索引列進行 = 操作的時候,eq_ref會被使用到。比較值可以使用一個常量也可以是一個表達式。這個表達示可以是其他的表的行。

  • const:用主鍵或唯一索引做了查詢條件,結果集中只返回一條數據

  • system:表中只有一條數據的情況

filtered(Percentage of rows filtered by table condition): 通過表條件過濾後剩下數據所佔的百分比

extra:本次查詢使用的索引等一些額外信息

相關explain結果的官方解釋

2.2 導致索引失效的情況:

  1. OR條件兩邊有一邊沒有建立索引、

  2. 字段類型隱式轉換(eg:當表的字段類型位char,查詢的時候字段賦值的是非字符串類型)、

  3. like以%開頭(%出現在字符串後面可以使用到索引)、

  4. 索引列是計算或者函數的一部分(eg: … salary*22>11000(salary是索引列),這種將不使用索引)、

  5. 最左匹配原則:mysql查詢優化器,會尋找 where 條件中有索引的列並先執行

2.3 使用了索引,但覆蓋索引失效導致查詢變慢的情況:

什麼是覆蓋索引?

  如果查詢時,通過二級索引完全匹配到了需要的數據,那麼他不會去找聚簇索引,直接返回數據;如果通過二級索引沒找到對應的數據,那麼它會去走聚簇索引,回表查詢數據。簡單的講:SQL只通過二級索引就可以返回查找的數據,而不需要通過二級索引找到聚簇索引之後,再回表查找對應的數據。

  在下面的 limit 測試用例中,就有一條 extra 爲 Using where; Using filesort;該查詢中 ORDER BY 的字段沒有索引,所以使用到了 Using filesort,便是覆蓋索引失效導致查詢變慢的一種情況,此時按情況考慮爲 ORDER BY 的字段添加索引。

用例可參考

3. 減少表鎖、行鎖等待時間的優化

  show status like ‘%lock%’;可以查看DB中,關於各種鎖的等待時間。

行鎖(InnoDB中)的優化:

  1. 在所有需要加鎖的 SQL 中,儘量使用到索引,避免行級鎖上升爲表及鎖

  2. 在使用索引的範圍加鎖時,儘量縮小範圍,來避免間隙鎖鎖住範圍後,無法對該範圍內的數據進行加鎖操作

  3. 在業務允許的情況下,縮小事務範圍,減少事務的鎖定數據範圍和時間

  4. 在事務中,爲避免死鎖的出現,可以採用一次性鎖定需要的數據、所有事務中按一定的順序鎖定數據、對於容易產生死鎖的業務,可以使用表鎖來解決等。

關於InnoDB鎖參考

4. 索引的構建與選擇

4.1 前綴索引

是什麼?可以達到什麼目的?

  MySQL 前綴索引是爲字符串列的一部,創建一個區分度高、空間佔用小的索引,可以提高對錶數據的查找和一定程度上的插入速度。

  爲字符串列添加一個前綴:alter table tablename add index key_name(field_name(length))

官方文檔

帶來了那些問題?

  與覆蓋索引相比,如果滿足了覆蓋索引的條件,那麼 MySQL 將不會執行回表查數據操作。但如果使用了前綴索引,那麼就一定有回表操作,去進行完整的對比。

實際業務中的考慮與選擇

情況1:當一個字符串字段比較長,它的前n位區分度已經比較高了,那麼可以對該字段建立前綴索引

情況2:當一個字符串字段的前n位區分度不高,但後面面m位區分度高時,可以將字段反轉後再入庫,索引建立時使用前綴索引。

情況3:當有一個很長的字符串需要做索引時,不建議直接在該字符串上建立索引,而是對該字符串的 hash 值作爲它的索引列。

4.2 唯一索引與普通索引的選擇

  當我們的業務中,一個字段需要保證唯一時,加唯一索引,優先考慮業務。(Q:)但當一個字段即可以使用唯一索引,也可以選擇普通索引時,如何選擇?

  (A:)還是需要根據不同的業務來討論。如果該表涉及的業務 讀多寫少,或寫入後短時間內會讀取 ,那麼使用唯一索引;如果涉及到業務寫多讀少,或寫入後長時間內都不會去讀取,那麼使用普通索引,如賬單類、日誌類的業務模塊。

理由
  其中涉及到了change buffer 知識點, InooDB 會將更新操作緩存在 change buffer 中,便不需要從磁盤中讀入這個數據頁。下次查詢需要訪問這個數據頁的時候,將數據頁讀入內存,然後執行 change buffer 中與這個頁有關的操作,來保證數據的一致性。
  change buffer 是 buffer pool 中的一部分,有兩個重要的參數

  • innodb_change_buffer_max_size:表示 Change Buffer 最大大小佔 Buffer Pool 的百分比,默認爲 25%,最大可以設置爲 50%。
  • innodb_change_buffering:用來控制對哪些操作啓用 Change Buffer 功能,默認是:all,對所有修改操作使用 Change Buffer 。

更多知識細節參考1

下圖是 InnoDB 在更新數據時的一個流程圖。
在這裏插入圖片描述
  瞭解了相關的知識點後,回到最開始的問題。

  在我們對一個唯一索引的字段做更新操作時,要先判斷表中的數據與當前的數據是否違反唯一性約束,而這必須要將數據頁讀入內存才能判斷,就沒必要使用 change buffer 了。這也是爲什麼唯一索引適合 讀多寫少,或寫入後短時間內會讀取 的業務場景了。

  而對於普通索引,數據的更新操作可以用 change buffer 來記錄,而不用把對應的數據頁讀取到內存中,且短時間內不會讀取修改數據的數據頁(即數據頁在做merge之前,change buffer記錄的變更越多),實現提高數據庫的操作效率。


關於limit(offset,rows)調優的測試:

  limit用於返回結果集中偏移量(offset,從0開始)到rows行(多少行的)的數據。

當表中數據較多時,offset越大,查詢效率越低,優化方案如下:

  1. 在業務允許的情況下限制頁數

  2. 使用索引來避免limit對全表的掃描(需要注意的是,自增id的主鍵不一定連續,即結果集行數與id不相對應)

EXPLAIN
select * from test_xs  WHERE bjdm LIKE '2%' ORDER BY user_xh limit 67000,10

-----------exlain分析結果表
1	SIMPLE	test_xs	ALL	bjdm_index				67642	Using where; Using filesort


優化:

---------------------------------------------------------------------
方法1:此處sso_id(主鍵)的獲取,可通過前臺的數據結果集來得到
EXPLAIN
select * from test_xs  WHERE bjdm LIKE '2%' AND sso_id>67000 ORDER BY user_xh limit 10

----------exlain分析結果表

1	SIMPLE	test_xs	index	PRIMARY,bjdm_index	user_xh_index	767		303	Using where

---------------------------------------------------------------------
方法二:通過offset來得到對應行的id

EXPLAIN
select * from test_xs  WHERE bjdm LIKE '2%' AND sso_id>(SELECT sso_id FROM test_xs  LIMIT 10,1) ORDER BY user_xh limit 10

---------exlain分析結果表
1	PRIMARY	test_xs	index	PRIMARY,bjdm_index	user_xh_index	767		20	Using where
2	SUBQUERY	test_xs	index		bjdm_index	36		67642	Using index


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章