Sql-Server

負載測試:查詢的性能隨著並行執行次數的增加而下降

  • February 9, 2017

在我的應用程序上進行負載測試時,我發現速度變慢了。我有一個執行大約需要 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,我有這個部落格

引用自:https://dba.stackexchange.com/questions/163560