真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

php高并發(fā)數(shù)據(jù)庫優(yōu)化 php高并發(fā)實戰(zhàn)

如何處理大量數(shù)據(jù)并發(fā)操作

處理大量數(shù)據(jù)并發(fā)操作可以采用如下幾種方法:

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:域名注冊、網(wǎng)絡(luò)空間、營銷軟件、網(wǎng)站建設(shè)、祁連網(wǎng)站維護、網(wǎng)站推廣。

1.使用緩存:使用程序直接保存到內(nèi)存中。或者使用緩存框架: 用一個特定的類型值來保存,以區(qū)別空數(shù)據(jù)和未緩存的兩種狀態(tài)。

2.數(shù)據(jù)庫優(yōu)化:表結(jié)構(gòu)優(yōu)化;SQL語句優(yōu)化,語法優(yōu)化和處理邏輯優(yōu)化;分區(qū);分表;索引優(yōu)化;使用存儲過程代替直接操作。

3.分離活躍數(shù)據(jù):可以分為活躍用戶和不活躍用戶。

4.批量讀取和延遲修改: 高并發(fā)情況可以將多個查詢請求合并到一個。高并發(fā)且頻繁修改的可以暫存緩存中。

5.讀寫分離: 數(shù)據(jù)庫服務(wù)器配置多個,配置主從數(shù)據(jù)庫。寫用主數(shù)據(jù)庫,讀用從數(shù)據(jù)庫。

6.分布式數(shù)據(jù)庫: 將不同的表存放到不同的數(shù)據(jù)庫中,然后再放到不同的服務(wù)器中。

7.NoSql和Hadoop: NoSql,not only SQL。沒有關(guān)系型數(shù)據(jù)庫那么多限制,比較靈活高效。Hadoop,將一個表中的數(shù)據(jù)分層多塊,保存到多個節(jié)點(分布式)。每一塊數(shù)據(jù)都有多個節(jié)點保存(集群)。集群可以并行處理相同的數(shù)據(jù),還可以保證數(shù)據(jù)的完整性。

拓展資料:

大數(shù)據(jù)(big data),指無法在一定時間范圍內(nèi)用常規(guī)軟件工具進行捕捉、管理和處理的數(shù)據(jù)集合,是需要新處理模式才能具有更強的決策力、洞察發(fā)現(xiàn)力和流程優(yōu)化能力的海量、高增長率和多樣化的信息資產(chǎn)。

在維克托·邁爾-舍恩伯格及肯尼斯·庫克耶編寫的《大數(shù)據(jù)時代》中大數(shù)據(jù)指不用隨機分析法(抽樣調(diào)查)這樣捷徑,而采用所有數(shù)據(jù)進行分析處理。大數(shù)據(jù)的5V特點(IBM提出):Volume(大量)、Velocity(高速)、Variety(多樣)、Value(低價值密度)、Veracity(真實性)。

參考資料:網(wǎng)頁鏈接

php 高并發(fā)解決思路解決方案

php 高并發(fā)解決思路解決方案,如何應(yīng)對網(wǎng)站大流量高并發(fā)情況。本文為大家總結(jié)了常用的處理方式,但不是細節(jié),后續(xù)一系列細節(jié)教程給出。希望大家喜歡。

一 高并發(fā)的概念

在互聯(lián)網(wǎng)時代,并發(fā),高并發(fā)通常是指并發(fā)訪問。也就是在某個時間點,有多少個訪問同時到來。

二 高并發(fā)架構(gòu)相關(guān)概念

1、QPS (每秒查詢率) : 每秒鐘請求或者查詢的數(shù)量,在互聯(lián)網(wǎng)領(lǐng)域,指每秒響應(yīng)請求數(shù)(指 HTTP 請求)

2、PV(Page View):綜合瀏覽量,即頁面瀏覽量或者點擊量,一個訪客在 24 小時內(nèi)訪問的頁面數(shù)量

--注:同一個人瀏覽你的網(wǎng)站的同一頁面,只記做一次 pv

3、吞吐量(fetches/sec) :單位時間內(nèi)處理的請求數(shù)量 (通常由 QPS 和并發(fā)數(shù)決定)

4、響應(yīng)時間:從請求發(fā)出到收到響應(yīng)花費的時間

5、獨立訪客(UV):一定時間范圍內(nèi),相同訪客多次訪問網(wǎng)站,只計算為 1 個獨立訪客

6、帶寬:計算帶寬需關(guān)注兩個指標(biāo),峰值流量和頁面的平均大小

7、日網(wǎng)站帶寬: PV/統(tǒng)計時間(換算到秒) * 平均頁面大小(kb)* 8

三 需要注意點:

1、QPS 不等于并發(fā)連接數(shù)(QPS 是每秒 HTTP 請求數(shù)量,并發(fā)連接數(shù)是系統(tǒng)同時處理的請求數(shù)量)

2、峰值每秒請求數(shù)(QPS)= (總 PV 數(shù)*80%)/ (六小時秒數(shù)*20%)【代表 80%的訪問量都集中在 20%的時間內(nèi)】

3、壓力測試: 測試能承受的最大并發(fā)數(shù) 以及測試最大承受的 QPS 值

4、常用的性能測試工具【ab,wrk,httpload,Web Bench,Siege,Apache JMeter】

四 優(yōu)化

1、當(dāng) QPS 小于 50 時

優(yōu)化方案:為一般小型網(wǎng)站,不用考慮優(yōu)化

2、當(dāng) QPS 達到 100 時,遇到數(shù)據(jù)查詢瓶頸

優(yōu)化方案: 數(shù)據(jù)庫緩存層,數(shù)據(jù)庫的負載均衡

3、當(dāng) QPS 達到 800 時, 遇到帶寬瓶頸

優(yōu)化方案:CDN 加速,負載均衡

4、當(dāng) QPS 達到 1000 時

優(yōu)化方案: 做 html 靜態(tài)緩存

5、當(dāng) QPS 達到 2000 時

優(yōu)化方案: 做業(yè)務(wù)分離,分布式存儲

五、高并發(fā)解決方案案例:

1、流量優(yōu)化

防盜鏈處理(去除惡意請求)

2、前端優(yōu)化

(1) 減少 HTTP 請求[將 css,js 等合并]

(2) 添加異步請求(先不將所有數(shù)據(jù)都展示給用戶,用戶觸發(fā)某個事件,才會異步請求數(shù)據(jù))

(3) 啟用瀏覽器緩存和文件壓縮

(4) CDN 加速

(5) 建立獨立的圖片服務(wù)器(減少 I/O)

3、服務(wù)端優(yōu)化

(1) 頁面靜態(tài)化

(2) 并發(fā)處理

(3) 隊列處理

4、數(shù)據(jù)庫優(yōu)化

(1) 數(shù)據(jù)庫緩存

(2) 分庫分表,分區(qū)

(3) 讀寫分離

(4) 負載均衡

5、web 服務(wù)器優(yōu)化

(1) nginx 反向代理實現(xiàn)負載均衡

(2) lvs 實現(xiàn)負載均衡

php怎么處理高并發(fā)

