負載測試:查詢的性能隨著並行執行次數的增加而下降
在我的應用程序上進行負載測試時,我發現速度變慢了。我有一個執行大約需要 350 毫秒的查詢,但是我並行執行 8 次(更不用說 32 次),它最多需要 2.5 秒。
我在探查器上驗證了執行確實佔用了時間。
查詢:
SELECT SUM([_Facts_].[Sales]) [_measures___Sum Sales_], [_Date_].[year] [_Date___year_] FROM [pp].[Facts] [_Facts_], [pp].[Date] [_Date_] WHERE [_Facts_].[dateKey] = [_Date_].[dateKey] GROUP BY [_Date_].[year] ORDER BY [_Date_].[year] ASC
我正在執行 8 個並行程序,依次進行 10 次呼叫。對於 1 個並行,我得到:
360, 350, 345, 360, 365, 360, 395, 786, 395, 370, avg:408
對於 8 個並行:
515, 1571, 1471, 1326, 1862, 2478, 1922, 3098, 2413, 2032, 2773, 3048, 2453, 2092, 2077, 3359, 2898, 2733, 3018, 2483, 1887, 3023, 3088, 3724, 2317, 2753, 2643, 3284, 3299, 2418, 1907, 1862, 2498, 2838, 2518, 3203, 2613, 2207, 3434, 2613, 3198, 2257, 2593, 2448, 2518, 2968, 2828, 2122, 2963, 2212, 3299, 2988, 3153, 2803, 2157, 2543, 2758, 2998, 2538, 2257, 2788, 2443, 2082, 2613, 3173, 4205, 2603, 2387, 1747, 3854, 3068, 2788, 2603, 3103, 2703, 3198, 1832, 1421, 2217, 1326
平均:
2535, 2523, 2571, 2627, 2689, 2469, 2521, 2610
當並行到 16 時,它會上升到更多。
我嘗試在 MySql 上進行測試並得到相同的跳轉(不同的測試時間,但並行時至少慢 4 倍)。
這些數據庫不能處理負載嗎?!
這在 C# 和 Java 中都會發生:C#:
class Program { static void Main(string[] args) { Parallel.For(0, 8, i => run()); } static void run() { using (var conn = new SqlConnection("Data Source=.;Initial Catalog=;User ID=;Password=")) { conn.Open(); var cnt = 10; long avg = 0; for (int i = 0; i < cnt; i++) { var sw = DateTime.Now.Ticks; using (var cmd = new SqlCommand("SET ARITHABORT ON", conn)) { cmd.CommandText = "SELECT SUM([_Facts_].[Sales]) [_measures___Sum Sales_], [_Date_].[year] [_Date___year_]\n" + "FROM [pp].[Facts] [_Facts_], [pp].[Date] [_Date_]\n" + "WHERE [_Facts_].[dateKey] = [_Date_].[dateKey] \n" + "GROUP BY [_Date_].[year]\n" + "ORDER BY [_Date_].[year] ASC"; using (var reader = cmd.ExecuteReader()) { var x = 0; while (reader.Read()) { x++; } reader.Close(); } } var dif = (DateTime.Now.Ticks - sw) / 1000; avg += dif; Console.WriteLine(dif); } conn.Close(); Console.WriteLine("avg:" + avg / cnt); } } }
爪哇:
@Test public void asdf() throws SQLException { Runnable r = () -> { try (Connection conn = DriverManager.getConnection("jdbc:sqlserver://;databaseName=", "", "")) { long avg = 0; for (int i = 0; i < 10; i++) { try (Statement statement = conn.createStatement()) { StopWatch sw = new StopWatch(); sw.start(); ResultSet resultSet = statement.executeQuery("SELECT SUM([_Facts_].[Sales]) [_measures___Sum Sales_], [_Date_].[year] [_Date___year_]\n" + "FROM [pp].[Facts] [_Facts_], [pp].[Date] [_Date_]\n" + "WHERE [_Facts_].[dateKey] = [_Date_].[dateKey] \n" + "GROUP BY [_Date_].[year]\n" + "ORDER BY [_Date_].[year] ASC"); int x = 0; while (resultSet.next()) { x++; } sw.stop(); avg+=sw.getTime(); System.out.println(sw); } } System.out.println(avg/10); } catch (SQLException e) { e.printStackTrace(); } finally { } }; ExecutorService executor = Executors.newFixedThreadPool(8); for (int i = 0; i < 8; i++) { executor.execute(r); } try { executor.shutdown(); executor.awaitTermination(12312313, TimeUnit.MINUTES); } catch (InterruptedException e) { e.printStackTrace(); } }
更多資訊
這 2 個表是 ~1,000 行和 ~420,000 行。連接是 ~420,000 行,結果是 3 行。
date.dateKey 上有一個 PK,事實上有一個 FK。這些是表,而不是視圖。
我檢查了探查器以驗證持續時間是查詢的實際執行時間,而不是應用程序的執行時間。
SqlServer 有 16 個核心,所以我希望 8 個查詢並行執行而不是疊加。
在伺服器上,1 個查詢的 CPU 高達 50%。對於 8,它達到 95%。
網路/記憶體/IO 似乎沒有太大變化。
更新
所以我試著玩弄並行性的限制。結論是數據庫為每個查詢使用所有核心,並且核心被多次呼叫所淹沒。如果我將並行度降低到 1,那麼每個查詢都會變慢,但我可以執行多個查詢而不會受到影響。
猜測這只是硬體限制,一般情況下沒有“神奇”的解決方案 - 只是調整查詢。
順便說一句,似乎命中的最大部分是加入。
感謝大家!
如果以串列方式多次執行查詢需要 16 核 CPU 的 50%,這意味著它在內部是並行的(即 SQL Server 將工作分配到多個核上),並且您不能期望通過執行它來獲得線性收益並行“外部”(或任何正確的術語……)。
在查詢中添加“OPTION(MAXDOP 1)”再次嘗試測試(僅限 SQL Server),這將確保每次執行僅使用一個核心,我想您會明白我的意思。
如果它是 I/O 密集型的,那麼並行性將無濟於事。一旦 I/O 完全飽和,就不能再壓縮時間了。
假設它不受 I/O 限制,讓我們討論一下 CPU。執行緒之間存在爭用——通常以鎖定公共資源(記憶體、I/O 請求等)的形式出現。這意味著拆分任何工作 N 方式不會給您帶來完整的 N 倍改進。
但是有一個解決方案,可能快 10 倍,可能在 SQL Server 和 MySQL 中同樣有效。您的查詢似乎是我所說的針對數據倉庫的“報告”,對嗎?做到這一點的最好方法是提前思考並建立一個“匯總表”。(在 SQL Server 中,“物化視圖”可能是合適的;在 MySQL 中,您需要自定義程式碼來執行類似的操作。)然後查詢非常快,針對摘要表執行,您不需要並行化它. 對於 MySQL,我有這個部落格。