日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

作者介紹

劉書浩,“移動云”DBA,負責“移動云”業務系統的數據庫運維、標準化等工作;擅長MySQL技術領域,熟悉MySQL復制結構、Cluster架構及運維優化;具有自動化運維經驗,負責“移動云”數據庫管理平臺的搭建。

前言

隨著移動云平臺系統業務不斷增長,必然需要對各系統進行更新或者發布新版本,以滿足用戶的需求。但是在系統更新或者發布新版本時,往往會引起各種的性能隱患,使得系統的穩定性大大降低,這些性能隱患的根本原因大部分都在于軟件開發標準不規范引起。為了加強開發標準規范,提高各系統的穩定性,本文從影響MySQL數據庫性能的常見因素入手,重點論述下如何編寫高效的SQL語句,并以合適的方式創建表和索引,以達到系統在不斷更新和升級時仍能保持良好的穩定性。

在這之前,首先簡單了解一下MySQL的體系結構。

MySQL的邏輯結構

MySQL DBA必讀:萬字歸總表設計與SQL編寫技巧

Connectors:用來與客戶端應用程序建立連接的數據庫接口。

Management Services & Utilities:系統管理和服務控制相關的輔助工具。

Connection Pool:負責處理與用戶訪問有關的各種用戶登錄、線程處理、內存和進程緩存需求。

Sql Interface:提供從用戶接受命令并把結果返回給用戶的機制。

Parser:對SQL語句進行語法分析和解析,構造一個月來執行查詢的數據結構。

Optimizer:優化查詢語句,以保證數據檢索動作的效率達到或者非常接近最最優。使用一種“選取-投影-聯結”策略來處理查詢,即先根據有關的限制條件進行選取(Select 操作)以減少將要處理的元組個數,再進行投影以減少被選取元組力的屬性字段的個數,最后根據連接條件生產最終的查詢結果。

Caches & Buffers:保證使用頻率最高的數據或結構能夠以最有效率的方式被訪問,緩存的類型有:表緩存、記錄緩存、鍵緩存、權限緩存、主機名緩存等。

Query流程

MySQL DBA必讀:萬字歸總表設計與SQL編寫技巧

1、查詢緩存

檢查查詢緩存是否打開,檢查是否命中緩存中的數據(通過對大小寫敏感的HASH查找實現的),若不命中則進行下一階段的處理。若命中查詢緩存,檢查用戶權限,若權限沒問題,則直接把緩存數據返回給客戶端。

2、語法解析器和預處理器

詞法/語法解析器:將會進行語法規則的驗證和解析查詢(對語法解析),生成語法分析樹。

預處理器:根據MySQL規則進一步檢查語法分析樹是否合法。例如檢查表或列是否存在,解析名字和別名有沒有歧義。下一步預處理器會驗證權限。

3、查詢優化器

優化器的作用就是找到最好的執行計劃。MySQL使用CBO優化器。MySQL使用很多優化策略生成最優的執行計劃,可以分為兩類:靜態優化(編譯時優化)、動態優化(運行時優化)。

4、查詢執行引擎

MySQL只是簡單的根據執行計劃給出的指令逐步執行。調用存儲引擎實現的接口來完成執行計劃。優化器根據接口可以獲取表的相關信息,包括表的所有列名、索引統計信息等。將結果返回給客戶端,或者返回這個查詢的一些信息,如查詢影響到的行數。如果查詢可以被緩存,那么MySQL會將結果存放到查詢緩存中。

影響MySQL數據庫的常見因素

1、服務器硬件

CPU:一般情況下CPU資源不會是性能瓶頸的直接原因;MySQL不支持多cpu對同一SQL并發處理。

內存:直接影響MySQL緩沖池的大小及MySQL數據庫的整體運行穩定性;如內存資源不足,容易造成MySQL的會話擁堵甚至實例重啟。

存儲IO:直接影響MySQL的處理性能;在大量數據變更的業務場景下,對存儲的IO性能要求往往較高。

2、數據庫存儲引擎

MyISAM:不支持事務型查詢,在OLTP類型業務場景中不建議使用。

InnoDB:支持事務型查詢,支持行級鎖,對并發業務支持較好。

3、MySQL參數

1)連接請求的參數:max_connections

MySQL的最大連接數,增加該值增加mysqld要求的文件描述符的數量。連接請求量大時,建議調高此值調的越高,內存開銷越大。

mysql>show variables like 'max_connections';

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

|Variable_name|Value|

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

|max_connections|512 |

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

mysql>show status like 'max%connections';

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

|Variable_name|Value|

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

|max_used_connections|512 |

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

2)全局緩存參數

key_buffer_size指定索引緩沖區的大小,它決定索引處理的速度,尤其是索引讀的速度。Key_reads是內存中沒有找到索引直接從硬盤讀取索引的數量。

mysql>show variables like' key_buffer_size';

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

|Variable_name|Value|

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

|key_buffer_size|536870912 |

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

mysql>show status like 'key_read%';

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

|Variable_name|Value|

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

|Key_read_requests|178306331520 |

|Key_reads|67 |

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

使用查詢緩沖,MySQL將查詢結果存放在緩沖區中,今后對于同樣的SELECT語句(區分大小寫),將直接從緩沖區中讀取結果。

mysql>show variables like ' key_buffer_size';