以下內(nèi)容轉(zhuǎn)載自徐漢彬大牛的博客?億級Web系統(tǒng)搭建——單機到分布式集群?

當(dāng)一個Web系統(tǒng)從日訪問量10萬逐步增長到1000萬,甚至超過1億的過程中,Web系統(tǒng)承受的壓力會越來越大,在這個過程中,我們會遇到很多的問題。為了解決這些性能壓力帶來問題,我們需要在Web系統(tǒng)架構(gòu)層面搭建多個層次的緩存機制。在不同的壓力階段,我們會遇到不同的問題,通過搭建不同的服務(wù)和架構(gòu)來解決。

Web負載均衡?

Web負載均衡(Load Balancing),簡單地說就是給我們的服務(wù)器集群分配“工作任務(wù)”,而采用恰當(dāng)?shù)姆峙浞绞?,對于保護處于后端的Web服務(wù)器來說,非常重要。

負載均衡的策略有很多,我們從簡單的講起哈。

1.?HTTP重定向

當(dāng)用戶發(fā)來請求的時候,Web服務(wù)器通過修改HTTP響應(yīng)頭中的Location標(biāo)記來返回一個新的url,然后瀏覽器再繼續(xù)請求這個新url,實際上就是頁面重定向。通過重定向,來達到“負載均衡”的目標(biāo)。例如,我們在下載PHP源碼包的時候,點擊下載鏈接時,為了解決不同國家和地域下載速度的問題,它會返回一個離我們近的下載地址。重定向的HTTP返回碼是302

這個重定向非常容易實現(xiàn),并且可以自定義各種策略。但是,它在大規(guī)模訪問量下,性能不佳。而且,給用戶的體驗也不好,實際請求發(fā)生重定向,增加了網(wǎng)絡(luò)延時。

2. 反向代理負載均衡

反向代理服務(wù)的核心工作主要是轉(zhuǎn)發(fā)HTTP請求,扮演了瀏覽器端和后臺Web服務(wù)器中轉(zhuǎn)的角色。因為它工作在HTTP層(應(yīng)用層),也就是網(wǎng)絡(luò)七層結(jié)構(gòu)中的第七層,因此也被稱為“七層負載均衡”。可以做反向代理的軟件很多,比較常見的一種是Nginx。

Nginx是一種非常靈活的反向代理軟件,可以自由定制化轉(zhuǎn)發(fā)策略,分配服務(wù)器流量的權(quán)重等。反向代理中,常見的一個問題,就是Web服務(wù)器存儲的session數(shù)據(jù),因為一般負載均衡的策略都是隨機分配請求的。同一個登錄用戶的請求,無法保證一定分配到相同的Web機器上,會導(dǎo)致無法找到session的問題。

解決方案主要有兩種:

1.?配置反向代理的轉(zhuǎn)發(fā)規(guī)則,讓同一個用戶的請求一定落到同一臺機器上(通過分析cookie),復(fù)雜的轉(zhuǎn)發(fā)規(guī)則將會消耗更多的CPU,也增加了代理服務(wù)器的負擔(dān)。

2.?將session這類的信息,專門用某個獨立服務(wù)來存儲,例如redis/memchache,這個方案是比較推薦的。

反向代理服務(wù),也是可以開啟緩存的,如果開啟了,會增加反向代理的負擔(dān),需要謹慎使用。這種負載均衡策略實現(xiàn)和部署非常簡單,而且性能表現(xiàn)也比較好。但是,它有“單點故障”的問題,如果掛了,會帶來很多的麻煩。而且,到了后期Web服務(wù)器繼續(xù)增加,它本身可能成為系統(tǒng)的瓶頸。

3. IP負載均衡

IP負載均衡服務(wù)是工作在網(wǎng)絡(luò)層(修改IP)和傳輸層(修改端口,第四層),比起工作在應(yīng)用層(第七層)性能要高出非常多。原理是,他是對IP層的數(shù)據(jù)包的IP地址和端口信息進行修改,達到負載均衡的目的。這種方式,也被稱為“四層負載均衡”。常見的負載均衡方式,是LVS(Linux Virtual Server,Linux虛擬服務(wù)),通過IPVS(IP Virtual Server,IP虛擬服務(wù))來實現(xiàn)。

在負載均衡服務(wù)器收到客戶端的IP包的時候,會修改IP包的目標(biāo)IP地址或端口,然后原封不動地投遞到內(nèi)部網(wǎng)絡(luò)中,數(shù)據(jù)包會流入到實際Web服務(wù)器。實際服務(wù)器處理完成后,又會將數(shù)據(jù)包投遞回給負載均衡服務(wù)器,它再修改目標(biāo)IP地址為用戶IP地址,最終回到客戶端。

上述的方式叫LVS-NAT,除此之外,還有LVS-RD(直接路由),LVS-TUN(IP隧道),三者之間都屬于LVS的方式,但是有一定的區(qū)別,篇幅問題,不贅敘。

IP負載均衡的性能要高出Nginx的反向代理很多,它只處理到傳輸層為止的數(shù)據(jù)包,并不做進一步的組包,然后直接轉(zhuǎn)發(fā)給實際服務(wù)器。不過,它的配置和搭建比較復(fù)雜。

4. DNS負載均衡

DNS(Domain Name System)負責(zé)域名解析的服務(wù),域名url實際上是服務(wù)器的別名,實際映射是一個IP地址,解析過程,就是DNS完成域名到IP的映射。而一個域名是可以配置成對應(yīng)多個IP的。因此,DNS也就可以作為負載均衡服務(wù)。

這種負載均衡策略,配置簡單,性能極佳。但是,不能自由定義規(guī)則,而且,變更被映射的IP或者機器故障時很麻煩,還存在DNS生效延遲的問題。?

5. DNS/GSLB負載均衡

我們常用的CDN(Content Delivery Network,內(nèi)容分發(fā)網(wǎng)絡(luò))實現(xiàn)方式,其實就是在同一個域名映射為多IP的基礎(chǔ)上更進一步,通過GSLB(Global Server Load Balance,全局負載均衡)按照指定規(guī)則映射域名的IP。一般情況下都是按照地理位置,將離用戶近的IP返回給用戶,減少網(wǎng)絡(luò)傳輸中的路由節(jié)點之間的跳躍消耗。

“向上尋找”,實際過程是LDNS(Local DNS)先向根域名服務(wù)(Root Name Server)獲取到頂級根的Name Server(例如.com的),然后得到指定域名的授權(quán)DNS,然后再獲得實際服務(wù)器IP。

CDN在Web系統(tǒng)中,一般情況下是用來解決大小較大的靜態(tài)資源(html/Js/Css/圖片等)的加載問題,讓這些比較依賴網(wǎng)絡(luò)下載的內(nèi)容,盡可能離用戶更近,提升用戶體驗。

例如,我訪問了一張imgcache.gtimg.cn上的圖片(騰訊的自建CDN,不使用qq.com域名的原因是防止http請求的時候,帶上了多余的cookie信息),我獲得的IP是183.60.217.90。

這種方式,和前面的DNS負載均衡一樣,不僅性能極佳,而且支持配置多種策略。但是,搭建和維護成本非常高。互聯(lián)網(wǎng)一線公司,會自建CDN服務(wù),中小型公司一般使用第三方提供的CDN。

