爲了提高PHP的運行效率,程序員不光須要寫出邏輯清晰,效率很高的代碼,還要能對query語句進行優化。雖然咱們對數據庫的讀取寫入速度上倒是無能爲力,但在一些數據庫類擴展像memcache、mongodb、redis這樣的數據存儲服務器的幫助下,PHP也能達到更快的存取速度,因此瞭解學習這些擴展也是很是必要。php
大型存儲方面優化mysql
數據庫主從複製和讀寫分離laravel
一、master將改變記錄到二進制日誌中,slave將master的二進制拷貝到它的中繼日誌中,從新將數據返回到它本身的數據中,達到複製主服務器數據的目的。 主從複製能夠用做:數據庫負載均衡、數據庫備份、讀寫分離等功能。 二、配置主服務器master 修改my.ini/my.conf [mysqld] log-bin=mysql-bin //啓用二進制日誌 server-id=102 //服務器惟一ID 三、配置從服務器slave log-bin=mysql-bin //啓用二進制日誌 server-id=226 //服務器惟一ID 四、在主服務器上受權從服務器 GRANT REPLICATION SLAVE ON *.* to 'slavename'@'IP' identified by 'root' 五、在從服務器上使用 change master to master_host="masterip", master_user="masteruser", master_password="masterpasswd"; 六、而後使用start slave命令開始進行主從複製。 不要忘記在每次修改配置後重啓服務器,而後能夠在主從服務器上用show master/slave status查看主/從狀態。 實現數據庫的讀寫分離要依賴MySQL的中間件,如mysql_proxy,atlas等。經過配置這些中間件來對主從服務器進行讀寫分離,使從服務器承擔被讀取的責任,從而減輕主服務器的負擔。
數據庫的sharding
在數據庫中數據表中的數據量很是龐大的時候,不管是索引仍是緩存等壓力都很大,對數據庫進行sharding,使之分別以多個數據庫服務器或多個表存儲,以減輕查詢壓力。方式有垂直切分、水平切分和聯合切分。程序員
垂直切分:在數據表很是多的時候,把數據庫中關係緊密(如同一模塊,常常鏈接查詢)的表切分出來分別放到不一樣的主從server上。面試
水平切分:在表很少,而表裏的數據量很是大的時候,爲了加快查詢,能夠用哈希等算法,將一個數據表分爲幾個,分別放到不一樣的服務器上,加快查詢。水平切分和數據表分區的區別在於其存儲介質上的不一樣。redis
聯合切分:更多的狀況是數據表和表中的數據量都很是大,則要進行聯合切分,即同時進行垂直和水平分表,將數據庫切分爲一個分佈式的矩陣來存儲。
這些數據庫的優化方式,每一種拿出來均可以寫做一篇文章,可謂是博大精深,瞭解並記憶了這些方式,能夠在有須要的時候進行有目的的選擇優化,達到數據庫效率的高效。算法
索引方面優化sql
在MySQL中,索引屬於存儲引擎級別的概念,不一樣存儲引擎對索引的實現方式是不一樣的,下面主要討論MyISAM和InnoDB兩個存儲引擎的索引實現方式。mongodb
MyISAM索引實現
MyISAM引擎使用B+Tree做爲索引結構,葉節點的data域存放的是數據記錄的地址。下圖是MyISAM索引的原理圖:shell
這裏設表一共有三列,假設咱們以Col1爲主鍵,則圖1是一個MyISAM表的主索引(Primary key)示意。能夠看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是惟一的,而輔助索引的key能夠重複。若是咱們在Col2上創建一個輔助索引,則此索引的結構以下圖所示:
一樣也是一顆B+Tree,data域保存數據記錄的地址。所以,MyISAM中索引檢索的算法爲首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,則取出其data域的值,而後以data域的值爲地址,讀取相應數據記錄。
MyISAM的索引方式也叫作「非彙集」的,之因此這麼稱呼是爲了與InnoDB的彙集索引區分。
InnoDB索引實現
雖然InnoDB也使用B+Tree做爲索引結構,但具體實現方式卻與MyISAM大相徑庭。
第一個重大區別是InnoDB的數據文件自己就是索引文件。從上文知道,MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。而在InnoDB中,表數據文件自己就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,所以InnoDB表數據文件自己就是主索引。
圖3
圖3是InnoDB主索引(同時也是數據文件)的示意圖,能夠看到葉節點包含了完整的數據記錄。這種索引叫作彙集索引。由於InnoDB的數據文件自己要按主鍵彙集,因此InnoDB要求表必須有主鍵(MyISAM能夠沒有),若是沒有顯式指定,則MySQL系統會自動選擇一個能夠惟一標識數據記錄的列做爲主鍵,若是不存在這種列,則MySQL自動爲InnoDB表生成一個隱含字段做爲主鍵,這個字段長度爲6個字節,類型爲長整形。
第二個與MyISAM索引的不一樣是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的全部輔助索引都引用主鍵做爲data域。例如,圖4爲定義在Col3上的一個輔助索引:
圖4
這裏以英文字符的ASCII碼做爲比較準則。彙集索引這種實現方式使得按主鍵的搜索十分高效,可是輔助索引搜索須要檢索兩遍索引:首先檢索輔助索引得到主鍵,而後用主鍵到主索引中檢索得到記錄。
瞭解不一樣存儲引擎的索引實現方式對於正確使用和優化索引都很是有幫助,例如知道了InnoDB的索引實現後,就很容易明白爲何不建議使用過長的字段做爲主鍵,由於全部輔助索引都引用主索引,過長的主索引會令輔助索引變得過大。
再例如,用非單調的字段做爲主鍵在InnoDB中不是個好主意,由於InnoDB數據文件自己是一顆B+Tree,非單調的主鍵會形成在插入新記錄時數據文件爲了維持B+Tree的特性而頻繁的分裂調整,十分低效,而使用自增字段做爲主鍵則是一個很好的選擇。
數據查詢方面優化
在每個消耗大量時間的查詢案例中,都能看到一些沒必要要的額外操做、某些操做被額外地重複了不少次、某些操做執行得太慢等。優化查詢的目的就是減小和消除這些操做所花費的時間。
1、首選要優化數據訪問
查詢性能底下最基本的緣由是訪問的數據太多。因此,對於低效的查詢,通常經過兩個步驟來分析:
確認應用程序是否在檢索大量超過須要的數據。這一般意味着訪問了太多的行,但有時候也多是訪問了太多的列。確認MySQL服務器層是否在分析大量超過須要的數據行。
1.一、是否向數據庫請求了不須要的數據
在訪問數據庫時,應該只請求須要的行和列,請求多餘的行和列會消耗MySQL服務器的CPU和內存資源,並增長網絡開銷。
一、在處理分頁時,應該使用LIMIT限制MySQL只返回須要的數據,而不是嚮應用程序返回所有數據後,再由應用程序過濾不須要的行。
二、多表關聯時,或獲取單表數據時,儘可能避免不加思考地使用SELECT *
三、當一些數據被屢次使用時能夠考慮將數據緩存起來,避免每次使用都要到MySQL查詢。
1.二、MySQL是否在掃描額外的記錄,應該讓MySQL使用最合適的方式查詢數據
對於MySQL,最簡單的衡量查詢開銷有三個指標:響應時間、掃描的行數和返回的行數。這裏主要考慮提升掃描的方式,即查詢數據的方式。
查詢數據的方式有全表掃描、索引掃描、範圍掃描、惟一索引查詢、常數引用等。這些查詢方式,速度從慢到快,掃描的行數也是從多到少。能夠經過EXPLAIN語句中的type列反應查詢採用的是哪一種方式。
一般能夠經過添加合適的索引改善查詢數據的方式,使其儘量減小掃描的數據行,加快查詢速度。
例如,當發現查詢須要掃描大量的數據行但只返回少數的行,那麼能夠考慮使用覆蓋索引,即把全部須要用到的列都放到索引中。這樣存儲引擎無須回表獲取對應行就能夠返回結果了。
2、重構查詢的方法
設計查詢的時候須要考慮是否須要把一個複雜的查詢分紅多個簡單的查詢。在個人印象中,曾經無數次聽到一個經驗法則:能夠在數據庫中作的事不要放在應用程序中,數據庫比咱們想象的要厲害的多。這個經驗法則是在華夏基金使用Oracle編寫SQL時一位Oracle牛人告訴個人,後來我把它使用到MySQL上,真是吃盡苦頭。
固然這其中的緣由有Oracle和MySQL本來就不是同樣的處理邏輯,而且如今的網絡通訊、查詢解析和優化的代價並無之前那麼高啦。再次說明,經驗法則有在某種特定籠子裏纔有效。
分解複雜的查詢:
能夠將一個大查詢切分紅多個小查詢執行,每一個小查詢只完成整個查詢任務的一小部分,每次只返回一小部分結果。
刪除舊的數據是一個很好的例子。
若是隻用一條語句一次性執行一個大的刪除操做,則可能須要一次鎖住不少數據,佔滿整個事務日誌,耗盡系統資源、阻塞不少小的但重要的查詢。將一個大的刪除操做分解成多個較小的刪除操做能夠將服務器上本來一次性的壓力分散到屢次操做上,儘量小地影響MySQL性能,減小刪除時鎖的等待時間,同時也減小了MySQL主從複製的延遲。這個方法我一直在用。
另外一個例子是分解關聯查詢,即對每一個要關聯的表進行單表查詢,而後將結果在應用程序中進行關聯。我在以前一家公司和一位在阿里待過不少年的同事一塊兒編碼時,他就是這麼幹的。後來我在心中默默地鄙視着他,由於我內心有這麼一個經驗法則(能夠在數據庫中作的事不要放在應用程序中,數據庫比咱們想象的要厲害的多),而且我在行動上也是保持能用一個SQL解決的事絕對不會用兩個SQL。
這麼作固然處理經驗法則的緣由以外還有一個緣由是:獲取數據的邏輯儘可能與業務代碼分離,這樣之後在切換數據庫時也很方便。其實是這樣嗎?未必啊。那次的無知讓我吃盡苦頭啊,後來由於SQL的性能問題再把我寫的大部分SQL進行分解。
用分解關聯查詢的方式重構查詢有以下的優點:
讓緩存的效率更高。許多應用程序能夠方便地緩存單表查詢對應的結果對象。將查詢分解後,執行單個查詢能夠減小鎖的競爭。在應用層作關聯,能夠更容易對數據庫進行拆分,更容易作到高性能和可擴展。查詢自己效率也可能會有所提高。能夠減小冗餘記錄的查詢。在應用層作關聯查詢,
意味着對於某條記錄應用只須要查詢一次,而在數據庫中作關聯查詢,則可能須要重複地訪問一部分數據。從這點看,這樣的重構還可能會減小網絡和內存的消耗。更進一步,這樣作至關於在應用中實現了哈希關聯,而不是使用MySQL的嵌套循環關聯。某些場景哈希關聯的效率要高不少。
數據庫設計方面優化
一、數據庫設計符合第三範式,爲了查詢方即可以有必定的數據冗餘。 二、選擇數據類型優先級 int > date,time > enum,char>varchar > blob,選擇數據類型時,能夠考慮替換,如ip地址能夠用ip2long()函數轉換爲unsign int型來進行存儲。 三、對於char(n)類型,在數據完整的狀況下儘可能較小的的n值。 四、在建表時用partition命令對單個表分區能夠大大提高查詢效率,MySQL支持RANGE,LIST,HASH,KEY分區類型,其中以RANGE最爲經常使用,分區方式爲: CREATE TABLE tablename{ }ENGINE innodb/myisam CHARSET utf8 //選擇數據庫引擎和編碼 PARTITION BY RANGE/LIST(column),//按範圍和預約義列表進行分區 PARTITION partname VALUES LESS THAN /IN(n),//命名分區並詳細限定分區的範圍 五、選擇數據庫引擎時要注意innodb 和 myisam的區別。 存儲結構:MyISAM在磁盤上存儲成三個文件。而InnoDB全部的表都保存在同一個數據文件中,通常爲2GB 事務支持:MyISAM不提供事務支持。InnoDB提供事務支持事務。 表鎖差別:MyISAM只支持表級鎖。InnoDB支持事務和行級鎖。 全文索引:MyISAM支持 FULLTEXT類型的全文索引(不適用中文,因此要用sphinx全文索引引擎)。InnoDB不支持。 表的具體行數:MyISAM保存有表的總行數,查詢count(*)很快。InnoDB沒有保存表的總行數,須要從新計算。 外鍵:MyISAM不支持。InnoDB支持
幾條MySQL小技巧
一、SQL語句中的關鍵詞最好用大寫來書寫,第一易於區分關鍵詞和操做對象,第二,SQL語句在執行時,MySQL會將其轉換爲大寫,手動寫大寫能增長查詢效率(雖然很小)。
二、若是咱們們經對數據庫中的數據行進行增刪,那麼會出現數據ID過大的狀況,用ALTER TABLE tablename AUTO_INCREMENT=N,使自增ID從N開始計數。
三、對int類型添加 ZEROFILL 屬性能夠對數據進行自動補0
四、導入大量數據時最好先刪除索引再插入數據,再加入索引,否則,mysql會花費大量時間在更新索引上。
五、建立數據庫書寫sql語句時 ,咱們能夠在IDE裏建立一個後綴爲.sql的文件,IDE會識別sql語法,更易於書寫。更重要的是,若是你的數據庫丟失了,你還能夠找到這個文件,在當前目錄下使用/path/mysql -uusername -ppassword databasename < filename.sql來執行整個文件的sql語句(注意-u和-p後緊跟用戶名密碼,無空格)。
以上內容但願幫助到你們, 不少PHPer在進階的時候總會遇到一些問題和瓶頸,業務代碼寫多了沒有方向感,不知道該從那裏入手去提高,對此我整理了一些資料,包括但不限於:分佈式架構、高可擴展、高性能、高併發、服務器性能調優、TP6,laravel,Redis,Swoole、Swoft、Kafka、Mysql優化、shell腳本、Docker、微服務、Nginx等多個知識點高級進階乾貨須要的能夠免費分享給你們 ,須要戳這裏 PHP進階架構師>>>實戰視頻、大廠面試文檔免費獲取