mysql>show status like ' key_read%';

查詢緩存碎片率= Qcache_free_blocks/ Qcache_total_blocks* 100%

查詢緩存利用率= (query_cache_size–Qcache_free_memory) / query_cache_size* 100%

查詢緩存命中率= (Qcache_hits–Qcache_inserts) / Qcache_hits* 100%

3)每個連接的緩存參數

① Sort_buffer_size

每個需要進行排序的線程分配該大小的一個緩沖區。增加這值加速ORDER BY或GROUP BY操作。默認數值是2097144(2M),可改為16777208 (16M)。

② Join_buffer_size

聯合查詢操作所能使用的緩沖區大小。

record_buffer_size,read_rnd_buffer_size,sort_buffer_size,join_buffer_size為每個線程獨占,也就是說,如果有100個線程連接,則占用為16M*100。

③ table_open_cache

表高速緩存的大小。每當MySQL訪問一個表時,如果在表緩沖區中還有空間,該表就被打開并放入其中,這樣可以更快地訪問表內容。

mysql> show global status like 'open%tables%';

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

| Variable_name| Value |

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

| Open_tables| 1024 |

| Opened_tables| 1465 |

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

mysql>showvariableslike'table_open_cache';

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

|Variable_name|Value|

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

|table_open_cache|1024|

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

④ tmp_table_size

臨時表大小。通過設置tmp_table_size選項來增加一張臨時表的大小,例如做高級GROUP BY操作生成的臨時表。

mysql>showglobal statuslike' created_tmp%';

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

|Variable_name|Value |

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

|Created_tmp_disk_tables|21197|

| Created_tmp_files| 58|

| Created_tmp_tables| 1771587 |

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

mysql> show variables like 'tmp_table_size';

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

| Variable_name| Value |

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

| tmp_table_size| 16777216 |

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

⑤ thread_cache_size

可以復用的保存在緩沖區中的線程的數量。當客戶端斷開之后,服務器處理此客戶的線程將會緩存起來以響應下一個客戶而不是銷毀(前提是緩存數未達上限)。

mysql>show global status like 'Thread%';

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

|Variable_name|Value|

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

|Threads_cached|31|

|Threads_connected|239|

|Threads_created|2914|

|Threads_running|4|

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

mysql>show variables like 'thread_cache_size';

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

|Variable_name|Value|

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

|thread_cache_size|32|

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

4)配置InnoDB的參數

① Innodb_buffer_pool_size

InnoDB使用該參數指定大小的內存來緩沖數據和索引,其對InnoDB的重要性等于key_buffer_size對MyISAM的重要性。

② Innodb_log_buffer_size

Innodb_log緩存大小,一般為1-8M,默認為1M,對于較大的事務,可以增大緩存大小??稍O置為4M或8M。

5)慢查詢參數:log_slow_queries

4、數據庫表設計

表體量過大:字段過多或者記錄數過多的“大表”,在查詢中會消耗大量資源,且執行效率低;建議根據業務類型拆分大表(分區表)。

使用外鍵:無論是MySQL還是Oracle,都不建議采用外鍵進行表關聯。

缺少主鍵:無論對于主從同步還是查詢性能,主鍵發揮的作用都非常重要;建議所有業務表都添加主鍵。

5、SQL語句

多表關聯:多表關聯容易造成關聯數據過大,影響查詢效率;建議查詢中的關聯表數量不超過2個。

全表掃描:觸發全表掃描容易造成大量IO讀寫,嚴重降低查詢效率;建議在查詢條件中加入帶索引的過濾條件。

根據現網環境優化執行的難易度,在優化順序可以按照:SQL語句->數據庫表設計->數據庫參數配置->數據庫存儲引擎->服務器硬件。

下文我們重點論述上面第四、第五點,通過編寫高效的SQL語句,并以合適的方式創建表和索引,使系統始終保持良好的性能。

表設計建議

以合適的方式建立表,可以提高數據庫運行效率,有效降低歷史數據清理時的維護工作難度。

1、選定存儲引擎

MySQL支持多種存儲引擎,在處理不同類型的應用時,可以通過選擇使用不同的存儲引擎提高應用的效率,或者提供靈活的存儲。MySQL的存儲引擎包括:MyISAM、 InnoDB、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE、CSV、BLACKHOLE、FEDERATED等。下面是幾種常用的存儲引擎的對比和推薦使用方式。

MySQL DBA必讀:萬字歸總表設計與SQL編寫技巧

其中,InnoDB 存儲引擎提供了具有提交、回滾和崩潰恢復能力的事務安全。其設計目的主要面向在線事務處理(OLTP)及應用。但是對比 Myisam的存儲引擎,InnoDB 寫的處理效率差一些并且會占用更多的磁盤空間以保留數據和索引。從MySQL5.5版本開始,InnoDB存儲引擎是默認的存儲引擎。Myisam存儲引擎不支持事務,表鎖設計,支持群文索引,主要面向一些OLAP數據庫應用及Web應用。每個MyISAM在磁盤上存儲成三個文件。文件名都和表名相同,擴展名分別是.frm(存儲表定義)、.MYD (MYData,存儲數據)、.MYI (MYIndex,存儲索引)。數據文件和索引文件可以放置在不同的目錄,平均分布IO,獲得更快的速度。在移動云生產環境中我們建議所有業務表必須是innodb表。