Web系統(tǒng)的緩存機制的建立和優(yōu)化

剛剛我們講完了Web系統(tǒng)的外部網(wǎng)絡(luò)環(huán)境,現(xiàn)在我們開始關(guān)注我們Web系統(tǒng)自身的性能問題。我們的Web站點隨著訪問量的上升,會遇到很多的挑戰(zhàn),解決這些問題不僅僅是擴容機器這么簡單,建立和使用合適的緩存機制才是根本。

最開始,我們的Web系統(tǒng)架構(gòu)可能是這樣的,每個環(huán)節(jié),都可能只有1臺機器。

我們從最根本的數(shù)據(jù)存儲開始看哈。

一、 MySQL數(shù)據(jù)庫內(nèi)部緩存使用

MySQL的緩存機制,就從先從MySQL內(nèi)部開始,下面的內(nèi)容將以最常見的InnoDB存儲引擎為主。

1. 建立恰當(dāng)?shù)乃饕?/p>

最簡單的是建立索引,索引在表數(shù)據(jù)比較大的時候,起到快速檢索數(shù)據(jù)的作用,但是成本也是有的。首先,占用了一定的磁盤空間,其中組合索引最突出,使用需要謹慎,它產(chǎn)生的索引甚至?xí)仍磾?shù)據(jù)更大。其次,建立索引之后的數(shù)據(jù)insert/update/delete等操作,因為需要更新原來的索引,耗時會增加。當(dāng)然,實際上我們的系統(tǒng)從總體來說,是以select查詢操作居多,因此,索引的使用仍然對系統(tǒng)性能有大幅提升的作用。

2. 數(shù)據(jù)庫連接線程池緩存

如果,每一個數(shù)據(jù)庫操作請求都需要創(chuàng)建和銷毀連接的話,對數(shù)據(jù)庫來說,無疑也是一種巨大的開銷。為了減少這類型的開銷,可以在MySQL中配置thread_cache_size來表示保留多少線程用于復(fù)用。線程不夠的時候,再創(chuàng)建,空閑過多的時候,則銷毀。

其實,還有更為激進一點的做法,使用pconnect(數(shù)據(jù)庫長連接),線程一旦創(chuàng)建在很長時間內(nèi)都保持著。但是,在訪問量比較大,機器比較多的情況下,這種用法很可能會導(dǎo)致“數(shù)據(jù)庫連接數(shù)耗盡”,因為建立連接并不回收,最終達到數(shù)據(jù)庫的max_connections(最大連接數(shù))。因此,長連接的用法通常需要在CGI和MySQL之間實現(xiàn)一個“連接池”服務(wù),控制CGI機器“盲目”創(chuàng)建連接數(shù)。

建立數(shù)據(jù)庫連接池服務(wù),有很多實現(xiàn)的方式,PHP的話,我推薦使用swoole(PHP的一個網(wǎng)絡(luò)通訊拓展)來實現(xiàn)。

3. Innodb緩存設(shè)置(innodb_buffer_pool_size)

innodb_buffer_pool_size這是個用來保存索引和數(shù)據(jù)的內(nèi)存緩存區(qū),如果機器是MySQL獨占的機器,一般推薦為機器物理內(nèi)存的80%。在取表數(shù)據(jù)的場景中,它可以減少磁盤IO。一般來說,這個值設(shè)置越大,cache命中率會越高。

4. 分庫/分表/分區(qū)。

MySQL數(shù)據(jù)庫表一般承受數(shù)據(jù)量在百萬級別,再往上增長,各項性能將會出現(xiàn)大幅度下降,因此,當(dāng)我們預(yù)見數(shù)據(jù)量會超過這個量級的時候,建議進行分庫/分表/分區(qū)等操作。最好的做法,是服務(wù)在搭建之初就設(shè)計為分庫分表的存儲模式,從根本上杜絕中后期的風(fēng)險。不過,會犧牲一些便利性,例如列表式的查詢,同時,也增加了維護的復(fù)雜度。不過,到了數(shù)據(jù)量千萬級別或者以上的時候,我們會發(fā)現(xiàn),它們都是值得的。?

二、 MySQL數(shù)據(jù)庫多臺服務(wù)搭建

1臺MySQL機器,實際上是高風(fēng)險的單點,因為如果它掛了,我們Web服務(wù)就不可用了。而且,隨著Web系統(tǒng)訪問量繼續(xù)增加,終于有一天,我們發(fā)現(xiàn)1臺MySQL服務(wù)器無法支撐下去,我們開始需要使用更多的MySQL機器。當(dāng)引入多臺MySQL機器的時候,很多新的問題又將產(chǎn)生。

1. 建立MySQL主從,從庫作為備份

這種做法純粹為了解決“單點故障”的問題,在主庫出故障的時候,切換到從庫。不過,這種做法實際上有點浪費資源,因為從庫實際上被閑著了。

2. MySQL讀寫分離,主庫寫,從庫讀。

兩臺數(shù)據(jù)庫做讀寫分離,主庫負責(zé)寫入類的操作,從庫負責(zé)讀的操作。并且,如果主庫發(fā)生故障,仍然不影響讀的操作,同時也可以將全部讀寫都臨時切換到從庫中(需要注意流量,可能會因為流量過大,把從庫也拖垮)。

3. 主主互備。

兩臺MySQL之間互為彼此的從庫,同時又是主庫。這種方案,既做到了訪問量的壓力分流,同時也解決了“單點故障”問題。任何一臺故障,都還有另外一套可供使用的服務(wù)。

不過,這種方案,只能用在兩臺機器的場景。如果業(yè)務(wù)拓展還是很快的話,可以選擇將業(yè)務(wù)分離,建立多個主主互備。

三、 MySQL數(shù)據(jù)庫機器之間的數(shù)據(jù)同步

每當(dāng)我們解決一個問題,新的問題必然誕生在舊的解決方案上。當(dāng)我們有多臺MySQL,在業(yè)務(wù)高峰期,很可能出現(xiàn)兩個庫之間的數(shù)據(jù)有延遲的場景。并且,網(wǎng)絡(luò)和機器負載等,也會影響數(shù)據(jù)同步的延遲。我們曾經(jīng)遇到過,在日訪問量接近1億的特殊場景下,出現(xiàn),從庫數(shù)據(jù)需要很多天才能同步追上主庫的數(shù)據(jù)。這種場景下,從庫基本失去效用了。

于是,解決同步問題,就是我們下一步需要關(guān)注的點。

1. MySQL自帶多線程同步

MySQL5.6開始支持主庫和從庫數(shù)據(jù)同步,走多線程。但是,限制也是比較明顯的,只能以庫為單位。MySQL數(shù)據(jù)同步是通過binlog日志,主庫寫入到binlog日志的操作,是具有順序的,尤其當(dāng)SQL操作中含有對于表結(jié)構(gòu)的修改等操作,對于后續(xù)的SQL語句操作是有影響的。因此,從庫同步數(shù)據(jù),必須走單進程。

2. 自己實現(xiàn)解析binlog,多線程寫入。

