轉自:http://stblog.baidu-tech.com/?p=851
摘要
資料的儲存方式對應用程式的整體效能有著極大的影響。對資料的存取,是順利讀寫還是隨機讀寫?將資料放磁碟上還將資料放flash卡上?多線程讀寫對效能影響?面對著多種資料存放區方式,我們如何選擇?本文給大家提供了一份不同儲存模式下的效能測試資料,方便大家在今後的程式開發過程中可以利用這份資料選擇合適的資料存放區模式。
TAG
儲存效能,innodb效能,儲存介質
目錄
目錄… 1
簡介… 1
儲存效能分析… 2
測試程式說明:… 2
儲存測試資料:… 2
Mysql innodb效能測試… 4
Mysql(InnoDB)刷盤策略… 6
c/s模式通訊效能… 6
直接檔案儲存體… 7
檔案IO方式… 7
完全隨機寫還是跳躍,5倍的效能差距… 8
多線程隨機讀、處理速度、回應時間… 9
系統緩衝… 10
系統緩衝相關的幾個核心參數… 10
dirty頁的write back. 10
總結… 11
簡介
資料的儲存方式對應用程式的整體效能有著極大的影響。對資料的存取,是順利讀寫還是隨機讀寫?將資料放磁碟上還將資料放flash卡上?多線程讀寫對效能影響?面對著多種資料存放區方式,我們如何選擇?
本文會對不同儲存方式做詳細的效能測試,以提供給大家一份不同儲存方式下的效能測試資料為主,同時會簡單介紹各種儲存方式之間的效能差異。
儲存效能分析
影響儲存速度有各個方面的原因,包括儲存介質、讀寫盤方式以及硬體環境對讀寫盤時的影響。這裡主要分享儲存速度的一些調研結果。
硬體環境如下:
CPU: INTER Nehalem E5620 2.4GHZx2
記憶體: PC-8500 4GB*8
硬碟: 300G 10k*2, RAID:1
Flash: SSD 160GB_MLC X25-M G2×6
網卡: 千兆
資料量:117G
測試程式說明:
測試共分兩套程式:
- A. 儲存測試
a) 儲存測試程式均使用pread/pwrite進行儲存測試,塊鏈遍曆速度採用frs開發的塊鏈庫進行。
b) 為了減少隨機讀寫時系統緩衝帶來的影響
i. 將資料量增大至117G
ii. 每次資料只被測試一次
iii. 程式入口處清記憶體
c) 測順序讀寫時,一次讀寫所有資料。
d) 測隨機讀寫時,每次讀4KB,讀381MB。
- B. 網路效能測試
a) 採用ub + ubrpc實現壓力工具的伺服器端和用戶端。
b) UBSVR_NODELAY
c) 常用的Idl規範
d) 對兩個不同大小的包請求進行測試
儲存測試資料:
磁碟
順序讀:145.59MB/s
隨機讀:0.91MB/s (每次讀4KB,讀381MB)
順序寫:83.1MB/s
隨機寫:0.34MB/s (每次寫4KB, 寫381MB)
Flash
順序讀:61.5MB/s
隨機讀:14.9MB/s (每次讀4KB,讀381MB)
順序寫:59.8MB/s
隨機寫:1.93MB/s (每次寫4KB, 寫381MB)
記憶體
順序寫:1655MB/s
隨機寫:1496MB/s
Eg: 塊鏈遍曆速度1000萬元素, 565582 us
磁碟與flash卡的順序讀寫效能對比(單位MB):
磁碟與flash卡的隨機讀寫效能對比(單位MB):
對比磁碟和flash卡的隨機讀寫效能,我們可以看到:對於寫操作而言,其在磁碟和flash卡上的效能差異較小,且事實上其效能差異會隨著隨機寫時每次寫入的資料量以及flash卡塊大小等其它因素而產生波動;同時,在帶寫最佳化的flash上,當將資料寫入flash卡時,資料會先寫到一塊buffer中,當滿足一定條件(如buffer滿)後,會將buffer的資料刷入flash,此時會阻塞寫,因此會帶來效能抖動。所以,當應用程式的多數操作是寫入操作時,若沒有flash卡也可以勉強將資料放到磁碟上。
但是相反,從測試結果看,在磁碟和flash卡上的隨機讀效能有著8倍甚至更多的差距,所以當程式讀磁碟操作相當多時,把資料放到flash卡上是一個比較好的選擇。比如,對於一個隨機查詢較多資料庫應用程式,我們可以考慮把資料庫的隱藏檔放到flash卡上。
另一方面,我們可以直觀地看到,無論順序讀還是順序寫,在磁碟上的速度都遠遠高於在flash卡上的速度。所以如果程式所需的資料是從磁碟一次載入,載入後對資料的修改都是記憶體操作,不直接寫盤,當需要寫盤時,也是一次將記憶體中的資料dump到磁碟上時。我們都應該將資料放到磁碟,而不是flash卡上。
Mysql innodb效能測試Mysql測試一:存讀
硬碟環境與配置:
innodb_buffer_pool_size = 5120M
innodb_flush_log_at_trx_commit = 0
機器記憶體:32G
cpu:4核 Intel(R) Xeon(R) CPU 5150 @ 2.66GHz
flash:256G,slc 沒有寫最佳化,沒有raid。
壓力:
mysqlab args:-uroot -proot -h127.0.0.1 -P3306 -dfrs -fr.sql -c1 –t40 -s1000
50個線程,每個線程1000req/s的壓力:
結論:
每秒處理次數 |
4700左右。 |
max回應時間 |
139374 us |
平均回應時間 |
8535us |
|
|
iostat:
從系統狀態來看,
flash效能已經到極限。
Mysql測試二:讀寫衝突測試
讀壓力 |
真實壓力 |
寫壓力 |
真實寫壓力 |
0 |
0 |
單線程1000 |
300左右(起初很快,但最終會降下來) |
0 |
0 |
10線程*1000 |
300左右(起初很快,但會降下來,降下來後,很不穩定) |
40線程*80 |
2000上下浮動 |
單線程300 |
平均60+ |
40線程*50 |
1000+上下浮動 |
單線程300 |
平均80+ |
40線程*40 |
1500 |
單線程100 |
80+ |
可見,flash讀寫衝突非常嚴重,如果壓力太高,會導致被壓垮,反而在寫壓力恒定在100次的時候,可以支援1500以上的並發讀請求,且比較穩定。
線上,最還還是使用寫最佳化的mlc。以減少讀寫衝突。
Mysql測試三:mlc+寫最佳化flash
只有讀的情況:
讀壓力 |
真實讀壓力 |
寫壓力 |
真實寫壓力 |
40線程*1000 |
8300+ |
0 |
0 |
40線程*80 |
2900 |
單線程*200 |
160-190 |
40線程*125 |
4000+ |
單線程*200 |
160~190 |
可見,mlc+寫最佳化後,比slc在效能上有較大提升。
可以支援4000次讀+160以上的寫請求。
Mysql(InnoDB)刷盤策略
上面做了Mysql InnDB的一些效能測試,這裡講一下InnoDB的刷盤策略。InnoDB儲存引擎會有一個緩衝池(緩衝池的大小可根據配置來修改innodb_buffer_pool_size),InnoDB在讀資料庫檔案的資料時,會先將資料庫檔案按頁讀取這個buffer,然後按LRU的演算法來保留緩衝池中的資料。當需要修改資料時,會先修改buffer裡面的資料,此時buffer裡的資料可能為髒資料,然後InnoDB會按一定的頻率將緩衝池裡的髒頁重新整理到檔案。
對於刷髒頁的時機和數量,Innodb會根據當然系統的IO壓力等因素來決定是否刷盤。它會分每秒鐘觸發一次的刷盤和每10秒鐘觸發一次的刷盤。在每秒鐘觸發一次刷盤的條件是:當前緩衝池中髒頁的比例超過了設定檔中innodb_max_dirty_pages_pct這個參數。而在每10秒鐘觸發一次的刷盤中,InnoDB至少會刷10個髒頁(如果有的話)到磁碟,同時InnoDB引擎會判斷緩衝池中的髒頁的比例,如果超一定比例的話,會刷100個髒頁到磁碟。
c/s模式通訊效能
在這一節,我們用百度的UB框寫了兩個網路效能測試程式。主要測試資料包大小對網路服務的影響,以及跨機房之間的網路延遲資料。
程式&硬體:
網卡頻寬:1000Mb
通訊協定:ubrpc
配置:長串連、EPOOL、server配40線程
同機房:
請求包大小:13000byte/pack
單線程壓: 1600/s, 網卡流量: 20.6Mb
8個線種壓:9000/s, 網卡流量: 122Mb (基本達網卡極限)
請求包大小:1700byte/pack
單線程壓: 2900/s, 網卡流量: 4.7Mb
36個線種壓:穩定28000/s, 網卡流量: 50.3Mb (此時對於千兆網卡來說,頻寬還非常充裕,但CPU r值已經在10左右。再往上壓伺服器出現不穩定現象。
跨機房延遲: 817us
請求包大小:1700byte/pack
單線程壓: 860/s, 網卡流量: 1.4Mb
同樣的程式,單線程壓跨機房的效能明顯下降,不到同機房的1/3。每個請求延遲達817us
直接檔案儲存體檔案IO方式
直接檔案儲存體指將記憶體中的資料直接寫到磁碟上,我們通常用munmap、fwrite、pwrite和write等方式進行資料寫入。反之用mmap、fread、pread和read等方式從磁碟檔案載入資料到記憶體中。
Mmap/munmap
1. mmap/munmap是一種記憶體映射技術,它用於把磁碟檔案對應到記憶體中,對記憶體中資料的修改,會被映射回磁碟;Linux核心會維護一個資料結構,以建立虛擬位址空間的地區與相關資料之間的關聯。檔案區間與它所映射到的地址空間的關聯是通過優先樹完成的,(1.1)所示。Mmap技術無論從速度還是易用性上都有著非常不錯的表現。
圖1.1 struct file是指用open開啟的一個檔案控制代碼,其中f_mapping是包含一個指向inode和優先樹的address_space結構體的指標,用做記憶體映射。
pwrite/write
其次,先說一下pwrite和write,它們都屬於檔案IO,資料流是從“進程=>fd=>檔案”,都是直接調用系統調用的函數。兩者不同的是,pwrite相當於順序調用 lseek 和 write , 然後調用pread時,無法中斷其定位和讀操作,也就是說lseek和write相當於是原子操作;另外一點是pwrite不會更新檔案的指標。
在多線程io操作中,對io的操作盡量使用pread和pwrite,否則,如果使用seek+write/read的方式的話,就需要在操作時加鎖。這種加鎖會直接造成多線程對同一個檔案的操作在應用程式層就串列了。從而,多線程帶來的好處就被消除了。
使用pread方式,多線程也比單線程要快很多,可見pread系統調用並沒有因為同一個檔案描述符而相互阻塞。pread和pwrite系統調用在底層實現中是如何做到相同的檔案描述符而彼此之間不影響的?多線程比單線程的IOPS增高的主要因素在於調度演算法。多線程做pread時相互未嚴重競爭是次要因素。
核心在執行pread的系統調用時並沒有使用inode的訊號量,避免了一個線程讀檔案時阻塞了其他線程;但是pwrite的系統調用會使用inode的訊號量,多個線程會在inode訊號量處產生競爭。pwrite僅將資料寫入cache就返回,時間非常短,所以競爭不會很強烈。
在使用pread/pwrite的前提下,如果各個讀寫線程使用各自的一套檔案描述符,是否還能進一步提升io效能?
每個檔案描述符對應核心中一個叫file的對象,而每個檔案對應一個叫inode的對象。假設某個進程兩次開啟同一個檔案,得到了兩個檔案描述符,那麼在核心中對應的是兩個file對象,但只有一個inode對象。檔案的讀寫操作最終由inode對象完成。所以,如果讀寫線程開啟同一個檔案的話,即使採用各自獨佔的檔案描述符,但最終都會作用到同一個inode對象上。因此不會提升IO效能。
pwrite/fwrite
最後,說一下pwrite/fwrite。雖然他們的功能都是將記憶體中的資料存入檔案。但原理和過程都有所不同。剛剛說過pwrite是屬於檔案IO,資料流是從“進程=>fd=>檔案”,而fwrite是流/標準IO,其資料流是從“進程=>fp(FILE對象)=>流/緩衝=>檔案”;原本直接對檔案的操作,在fwrite庫函數中變為對流對象的操作,而“流=>檔案”這一層的操作將由庫函數為我們完成。流的邏輯表示就是FILE對象,而流的實體就是流使用的緩衝區,這些緩衝區相對於應用進程來說就是檔案的代表。
完全隨機寫還是跳躍,5倍的效能差距
全隨機寫無疑是最慢的寫入方式,在logic dump測試中很驚訝的發現,將200M的記憶體資料隨機的寫入到100G的磁碟資料裡面,竟然要2個小時之多。原因就是雖然只有200M的資料,但實際上卻是200萬次隨機寫,根據測試,在2850機器上,這樣完全的隨機寫,r/s 大約在150~350之間,在180機器上,r/s難以達到250,這樣計算,難怪需要2~3個小時之久。
如何改進這種單線程隨機寫慢的問題呢。一種方法就是盡量將完全隨機寫變成有序的跳躍隨機寫。實現方式,可以是簡單的在記憶體中緩衝一段時間,然後排序,使得在寫盤的時候,不是完全隨機的,而是使得磁碟磁頭的移動只向一個方向。根據測試,再一次讓我震驚,簡單的先在記憶體中排序,竟然直接使得寫盤時間縮短到1645秒,磁碟的r/s也因此提升到1000以上。寫盤的速度,一下子提高了5倍。
一個需要注意的地方,這種跳躍寫對效能的提升,來至與磁頭的單方向移動,它非常容易受其他因素的影響。測試中,上面提到的測試是唯寫block檔案,但如果在每個tid的處理中再增加一個寫index的小檔案。雖然如果唯寫index小檔案,所用時間幾乎可以忽略,但如果夾雜在寫block檔案中間的話,對整體的寫效能可能影響巨大,因為他可能使得磁碟的磁頭需要這兩個地方來回跑。根據測試,如果唯寫index檔案,只需要300s就可以寫完所有200萬個tid,單如果將寫索引和寫block放在一起,總時間就遠大於分別寫這兩部分的時間的和。針對這種情況,一種解決方案就是就不要將小資料量的資料即時的刷盤,使用應用程式層的cache來緩衝小資料量的index,這樣就可以消除對寫block檔案的影響。
從原理上解釋上面的表象,一般來說,硬碟讀取資料的過程是這樣的,首先是將磁頭移動到磁碟上資料所在的地區,然後才能進行讀取工作。磁頭移動的過程又可以分解為兩個步驟,其一是移動磁頭到指定的磁軌,也就是尋道,這是一個在磁碟碟片徑向上移動的步驟,花費的時間被稱為“尋道時間”;其二就是旋轉碟片到相應扇區,花費的時間被稱為“潛伏時間”(也被稱為延遲)。那麼也就是說在硬碟上讀取資料之前,做準備工作上需要花的時間主要就是“尋道時間”和“潛伏時間”的總和。真正的資料讀取時間,是由讀取資料大小和磁碟密度、磁碟轉速決定的固定值,在應用程式層沒有辦法改變,但應用程式層缺可以通過改變對磁碟的訪問模式來減少“尋道時間”和“潛伏時間”,
我們上面提到的在應用程式層使用cache然後排序的方式,無疑就是縮短了磁碟的定址時間。由於磁頭是物理裝置,也很容易理解,為什麼中間插入對其他小檔案的讀寫會導致速度變慢很多。
建議:盡量避免完全的隨機寫,在 不能使用多線處理的時候,盡量使用應用程式層cache,確保寫盤時盡量有順序性。對於小資料量的其他檔案,可以一直儲存在應用程式層cache裡面,避免對其他大資料量的資料寫入產生影響。
多線程隨機讀、處理速度、回應時間
多線程隨機讀的處理速度可以達到單線程隨機讀的10倍以上,但同上也帶來了回應時間的增大。測試結論如下:(每個線程盡量讀)
讀線程數 |
讀出100次耗時(um) |
讀平均相應時間(um) |
1 |
1329574 |
13291 |
5 |
251765 |
12976 |
10 |
149206 |
15987 |
20 |
126755 |
25450 |
50 |
96595 |
48351 |
結論標明增加線程數,可以有效提升程式整體的io處理速度。但同時,也使得每個io請求的回應時間上升很多。
從底層的實現上解釋這個現象:應用程式層的io請求在核心態會加入到io請求隊列裡面。核心在處理io請求的時候,並不是簡單的先到先處理,而是根據磁碟的特性,使用某種電梯演算法,在處理完一個io請求後,會優先處理最臨近的io請求。這樣可以有效減少磁碟的尋道時間,從而提升了系統整體的io處理速度。但對於每一個io請求來看,由於可能需要在隊列裡面等待,所以回應時間會有所提升。
回應時間上升,應該主要是由於我們測試的時候採用每個線程都盡量讀的方式。在實際的應用中,我們的程式都沒有達到這種壓力。所以,在io成為瓶頸的程式裡面,應該盡量使用同步多執行緒不同的請求。對於線程數的選擇,還需要通過效能測試來衡量。
系統緩衝系統緩衝相關的幾個核心參數
- /proc/sys/vm/dirty_background_ratio
該檔案表示髒資料到達系統整體記憶體的百分比,此時觸發pdflush進程把髒資料寫回磁碟。
預設設定:10
- /proc/sys/vm/dirty_expire_centisecs
該檔案表示如果髒資料在記憶體中駐留時間超過該值,pdflush進程在下一次將把這些資料寫回磁碟。
預設設定:3000(1/100秒)
- /proc/sys/vm/dirty_ratio
該檔案表示如果進程產生的髒資料到達系統整體記憶體的百分比,此時進程自行把髒資料寫回磁碟。
預設設定:40
- /proc/sys/vm/dirty_writeback_centisecs
該檔案表示pdflush進程周期性間隔多久把髒資料寫回磁碟。
預設設定:500(1/100秒)
dirty頁的write back
系統通常會在下面三種情況下回寫dirty頁
- 定時方式: 定時回寫是基於這樣的原則:/proc/sys/vm/dirty_writeback_centisecs的值表示多長時間會啟動回寫線程,由這個定時器啟動的回寫線程只回寫在記憶體中為dirty時間超過(/proc/sys/vm/didirty_expire_centisecs / 100)秒的頁(這個值預設是3000,也就是30秒),一般情況下dirty_writeback_centisecs的值是500,也就是5秒,所以預設情況下系統會5秒鐘啟動一次回寫線程,把dirty時間超過30秒的頁回寫,要注意的是,這種方式啟動的回寫線程只回寫逾時的dirty頁,不會回寫沒逾時的dirty頁,可以通過修改/proc中的這兩個值,細節查看核心功能wb_kupdate。
- 記憶體不足的時候: 這時並不將所有的dirty頁寫到磁碟,而是每次寫大概1024個頁面,直到空閑頁面滿足需求為止
- 寫操作時發現髒頁超過一定比例: 當髒頁占系統記憶體的比例超過/proc/sys/vm/dirty_background_ratio 的時候,write系統調用會喚醒pdflush回寫dirty page,直到髒頁比例低於/proc/sys/vm/dirty_background_ratio,但write系統調用不會被阻塞,立即返回.當髒頁占系統記憶體的比例超/proc/sys/vm/dirty_ratio的時候, write系統調用會被被阻塞,主動回寫dirty page,直到髒頁比例低於/proc/sys/vm/dirty_ratio
總結
本文給大家提供了一份不同儲存模式下的效能測試資料,方便大家在今後的程式開發過程中可以利用這份資料選擇合適的資料存放區模式。同時講述了關於檔案IO讀寫操作以及系統緩衝層面的一些問題。