2、表命名規范

1)命名大小寫規范:在 MySQL 中,數據庫對應數據目錄中的目錄。數據庫中的每個表至少對應數據庫目錄中的一個文件(也可能是多個,取決于存儲引擎)。因此,所使用操作系統的大小寫敏感性決定了數據庫名和表名的大小寫敏感性。這說明在大多數 Unix 中數據庫名和表名對大小寫敏感,而在 windows 中對大小寫不敏感。MySQL有配置參數lower_case_table_names,不可動態更改,linux系統默認為 0,即庫表名以實際情況存儲,大小寫敏感。如果是1,以小寫存儲,大小寫不敏感。如果是2,以實際情況存儲,但以小寫比較。MySQL5.6默認為0。若大小寫混合使用,易導致使用及管理混亂,且字段名顯式區分大小寫,但實際使用不區分,即不可以建立兩個名字一樣但大小寫不一樣的字段。因此,建議為了統一規范, 庫名、表名、字段名使用小寫字母,連接統一用下劃線‘_’。

2)命名字符長度規范:庫名、表名、字段名支持最多64個字符,但為了統一規范、易于辨識以及減少傳輸量,禁止超過32個字符。

3)避免使用MySQL保留字:當庫名、表名、字段名等屬性含有保留字時,SQL語句必須用反引號引用屬性名稱,這將使得SQL語句書寫、SHELL腳本中變量的轉義等變得非常復雜。

3、建立常規表

MySQL常規表對應到文件系統上單個數據文件。在MySQL5.6中建表時,不指定任何參數,默認會建立存儲引擎為innodb的常規表。常規表使用與大部分應用場景。默認情況下,由于部分操作系統對文件大小的限制,表大小限制為2G。

4、建立分區表

MySQL從5.1版本開始支持分區表,從5.6開始MySQL表分區以單個數據文件形式存儲于文件系統中,根據所使用的不同分區規則可以分成幾大類型:

RANGE 分區:基于屬于一個給定連續區間的列值,把多行分配給分區。比較常用如按照時間字段劃分分區,2019年1月的數據放到201901分區,2019年2月的數據放到201902分區以此類推。范圍分區方式適用于應用中頻繁對分區鍵值進行范圍查詢的場合。另外針對部監控表隨時間不斷累積數據,大量的歷史數據積壓,一方面會降低應用程序的效率,另一方面亦浪費大量的存儲空間。因此需要對歷史表進行定期清理,以基本保持當前總數據量?;谶@個原則,建議對所有歷史表按清理時間鍵值進行范圍分區,時間范圍建議按月進行。表分區的命名采用以下的規范:<表名>_pYYYYMMDD,其中YYYY為分區數據的年份,MM為分區數據的月份,DD為分區數據的日期。

LIST 分區:類似于按RANGE分區,區別在于LIST分區是基于列值匹配一個離散值集合中的某個值來進行選擇。列值分區與范圍分區有類似之處,該分區與范圍分區類似的是需要指定列的值,但是其分區值必須明確指定。

HASH分區:基于用戶定義的表達式的返回值來進行選擇的分區,該表達式使用將要插入到表中的這些行的列值進行計算。這個函數可以包含MySQL中有效的、產生非負整數值的任何表達式。此種分區方式最適用于查詢條件中,對分區字段進行單值查詢的情況(如,col=1)。但是hash分區,并不適用于對索引字段使用范圍查詢,如對字段使用大于>,小于<,操作的查詢語句中。

KEY分區:類似于按HASH分區,區別在于KEY分區只支持計算一列或多列,且MySQL服務器提供其自身的哈希函數。必須有一列或多列包含整數值。

復合分區:基于RANGE/LIST 類型的分區表中每個分區的再次分割。子分區可以是 HASH/KEY 等類型。

5、表字段規范

  • 盡量使用TINYINT、SMALLINT、MEDIUM_INT作為整數類型而非INT,如果非負則加上UNSIGNED;

  • VARCHAR的長度只分配真正需要的空間;

  • 使用枚舉或整數代替字符串類型;

  • 盡量使用TIMESTAMP而非DATETIME;

  • 單表不要有太多字段,建議在20以內;

  • 避免使用字段,很難查詢優化且占用額外索引空間;

  • 用整型來存IP。

6、統一字符集

系統、服務端、客戶端、庫、表、開發程序端需統一字符集,通常中英文環境用utf8。

表使用建議

根據MySQL的表建立規范,以及在實際維護中的表使用經驗相結合,對表使用作出如下的建議。

1、選擇合適的數據類型

InnoDB 存儲引擎和數據列。建議使用 varchar類型:對于InnoDB數據表,內部的行存儲格式沒有區分固定長度和可變長度列(所有數據行都使用指向數據列值的頭指針),因此在本質上,使用固定長度的 char列不一定比使用可變長度varchar列簡單。因而,主要的性能因素是數據行使用的存儲總量。由于CHAR平均占用的空間多于varchar,因此使用varchar來最小化需要處理的數據行的存儲總量和磁盤I/O是比較好的。

2、text和blob

在使用text和blob字段類型時要注意以下幾點,以便更好的發揮數據庫的性能:

1)text和blob值在執行了大量的刪除或更新操作的時候容易影響效率。