以數(shù)據(jù)庫的表為單位,解析binlog多張表同時做數(shù)據(jù)同步。這樣做的話,的確能夠加快數(shù)據(jù)同步的效率,但是,如果表和表之間存在結(jié)構(gòu)關(guān)系或者數(shù)據(jù)依賴的話,則同樣存在寫入順序的問題。這種方式,可用于一些比較穩(wěn)定并且相對獨立的數(shù)據(jù)表。

國內(nèi)一線互聯(lián)網(wǎng)公司,大部分都是通過這種方式,來加快數(shù)據(jù)同步效率。還有更為激進的做法,是直接解析binlog,忽略以表為單位,直接寫入。但是這種做法,實現(xiàn)復(fù)雜,使用范圍就更受到限制,只能用于一些場景特殊的數(shù)據(jù)庫中(沒有表結(jié)構(gòu)變更,表和表之間沒有數(shù)據(jù)依賴等特殊表)。?

四、 在Web服務(wù)器和數(shù)據(jù)庫之間建立緩存

實際上,解決大訪問量的問題,不能僅僅著眼于數(shù)據(jù)庫層面。根據(jù)“二八定律”,80%的請求只關(guān)注在20%的熱點數(shù)據(jù)上。因此,我們應(yīng)該建立Web服務(wù)器和數(shù)據(jù)庫之間的緩存機制。這種機制,可以用磁盤作為緩存,也可以用內(nèi)存緩存的方式。通過它們,將大部分的熱點數(shù)據(jù)查詢,阻擋在數(shù)據(jù)庫之前。

1. 頁面靜態(tài)化

用戶訪問網(wǎng)站的某個頁面,頁面上的大部分內(nèi)容在很長一段時間內(nèi),可能都是沒有變化的。例如一篇新聞報道,一旦發(fā)布幾乎是不會修改內(nèi)容的。這樣的話,通過CGI生成的靜態(tài)html頁面緩存到Web服務(wù)器的磁盤本地。除了第一次,是通過動態(tài)CGI查詢數(shù)據(jù)庫獲取之外,之后都直接將本地磁盤文件返回給用戶。

在Web系統(tǒng)規(guī)模比較小的時候,這種做法看似完美。但是,一旦Web系統(tǒng)規(guī)模變大,例如當(dāng)我有100臺的Web服務(wù)器的時候。那樣這些磁盤文件,將會有100份,這個是資源浪費,也不好維護。這個時候有人會想,可以集中一臺服務(wù)器存起來,呵呵,不如看看下面一種緩存方式吧,它就是這樣做的。

2. 單臺內(nèi)存緩存

通過頁面靜態(tài)化的例子中,我們可以知道將“緩存”搭建在Web機器本機是不好維護的,會帶來更多問題(實際上,通過PHP的apc拓展,可通過Key/value操作Web服務(wù)器的本機內(nèi)存)。因此,我們選擇搭建的內(nèi)存緩存服務(wù),也必須是一個獨立的服務(wù)。

內(nèi)存緩存的選擇,主要有redis/memcache。從性能上說,兩者差別不大,從功能豐富程度上說,Redis更勝一籌。

3. 內(nèi)存緩存集群

當(dāng)我們搭建單臺內(nèi)存緩存完畢,我們又會面臨單點故障的問題,因此,我們必須將它變成一個集群。簡單的做法,是給他增加一個slave作為備份機器。但是,如果請求量真的很多,我們發(fā)現(xiàn)cache命中率不高,需要更多的機器內(nèi)存呢?因此,我們更建議將它配置成一個集群。例如,類似redis cluster。

Redis cluster集群內(nèi)的Redis互為多組主從,同時每個節(jié)點都可以接受請求,在拓展集群的時候比較方便??蛻舳丝梢韵蛉我庖粋€節(jié)點發(fā)送請求,如果是它的“負責(zé)”的內(nèi)容,則直接返回內(nèi)容。否則,查找實際負責(zé)Redis節(jié)點,然后將地址告知客戶端,客戶端重新請求。

對于使用緩存服務(wù)的客戶端來說,這一切是透明的。

內(nèi)存緩存服務(wù)在切換的時候,是有一定風(fēng)險的。從A集群切換到B集群的過程中,必須保證B集群提前做好“預(yù)熱”(B集群的內(nèi)存中的熱點數(shù)據(jù),應(yīng)該盡量與A集群相同,否則,切換的一瞬間大量請求內(nèi)容,在B集群的內(nèi)存緩存中查找不到,流量直接沖擊后端的數(shù)據(jù)庫服務(wù),很可能導(dǎo)致數(shù)據(jù)庫宕機)。

4. 減少數(shù)據(jù)庫“寫”

上面的機制,都實現(xiàn)減少數(shù)據(jù)庫的“讀”的操作,但是,寫的操作也是一個大的壓力。寫的操作,雖然無法減少,但是可以通過合并請求,來起到減輕壓力的效果。這個時候,我們就需要在內(nèi)存緩存集群和數(shù)據(jù)庫集群之間,建立一個修改同步機制。

先將修改請求生效在cache中,讓外界查詢顯示正常,然后將這些sql修改放入到一個隊列中存儲起來,隊列滿或者每隔一段時間,合并為一個請求到數(shù)據(jù)庫中更新數(shù)據(jù)庫。

除了上述通過改變系統(tǒng)架構(gòu)的方式提升寫的性能外,MySQL本身也可以通過配置參數(shù)innodb_flush_log_at_trx_commit來調(diào)整寫入磁盤的策略。如果機器成本允許,從硬件層面解決問題,可以選擇老一點的RAID(Redundant Arrays of independent Disks,磁盤列陣)或者比較新的SSD(Solid State Drives,固態(tài)硬盤)。

5. NoSQL存儲

不管數(shù)據(jù)庫的讀還是寫,當(dāng)流量再進一步上漲,終會達到“人力有窮時”的場景。繼續(xù)加機器的成本比較高,并且不一定可以真正解決問題的時候。這個時候,部分核心數(shù)據(jù),就可以考慮使用NoSQL的數(shù)據(jù)庫。NoSQL存儲,大部分都是采用key-value的方式,這里比較推薦使用上面介紹過Redis,Redis本身是一個內(nèi)存cache,同時也可以當(dāng)做一個存儲來使用,讓它直接將數(shù)據(jù)落地到磁盤。

這樣的話,我們就將數(shù)據(jù)庫中某些被頻繁讀寫的數(shù)據(jù),分離出來,放在我們新搭建的Redis存儲集群中,又進一步減輕原來MySQL數(shù)據(jù)庫的壓力,同時因為Redis本身是個內(nèi)存級別的Cache,讀寫的性能都會大幅度提升。

國內(nèi)一線互聯(lián)網(wǎng)公司,架構(gòu)上采用的解決方案很多是類似于上述方案,不過,使用的cache服務(wù)卻不一定是Redis,他們會有更豐富的其他選擇,甚至根據(jù)自身業(yè)務(wù)特點開發(fā)出自己的NoSQL服務(wù)。

6. 空節(jié)點查詢問題

