- 資訊首頁(yè) > 互聯(lián)網(wǎng) > 經(jīng)驗分享 >
- 后端接口如何提高性能?從 MySQL、ES、HBASE 等技術(shù)
謝邀,利益相關(guān)。大多數互聯(lián)網(wǎng)應用場(chǎng)景都是讀多寫(xiě)少,業(yè)務(wù)邏輯更多分布在寫(xiě)上。對讀的要求大概就是要快。那么都有什么原因會(huì )導致我們完成一次出色的慢查詢(xún)呢?
在數據量不是很大時(shí),大多慢查詢(xún)可以用索引解決,大多慢查詢(xún)也因為索引不合理而產(chǎn)生。
MySQL 索引基于 B 樹(shù),這句話(huà)相信面試都背爛了,接著(zhù)就可以問(wèn)最左前綴索引、 B 樹(shù)和各種樹(shù)了。
說(shuō)到最左前綴,實(shí)際就是組合索引的使用規則,使用合理組合索引可以有效的提高查詢(xún)速度,為什么呢?
因為索引下推。如果查詢(xún)條件包含在了組合索引中,比如存在組合索引(a,b),查詢(xún)到滿(mǎn)足 a 的記錄后會(huì )直接在索引內部判斷 b 是否滿(mǎn)足,減少回表次數。同時(shí),如果查詢(xún)的列恰好包含在組合索引中,即為覆蓋索引,無(wú)需回表。索引規則估計都知道,實(shí)際開(kāi)發(fā)中也會(huì )創(chuàng )建和使用。問(wèn)題可能更多的是:為什么建了索引還慢?
建了索引還慢,多半是索引失效(未使用),可用 explain 分析。索引失效常見(jiàn)原因有 :
如果要 MySQL 給一個(gè)理由,還是那棵 B 樹(shù)。
當在 查詢(xún) where = 左側使用表達式或函數時(shí),如字段 A 為字符串型且有索引, 有 where length(a) = 6查詢(xún),這時(shí)傳遞一個(gè) 6 到 A 的索引樹(shù),不難想象在樹(shù)的第一層就迷路了。
隱式類(lèi)型轉換和隱式字符編碼轉換也會(huì )導致這個(gè)問(wèn)題。
至于 Like 語(yǔ)句 % 開(kāi)頭、字符串未加 ’’ 原因基本一致,MySQL 認為對索引字段的操作可能會(huì )破壞索引有序性就機智的優(yōu)化掉了。
不過(guò),對于如性別這種區分度過(guò)低的字段,索引失效就不是因為這個(gè)原因。
為什么索引區分度低的字段不要加索引。盲猜效率低,效率的確低,有時(shí)甚至會(huì )等于沒(méi)加。
對于非聚簇索引,是要回表的。假如有 100 條數據,在 sex 字段建立索引,掃描到 51 個(gè) male,需要再回表掃描 51 行。還不如直接來(lái)一次全表掃描呢。
所以,InnoDB 引擎對于這種場(chǎng)景就會(huì )放棄使用索引,至于區分度多低多少會(huì )放棄,大致是某類(lèi)型的數據占到總的 30% 左右時(shí),就會(huì )放棄使用該字段的索引,有興趣可以試一下。
前面說(shuō)到大多慢查詢(xún)都源于索引,怎么建立并用好索引。這里有一些簡(jiǎn)單的規則。
有時(shí),建立了猛一看挺正確的索引,但事情卻沒(méi)按計劃發(fā)展。就像“為啥 XXX 有索引,根據它查詢(xún)還是慢查詢(xún)”。
此刻沒(méi)準要自信點(diǎn):我的代碼不可能有 BUG,肯定是 MySQL 出了問(wèn)題。MySQL 的確可能有點(diǎn)問(wèn)題。
這種情況常見(jiàn)于建了一大堆索引,查詢(xún)條件一大堆。沒(méi)使用你想讓它用的那一個(gè),而是選了個(gè)區分度低的,導致過(guò)多的掃描。造成的原因基本有兩個(gè):
但根據我淺薄的經(jīng)驗來(lái)看,更可能是因為你建了些沒(méi)必要的索引導致的。不會(huì )真有人以為 MySQL 沒(méi)自己機靈吧?
除了上面這些索引原因外,還有下面這些不常見(jiàn)或者說(shuō)不好判斷的原因存在。
在 MySQL 5.5 版本中引入了 MDL,對一個(gè)表做 CRUD 操作時(shí),自動(dòng)加 MDL 讀鎖;對表結構做變更時(shí),加 MDL 寫(xiě)鎖。讀寫(xiě)鎖、寫(xiě)鎖間互斥。
當某語(yǔ)句拿 MDL 寫(xiě)鎖就會(huì )阻塞 MDL 讀鎖,可以使用show processlist命令查看處于Waiting for table metadata lock狀態(tài)的語(yǔ)句。
flush 很快,大多是因為 flush 命令被別的語(yǔ)句堵住,它又堵住了 select 。通過(guò)show processlist命令查看時(shí)會(huì )發(fā)現處于Waiting for table flush狀態(tài)。
某事物持有寫(xiě)鎖未提交。
InnoDB 默認級別是可重復讀。設想一個(gè)場(chǎng)景:事物 A 開(kāi)始事務(wù),事務(wù) B 也開(kāi)始執行大量更新。B 率先提交, A 是當前讀,就要依次執行 undo log ,直到找到事務(wù) B 開(kāi)始前的值。
在未二次開(kāi)發(fā)的 MYSQL 中,上億的表肯定算大表,這種情況即使在索引、查詢(xún)層面做到了較好實(shí)現,面對頻繁聚合操作也可能會(huì )出現 IO 或 CPU 瓶頸,即使是單純查詢(xún),效率也會(huì )下降。
且 Innodb 每個(gè) B 樹(shù)節點(diǎn)存儲容量是 16 KB,理論上可存儲 2kw 行左右,這時(shí)樹(shù)高為3層。我們知道,innodb_buffer_pool 用來(lái)緩存表及索引,如果索引數據較大,緩存命中率就堪憂(yōu),同時(shí) innodb_buffer_pool 采用 LRU 算法進(jìn)行頁(yè)面淘汰,如果數據量過(guò)大,對老或非熱點(diǎn)數據的查詢(xún)可能就會(huì )把熱點(diǎn)數據給擠出去。
所以對于大表常見(jiàn)優(yōu)化即是分庫分表和讀寫(xiě)分離了。
是分庫還是分表呢?這要具體分析。
水平即切分數據,分散原有數據到更多的庫表中。
垂直即按照業(yè)務(wù)對庫,按字段對表切分。
工具方面有 sharding-sphere、TDDL、Mycat。動(dòng)起手來(lái)需要先評估分庫、表數,制定分片規則選 key,再開(kāi)發(fā)和數據遷移,還要考慮擴容問(wèn)題。
實(shí)際運行中,寫(xiě)問(wèn)題不大,主要問(wèn)題在于唯一 ID 生成、非 partition key 查詢(xún)、擴容。
當然,如果分庫還會(huì )面臨事務(wù)一致性和跨庫 join 等問(wèn)題。
分表針對大表解決 CPU 瓶頸,分庫解決 IO 瓶頸,二者將存儲壓力解決了。但查詢(xún)還不一定。
如果落到 DB 的 QPS 還是很高,且讀遠大于寫(xiě) ,就可以考慮讀寫(xiě)分離,基于主從模式將讀的壓力分攤,避免單機負載過(guò)高,同時(shí)也保證了高可用,實(shí)現了負載均衡。
主要問(wèn)題有過(guò)期讀和分配機制。
以上列舉了 MySQL 常見(jiàn)慢查詢(xún)原因和處理方法,介紹了應對較大數據場(chǎng)景的常用方法。
分庫分表和讀寫(xiě)分離是針對大數據或并發(fā)場(chǎng)景的,同時(shí)也為了提高系統的穩定和拓展性。但也不是所有的問(wèn)題都最適合這么解決。
前文有提到對于關(guān)鍵字查詢(xún)可以使用 ES。那接著(zhù)聊聊 ES 。
ES 是基于 Lucene 的近實(shí)時(shí)分布式搜索引擎。使用場(chǎng)景有全文搜索、NoSQL Json 文檔數據庫、監控日志、數據采集分析等。
對非數據開(kāi)發(fā)來(lái)說(shuō),常用的應該就是全文檢索和日志了。ES 的使用中,常和 Logstash, Kibana 結合,也成為 ELK 。先來(lái)瞧瞧日志怎么用的。
下面是我司日志系統某檢索操作:打開(kāi) Kibana 在 Discover 頁(yè)面輸入格式如 “xxx” 查詢(xún)。
該操作可以在 Dev Tools 的控制臺替換為:
GET yourIndex/_search{ "from" : 0, "size" : 10, "query" : { "match_phrase" : { "log" : "xxx" } }}
什么意思?Discover 中加上 “” 和 console 中的 match_phrase 都代表這是一個(gè)短語(yǔ)匹配,意味著(zhù)只保留那些包含全部搜索詞項,且位置與搜索詞項相同的文檔。
在 ES 7.0 之前存儲結構是 Index -> Type -> Document,按 MySQL 對比就是 database – table – id(實(shí)際這種對比不那么合理)。7.0 之后 Type 被廢棄了,就暫把 index 當做 table 吧。
在 Dev Tools 的 Console 可以通過(guò)以下命令查看一些基本信息。也可以替換為 crul 命令。
1. GET /_cat/health?v&pretty:查看集群健康狀態(tài)2. GET /_cat/shards?v :查看分片狀態(tài)3. GET yourindex/_mapping :index mapping結構4. GET yourindex/_settings :index setting結構5. GET /_cat/indices?v :查看當前節點(diǎn)所有索引信息
重點(diǎn)是 mapping 和 setting ,mapping 可以理解為 MySQL 中表的結構定義,setting 負責控制如分片數量、副本數量。
以下是截取了某日志 index 下的部分 mapping 結構,ES 對字符串類(lèi)型會(huì )默認定義成 text ,同時(shí)為它定義一個(gè)叫做 keyword 的子字段。這兩的區別是:text 類(lèi)型會(huì )進(jìn)行分詞, keyword 類(lèi)型不會(huì )進(jìn)行分詞。
"******": { "mappings": { "doc": { "properties": { "appname": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } }
分詞是什么意思?看完 ES 的索引原理你就 get 了。
ES 基于倒排索引。嘛意思?傳統索引一般是以文檔 ID 作索引,以?xún)热葑鳛橛涗?。倒排索引相反,根據已有屬性值,去找到相應的行所在的位置,也就是將單詞或內容作為索引,將文檔 ID 作為記錄。
下圖是 ES 倒排索引的示意圖,由 Term index,Team Dictionary 和 Posting List 組成。
圖中的 Ada、Sara 被稱(chēng)作 term,其實(shí)就是分詞后的詞了。如果把圖中的 Term Index 去掉,是不是有點(diǎn)像 MySQL 了?Term Dictionary 就像二級索引,但 MySQL 是保存在磁盤(pán)上的,檢索一個(gè) term 需要若干次的 random access 磁盤(pán)操作。
而 ES 在 Term Dictionary 基礎上多了層 Term Index ,它以 FST 形式保存在內存中,保存著(zhù) term 的前綴,借此可以快速的定位到 Term dictionary 的本 term 的 offset 。而且 FST 形式和 Term dictionary 的 block 存儲方式都很節省內存和磁盤(pán)空間。
到這就知道為啥快了,就是因為有了內存中的 Term Index , 它為 term 的索引 Term Dictionary 又做了一層索引。
不過(guò),也不是說(shuō) ES 什么查詢(xún)都比 MySQL 快。檢索大致分為兩類(lèi)。
ES 的索引存儲的就是分詞排序后的結果。比如圖中的 Ada,在 MySQL 中
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站