刪除該類型值會在數據表中留下很大的"空洞",以后填入這些"空洞"的記錄可能長度不同,為了提高性能,建議定期使用 OPTIMIZE TABLE 功能對這類表進行碎片整理。

2)使用合成的(synthetic)索引。

合成的索引列在某些時候是有用的。一種辦法是根據其它的列的內容建立一個散列值,并把這個值存儲在單獨的數據列中。之后可以通過檢索散列值找到數據。但是,這種索引只能用于精確匹配的查詢(散列值對于類似<或>=等范圍搜索操作符 是沒有用處的)??梢允褂肕D5函數生成散列值,也可以使用SHA1或CRC32,或者使用自己的應用程序邏輯來計算散列值。需注意數值型散列值可以很高效率地存儲。同樣,如果散列算法生成的字符串帶有尾部空格,此時不要把它們存儲在char與varchar列中,它們會受到尾部空格去除的影響。合成的散列索引對于那些text和blob數據列特別有用。用散列標識符值查找的速度比搜索blob列本身的速度快很多。

3)把text或blob列分離到單獨的表中。

通過把這些數據列移動到單獨的數據表中,可以讓你把原數據表中的數據列轉換為固定長度的數據行格式。這會減少主表中的碎片,使你得到固定長度數據行的性能優勢。此時能避免在主數據表上運行 SELECT *查詢的時候通過網絡傳輸大量的text或blob值。

3、拆分大字段、訪問頻率低的字段

將大字段、訪問頻率低的字段拆分到單獨的表中存儲,分離冷熱數據。有利于有效利用緩存,防止讀入無用的冷數據,較少磁盤IO,同時保證熱數據常駐內存提高緩存命中率。

4、數據文件磁盤分離

MySQL表以數據文件形式存儲于文件系統,針對不同的表的讀寫會打開不同的數據文件。建議對不同的熱表進行存儲的磁盤分離。通過將不同的熱表建立在不同的lun上,分散I/O,這樣就能進一步減少I/O消耗的瓶頸。

索引建立規范

建立合適的索引,是提高數據庫運行效率的一個很好的工具,這種效果是立竿見影的,但這里也不并不是說表上的索引越多越好,過之而不及。在數據庫設計過程中,需要為表選擇一些合適的索引。在數據庫中索引的維護代價是表的3倍,寧缺勿濫,這是建立索引時的一個遵循標準。

索引使用規范

根據MySQL的索引使用經驗相結合,對索引使用做出如下的建議。

1、根據表數據量評估索引

詳細評估和分析建立索引所在表的實際數據量,數據量達到GB級別、記錄數達到百萬級別、訪問頻繁的表,需要建立合適的索引。相反,在數據量較少且訪問頻率不高的情況下,如只有一百行記錄以下的表不需要建立索引。因為在數據量少的情況下,使用全表掃描效果比走索引更好。

2、選擇適當的索引字段

索引字段的選擇需要結合業務需求,評估出應用中作為查詢條件出現比較頻繁的字段,在此字段上建立單獨或者復合索引。選擇建立索引的字段,應該遵循以下的原則:

1)高選擇性,選擇性是指通過索引字段查詢返回結果集占表總數據量的百分比,結果集占表總數據量的百分比越小選擇性越高,反之越低。選擇性越高,通過索引查詢返回的結果集越少,索引更為高效。在OLTP應用系統中,選擇性應高于1,也就是結果集占表總數據量的百分比應<1%。

2)空值少,避免在空值很多的字段上建立B-tree索引,大量空值會降低索引效率,索引字段中的空值占總數據量的百分比應少于10%。

3)數據分布均勻,索引字段中,個別數據值占總數據量的百分率明顯比其它數據值占總數據量的百分率高,表明該字段數據值分布不均,容易引起數據庫選擇錯誤索引,生成錯誤的查詢執行計劃。應該避免在數據值分布不均的字段上建立索引。

3、避免過度索引

每個額外的索引都要占用額外的磁盤空間,并降低寫操作的性能,這一點我們前面已經介紹過。在修改表的內容時,索引必須進行更新,有時可能需要重構,因此,索引越多,所花的時間越長。如果有一個索引很少利用或從不使用,那么會不必要地減緩表的修改速度。此外,MySQL在生成一個執行計劃時,要考慮各個索引,這也要費時間。創建多余的索引給查詢優化帶來了更多的工作。索引太多,也可能會使 MySQL 選擇不到所要使用的最好索引。只保持所需的索引有利于查詢優化。如果想給已索引的表增加索引,應該考慮所要增加的索引是否是現有多列索引的最左索引。如果是,則就不要費力去增加這個索引了,因為已經有了。

4、使用唯一索引

考慮某列中值的分布。對于唯一值的列,索引的效果最好,而具有多個重復值的列,其索引效果最差。例如,存放年齡的列具有不同值,很容易區分各行。而用來記錄性別的列,只含有“ 男”和“女”,則對此列進行索引沒有多大用處(不管搜索哪個值,都會得出大約一半的行)。

5、使用短索引

如果對串列進行索引,應該指定一個前綴長度,只要有可能就應該這樣做。例如,如果有一個 CHAR(200) 列,如果在前10個或20個字符內,多數值是惟一的,那么就不要對整個列進行索引。對前10個或20個字符進行索引能夠節省大量索引空間,也可能會使查詢更快。較小的索引涉及的磁盤 I/O 較少,較短的值比較起來更快。更為重要的是,對于較短的鍵值,索引高速緩存中的塊能容納更多的鍵值,因此,MySQL也可以在內存中容納更多的值。這增加了找到行而不用讀取索引中較多塊的可能性。