當(dāng)我們搭建完前面所說的全部服務(wù),認為Web系統(tǒng)已經(jīng)很強的時候。我們還是那句話,新的問題還是會來的??展?jié)點查詢,是指那些數(shù)據(jù)庫中根本不存在的數(shù)據(jù)請求。例如,我請求查詢一個不存在人員信息,系統(tǒng)會從各級緩存逐級查找,最后查到到數(shù)據(jù)庫本身,然后才得出查找不到的結(jié)論,返回給前端。因為各級cache對它無效,這個請求是非常消耗系統(tǒng)資源的,而如果大量的空節(jié)點查詢,是可以沖擊到系統(tǒng)服務(wù)的。

在我曾經(jīng)的工作經(jīng)歷中,曾深受其害。因此,為了維護Web系統(tǒng)的穩(wěn)定性,設(shè)計適當(dāng)?shù)目展?jié)點過濾機制,非常有必要。

我們當(dāng)時采用的方式,就是設(shè)計一張簡單的記錄映射表。將存在的記錄存儲起來,放入到一臺內(nèi)存cache中,這樣的話,如果還有空節(jié)點查詢,則在緩存這一層就被阻擋了。

異地部署(地理分布式)

完成了上述架構(gòu)建設(shè)之后,我們的系統(tǒng)是否就已經(jīng)足夠強大了呢?答案當(dāng)然是否定的哈,優(yōu)化是無極限的。Web系統(tǒng)雖然表面上看,似乎比較強大了,但是給予用戶的體驗卻不一定是最好的。因為東北的同學(xué),訪問深圳的一個網(wǎng)站服務(wù),他還是會感到一些網(wǎng)絡(luò)距離上的慢。這個時候,我們就需要做異地部署,讓W(xué)eb系統(tǒng)離用戶更近。

一、 核心集中與節(jié)點分散

有玩過大型網(wǎng)游的同學(xué)都會知道,網(wǎng)游是有很多個區(qū)的,一般都是按照地域來分,例如廣東專區(qū),北京專區(qū)。如果一個在廣東的玩家,去北京專區(qū)玩,那么他會感覺明顯比在廣東專區(qū)卡。實際上,這些大區(qū)的名稱就已經(jīng)說明了,它的服務(wù)器所在地,所以,廣東的玩家去連接地處北京的服務(wù)器,網(wǎng)絡(luò)當(dāng)然會比較慢。

當(dāng)一個系統(tǒng)和服務(wù)足夠大的時候,就必須開始考慮異地部署的問題了。讓你的服務(wù),盡可能離用戶更近。我們前面已經(jīng)提到了Web的靜態(tài)資源,可以存放在CDN上,然后通過DNS/GSLB的方式,讓靜態(tài)資源的分散“全國各地”。但是,CDN只解決的靜態(tài)資源的問題,沒有解決后端龐大的系統(tǒng)服務(wù)還只集中在某個固定城市的問題。

這個時候,異地部署就開始了。異地部署一般遵循:核心集中,節(jié)點分散。

·?核心集中:實際部署過程中,總有一部分的數(shù)據(jù)和服務(wù)存在不可部署多套,或者部署多套成本巨大。而對于這些服務(wù)和數(shù)據(jù),就仍然維持一套,而部署地點選擇一個地域比較中心的地方,通過網(wǎng)絡(luò)內(nèi)部專線來和各個節(jié)點通訊。

·?節(jié)點分散:將一些服務(wù)部署為多套,分布在各個城市節(jié)點,讓用戶請求盡可能選擇近的節(jié)點訪問服務(wù)。

例如,我們選擇在上海部署為核心節(jié)點,北京,深圳,武漢,上海為分散節(jié)點(上海自己本身也是一個分散節(jié)點)。我們的服務(wù)架構(gòu)如圖:

需要補充一下的是,上圖中上海節(jié)點和核心節(jié)點是同處于一個機房的,其他分散節(jié)點各自獨立機房。?

國內(nèi)有很多大型網(wǎng)游,都是大致遵循上述架構(gòu)。它們會把數(shù)據(jù)量不大的用戶核心賬號等放在核心節(jié)點,而大部分的網(wǎng)游數(shù)據(jù),例如裝備、任務(wù)等數(shù)據(jù)和服務(wù)放在地區(qū)節(jié)點里。當(dāng)然,核心節(jié)點和地域節(jié)點之間,也有緩存機制。?

二、 節(jié)點容災(zāi)和過載保護

節(jié)點容災(zāi)是指,某個節(jié)點如果發(fā)生故障時,我們需要建立一個機制去保證服務(wù)仍然可用。毫無疑問,這里比較常見的容災(zāi)方式,是切換到附近城市節(jié)點。假如系統(tǒng)的天津節(jié)點發(fā)生故障,那么我們就將網(wǎng)絡(luò)流量切換到附近的北京節(jié)點上??紤]到負載均衡,可能需要同時將流量切換到附近的幾個地域節(jié)點。另一方面,核心節(jié)點自身也是需要自己做好容災(zāi)和備份的,核心節(jié)點一旦故障,就會影響全國服務(wù)。

過載保護,指的是一個節(jié)點已經(jīng)達到最大容量,無法繼續(xù)接接受更多請求了,系統(tǒng)必須有一個保護的機制。一個服務(wù)已經(jīng)滿負載,還繼續(xù)接受新的請求,結(jié)果很可能就是宕機,影響整個節(jié)點的服務(wù),為了至少保障大部分用戶的正常使用,過載保護是必要的。

解決過載保護,一般2個方向:

·?拒絕服務(wù),檢測到滿負載之后,就不再接受新的連接請求。例如網(wǎng)游登入中的排隊。

·?分流到其他節(jié)點。這種的話,系統(tǒng)實現(xiàn)更為復(fù)雜,又涉及到負載均衡的問題。

小結(jié)

Web系統(tǒng)會隨著訪問規(guī)模的增長,漸漸地從1臺服務(wù)器可以滿足需求,一直成長為“龐然大物”的大集群。而這個Web系統(tǒng)變大的過程,實際上就是我們解決問題的過程。在不同的階段,解決不同的問題,而新的問題又誕生在舊的解決方案之上。

系統(tǒng)的優(yōu)化是沒有極限的,軟件和系統(tǒng)架構(gòu)也一直在快速發(fā)展,新的方案解決了老的問題,同時也帶來新的挑戰(zhàn)。

MySQL數(shù)據(jù)庫性能優(yōu)化之分區(qū)分表分庫

分表是分散數(shù)據(jù)庫壓力的好方法。

分表,最直白的意思,就是將一個表結(jié)構(gòu)分為多個表,然后,可以再同一個庫里,也可以放到不同的庫。

當(dāng)然,首先要知道什么情況下,才需要分表。個人覺得單表記錄條數(shù)達到百萬到千萬級別時就要使用分表了。

分表的分類

**1、縱向分表**

將本來可以在同一個表的內(nèi)容,人為劃分為多個表。(所謂的本來,是指按照關(guān)系型數(shù)據(jù)庫的第三范式要求,是應(yīng)該在同一個表的。)

分表理由:根據(jù)數(shù)據(jù)的活躍度進行分離,(因為不同活躍的數(shù)據(jù),處理方式是不同的)

