亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 數據庫 > MySQL > 正文

MySQL 的 20+ 條最佳實踐

2024-07-24 13:11:31
字體:
來源:轉載
供稿:網友

數據庫操作是當今 Web 應用程序中的主要瓶頸。 不僅是 DBA(數據庫管理員)需要為各種性能問題操心,程序員為做出準確的結構化表,優化查詢性能和編寫更優代碼,也要費盡心思。 在本文中,我列出了一些針對程序員的 MySQL 優化技術。
在我們開始學習之前,我補充一點:你可以在 Envato Market 上找到大量的 MySQL 腳本和實用程序。

MySQL,最佳實踐

1.優化查詢的查詢緩存

大部分MySQL服務器都有查詢緩存功能。這是提高性能的最有效的方法之一,這是由數據庫引擎私下處理的。當同一個查詢被多次執行,結果會直接從緩存里提取,這樣速度就很快。

主要的問題是,這對程序員來說太簡單了,不容易看到,我們很多人都容易忽略。我們實際上是可以組織查詢緩存執行任務的。

// query cache does NOT work$r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");// query cache works!$today = date("Y-m-d");$r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");

查詢緩存在第一行不執行的原因在于CURDTE()功能的使用。這適用于所有的非確定性功能,就像NOW()和RAND()等等。。。因為功能返回的結果是可變的。MySQL決定禁用查詢器的查詢緩存。我們所需要做的是通過添加一額外一行PHP,在查詢前阻止它發生。

2. EXPLAIN你的選擇查詢

使用EXPLAIN關鍵詞可以幫助了解MySQL是怎樣運行你的查詢的。這有助于發現瓶頸和查詢或表結構的其它問題。

EXPLAIN的查詢結果會展示哪一個索引被使用過,表示怎樣掃描和儲存的,等等。。。

選擇一個SELECT查詢(一個有連接的復雜查詢會更好),在它的前面添加關鍵詞EXPLAIN,這樣就可以直接使用數據庫了。結果會以一個漂亮的表來展示。例如,就好比我執行連接時忘了添加一欄的索引:

MySQL,最佳實踐

現在它只會從表2里面掃描9和16行,而非掃描7883行。經驗法則是乘以所有“行”那一欄的數字,你的查詢性能會跟結果數字成比例的。

3. 獲取唯一行時使用LIMIT 1

有時當你查表時,你已經知道你正在查找的結果只有一行。你可能正在獲取唯一記錄,或者你可能只是查詢是否存在滿足你的WHERE子句條件的記錄。

在這種情況下,將LIMIT 1添加到查詢條件中可以提高性能。這樣,數據庫引擎將在找到剛剛第一個記錄之后停止掃描記錄,而不是遍歷整個表或索引。

// do I have any users from Alabama?// what NOT to do:$r = mysql_query("SELECT * FROM user WHERE state = 'Alabama'");if (mysql_num_rows($r) > 0) {  // ...}// much better:$r = mysql_query("SELECT 1 FROM user WHERE state = 'Alabama' LIMIT 1");if (mysql_num_rows($r) > 0) {  // ...}

4. 索引搜索字段

索引不僅僅是為了主鍵或唯一鍵。如果你會在你的表中按照任何列搜索,你就都應該索引它們。

MySQL,最佳實踐

正如你所看到的,這個規則也適用于如 "last_name LIKE 'a%'"的部分字符串搜索。當從字符串的開頭搜索時,MySQL就可以使用那一列的索引。

你也應該明白什么樣搜索可以不使用有規律的索引。例如,當搜索一個單詞時(例如,"WHERE post_content LIKE '%apple%'"),你將不會看到普通索引的好處。你最好使用 mysql 全文搜索或者構建你自己的索引解決方案。

5. 索引并對連接使用同樣的字段類型

如果你的應用程序包含許多連接查詢, 你需要確保連接的字段在兩張表上都建立了索引。 這會影響MySQL如何內部優化連接操作。

