2016-08-30 64 views
0

我目前正在爲我的應用程序開發的API後端運行Flask + SQLAlchemy + uWSGI + nginx堆棧。 我試圖通過使用ApacheBench並向服務器上的端點發送不同數量的併發請求來查看我的服務器上可以擁有的最大併發連接數。提高SQLAlchemy的性能?

此端點將採用JSON請求正文,提取某些值,運行查詢,然後根據查詢結果返回JSON響應。

我跑了的基本測試,併發請求 10個請求,我的平均響應時間爲60毫秒。
運行與10併發請求的另一測試爲100個請求返回的150毫秒的平均,100併發請求 1000返回1500毫秒和500 併發請求返回周圍7000-9000ms。

Concurrency Level:  500 
Time taken for tests: 38.710 seconds 
Complete requests:  5000 
Failed requests:  0 
Total transferred:  1310000 bytes 
Total body sent:  1105000 
HTML transferred:  110000 bytes 
Requests per second: 129.17 [#/sec] (mean) 
Time per request:  3870.986 [ms] (mean) 
Time per request:  7.742 [ms] (mean, across all concurrent requests) 
Transfer rate:   33.05 [Kbytes/sec] received 
         27.88 kb/s sent 
         60.93 kb/s total 

Connection Times (ms) 
       min mean[+/-sd] median max 
Connect:  24 63 185.1  25 3025 
Processing: 161 3737 2778.7 3316 26719 
Waiting:  157 3737 2778.7 3316 26719 
Total:  187 3800 2789.7 3366 26744 

Percentage of the requests served within a certain time (ms) 
    50% 3366 
    66% 4135 
    75% 4862 
    80% 5711 
    90% 7449 
    95% 9158 
    98% 11794 
    99% 13373 
100% 26744 (longest request) 

延遲似乎是線性增加這是有道理的,但它似乎很快增加了太快。在做了很多修補和分析之後,我發現瓶頸在於查詢。

在基準測試開始時,查詢會在10-50毫秒內處理並快速返回,但會迅速增加,並且在某些情況下可以看到10000-15000毫秒的延遲。

我無法弄清楚爲什麼數據庫減慢了這麼多,特別是因爲它是空的(禁止測試數據)。

我試着在沒有連接池的情況下運行應用程序,結果顯示延遲下降(7-9s到5-6s)。我不認爲這是可能的,因爲我讀的所有內容都建議使用連接池,這會讓事情變得更快,因爲您可以避免每次發出請求時建立新連接的開銷。

我還試驗了增加連接池大小(從默認的5到50),並且減少的延遲甚至比無池設置(5-6s到3-4s)還要多。

Concurrency Level:  500 
Time taken for tests: 4.842 seconds 
Complete requests:  836 
Failed requests:  0 
Non-2xx responses:  679 
Total transferred:  272673 bytes 
Total body sent:  294593 
HTML transferred:  126353 bytes 
Requests per second: 172.67 [#/sec] (mean) 
Time per request:  2895.662 [ms] (mean) 
Time per request:  5.791 [ms] (mean, across all concurrent requests) 
Transfer rate:   55.00 [Kbytes/sec] received 
         59.42 kb/s sent 
         114.42 kb/s total 

Connection Times (ms) 
       min mean[+/-sd] median max 
Connect:  24 170 273.1  93 1039 
Processing: 25 665 1095.2 149 4753 
Waiting:  25 665 1095.2 149 4753 
Total:   51 835 1123.9 279 4786 

Percentage of the requests served within a certain time (ms) 
    50% 279 
    66% 487 
    75% 1050 
    80% 1059 
    90% 2935 
    95% 3658 
    98% 4176 
    99% 4337 
100% 4786 (longest request) 

等待時間仍然是非常高的(3-4秒的API似乎通過任何標準不合理),而我試圖找出我怎麼能更加減少它。答案只是更多的連接?

注: 我正在4個ram和四核處理器的服務器上運行4個uWSGI進程,每個進程有100個線程。 我正在使用psycopg2-cffi適配器進行連接,並且該應用程序正在PyPy上運行。

+0

嘗試調整過程的數量。它會以犧牲資源爲每個進程創建一個連接池。後續步驟:如果在一臺服務器上運行:單獨分貝和燒瓶。最後的最後時刻:現在4Gb並不多。只有一件事可以替代RAM,更多的RAM。 –

+0

@KlausD。 :有多少個進程太多?我知道gunicorn國家2xcores + 1是建議的金額,但uWSGI文件聲明,沒有他們的等值。 也是內存相關的這個問題?我知道通常當人們談論數據庫延遲時,他們通過緩存來解決它,這自然需要更多的RAM,但這看起來像是連接問題。當然,我不太熟悉它,所以你可能是對的,但這是我迄今爲止收集的。 – lonewaft

+0

進程數量沒有最終公式。特別是當您有超線程技術時,每個物理內核有兩個「虛擬」內核,性能可能因負載分配方式以及應用程序的工作方式而異。你將不得不嘗試找到最好的價值。 RAM對於數據庫來說總是很重要。這適用於數量和速度。還有更多的進程需要更多的RAM。 –

回答

0

如果數據庫必須按順序處理查詢,則線性增加非常正常。實質上,所有併發請求都是在同一時間開始的,但是一個接一個地完成,因此,假設一個連接具有單個連接,每個請求60ms,併發10個併發請求,您將看到請求需要60ms,120ms,180ms ,240ms,300ms,360ms,420ms,480ms,540ms,600ms,600ms,...,600ms,540ms,480ms,...。我們可以計算出多少時間花費的平均請求,給予n請求和m併發請求:

f(n, m) = 60ms * (((m + 1) * m/2) * 2 + (n - 2m) * m)/n 
f(100, 10) = 546ms 
f(1000, 100) = 5406ms 
f(1000, 500) = 15,030ms 

這些數字是類似於你所看到的。

現在來了一個大問題。爲什麼數據庫進程幾乎按順序查詢?我能想到的有幾個原因:

  • 鎖定:每次啓動的事務需要鎖定一些資源獨佔所以只有一個(或幾個)事務可以同時運行
  • CPU綁定的查詢:每筆交易花費顯著CPU資源,使其他交易必須等待CPU時間
  • 大表掃描:數據庫不能保持桌面的全部在內存中,因此必須從磁盤讀取對每一項交易

你如何解決這個問題?這是一個複雜的問題,但有幾個潛在的解決方案:

  • 優化您的查詢;或者優化它們,以便它們不會爭奪同一資源,或者優化它們以使它們不會花費太長時間
  • 批處理您的查詢,以便您總共需要運行較少
  • 緩存您的響應這樣它們根本不會碰到數據庫