案例:

對于一個博客系統(tǒng),文章標(biāo)題,作者,分類,創(chuàng)建時間等,是變化頻率慢,查詢次數(shù)多,而且最好有很好的實時性的數(shù)據(jù),我們把它叫做冷數(shù)據(jù)。而博客的瀏覽量,回復(fù)數(shù)等,類似的統(tǒng)計信息,或者別的變化頻率比較高的數(shù)據(jù),我們把它叫做活躍數(shù)據(jù)。所以,在進行數(shù)據(jù)庫結(jié)構(gòu)設(shè)計的時候,就應(yīng)該考慮分表,首先是縱向分表的處理。

這樣縱向分表后:

首先存儲引擎的使用不同,冷數(shù)據(jù)使用MyIsam 可以有更好的查詢數(shù)據(jù)?;钴S數(shù)據(jù),可以使用Innodb ,可以有更好的更新速度。

其次,對冷數(shù)據(jù)進行更多的從庫配置,因為更多的操作時查詢,這樣來加快查詢速度。對熱數(shù)據(jù),可以相對有更多的主庫的橫向分表處理。

其實,對于一些特殊的活躍數(shù)據(jù),也可以考慮使用memcache ,redis之類的緩存,等累計到一定量再去更新數(shù)據(jù)庫。或者mongodb 一類的nosql 數(shù)據(jù)庫,這里只是舉例,就先不說這個。

**2、橫向分表**

字面意思,就可以看出來,是把大的表結(jié)構(gòu),橫向切割為同樣結(jié)構(gòu)的不同表,如,用戶信息表,user_1,user_2等。表結(jié)構(gòu)是完全一樣,但是,根據(jù)某些特定的規(guī)則來劃分的表,如根據(jù)用戶ID來取模劃分。

分表理由:根據(jù)數(shù)據(jù)量的規(guī)模來劃分,保證單表的容量不會太大,從而來保證單表的查詢等處理能力。

案例:同上面的例子,博客系統(tǒng)。當(dāng)博客的量達到很大時候,就應(yīng)該采取橫向分割來降低每個單表的壓力,來提升性能。例如博客的冷數(shù)據(jù)表,假如分為100個表,當(dāng)同時有100萬個用戶在瀏覽時,如果是單表的話,會進行100萬次請求,而現(xiàn)在分表后,就可能是每個表進行1萬個數(shù)據(jù)的請求(因為,不可能絕對的平均,只是假設(shè)),這樣壓力就降低了很多很多。

延伸:為什么要分表和分區(qū)?

日常開發(fā)中我們經(jīng)常會遇到大表的情況,所謂的大表是指存儲了百萬級乃至千萬級條記錄的表。這樣的表過于龐大,導(dǎo)致數(shù)據(jù)庫在查詢和插入的時候耗時太長,性能低下,如果涉及聯(lián)合查詢的情況,性能會更加糟糕。分表和表分區(qū)的目的就是減少數(shù)據(jù)庫的負擔(dān),提高數(shù)據(jù)庫的效率,通常點來講就是提高表的增刪改查效率。

什么是分表?

分表是將一個大表按照一定的規(guī)則分解成多張具有獨立存儲空間的實體表,我們可以稱為子表,每個表都對應(yīng)三個文件,MYD數(shù)據(jù)文件,.MYI索引文件,.frm表結(jié)構(gòu)文件。這些子表可以分布在同一塊磁盤上,也可以在不同的機器上。app讀寫的時候根據(jù)事先定義好的規(guī)則得到對應(yīng)的子表名,然后去操作它。

什么是分區(qū)?

分區(qū)和分表相似,都是按照規(guī)則分解表。不同在于分表將大表分解為若干個獨立的實體表,而分區(qū)是將數(shù)據(jù)分段劃分在多個位置存放,可以是同一塊磁盤也可以在不同的機器。分區(qū)后,表面上還是一張表,但數(shù)據(jù)散列到多個位置了。app讀寫的時候操作的還是大表名字,db自動去組織分區(qū)的數(shù)據(jù)。

**MySQL分表和分區(qū)有什么聯(lián)系呢?**

1、都能提高mysql的性高,在高并發(fā)狀態(tài)下都有一個良好的表現(xiàn)。

2、分表和分區(qū)不矛盾,可以相互配合的,對于那些大訪問量,并且表數(shù)據(jù)比較多的表,我們可以采取分表和分區(qū)結(jié)合的方式(如果merge這種分表方式,不能和分區(qū)配合的話,可以用其他的分表試),訪問量不大,但是表數(shù)據(jù)很多的表,我們可以采取分區(qū)的方式等。

3、分表技術(shù)是比較麻煩的,需要手動去創(chuàng)建子表,app服務(wù)端讀寫時候需要計算子表名。采用merge好一些,但也要創(chuàng)建子表和配置子表間的union關(guān)系。

4、表分區(qū)相對于分表,操作方便,不需要創(chuàng)建子表。

我們知道對于大型的互聯(lián)網(wǎng)應(yīng)用,數(shù)據(jù)庫單表的數(shù)據(jù)量可能達到千萬甚至上億級別,同時面臨這高并發(fā)的壓力。Master-Slave結(jié)構(gòu)只能對數(shù)據(jù)庫的讀能力進行擴展,寫操作還是集中在Master中,Master并不能無限制的掛接Slave庫,如果需要對數(shù)據(jù)庫的吞吐能力進行進一步的擴展,可以考慮采用分庫分表的策略。

**1、分表**

在分表之前,首先要選中合適的分表策略(以哪個字典為分表字段,需要將數(shù)據(jù)分為多少張表),使數(shù)據(jù)能夠均衡的分布在多張表中,并且不影響正常的查詢。在企業(yè)級應(yīng)用中,往往使用org_id(組織主鍵)做為分表字段,在互聯(lián)網(wǎng)應(yīng)用中往往是userid。在確定分表策略后,當(dāng)數(shù)據(jù)進行存儲及查詢時,需要確定到哪張表里去查找數(shù)據(jù),

數(shù)據(jù)存放的數(shù)據(jù)表 = 分表字段的內(nèi)容 % 分表數(shù)量

**2、分庫**

分表能夠解決單表數(shù)據(jù)量過大帶來的查詢效率下降的問題,但是不能給數(shù)據(jù)庫的并發(fā)訪問帶來質(zhì)的提升,面對高并發(fā)的寫訪問,當(dāng)Master無法承擔(dān)高并發(fā)的寫入請求時,不管如何擴展Slave服務(wù)器,都沒有意義了。我們通過對數(shù)據(jù)庫進行拆分,來提高數(shù)據(jù)庫的寫入能力,即所謂的分庫。分庫采用對關(guān)鍵字取模的方式,對數(shù)據(jù)庫進行路由。

數(shù)據(jù)存放的數(shù)據(jù)庫=分庫字段的內(nèi)容%數(shù)據(jù)庫的數(shù)量

**3、即分表又分庫**

數(shù)據(jù)庫分表可以解決單表海量數(shù)據(jù)的查詢性能問題,分庫可以解決單臺數(shù)據(jù)庫的并發(fā)訪問壓力問題。

