實現方案一:html
(1)直接執行select count(*)。 (2)可是此sql語句在數據量愈來愈大的時候,不一樣的存儲引擎會帶來不一樣的性能問題。 (3)myisam存儲引擎是非事務性存儲引擎,而且其內部存在內置的計數器,直接保存着代表細的總數目,若是執行select count(*)那麼將很容易就取出,即便在數據量很大的狀況下也不會出現太大的問題。 (4)可是若是是InnoDB存儲引擎,其內部並無內置的計數器保存代表細總數量,每次執行select count(*)都會全表掃描 (5)因而可知,當數據量在很大的一個量級的時候,加上併發很大的狀況下,很容易出現性能瓶頸。
實現方案二:mysql
(1)方案一能夠適用於myisam存儲引擎,可是對於innodb存儲引擎就不行了。 (2)那咱們是否能夠考慮爲這個功能單獨創建一個表,就只有一個字段一條記錄,就存放這個統計量,每次有新的帖子的時候就更新一次這個統計量,到時候直接查詢這個字段便可實現。 (3)這種方案也可能很好的提高查詢效率,可是在咱們的系統帖子產生很快的時候,在高峯時期可能每秒就有幾十個甚至上百個帖子新增操做的時候,這個統計表可能又回出現問題。 (4)要麼由於併發的問題形成統計結果不許確,要麼由於鎖資源爭用嚴重形成總體性能的降低。
實現方案三:web
(1)方案一和方案二都有必定的優缺點,可是若是咱們想要在它們的缺陷處儘量的完美避開性能瓶頸呢? (2)這時候咱們從新考慮實時更新的需求,用戶對於這個實時更新是很容易感知的嗎? (3)當一個論壇的帖子數量很大了以後,到底有多少人會關注這個數量的實時變化?我想應該不會,所以,咱們對此需求是否是能夠不那麼嚴格,是否是能夠容許必定的誤差,達到準實時的標準? (4)基於此考慮,那麼咱們的方案三就能夠建立一個統計表,而後經過定時任務隔一段時間更新一次統計表數據,這樣既能夠解決統計表查詢的效率問題,又能夠保證不影響發帖的效率,一箭雙鵰。
實際上,如下幾類數據都是不適合在數據庫中存放的:sql
(1)二進制多媒體數據: 【1】將二進制多媒體數據存放在數據庫中,一個問題是數據庫空間資源耗用很是嚴重,另外一個問題是這些數據的存儲很消耗數據庫主機的cpu資源。 【2】這種數據主要包括圖片、音頻、視頻和其餘一些相關的二進制文件。 【3】這些數據的處理本不是數據庫的優點,若是咱們硬要將它們塞入數據庫,確定會形成數據庫的處理資源消耗嚴重。 (2)流水隊列數據: 【1】咱們都知道,數據庫爲了保證事務的安全性以及可恢復性,都是須要記錄全部變動的日誌信息的。 【2】而流水隊列數據的用途就決定了存放這種數據的表中的數據會不斷的被INSERT、UPDATE、DELETE,而每個操做都會生成與之對應的日誌信息。 【3】在mysql中,若是是支持事務的存儲引擎(InnoDB),這個日誌的產生量將更是翻倍。 【4】而若是咱們經過一些成熟的第三方隊列軟件來實現這個Queue數據的處理功能,性能將會成倍的提高。 (3)超大文本數據: 【1】對於5.0.3以前的mysql版本,varchar類型的數據最長只能存放255個字節,若是須要存儲更長的文本數據到一個字段,咱們就必須使用text類型(最大可存放64KB),甚至是更大的longtext類型(最大4G)。 【2】而text類型數據的處理性能要遠比varchar類型數據的處理性能低下不少。 【3】從5.0.3版本開始,varchar類型的最大長度被調整爲64KB,可是當實際數據小於255Bytes時,實際存儲空間和數據的實際長度同樣,可一旦數據長度超過了255Bytes以後,所佔用的存儲空間就是實際數據長度的兩倍。 【4】因此,超大文本數據存放在數據庫中不只會帶來性能低下的問題,還會帶來空間佔用的浪費問題。
是否合理的利用了應用層cache機制?數據庫
(1)對於web應用,活躍數據的數據量老是不會特別大,有些活躍數據更是不多變化。 (2)對於這中類型的數據,咱們是否有必要每次須要的適合都去數據庫中查詢呢? (3)若是咱們可以將變化相對較少的部分活躍數據經過應用層的cache機制cache到內存中,對性能的提高確定是成數量級的,並且因爲是活躍數據,對系統總體性能的影響也會很大。 (4)固然,經過cache機制成功的案例數不勝數,如何合理的經過cache技術讓系統性能獲得較大的提高也並非經過寥寥幾筆就能說明清楚的,這裏根據以往經驗列舉一下什麼樣的數據適合經過cache技術來提高系統性能: 【1】系統各類配置及規則數據:因爲這些配置信息變更的頻率很是低,訪問機率又很高,因此很是適合使用cache。 【2】活躍用戶的基本信息數據: 「1」雖然咱們常常會聽到某某網站的用戶量達到成百上千萬,可是不多有系統的活躍用戶量都能達到這個量級。 「2」也不多有用戶沒事幹去將本身的基本信息改來改去。 「3」更爲重要的一點是用戶的基本信息在應用系統中的訪問頻率及其頻繁。 「4」因此,用戶基本信息的cache,很容易讓整個系統的性能出現一個質的提高。 【3】活躍用戶的個性化定製信息數據: 「1」雖然用戶個性化定製的數據從訪問頻率來看,可能並無用戶的基本信息那麼頻繁,但相對於系統總體來講,也佔了很大的比例,並且變動規律同樣不會太多。 「2」從Ebay的PayPal經過mysql的memory存儲引擎實現用戶個性化定製數據的成功案例咱們就能看出對這部分信息進行cache的價值。 「3」雖然經過mysql的memory存儲引擎並不像咱們傳統意義上的cache機制,但正是對cache技術的合理利用和擴充造就了項目總體的成功。 【4】準實時的統計信息數據: 「1」所謂準實時的統計信息數據,實際上就是基於時間段的統計數據。 「2」這種數據不會實時更新,也不多須要增量更新,只有當達到從新build該統計數據的時候須要作一次全量更新操做。 「3」雖然這種數據即便經過數據庫來讀取效率可能也會很高,可是執行效率很高以後,一樣會消耗很多資源。既然數據庫資源很是珍貴,咱們爲何不放在應用相關的cache中呢? 【5】其餘一些訪問頻繁可是變動較少的數據: 「1」除了上述四種數據以外,在咱們面對的各類系統環境中確定還會有各類各樣的變動較少可是訪問很頻繁的數據。 「2」只要合適,咱們均可以對他們的訪問從數據庫移到cache中。
咱們的數據層的實現都是最精簡的嗎?安全
(1)從以往的經驗來看,一個合理的數據存取實現和一個拙劣的實現相比,在性能方面的差別常常會超出一個甚至幾個數量級。 (2)咱們先來分析一個實例: 【1】在咱們的網站中,如今要實現每一個用戶查看各自相冊列表,假設每一個列表10張圖片,可以在相片名稱後邊顯示該相片的留言數量。 【2】實現方案一: 「1」經過「SELECT id,subject,url FROM photo WHERE user_id = ? limit 10」獲得第一頁的相片相關信息; 「2」經過第一步結果集中的10個相片的id循環運行十次"SELECT COUNT(*) FROM photo_comment WHERE photh_id = ?"來獲得每張相片的回覆數量而後再拼裝展示對象。 【1】實現方案二: 「1」和方案一中的第一步同樣的操做步驟; 「2」經過程序拼裝上面獲得的10個photo的id,再經過in查詢「SELECT photo_id,count(*) FROM photo_comment WHERE photo_id in (?) GROUP BY photo_id」一次獲得10個photo的全部回覆數量,再組裝兩個結果集獲得展示對象 (3)下面對兩種方案作一下簡單的比較: 【1】從mysql的sql執行數量來看,方案一爲11條sql語句,方案二爲2條sql語句; 【2】從應用程序於數據庫交互來看,方案一是11次,方案二是2次; 【3】從數據庫IO操做來看,簡單假設每次sql爲1個IO,方案一最少11次IO,方案二小於等於11次IO,並且只有當數據很是之離散的時候纔會須要11次; 【4】從數據庫處理的查詢複雜度來看,方案一是兩類很簡單的查詢,方案二有一條sql有group by操做,比第一種解決方案增長了排序分組操做; 【5】從應用程序結果集處理來看,方案一11次結果集的處理,方案二2次結果集的處理,可是方案二中第二次結果集數量是第一次的10倍; 【6】從應用程序數據處理來看,方案二比方案一多了一個拼裝photo_id的過程。 (4)咱們從以上6點作一個性能消耗的分析: 【1】因爲mysql對客戶端每次提交的sql無論是相同仍是不一樣,都須要進行徹底解析,這個動做主要消耗的資源是數據庫主機的CPU,那麼這裏第一種方案和第二種方案消耗的CPU的比例是11:2。sql語句的解析動做在整個sql語句執行過程當中的總體消耗的CPU比例是比較多的; 【2】應用程序與數據庫交互消耗的資源基本上都在網絡方面,一樣是11:2; 【3】數據庫IO操做資源消耗小於或者等於1:1; 【4】方案二須要比方案一多消耗內存資源進行排序分組操做,因爲數據量不大,多出的消耗在語句總體消耗中佔用比例會比較小,大概不會超過20%,你們能夠針對性測試; 【5】結果集處理次數爲11:2,可是方案二比方案一處理數量大,總體來講兩次的性能消耗區別不大; 【6】應用程序數據處理方面所多出的這個photo_id的拼裝所消耗的資源是很是小的,甚至比應用程序與mysql作一次簡單的交互所消耗的資源還要少。 (5)總體分析後,得出結論,從總體消耗資源來看,方案二遠遠優於方案一。
過渡依賴數據庫sql語句的功能形成數據庫操做效率低下性能優化
(1)前面的案例是開發工程師過渡弱化SQL語句的功能形成的資源浪費案例,而這裏咱們再來分析一個徹底相反的案例:在羣組簡介頁面須要顯示羣名稱和簡介,每一個羣成員的nick_name,以及羣主的我的簽名信息。 (2)需求中所需信息存放在如下四個表中:user,user_profile,groups,user_group (3)方案一: 【1】SELECT name,description,user_type,nick_name,sign FROM groups,user_group,user ,user_profile WHERE groups.id = ? AND groups.id = user_group.group_id AND user_group.user_id = user.id AND user_profile.user_id = user.id (4)方案二: 【1】首先取得全部須要展現的group的相關信息和全部羣組員的nick_name信息和組員類別: SELECT name,description,user_type,nick_name FROM groups,user_group,user WHERE groups.id = ? AND groups.id = user_group.group_id AND user_group.user_id = user.id 【2】而後在程序中經過上面結果集中的user_type找到羣主的user_id再到user_profile表中取得羣主的簽名信息: SELECT sign FROM user_profile WHERE user_id = ? (5)你們應該可以看出二者的區別吧,兩種解決方案最大的區別在於交互次數和 SQL複雜度。 (6)而帶來的實際影響是第一種解決方案對user_profile表有沒必要要的訪問(非羣主的profile信息),形成IO訪問的直接增長在20%左右。 (7)而你們都知道,IO操做在數據庫應用系統中是很是昂貴的資源。尤爲是當這個功能的PV較大的時候,第一種方案形成的IO損失是至關大的。
其餘比較常見的架構設計實現不當帶來的性能問題和資源浪費狀況:服務器
(1)重複執行相同的SQL形成資源浪費 (2)Cache系統的不合理利用致使Cache命中率低下形成數據庫訪問量的增長,同時也浪費了Cache系統的硬件資源投入; (3)對可擴展性的過分追求,促使系統設計的時候將對象拆得過於離散,形成系統中大量的複雜Join語句,而MySQL Server在各數據庫系統中的主要優點在於處理簡單邏輯的查詢,這與其鎖定的機制也有較大關係; (4)對數據庫的過分依賴,將大量更適合存放於文件系統中的數據存入了數據庫中,形成數據庫資源的浪費,影響到系統的總體性能,如各類日誌信息; (5)過分理想化系統的用戶體驗,使大量非核心業務消耗過多的資源,如大量不須要實時更新的數據作了實時統計計算。
爲何獲得相同結果集的不一樣SQL語句,在執行性能上存在差別呢?這裏咱們先從sql語句在數據庫中執行並獲取所需數據的過程來進行分析。網絡
(1)當mysql server的鏈接線程接收到Client端發送過來的sql請求以後,會通過一系列的分解Parse,進行相應的分析。 (2)而後mysql會經過查詢優化器模塊根據該sql所涉及到的數據表的相關統計信息進行計算分析,而後再得出一個mysql認爲最合理最優化的數據訪問方式,就是咱們平時說得「執行計劃」。 (3)再根據獲得的執行計劃經過存儲引擎接口來獲取相應數據。 (4)最後再將存儲引擎返回的數據進行相關處理,並以Client端要求的格式做爲結果集返回給Client端的應用程序。 (5)注意:這裏所說的統計數據,是mysql經過ANALYZE TABLE命令同志mysql對錶的相關數據作分析以後所得到的一些數據統計量。這些統計數據對mysql優化器來講很是重要,優化器所生成的執行計劃的好壞,主要就是由這些統計數據所決定的。 (6)咱們都知道,在數據庫管理軟件中,最大的性能瓶頸就是在於磁盤IO,也就是數據存取操做上面。 (7)而對於同一份數據,當咱們以不一樣方式去尋找某一點的數據時,所須要讀取的數據量可能會有天壤之別,所消耗的資源也天然是區別深大。 (8)因此,當咱們須要從數據庫中查詢某個數據的時候,所消耗資源的多少主要取決於數據庫以一個什麼樣的數據讀取方式來完成咱們的查詢請求,也就是取決於sql語句的執行計劃。 (9)而對於惟一sql語句來講,通過mysql Parse以後分解後的結構都是固定的,只要統計信息穩定,其執行計劃基本上都是比較固定的。 (10)而不一樣寫法的sql語句,通過Mysql Parse以後分解的結構就可能會不一樣,即便優化器使用徹底同樣的統計信息來進行優化,最後所獲得的執行計劃也可能徹底不同。 (11)而執行計劃是決定一個sql語句最終的資源消耗量的主要因素。 (12)因此,實現功能徹底同樣的SQL語句,在性能上面可能會有差異巨大的性能消耗。 (13)固然,若是功能同樣,並且通過 MySQL的優化器優化以後的執行計劃也徹底一致的不一樣SQL語句在資源消耗方面可能就相差很小了。固然這裏所指的消耗主要是IO資源的消耗,並不包括 CPU的消耗。
簡要分析:mysql優化
(1)須要存放用戶數據的表; (2)須要存放分組信息和存放用戶與組關係的表 (3)須要存放討論信息的表;
兩方案比較:
(1)咱們先來比較下兩個解決方案所設計的Schema的區別。 (2)區別主要體如今兩點。一個區別就是group_message表中添加了author字段來存放發帖做者的暱稱,與user表的nick_name相對應,另一個就是方案二將user表和group_message表都拆分紅了兩個表,關係分別一一對應。 (3)方案二看起來複雜一些,首先是表的數量多2個,而後是group_message中冗餘了做者的暱稱。 (4)咱們試想一下,一個討論區系統,訪問最多的頁面會是什麼? (5)我想你們都會很清楚是帖子標題列表頁面。而帖子標題列表頁面最主要的信息就是都是來自 group_message表中,同時帖子標題後面的做者通常都是經過用戶名(暱稱)來展現。按照第一種解決方案來設計的 Schema,咱們就須要執行相似以下這樣的SQL語句來獲得數據: 【1】SELECT t.id, t.subject,user.id, u.nick_name FROM ( SELECT id, user_id, subject FROM group_message WHERE group_id = ? ORDER BY gmt_modified DESC LIMIT 20 ) t, user u WHERE t.user_id = u.id (6)可是方案二所需執行的sql就會簡單不少: SELECT t.id, t.subject, t.user_id, t.author FROM group_message WHERE group_id = ? ORDER BY gmt_modified DESC LIMIT 20 (7)兩個sql相比較,很明顯能夠看出方案一須要join兩個表的數據,與方案二的sql相比性能相差很大,尤爲是若是第一個再寫的差一點,性能更是很是糟糕,二者所帶來的資源消耗就更相差玄虛了。 (8)不只僅如此,因爲第一個方案中的group_message表中還包含一個大字段「content」,該字段所存放的信息要佔整個表的絕大部分存儲空間,但在這條系統中執行最頻繁的 SQL之一中是徹底不須要該字段所存放信息的,可是因爲這個SQL又沒辦法作到不訪問group_message表的數據,因此第一條SQL在數據讀取過程當中會須要讀取大量沒有任何意義的數據。 (9)在系統中用戶數據的讀取也是比較頻繁的,可是大多數地方所須要的用戶數據都只是用戶的幾個基本屬性,如用戶的id,暱稱,密碼,狀態,郵箱等,因此將用戶表的這幾個屬性單獨分離出來後,也會讓大量的SQL語句在運行的時候減小數據的檢索量,從而提升性能。 (10)可能有人會以爲,在咱們將一個表分紅兩個表的時候,咱們若是要訪問被分拆出去的信息的時候,性能不是就會變差了嗎?是的,對於那些須要訪問如user的sign,msn等原來只須要一個表就能夠完成的SQL來講,如今都須要兩條SQL來完成,性能確實會 有所下降,可是因爲兩個表都是一對一的關聯關係,關聯字段的過濾性也很是高,並且這樣的查詢需求在整個系統中所佔有的比例也並不高,因此這裏所帶來的性能損失實際上要遠遠小於在其餘SQL上所節省出來的資源,因此徹底沒必要爲此擔憂
三類影響數據庫主機性能的最主要因素部件
(1)與IO相關的磁盤和內存: 【1】首先,數據庫主機是存取數據的地方,那麼其IO操做天然不會少,因此數據庫主機的IO性能確定是須要最優先考慮的一個因素,這一點無論是什麼類型的數據庫應用都是適用的。 【2】不過,這裏的IO性能並不只僅只是指物理的磁盤IO,而是主機的總體IO性能,是主機整個IO系統的整體IO性能。 【3】而IO性能自己又能夠分爲兩類,一類是每秒可提供的IO訪問次數,也就是咱們常說的IOPS數量,還有一種就是每秒的IO總流量,也就是咱們常說的IO吞吐量。 【4】在主機中決定 IO性能部件主要由磁盤和內存所決定,固然也包括各類與IO相關的板卡。 (2)CPU: 【1】其次,因爲數據庫主機和普通的應用程序服務器相比,資源要相對集中不少,單臺主機上所須要進行的計算量天然也就比較多,因此數據庫主機的CPU處理能力也不能忽視。 (3)網絡設備: 【1】最後,因爲數據庫負責數據的存儲,與各應用程序的交互中傳遞的數據量比其餘各種服務器都要多,因此數據庫主機的網絡設備的性能也可能會成爲系統的瓶頸。
因此後面咱們經過對各類類型的應用作一個簡單的分析,再針對性的給出這三類部件的基本選型建議。
(1)典型OLTP應用系統 【1】對於各類數據庫系統環境中你們最多見的OLTP系統 【2】其特色是併發量大,總體數據量比較多,但每次訪問的數據比較少,且訪問的數據比較離散,活躍數據佔整體數據的比例不是太大。 【3】對於這類系統的數據庫其實是最難維護,最難以優化的,對主機總體性能要求也是最高的。由於他不只訪問量很高,數據量也不小。 【4】針對上面的這些特色和分析,咱們能夠對OLTP的得出一個大體的方向: 「1」雖然系統整體數據量較大,可是系統活躍數據在數據總量中所佔的比例不大,那麼咱們能夠經過擴大內存容量來儘量多的將活躍數據cache到內存中; 「2」 雖然IO訪問很是頻繁,可是每次訪問的數據量較少且很離散,那麼咱們對磁盤存儲的要求是IOPS表現要很好,吞吐量是次要因素; 「3」 併發量很高,CPU每秒所要處理的請求天然也就不少,因此CPU處理能力須要比較強勁; 「4」 雖然與客戶端的每次交互的數據量並非特別大,可是網絡交互很是頻繁,因此主機與客戶端交互的網絡設備對流量能力也要求不能太弱。 (2)典型OLAP應用系統 【1】用於數據分析的OLAP系統的主要特色就是數據量很是大,併發訪問很少,但每次訪問所須要檢索的數據量都比較多,並且數據訪問相對較爲集中,沒有太明顯的活躍數據概念。 【2】基於OLAP系統的各類特色和相應的分析,針對OLAP系統硬件優化的大體策略以下: 「1」數據量很是大,因此磁盤存儲系統的單位容量須要儘可能大一些; 「2」單次訪問數據量較大,並且訪問數據比較集中,那麼對IO系統的性能要求是須要有儘量大的每秒IO吞吐量,因此應該選用每秒吞吐量儘量大的磁盤; 「3」雖然IO性能要求也比較高,可是併發請求較少,因此CPU處理能力較難成爲性能瓶頸,因此CPU處理能力沒有太苛刻的要求; 「4」雖然每次請求的訪問量很大,可是執行過程當中的數據大都不會返回給客戶端,最終返回給客戶端的數據量都較小,因此和客戶端交互的網絡設備要求並非過高; 「5」此外,因爲OLAP系統因爲其每次運算過程較長,能夠很好的並行化,因此通常的OLAP系統都是由多臺主機構成的一個集羣,而集羣中主機與主機之間的數據交互量通常來講都是很是大的,因此在集羣中主機之間的網絡設備要求很高。 (3)除了以上兩個典型應用以外,還有一類比較特殊的應用系統,他們的數據量不是特別大,可是訪問請求及其頻繁,並且大部分是讀請求。可能每秒須要提供上萬甚至幾萬次請求,每次請求都很是簡單,可能大部分都只有一條或者幾條比較小的記錄返回,就好比基於數據庫的 DNS服務就是這樣類型的服務。 「1」雖然數據量小,可是訪問極其頻繁,因此能夠經過較大的內存來 cache住大部分的數據,這可以保證很是高的命中率,磁盤IO量比較小,因此磁盤也不須要特別高性能的; 「2」併發請求很是頻繁,須要較強的CPU處理能力才能處理; 「3」雖然應用與數據庫交互量很是大,可是每次交互數據較少,整體流量雖然也會較大,可是通常來講普通的千兆網卡已經足夠了。