6、利用符合索引前置列

在創建一個 n 列的索引時,實際是創建了 MySQL 可利用的 n 個索引。多列索引可起幾個索引的作用,因為可利用索引中最左邊的列集來匹配行。這樣的列集稱為最左前綴。(這與索引一個列的前綴不同,索引一個列的前綴是利用該的前 n 個字符作為索引值。) 例如:(a,b,c)、(a,b),后者為冗余索引。當SQL的where條件包含a,b時,能正確的走前一索引,后者作為冗余沒有建立的必要。關鍵在于找到適合的前置列,可以避免建冗余的索引。

7、考慮在列上進行的比較類型

索引可用于“ <”、“ < = ”、“ = ”、“ > =”、“ >”和 BETWEEN 運算。在模式具有一個直接量前綴時,索引也用于 LIKE 運算。如果只將某個列用于其他類型的運算時(如 STRCMP( )),對其進行索引沒有價值。

高效SQL編寫規范建議

1、大批量插入數據

如果同時執行大量的插入,建議使用多個值的INSERT語句(方法二)。這比使用分開INSERT語句快(方法一),一般情況下批量插入效率有幾倍的差別。

方法一:

insert into tablename values(1,2);

insert into tablename values(1,3);

insert into tablename values(1,4);

方法二:

Insert into tablename values(1,2),(1,3),(1,4);

選擇后一種方法的原因有二。

  • 減少SQL語句解析的操作, MySQL沒有類似Oracle的share pool,采用方法二,只需要解析一次就能進行數據的插入操作;

  • SQL語句較短,可以減少網絡傳輸的IO。

此外,還有以下建議提高插入性能:

  • 通過使用 INSERT DELAYED 語句得到更高的速度。Delayed 的含義是讓 insert 語句馬上執行,其實數據都被放在內存的隊列中,并沒有真正寫入磁盤;

  • 這比每條語句分別插入要快的多,但需要注意,DELAYED關鍵字只用于MyISAM,MEMORY這類只支持表鎖的存儲引擎;

  • 將索引文件和數據文件分在不同的磁盤上存放(利用建表中的選項)。

2、查詢優先還是更新(insert、update、delete)優先

MySQL 還允許改變語句調度的優先級,它可以使來自多個客戶端的查詢更好地協作,這樣單個客戶端就不會由于鎖定而等待很長時間。改變優先級還可以確保特定類型的查詢被處理得更快。我們首先應該確定應用的類型,判斷應用是以查詢為主還是以更新為主的,是確保查詢效率還是確保更新的效率,決定是查詢優先還是更新優先。下面我們提到的改變調度策略的方法主要是針對只存在表鎖的存儲引擎,比如 MyISAM 、MEMROY、MERGE,對于Innodb 存儲引擎,語句的執行是由獲得行鎖的順序決定的。MySQL 的默認的調度策略可用總結如下:

1)寫入操作優先于讀取操作。

2)對某張數據表的寫入操作某一時刻只能發生一次,寫入請求按照它們到達的次序來處理。

3)對某張數據表的多個讀取操作可以同時地進行。MySQL 提供了幾個語句調節符,允許你修改它的調度策略:

  • LOW_PRIORITY關鍵字應用于DELETE、INSERT、LOAD DATA、REPLACE和UPDATE;

  • HIGH_PRIORITY關鍵字應用于SELECT和INSERT語句;

  • DELAYED關鍵字應用于INSERT和REPLACE語句。

如果寫入操作是一個 LOW_PRIORITY(低優先級)請求,那么系統就不會認為它的優先級高于讀取操作。在這種情況下,如果寫入者在等待的時候,第二個讀取者到達了,那么就允許第二個讀取者插到寫入者之前。只有在沒有其它的讀取者的時候,才允許寫入者開始操作。這種調度修改可能存在 LOW_PRIORITY寫入操作永遠被阻塞的情況。SELECT 查詢的HIGH_PRIORITY(高優先級)關鍵字也類似。它允許SELECT 插入正在等待的寫入操作之前,即使在正常情況下寫入操作的優先級更高。另外一種影響是,高優先級的 SELECT 在正常的 SELECT 語句之前執行,因為這些語句會被寫入操作阻塞。如果希望所有支持LOW_PRIORITY 選項的語句都默認地按照低優先級來處理,那么 請使用--low-priority-updates 選項來啟動服務器。通過使用 INSERTHIGH_PRIORITY 來把 INSERT 語句提高到正常的寫入優先級,可以消除該選項對單個INSERT語句的影響。

3、避免出現select *

select * 操作在任何類型數據庫中都不是一個好的SQL開發習慣。使用select * 取出全部列,會讓優化器無法完成索引覆蓋掃描這類優化,會影響優化器對執行計劃的選擇,也會增加網絡帶寬消耗,更會帶來額外的I/O,內存和CPU消耗。建議評估業務實際需要的列數,指定列名以取代select *。

  • 規范:Select col1,col2,col3… from t1;

  • 不規范:Select * from t1。

4、避免使用insert..selec..語句