當(dāng)數(shù)據(jù)庫同時面臨海量數(shù)據(jù)存儲和高并發(fā)訪問的時候,需要同時采取分表和分庫策略。一般分表分庫策略如下:

中間變量 = 關(guān)鍵字%(數(shù)據(jù)庫數(shù)量*單庫數(shù)據(jù)表數(shù)量)

庫 = 取整(中間變量/單庫數(shù)據(jù)表數(shù)量)

表 = (中間變量%單庫數(shù)據(jù)表數(shù)量)

實例:

1、分庫分表

很明顯,一個主表(也就是很重要的表,例如用戶表)無限制的增長勢必嚴重影響性能,分庫與分表是一個很不錯的解決途徑,也就是性能優(yōu)化途徑,現(xiàn)在的案例是我們有一個1000多萬條記錄的用戶表members,查詢起來非常之慢,同事的做法是將其散列到100個表中,分別從members0到members99,然后根據(jù)mid分發(fā)記錄到這些表中,牛逼的代碼大概是這樣子:

復(fù)制代碼 代碼如下:

?php

for($i=0;$i 100; $i++ ){

//echo "CREATE TABLE db2.members{$i} LIKE db1.members

";

echo "INSERT INTO members{$i} SELECT * FROM members WHERE mid%100={$i}

";

}

?

2、不停機修改mysql表結(jié)構(gòu)

同樣還是members表,前期設(shè)計的表結(jié)構(gòu)不盡合理,隨著數(shù)據(jù)庫不斷運行,其冗余數(shù)據(jù)也是增長巨大,同事使用了下面的方法來處理:

先創(chuàng)建一個臨時表:

/*創(chuàng)建臨時表*/

CREATE TABLE members_tmp LIKE members

然后修改members_tmp的表結(jié)構(gòu)為新結(jié)構(gòu),接著使用上面那個for循環(huán)來導(dǎo)出數(shù)據(jù),因為1000萬的數(shù)據(jù)一次性導(dǎo)出是不對的,mid是主鍵,一個區(qū)間一個區(qū)間的導(dǎo),基本是一次導(dǎo)出5萬條吧,這里略去了

接著重命名將新表替換上去:

/*這是個頗為經(jīng)典的語句哈*/

RENAME TABLE members TO members_bak,members_tmp TO members;

就是這樣,基本可以做到無損失,無需停機更新表結(jié)構(gòu),但實際上RENAME期間表是被鎖死的,所以選擇在線少的時候操作是一個技巧。經(jīng)過這個操作,使得原先8G多的表,一下子變成了2G多。

問個mysql優(yōu)化問題

在開始演示之前,我們先介紹下兩個概念。

概念一,數(shù)據(jù)的可選擇性基數(shù),也就是常說的cardinality值。

查詢優(yōu)化器在生成各種執(zhí)行計劃之前,得先從統(tǒng)計信息中取得相關(guān)數(shù)據(jù),這樣才能估算每步操作所涉及到的記錄數(shù),而這個相關(guān)數(shù)據(jù)就是cardinality。簡單來說,就是每個值在每個字段中的唯一值分布狀態(tài)。

比如表t1有100行記錄,其中一列為f1。f1中唯一值的個數(shù)可以是100個,也可以是1個,當(dāng)然也可以是1到100之間的任何一個數(shù)字。這里唯一值越的多少,就是這個列的可選擇基數(shù)。

那看到這里我們就明白了,為什么要在基數(shù)高的字段上建立索引,而基數(shù)低的的字段建立索引反而沒有全表掃描來的快。當(dāng)然這個只是一方面,至于更深入的探討就不在我這篇探討的范圍了。

概念二,關(guān)于HINT的使用。

這里我來說下HINT是什么,在什么時候用。

HINT簡單來說就是在某些特定的場景下人工協(xié)助MySQL優(yōu)化器的工作,使她生成最優(yōu)的執(zhí)行計劃。一般來說,優(yōu)化器的執(zhí)行計劃都是最優(yōu)化的,不過在某些特定場景下,執(zhí)行計劃可能不是最優(yōu)化。

比如:表t1經(jīng)過大量的頻繁更新操作,(UPDATE,DELETE,INSERT),cardinality已經(jīng)很不準確了,這時候剛好執(zhí)行了一條SQL,那么有可能這條SQL的執(zhí)行計劃就不是最優(yōu)的。為什么說有可能呢?

來看下具體演示

譬如,以下兩條SQL,

A:

select * from t1 where f1 = 20;

B:

select * from t1 where f1 = 30;

如果f1的值剛好頻繁更新的值為30,并且沒有達到MySQL自動更新cardinality值的臨界值或者說用戶設(shè)置了手動更新又或者用戶減少了sample page等等,那么對這兩條語句來說,可能不準確的就是B了。

這里順帶說下,MySQL提供了自動更新和手動更新表cardinality值的方法,因篇幅有限,需要的可以查閱手冊。

那回到正題上,MySQL 8.0 帶來了幾個HINT,我今天就舉個index_merge的例子。

示例表結(jié)構(gòu):

mysql desc t1;+------------+--------------+------+-----+---------+----------------+| Field ? ? ?| Type ? ? ? ? | Null | Key | Default | Extra ? ? ? ? ?|+------------+--------------+------+-----+---------+----------------+| id ? ? ? ? | int(11) ? ? ?| NO ? | PRI | NULL ? ?| auto_increment || rank1 ? ? ?| int(11) ? ? ?| YES ?| MUL | NULL ? ?| ? ? ? ? ? ? ? ?|| rank2 ? ? ?| int(11) ? ? ?| YES ?| MUL | NULL ? ?| ? ? ? ? ? ? ? ?|| log_time ? | datetime ? ? | YES ?| MUL | NULL ? ?| ? ? ? ? ? ? ? ?|| prefix_uid | varchar(100) | YES ?| ? ? | NULL ? ?| ? ? ? ? ? ? ? ?|| desc1 ? ? ?| text ? ? ? ? | YES ?| ? ? | NULL ? ?| ? ? ? ? ? ? ? ?|| rank3 ? ? ?| int(11) ? ? ?| YES ?| MUL | NULL ? ?| ? ? ? ? ? ? ? ?|+------------+--------------+------+-----+---------+----------------+7 rows in set (0.00 sec)

表記錄數(shù):

mysql select count(*) from t1;+----------+| count(*) |+----------+| ? ?32768 |+----------+1 row in set (0.01 sec)

這里我們兩條經(jīng)典的SQL:

SQL C:

select * from t1 where rank1 = 1 or rank2 = 2 or rank3 = 2;

SQL D:

select * from t1 where rank1 =100 ?and rank2 =100 ?and rank3 =100;

表t1實際上在rank1,rank2,rank3三列上分別有一個二級索引。

那我們來看SQL C的查詢計劃。

顯然,沒有用到任何索引,掃描的行數(shù)為32034,cost為3243.65。