此外,被連接的字段,需要使用同樣類型。例如, 如果你使用一個DECIMAL字段, 連接另一張表的INT字段, MySQL將無法使用至少一個索引。 即使字符編碼也需要使用相同的字符類型。

// looking for companies in my state$r = mysql_query("SELECT company_name FROM users  LEFT JOIN companies ON (users.state = companies.state)  WHERE users.id = $user_id"); // both state columns should be indexed// and they both should be the same type and character encoding// or MySQL might do full table scans

6. 不要ORDER BY RAND()

起初這是一個聽起來挺酷的技巧, 讓許多菜鳥程序員陷入了這個陷阱。但你可能不知道,一旦你開始在查詢中使用它,你創建了非??膳碌牟樵兤款i。

如果你真的需要對結果隨機排序, 這有一個更好的方法。補充一些額外代碼,你將可以防止當數據成指數級增長時造成的瓶頸。關鍵問題是,MySQL必須在排序之前對表中的每一行執行RAND()操作(這需要處理能力),并且僅僅給出一行。

// what NOT to do:$r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");  // much better: $r = mysql_query("SELECT count(*) FROM user");$d = mysql_fetch_row($r);$rand = mt_rand(0,$d[0] - 1); $r = mysql_query("SELECT username FROM user LIMIT $rand, 1");

所以挑選一個小于結果數的隨機數,并將其用作LIMIT子句中的偏移量。

7. 避免使用SELECT *

從數據表中讀取的數據越多,查詢操作速度就越慢。它增加了磁盤操作所需的時間。此外,當數據庫服務器與Web服務器分開時,由于必須在服務器之間傳輸數據,將會有更長的網絡延遲。

這是一個好習慣:當你使用SELECT語句時總是指定你需要的列。

// not preferred$r = mysql_query("SELECT * FROM user WHERE user_id = 1");$d = mysql_fetch_assoc($r);echo "Welcome {$d['username']}"; // better:$r = mysql_query("SELECT username FROM user WHERE user_id = 1");$d = mysql_fetch_assoc($r);echo "Welcome {$d['username']}"; // the differences are more significant with bigger result sets

8. 幾乎總是有一個id字段

在每個以id列為PRIMARY KEY的數據表中,優先選擇AUTO_INCREMENT或者INT。 也可以優選使用UNSIGNED,因為該值不能為負的。

即使你擁有一個具有唯一用戶名字段的用戶表,也不要將其作為主鍵。 VARCHAR字段作為主鍵(檢索)速度較慢。通過內部ID引用所有的用戶數據,你的代碼中將更加結構化。

有些后臺操作是由MySQL引擎本身完成的,它在內部使用主鍵字段。當數據庫設置越復雜(集群,分區等...),這就變得更加重要了。

這個規則的一個可能的例外是“關聯表”,用于兩個表之間的多對多類型的關聯。例如,“posts_tags”表中包含兩列:post_id,tag_id,用于保存表名為“post”和“tags”的兩個表之間的關系。這些表可以具有包含兩個id字段的PRIMARY鍵。

9. 相比VARCHAR優先使用ENUM

ENUM枚舉類型是非??焖俸途o湊的。在內部它們像TINYINT一樣存儲,但它們可以包含和顯示字符串值。這使他們成為某些領域的完美候選。

如果有一個字段只包含幾種不同的值,請使用ENUM而不是VARCHAR。例如,它可以是名為“status”的列,并且只包含諸如“active”,“inactive”,“pending”,“expired”等的值...

關于如何重構你的數據表,甚至有一種方法是可以從MySQL本身得到“建議”。 當你有一個VARCHAR字段,它實際上建議你將該列類型更改為ENUM。這通過調用PROCEDURE ANALYZE()來完成。

10. 使用PROCEDURE ANALYSE()獲取建議

PROCEDURE ANALYSE() 將使用MySQL分析列結構和表中的實際數據,為你提供一些建議。它只有在數據表中有實際數據時才有用,因為這在分析決策時很重要。