當使用insert...select...進行記錄的插入時,如果select的表是innodb類型的,不論insert的表是什么類型的表,都會對select的表的紀錄進行鎖定。對于那些從Oracle遷移過來的應用,需要特別的注意,因為Oracle并不存在類似的問題,所以在Oracle的應用中insert...select...操作非常常見。例如:有時候會對比較多的紀錄進行統計分析,然后將統計的中間結果插入到另外一個表,這樣的操作因為進行的非常少,所以可能并沒有設置相應的索引。

如果遷移到MySQL數據庫后不進行相應的調整,那么在進行這個操作期間,對需要select的表實際上是進行的全表掃描導致的所有記錄的鎖定,將會對應用的其他操作造成非常嚴重的影響。

究其主要原因,是因為MySQL在實現復制的機制時和Oracle是不同的,如果不進行select表的鎖定,則可能造成從數據庫在恢復期間插入結果集的不同,造成主從數據的不一致。如果不采用主從復制,關閉binlog并不能避免對select紀錄的鎖定。如果使用這個binlog進行從數據庫的恢復,或者進行主數據庫的災難恢復,都將可能和主數據庫的執行效果不同。

因此,我們并不推薦通過設置這個參數來避免insert...select...導致的鎖,如果需要進行可能會掃描大量數據的insert...select操作,我們推薦使用select...into outfile和load data infile的組合來實現,這樣是不會對紀錄進行鎖定的。

例子:

INSERT INTO SMAP_HISTORY.SMAP2_SESSION (SESSION_ID,SESSION_TICKET_ID) SELECT S.SESSION_ID,S.SESSION_TICKET_ID FROM SMAP.SMAP2_SESSION S WHERE SESSION_SID = #sessionId#;

以上語句會對表SMAP2_SESSION施加表鎖,而由于業務上該表存在大量insert語句,業務壓力大的時候極易造成嚴重的阻塞。

5、適當使用commit

適當使用commit可以釋放事務占用的資源而減少消耗,commit后能釋放的資源如下:

  • 事務占用的undo數據塊;

  • 事務在redo log中記錄的數據塊;

  • 釋放事務施加的,減少鎖爭用影響性能。特別是在需要使用delete刪除大量數據的時候,必須分解刪除量并定期commit。

6、減少表的鎖沖突

對 Innodb 類型的表:

1)首先要確認,在對表獲取行鎖的時候,要盡量的使用索引檢索紀錄,如果沒有使用索引訪問,那么即便你只是要更新其中的一行紀錄,也是全表鎖定的。要確保 sql 是使用索引來訪問紀錄的,必要的時候,請使用 explain 檢查 sql 的執行計劃,判斷是否按照預期使用了索引。

2)由于 MySQL 的行鎖是針對索引加的鎖,不是針對紀錄加的鎖,所以雖然是訪問不同行的紀錄,但是如果是相同的索引鍵,是會被加鎖的。應用設計的時候也要注意,這里和 Oracle 有比較大的不同。

3)當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,當表有主鍵或者唯一索引的時候,不是必須使用主鍵或者唯一索引鎖定紀錄,其他普通索引同樣可以用來檢索紀錄,并只鎖定符合條件的行。

4)如果要使用鎖定讀,(SELECT ... FOR UPDATE 或 ... LOCK IN SHARE MODE),嘗試用更低的隔離級別,比如 READ COMMITTED。

7、使用SQL_BUFFER_RESULT減少鎖定時間

將強制 MySQL 生成一個臨時結果集。只要所有臨時結果集生成后,所有表上的鎖定均被釋放。這能在遇到表鎖定問題時或要花很長時間將結果傳給客戶端時有所幫助。當處理一個會讓客戶端耗費點時間才能處理的大結果集時,可以考慮使用SQL_BUFFER_RESULT 提示字。這樣可以告訴MySQL將結果集保存在一個臨時表中,這樣可以盡早的釋放各種鎖。需注意,該參數不能用于子查詢中以及union之后 語法:SELECT SQL_BUFFER_RESULT …

8、正確使用hint優化語句

MySQL中可以使用hint指定優化器在執行時選擇或忽略特定的索引。一般而言,處于版本變更帶來的表結構索引變化,更建議避免使用hint,而是通過Analyze table多收集統計信息。但在特定場合下,指定hint可以排除其他索引干擾而指定更優的執行計劃。

1)USE INDEX 在你查詢語句中表名的后面,添加 USE INDEX 來提供希望 MySQL 去參考的索引列表,就可以讓 MySQL 不再考慮其他可用的索引。例子: SELECT col1 FROM table USE INDEX (mod_time, name)...

2)IGNORE INDEX 如果只是單純的想讓 MySQL 忽略一個或者多個索引,可以使用 IGNORE INDEX 作為 Hint。例子: SELECT col1 FROM table IGNORE INDEX (priority) ...

3)FORCE INDEX 為強制 MySQL 使用一個特定的索引,可在查詢中使用FORCE INDEX 作為Hint。例子: SELECT col1 FROM table FORCE INDEX (mod_time) ...

9、優化group by語句

默認情況下,MySQL 排序所有 “GROUP BY col1,col2,....;” 查詢的方法如同在查詢中指定 “ORDER BY col1,col2,...;” 如果顯式包括一個包含相同的列的 ORDER BY子句,MySQL 可以毫不減速地對它進行優化,盡管仍然進行排序。