mysql explain ?format=json select * from t1 ?where rank1 =1 or rank2 = 2 or rank3 = 2\G*************************** 1. row ***************************EXPLAIN: { ?"query_block": { ? ?"select_id": 1, ? ?"cost_info": { ? ? ?"query_cost": "3243.65" ? ?}, ? ?"table": { ? ? ?"table_name": "t1", ? ? ?"access_type": "ALL", ? ? ?"possible_keys": [ ? ? ? ?"idx_rank1", ? ? ? ?"idx_rank2", ? ? ? ?"idx_rank3" ? ? ?], ? ? ?"rows_examined_per_scan": 32034, ? ? ?"rows_produced_per_join": 115, ? ? ?"filtered": "0.36", ? ? ?"cost_info": { ? ? ? ?"read_cost": "3232.07", ? ? ? ?"eval_cost": "11.58", ? ? ? ?"prefix_cost": "3243.65", ? ? ? ?"data_read_per_join": "49K" ? ? ?}, ? ? ?"used_columns": [ ? ? ? ?"id", ? ? ? ?"rank1", ? ? ? ?"rank2", ? ? ? ?"log_time", ? ? ? ?"prefix_uid", ? ? ? ?"desc1", ? ? ? ?"rank3" ? ? ?], ? ? ?"attached_condition": "((`ytt`.`t1`.`rank1` = 1) or (`ytt`.`t1`.`rank2` = 2) or (`ytt`.`t1`.`rank3` = 2))" ? ?} ?}}1 row in set, 1 warning (0.00 sec)

我們加上hint給相同的查詢,再次看看查詢計劃。

這個時候用到了index_merge,union了三個列。掃描的行數(shù)為1103,cost為441.09,明顯比之前的快了好幾倍。

mysql explain ?format=json select /*+ index_merge(t1) */ * from t1 ?where rank1 =1 or rank2 = 2 or rank3 = 2\G*************************** 1. row ***************************EXPLAIN: { ?"query_block": { ? ?"select_id": 1, ? ?"cost_info": { ? ? ?"query_cost": "441.09" ? ?}, ? ?"table": { ? ? ?"table_name": "t1", ? ? ?"access_type": "index_merge", ? ? ?"possible_keys": [ ? ? ? ?"idx_rank1", ? ? ? ?"idx_rank2", ? ? ? ?"idx_rank3" ? ? ?], ? ? ?"key": "union(idx_rank1,idx_rank2,idx_rank3)", ? ? ?"key_length": "5,5,5", ? ? ?"rows_examined_per_scan": 1103, ? ? ?"rows_produced_per_join": 1103, ? ? ?"filtered": "100.00", ? ? ?"cost_info": { ? ? ? ?"read_cost": "330.79", ? ? ? ?"eval_cost": "110.30", ? ? ? ?"prefix_cost": "441.09", ? ? ? ?"data_read_per_join": "473K" ? ? ?}, ? ? ?"used_columns": [ ? ? ? ?"id", ? ? ? ?"rank1", ? ? ? ?"rank2", ? ? ? ?"log_time", ? ? ? ?"prefix_uid", ? ? ? ?"desc1", ? ? ? ?"rank3" ? ? ?], ? ? ?"attached_condition": "((`ytt`.`t1`.`rank1` = 1) or (`ytt`.`t1`.`rank2` = 2) or (`ytt`.`t1`.`rank3` = 2))" ? ?} ?}}1 row in set, 1 warning (0.00 sec)

我們再看下SQL D的計劃:

不加HINT,

mysql explain format=json select * from t1 where rank1 =100 and rank2 =100 and rank3 =100\G*************************** 1. row ***************************EXPLAIN: { ?"query_block": { ? ?"select_id": 1, ? ?"cost_info": { ? ? ?"query_cost": "534.34" ? ?}, ? ?"table": { ? ? ?"table_name": "t1", ? ? ?"access_type": "ref", ? ? ?"possible_keys": [ ? ? ? ?"idx_rank1", ? ? ? ?"idx_rank2", ? ? ? ?"idx_rank3" ? ? ?], ? ? ?"key": "idx_rank1", ? ? ?"used_key_parts": [ ? ? ? ?"rank1" ? ? ?], ? ? ?"key_length": "5", ? ? ?"ref": [ ? ? ? ?"const" ? ? ?], ? ? ?"rows_examined_per_scan": 555, ? ? ?"rows_produced_per_join": 0, ? ? ?"filtered": "0.07", ? ? ?"cost_info": { ? ? ? ?"read_cost": "478.84", ? ? ? ?"eval_cost": "0.04", ? ? ? ?"prefix_cost": "534.34", ? ? ? ?"data_read_per_join": "176" ? ? ?}, ? ? ?"used_columns": [ ? ? ? ?"id", ? ? ? ?"rank1", ? ? ? ?"rank2", ? ? ? ?"log_time", ? ? ? ?"prefix_uid", ? ? ? ?"desc1", ? ? ? ?"rank3" ? ? ?], ? ? ?"attached_condition": "((`ytt`.`t1`.`rank3` = 100) and (`ytt`.`t1`.`rank2` = 100))" ? ?} ?}}1 row in set, 1 warning (0.00 sec)

加了HINT,

mysql explain format=json select /*+ index_merge(t1)*/ * from t1 where rank1 =100 and rank2 =100 and rank3 =100\G*************************** 1. row ***************************EXPLAIN: { ?"query_block": { ? ?"select_id": 1, ? ?"cost_info": { ? ? ?"query_cost": "5.23" ? ?}, ? ?"table": { ? ? ?"table_name": "t1", ? ? ?"access_type": "index_merge", ? ? ?"possible_keys": [ ? ? ? ?"idx_rank1", ? ? ? ?"idx_rank2", ? ? ? ?"idx_rank3" ? ? ?], ? ? ?"key": "intersect(idx_rank1,idx_rank2,idx_rank3)", ? ? ?"key_length": "5,5,5", ? ? ?"rows_examined_per_scan": 1, ? ? ?"rows_produced_per_join": 1, ? ? ?"filtered": "100.00", ? ? ?"cost_info": { ? ? ? ?"read_cost": "5.13", ? ? ? ?"eval_cost": "0.10", ? ? ? ?"prefix_cost": "5.23", ? ? ? ?"data_read_per_join": "440" ? ? ?}, ? ? ?"used_columns": [ ? ? ? ?"id", ? ? ? ?"rank1", ? ? ? ?"rank2", ? ? ? ?"log_time", ? ? ? ?"prefix_uid", ? ? ? ?"desc1", ? ? ? ?"rank3" ? ? ?], ? ? ?"attached_condition": "((`ytt`.`t1`.`rank3` = 100) and (`ytt`.`t1`.`rank2` = 100) and (`ytt`.`t1`.`rank1` = 100))" ? ?} ?}}1 row in set, 1 warning (0.00 sec)

對比下以上兩個,加了HINT的比不加HINT的cost小了100倍。

總結(jié)下,就是說表的cardinality值影響這張的查詢計劃,如果這個值沒有正常更新的話,就需要手工加HINT了。相信MySQL未來的版本會帶來更多的HINT。


網(wǎng)站標(biāo)題:php高并發(fā)數(shù)據(jù)庫優(yōu)化 php高并發(fā)實戰(zhàn)
本文地址:http://weahome.cn/article/dossgcs.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部