例如,如果你創建了一個INT類型的主鍵,但沒有太多行,MySQL則可能建議您改用MEDIUMINT?;蛘呷绻闶褂肰ARCHAR字段,如果表里只有很少的取值,你可能會得到一個建議是將其轉換為ENUM。

你也可以在其中一個表視圖中單擊phpmyadmin中的“建議表結構”鏈接來執行此操作。

MySQL,最佳實踐

請記住,這些只是建議。 如果你的數據表變得越來越大,他們甚至可能不是正確的建議。至于如何修改最終是你來決定。

11. 如果可以的話使用NOT NULL

除非你有非常重要的理由使用NULL值,否則你應該設置你的列為NOT NULL。

首先,問一下你自己在空字符串值和NULL值之間(對應INT字段:0 vs. NULL)是否有任何的不同.如果沒有理由一起使用這兩個,那么你就不需要一個NULL字段(你知道在Oracle中NULL和空字符串是一樣的嗎?)。

NULL列需要額外的空間,他們增加了你的比較語句的復雜度。如果可以的話盡量避免它們。當然,我理解一些人,他們也許有非常重要的理由使用NULL值,這不總是一件壞事。

摘自MySQL 文檔:

"NULL列在行記錄它們的值是否為NULL時需要額外的空間。例如MyISAM 表,每一個NULL列擁有額外的一個比特,聚集在最近的字節。"

12. 預處理語句

使用預處理語句有諸多好處,包括更高的性能和更好的安全性。

預處理語句默認情況下會過濾綁定到它的變量,這對于避免SQL注入攻擊極為有效。當然你也可以指定要過濾的變量。但這些方法更容易出現人為錯誤,也更容易被程序員遺忘。這在使用框架或 ORM 的時候會出現一些問題。

既然我們關注性能,那就應該說說這個方面的好處。當在應用中多次使用同一個查詢的時候,它的好處特別明顯。既然向同一個預備好的語句中傳入不同的參數值,MySQL 對這個語句也只會進行一次解析。

同時,最新版本的 MySQL 在傳輸預備好的語句時會采用二進制形式,這樣做的作用非常明顯,而且對減少網絡延遲很有幫助。

曾經有一段時間,許多程序員為了一個重要的原因則避免使用預處理語句。這個原因就是,它們不會被MySQL 緩存。不過在 5.1 版本的某個時候,查詢緩存也得到的支持。

想在 PHP 中使用預處理語句,你可以看看 mysqli 擴展 或使用數據抽象層,如 PDO。

// create a prepared statementif ($stmt = $mysqli->prepare("SELECT username FROM user WHERE state=?")) {   // bind parameters  $stmt->bind_param("s", $state);   // execute  $stmt->execute();   // bind result variables  $stmt->bind_result($username);   // fetch value  $stmt->fetch();   printf("%s is from %s/n", $username, $state);   $stmt->close();}

13. 無緩沖查詢

通常當你從腳本執行一個查詢,在它可以繼續后面的任務之前將需要等待查詢執行完成。你可以使用無緩沖的查詢來改變這一情況。

在PHP 文檔中對  mysql_unbuffered_query() f函數有一個很好的解釋: 

"mysql_unbuffered_query() 發送SQL查詢語句到MySQL不會像 mysql_query()那樣自動地取并緩沖結果行。這讓產生大量結果集的查詢節省了大量的內存,在第一行已經被取回時你就可以立即在結果集上繼續工作,而不用等到SQL查詢被執行完成。"

然而,它有一定的局限性。你必須在執行另一個查詢之前讀取所有的行或調用mysql_free_result() 。另外你不能在結果集上使用mysql_num_rows() 或 mysql_data_seek() 。

14. 使用 UNSIGNED INT 存儲IP地址

很多程序員沒有意識到可以使用整數類型的字段來存儲 IP 地址,所以一直使用 VARCHAR(15) 類型的字段。使用 INT 只需要 4 個字節的空間,而且字段長度固定。