如果查詢包括 GROUP BY 但你想要避免排序結果的消耗,你可以指定 ORDER BY 禁止排序。例如:

SELECT a, COUNT(1) FROM table GROUP BY a ORDER BY ;

10、優化order by語句

在某些情況中,MySQL 可以使用一個索引來滿足 ORDER BY 子句,而不需要額外的排序。where 條件和 order by 使用相同的索引,并且 order by 的順序和索引順序相同 ,并且 order by 的字段都是升序或者都是降序。

例如:下列 SQL 可以使用索引。

SELECT col1 FROM t1 ORDER BY key_part1,key_part2,... ;

SELECT col1 FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC, key_part2 DESC; SELECT col1 FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;

以上復合索引包含字段key_part1,key_part2...

但是以下情況不使用索引:

SELECT col1 FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;

以上由于order by 的字段混合 ASC和 DESC 。

SELECT col1 FROM t1 WHERE key2=constant ORDER BY key1;

以上用于查詢行的關鍵字與 ORDER BY 中所使用的不相同。

SELECT col1 FROM t1 ORDER BY key1, key2;

對不同的索引關鍵字使用 ORDER BY:

11、優化join語句

MySQL中可以通過子查詢來使用 SELECT 語句來創建一個單列的查詢結果,然后把這個結果作為過濾條件用在另一個查詢中。使用子查詢可以一次性的完成很多邏輯上需要多個步驟才能完成的 SQL 操作,同時也可以避免事務或者表鎖死,并且寫起來也很容易。但是,有些情況下,子查詢可以被更有效率的連接(JOIN)..替代。

例子:假設要將所有沒有訂單記錄的用戶取出來,可以用下面這個查詢完成:

SELECT col1 FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID

FROM salesinfo )

如果使用連接(JOIN).. 來完成這個查詢工作,速度將會有所提升。尤其是當 salesinfo表中對 CustomerID 建有索引的話,性能將會更好,查詢如下:

SELECT col1 FROM customerinfo

LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo.CustomerID

WHERE salesinfo.CustomerID IS

連接(JOIN).. 之所以更有效率一些,是因為 MySQL 不需要在內存中創建臨時表來完成這個邏輯上的需要兩個步驟的查詢工作。

12、優化or條件

對于 or 子句,如果要利用索引,則or 之間的每個條件列都必須用到索引;如果沒有索引,則應該考慮增加索引。

13、優化union查詢

MySQL通過創建并填充臨時表的方式來執行union查詢。除非確實要消除重復的行,否則建議使用union all。原因在于如果沒有all這個關鍵詞,MySQL會給臨時表加上distinct選項,這會導致對整個臨時表的數據做唯一性校驗,這樣做的消耗相當高。

高效:

SELECT COL1, COL2, COL3

FROM TABLE

WHERE COL1 = 10

UNION ALL

SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST';

低效:

SELECT COL1, COL2, COL3

FROM TABLE WHERE COL1 = 10

UNION

SELECT COL1, COL2, COL3 FROM TABLE WHERE COL3= 'TEST';

14、拆分復雜SQL為多個小SQL,避免大事務

  • 簡單的SQL容易使用到MySQL的QUERY CACHE;

  • 減少鎖表時間特別是使用MyISAM存儲引擎的表;

  • 可以使用多核CPU。

15、使用truncate代替delete

當刪除全表中記錄時,使用delete語句的操作會被記錄到undo塊中,刪除記錄也記錄binlog,當確認需要刪除全表時,會產生很大量的binlog并占用大量的undo數據塊,此時既沒有很好的效率也占用了大量的資源。使用truncate替代,不會記錄可恢復的信息,數據不能被恢復。也因此使用truncate操作有其極少的資源占用與極快的時間。另外,使用truncate可以回收表的水位。

16、使用合理的分頁方式以提高分頁效率

使用合理的分頁方式以提高分頁效率 針對展現等分頁需求,合適的分頁方式能夠提高分頁的效率。

案例1:

select * from t

where thread_id = 10000

and deleted = 0

order by gmt_create asc limit 0, 15;

上述例子通過一次性根據過濾條件取出所有字段進行排序返回。數據訪問開銷=索引IO+索引全部記錄結果對應的表數據IO。因此,該種寫法越翻到后面執行效率越差,時間越長,尤其表數據量很大的時候。

適用場景:當中間結果集很?。?0000行以下)或者查詢條件復雜(指涉及多個不同查詢字段或者多表連接)時適用。

案例2:

select t.* from (

select id from t

where thread_id = 10000 and deleted = 0 order by gmt_create asc limit 0, 15) a, t

where a.id = t.id;

上述例子必須滿足t表主鍵是id列,且有覆蓋索引secondary key:(thread_id, deleted, gmt_create)。通過先根據過濾條件利用覆蓋索引取出主鍵id進行排序,再進行join操作取出其他字段。數據訪問開銷=索引IO+索引分頁后結果(例子中是15行)對應的表數據IO。因此,該寫法每次翻頁消耗的資源和時間都基本相同,就像翻第一頁一樣。

適用場景:當查詢和排序字段(即where子句和order by子句涉及的字段)有對應覆蓋索引時,且中間結果集很大的情況時適用。

17、避免不走索引的各種場景

在下面的SQL語句中的WHERE子句不使用索引:

1)條件中有or,且or左右列并非全部由索引 Select col1 from table where key1=1 or no_key=2

2)like查詢以%開頭

3)where條件僅包含復合索引非前置列

Select col1 from table where key_part2=1 and key_part3=2

索引包含key_part1,key_part2,key_part3三列,但SQL語句沒有包含索引前置列。

4)隱式類型轉換造成不使用索引

Select col1 from table where key_varchar=123;

上述語句由于索引對列類型為varchar,但給定的值為數值,涉及隱式類型轉換,造成不能正確走索引。

5)避免對索引字段進行計算

避免對索引字段進行任何計算操作,對索引字段的計劃操作會讓索引的作用失效,令數據庫選擇其他的較為低效率的訪問路徑。

6)避免對索引字段進行是否值判斷

避免使用索引列值是否可為空的索引,如果索引列值可以是空值,在SQL語句中那些要返回值的操作,將不會用到索引。

7)避免對索引字段不等于符號

使用索引列作為條件進行查詢時,需要避免使用<>或者!=等判斷條件。如確實業務需要,使用到不等于符號,需要在重新評估索引建立,避免在此字段上建立索引,改由查詢條件中其他索引字段代替。

18、避免重復查詢更新的數據

針對業務中經常出現的更新行同時又希望獲得改行信息的需求,MySQL并不支持PostgreSQL那樣的UPDATE RETURNING語法,在MySQL中可以通過變量實現。

例如,更新一行記錄的時間戳,同時希望查詢當前記錄中存放的時間戳是什么,簡單方法實現:

Update t1 set time=now where col1=1;

Select time from t1 where id =1;

使用變量,可以重寫為以下方式:

Update t1 set time=now where col1=1 and @now: = now ;

Select @now;

前后二者都需要兩次網絡來回,但使用變量避免了再次訪問數據表,特別是當t1表數據量較大時,后者比前者快很多。

19、避免出現不確定結果的函數

特定針對主從復制這類業務場景。由于原理上從庫復制的是主庫執行的語句,使用如now、rand、sysdate、current_user等不確定結果的函數很容易導致主庫與從庫相應的數據不一致。另外不確定值的函數,產生的SQL語句無法利用QUERY CACHE。

使用EXPLAIN分析SQL性能

1、執行計劃

執行計劃是一條查詢語句在數據庫中的執行過程或訪問路徑的描述。

2、怎樣查看MySQL執行計劃

在需要查看執行計劃的SQL前面添加explain并執行,即可獲取。

MySQL DBA必讀:萬字歸總表設計與SQL編寫技巧

3、讀EXPLAIN中的信息

1)table

顯示這一行的數據是關于哪張表的。

2)type

這是重要的列,顯示連接使用了何種類型。

從最好到最差的連接類型為const、eq_reg、ref、range、index和ALL。

3)possible_keys

顯示可能應用在這張表中的索引。如果為空,沒有可能的索引。

4)key

實際使用的索引。如果為,則沒有使用索引。很少的情況下,MYSQL會選擇優化不足的索引。

5)key_len

使用的索引的長度。在不損失精確性的情況下,長度越短越好。

6)ref

顯示索引的哪一列被使用了,如果可能的話,是一個常數。

7)rows

MYSQL認為必須檢查的用來返回請求數據的行數。

8)Extra

關于MYSQL如何解析查詢的額外信息。效率最低的是Using temporary和Using filesort,意味著MYSQL根本不能使用索引,所以檢索會很慢。

4、使用EXPLAIN中分析SQL性能

例1:

MySQL DBA必讀:萬字歸總表設計與SQL編寫技巧

看左邊sql語句的執行計劃,看得出沒有走任何索引,屬于全表掃描,導致執行時間比較長。通過給業務表添加適當的索引,這條語句的執行時間由9秒變為60毫秒,掃描行數由257910行變成1行,效率提升明顯。

例2:

MySQL DBA必讀:萬字歸總表設計與SQL編寫技巧

原語句執行的時候是全表掃面,分頁查詢效率低,改為利用主鍵來獲取后,執行時間由33秒變為1.2秒,效率提升明顯。

慢日志查詢管理平臺

為了提高SQL分析和優化的效率,我們開始利用自動化的方式來實現慢SQL的獲取和智能分析,平臺頁面如下:

MySQL DBA必讀:萬字歸總表設計與SQL編寫技巧

這個是我們現在在做的,慢日志查詢和智能分析平臺,可以很方便的獲取慢查詢語句,并通過預先設置的優化規則,自動得到一些優化建議。

MySQL DBA必讀:萬字歸總表設計與SQL編寫技巧

上述功能并不是完全是自主開發的,而是借助了開源工具yearningSQL并做了一些擴展,也就是加入我們自定義的一些規則。然后,平臺對要執行的SQL做分析。通過觸碰事先定義好的規則來判斷這個SQL是否可以通過審核,無法通過自動審核的SQL再由人工來處理。

總結

本文從MySQL的邏輯原理和影響MySQL數據庫性能的常見因素入手,著重討論了如何以合適的方式創建表和索引以及編寫高效的SQL語句,并通過EXPLAIN對SQL的性能進行分析,最后給出了初步的自動化解決方案,希望能夠對大家有所幫助。

分享到:
標簽:MySQL DBA
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定