必須確保列是 UNSINGED INT 類型,因為 IP 地址可能會用到 32 位無符號整型數據的每一個位。

在查詢中可以使用 INET_ATON() 來把一個IP轉換為整數,用 INET_NTOA() 來進行相反的操作。在 PHP 也有類似的函數,ip2long() 和 long2ip()。

$r = "UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id";

15. 固定長度(靜態)的表會更快

(譯者注:這里提到的表的長度,實際是指表頭的長度,即表中每條數據占用的空間大小,而不是指表的數據量)

如果表中所有列都是“固定長度”,那么這個表被認為是“靜態”或“固定長度”的。不固定的列類型包括 VARCHAR、TEXT、BLOB等。即使表中只包含一個這些類型的列,這個表就不再是固定長度的,MySQL 引擎會以不同的方式來處理它。

固定長度的表會提高性能,因為 MySQL 引擎在記錄中檢索的時候速度會更快。如果想讀取表中的某一地,它可以直接計算出這一行的位置。如果行的大小不固定,那就需要在主鍵中進行檢索。

它們也易于緩存,崩潰后容易重建。不過它們也會占用更多空間。例如,如果你把一個 VARCHAR(20) 的字符改為 CHAR(20) 類型,它會總是占用 20 個字節,不管里面存的是什么內容。

你可以使用“垂直分區”技術,將長度變化的列拆分到另一張表中。來看看:

16. 垂直分區

垂直分區是為了優化表結構而對其進行縱向拆分的行為。

示例 1: 你可能會有一張用戶表,包含家庭住址,而這個不是一個常用數據。這時候你可以選擇把表拆分開,將住址信息保存到另一個表中。這樣你的主用戶表就會更小。如你所知,表越小越快。

示例 2: 表中有一個 "last_login" 字段,用戶每次登錄網站都會更新這個字段,而每次更新都會導致這個表緩存的查詢數據被清空。這種情況下你可以將那個字段放到另一張表里,保持用戶表更新量最小。

不過你也需要確保不會經常聯合查詢分開后的兩張表,要不然你就得忍受由這帶來的性能下降。

17. 拆分大型DELETE或INSERT語句

如果你需要在網站上執行大型DELETE或INSERT查詢,則需要注意不要影響網絡流量。當執行大型語句時,它會鎖表并使你的Web應用程序停止。

Apache運行許多并行進程/線程。 因此它執行腳本效率很高。所以服務器不期望打開過多的連接和進程,這很消耗資源,特別是內存。

如果你鎖表很長時間(如30秒或更長),在一個高流量的網站,會導致進程和查詢堆積,處理這些進程和查詢可能需要很長時間,最終甚至使你的網站崩潰。

如果你的維護腳本需要刪除大量的行,只需使用LIMIT子句,以避免阻塞。

while (1) {  mysql_query("DELETE FROM logs WHERE log_date <= '2009-10-01' LIMIT 10000");  if (mysql_affected_rows() == 0) {    // done deleting    break;  }  // you can even pause a bit  usleep(50000);}

18. 越小的列越快

對于數據庫引擎來說,磁盤空間可能是最需要注意的瓶頸。對性能而言,“小”和“緊縮”有助于減少磁盤傳輸量。

MySQL 文檔中有一個列表,列舉了各種數據類型所需要的存儲空間。

如果數據表預計只會有少量的行,那就沒必要把主鍵定義為 INT 類型,可以用 MEDIUMINT、SMALLINT 甚至 TINYINT 來代替。(譯者注:對于日期數據,)如果不需要時間部分,就應該使用 DATE 而不是 DATETIME。

請確保留出合理的數據成長空間,不然就可能造成像Slashdot那樣的結果(譯者注:Slashdot 因為數據增長將評論表的主鍵改為了 INT 型,但沒有修改其父表中的相應的數據類型,雖然一個 ALTER 語句就可以解決問題,但是需要至少停止某些業務三個小時)。

19. 選擇正確的存儲引擎

MySQL 有兩個主要的存儲引擎:MyISAM 和 InnoDB,它們各有利弊。

MyISAM 適用于讀請求特別多的應用,但不適用于有大量寫請求的情況。甚至你只是要更新一行中的某個字段,都會造成整張表被鎖,然后直到這個查詢完成,其它進程都不能從這張表讀取數據。MyISAM 在計算 SELECT COUNT(*) 這種類型的查詢時速度非???。

InnoDB 是一個復雜的存儲引擎,在多數小型應用中它比 MyISAM 慢。但是它支持行級鎖,有更好的尺度。它還支持一些高級特性,比如事務。

  • MyISAM 存儲引擎

  • InnoDB 存儲引擎

20. 使用對象關系映射器(ORM, Object Relational Mapper)

通過使用ORM(對象關系映射器),你可以獲得一定的性能提升。ORM可以完成的一切事情,手動編碼也可完成。但這可能意味著需要太多額外的工作,并且需要高水平的專業知識。

ORM以“延遲加載”著稱。這意味著它們僅在需要時獲取實際值。但是你需要小心處理他們,否則你可能最終創建了許多微型查詢,這會降低數據庫性能。

ORM還可以將多個查詢批處理到事務中,其操作速度比向數據庫發送單個查詢快得多。

目前我最喜歡的PHP-ORM是Doctrine。我寫了一篇關于如何安裝Doctrine與CodeIgniter的文章(install Doctrine with CodeIgniter)。

21. 小心使用持久連接

持久連接意味著減少重建連接到MySQL的成本。 當持久連接被創建時,它將保持打開狀態直到腳本完成運行。 因為Apache重用它的子進程,下一次進程運行一個新的腳本時,它將重用相同的MySQL連接。

  • PHP:mysql_pconnect()

理論上看起來不錯。 但從我個人(和許多其他人)的經驗看來,這個功能可能會導致更多麻煩。 你可能會出現連接數限制問題、內存問題等等。

Apache總是并行運行的,它創建許多子進程。 這是持久連接在這種環境中不能很好工作的主要原因。 在你考慮使用mysql_pconnect()之前,請咨詢你的系統管理員。

原文出處: Burak Guzel   譯文出處:開源中國   

 

注:相關教程知識閱讀請移步到MYSQL教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲乱亚洲乱妇无码| 国产精品青青在线观看爽香蕉| 亚洲wwwav| 亚洲性猛交xxxxwww| 亚洲美女av电影| 国产一区二区久久精品| 国产精品久久色| 日韩av网址在线观看| 成人做爽爽免费视频| 国产丝袜视频一区| 亚洲片在线资源| 欧美精品在线免费观看| 国产欧美日韩专区发布| 黑人狂躁日本妞一区二区三区| 日韩av片电影专区| 26uuu另类亚洲欧美日本一| 国产女人精品视频| 国产亚洲视频在线| 欧美国产亚洲精品久久久8v| 亚洲性xxxx| 尤物九九久久国产精品的分类| 亚洲欧美国内爽妇网| 日韩在线欧美在线| 欧美视频一区二区三区…| 欧美超级乱淫片喷水| 欧美成年人视频网站| 国产成人精品最新| 久久99热这里只有精品国产| 亚洲九九九在线观看| 69久久夜色精品国产69| 成人国产精品久久久久久亚洲| 国产精品亚洲аv天堂网| 欧美成aaa人片在线观看蜜臀| 国产69精品久久久久99| 日韩av影院在线观看| 成人激情视频在线播放| 5566成人精品视频免费| 欧美日韩性生活视频| 亚洲aⅴ日韩av电影在线观看| 高清欧美性猛交| 日韩免费电影在线观看| 中文字幕综合一区| 国产专区精品视频| 91老司机精品视频| 国产精品香蕉av| 国产精品久久久久久搜索| 亚洲va欧美va国产综合剧情| 国产成人在线亚洲欧美| 国产精品爽黄69| 久久久免费高清电视剧观看| 日本久久中文字幕| 国产精品扒开腿做爽爽爽男男| 久久精品亚洲精品| 国产午夜精品全部视频播放| 久久99国产精品自在自在app| 日韩在线观看视频免费| 日韩精品中文字幕有码专区| 欧美一级高清免费播放| 久久天堂电影网| 欧美激情第三页| 国产成人精品日本亚洲| 色偷偷av一区二区三区乱| 国产精品久久久久久久av电影| 欧美黑人一级爽快片淫片高清| 韩国国内大量揄拍精品视频| 国产日韩在线播放| 国产激情视频一区| 在线观看日韩视频| 日韩在线播放av| 久久久久久网站| 91精品国产高清久久久久久91| 91久热免费在线视频| 国产91精品网站| 国产成人精品免费久久久久| 亚洲free嫩bbb| 国产成人aa精品一区在线播放| 久久综合免费视频| 国产亚洲精品激情久久| 91精品国产高清久久久久久91| 欧美激情国内偷拍| 亚洲精品欧美极品| 亚洲一区二区三区视频| 久久精品国产亚洲精品| 九九视频这里只有精品| 国产日韩综合一区二区性色av| 亚洲一区av在线播放| 亚洲欧美日韩综合| 国产精品欧美久久久| 国内精品久久久久久中文字幕| 日本国产欧美一区二区三区| 久久国产一区二区三区| 97在线看免费观看视频在线观看| 欧美午夜精品伦理| 中文字幕精品—区二区| 中文字幕亚洲一区二区三区| 一区二区三区www| 日韩中文字幕久久| 中文字幕亚洲色图| 久久精品福利视频| 日韩av观看网址| 亚洲国产高潮在线观看| 在线观看欧美日韩国产| 国产精品永久免费视频| 久久久久久午夜| 亚洲久久久久久久久久久| 欧美在线影院在线视频| 久久精品99久久久香蕉| 欧美大片欧美激情性色a∨久久| 欧美日韩国产中文精品字幕自在自线| 亚洲精品资源美女情侣酒店| 日韩一区二区三区在线播放| 色老头一区二区三区在线观看| 一区二区三区视频观看| 亚洲国产精彩中文乱码av在线播放| 国产精品免费视频xxxx| 亚洲а∨天堂久久精品喷水| 国产视频999| 日韩欧美在线国产| 精品少妇一区二区30p| 一本一道久久a久久精品逆3p| 欧美做受高潮电影o| 欧美伊久线香蕉线新在线| 中文字幕一区二区精品| 正在播放亚洲1区| xvideos成人免费中文版| 国产脚交av在线一区二区| www.亚洲男人天堂| 久久99久久久久久久噜噜| 国产精品一区二区3区| 色婷婷av一区二区三区在线观看| 国产日本欧美在线观看| 成人网页在线免费观看| 国产一区二区黄| 欧美黑人极品猛少妇色xxxxx| 亚洲欧美精品中文字幕在线| 亚洲毛片在线观看| 日韩高清电影免费观看完整版| 亚洲欧美日韩精品久久| 俺去啦;欧美日韩| 亚洲美女动态图120秒| 成人午夜激情网| 自拍偷拍亚洲区| 国产日韩欧美中文| 欧美亚洲在线观看| 91热精品视频| 久久亚洲精品中文字幕冲田杏梨| 黑人狂躁日本妞一区二区三区| 97在线日本国产| 国产精品欧美在线| 91香蕉国产在线观看| 高清欧美性猛交xxxx黑人猛交| 26uuu国产精品视频| 中文字幕精品—区二区| 国产亚洲精品91在线| 欧美激情精品久久久久久蜜臀| 中文字幕在线日韩| 久久久久久久久国产精品| 黑人巨大精品欧美一区免费视频| 欧美性猛交xxxx免费看漫画| 日韩少妇与小伙激情| 美女视频黄免费的亚洲男人天堂| 国产精品日日做人人爱| 欧美精品18videosex性欧美| 成人乱色短篇合集|