なぜCOUNTは遅いですが、SLOW LOGを書かないですか?


MySQL対COUNT(*)は常に最適化されています。
  • 問題はある日、友達のフィードバック問題が大きいテーブルのCOUNT(*)に対してとても遅いですが、slow logsに記録されないのはなぜですか?彼が提供した情報に基づいて、自分でこの問題を再現しました。
    # MySQL   8.0.20
    [[email protected]]>\s
    ...
    Server version:        8.0.20 MySQL Community Server - GPL
    ...
    
    #    long_query_time
    [[email protected]]>select @@global.long_query_time, @@session.long_query_time;
    +--------------------------+---------------------------+
    | @@global.long_query_time | @@session.long_query_time |
    +--------------------------+---------------------------+
    |                 0.010000 |                  0.010000 |
    +--------------------------+---------------------------+
    
    #    COUNT(*),     0.01, slow log    
    [[email protected]]>select count(*) from t1;
    +----------+
    | count(*) |
    +----------+
    |   799994 |
    +----------+
    1 row in set (0.27 sec)
    
    これは一体なぜですか?
  • 問題については、まず、slow logsに関するすべてのパラメータをチェックします。
  • [[email protected]]>show global variables;
    ...
    | log_slow_admin_statements              | OFF      |
    | log_slow_extra                         | ON       |
    | log_slow_slave_statements              | OFF      |
    | long_query_time                        | 0.010000 |
    | slow_query_log                         | ON       |
    | slow_query_log_file                    | slow.log |
    | log_output                             | FILE     |
    | min_examined_row_limit                 | 100      |
    | log_queries_not_using_indexes          | 1        |
    | log_throttle_queries_not_using_indexes | 60       |
    ...
    
    上のいくつかのパラメータの中で、より疑わしいのは次のいくつかです。
    | min_examined_row_limit                 | 100      |
    | log_queries_not_using_indexes          | 1        |
    | log_throttle_queries_not_using_indexes | 60       |
    
    まずロゴを言いますqueriesnot_アメリカ.indexesは、インデックスを使用していないSQLもslow queryとして記録されていることを示していますが、この例ではインデックスが付いています。
    [[email protected]]>desc select count(*) from t1\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: t1
       partitions: NULL
             type: index
    possible_keys: NULL
              key: PRIMARY
          key_len: 4
              ref: NULL
             rows: 42760
         filtered: 100.00
            Extra: Using index
    
    これによりパラメータロゴも排除されました。スロットルqueriesnot_アメリカ.indexesの疑い。では、パラメータmin_だけが残ります。exmined_row_limitの疑いは、スキャン行数が設定値より少ない場合、このSQLもslow queryとして記録されないということです。では、本例のCOUNT(*)はこのような状況に合っていますか?まずパラメーターをmin_上げます。exmined_row_limit値は0に設定されています。つまり、デフォルトの値です。
    [[email protected]]>set global min_examined_row_limit=0;
    [[email protected]]>set session min_examined_row_limit=0;
    [[email protected]]>select @@global.min_examined_row_limit, @@session.min_examined_row_limit;
    +---------------------------------+----------------------------------+
    | @@global.min_examined_row_limit | @@session.min_examined_row_limit |
    +---------------------------------+----------------------------------+
    |                               0 |                                0 |
    +---------------------------------+----------------------------------+
    
    もう一度COUNT(*)クエリを実行します。
    [[email protected]]>select count(*) from t1;
    +----------+
    | count(*) |
    +----------+
    |    43462 |
    +----------+
    1 row in set (0.02 sec)
    
    やはり、今回はslow logsに記録されました。
    # Query_time: 0.026083  Lock_time: 0.000110 Rows_sent: 1  Rows_examined: 0
    ...
    select count(*) from t1;
    
    ローソンに気づいたexminedの値は0です。えっと、あまり科学的ではないようです。
    ここに来て、原因が明らかになりました。パラメータmin_exmined_row_limitの値は0より大きく設定されていますが、本例のCOUNT(*)の操作はRows_のためです。exmined=0ですので、slow logsには記録されません。
  • 問題の解釈は問題の原因が分かりますが、Rows_exminedはどういう意味ですか?文書の説明は以下の通りです。
  • • Rows_examined: 
    The number of rows examined by the server layer (not counting any processing internal to storage engines).
    
    字面の意味ではよく理解できないように見えるかもしれませんが、つまりSQLを実行した後、状態変数の中でHandler_という名前を確認します。レイドのいくつかの指標を達成することができます。例えば、
    [[email protected]]> flush status;
    [[email protected]]> select count(*) from t1;
    ...
    [[email protected]]> show status like 'handler%read%';
    +-----------------------+-------+
    | Variable_name         | Value |
    +-----------------------+-------+
    | Handler_read_first    | 0     |
    | Handler_read_key      | 0     |
    | Handler_read_last     | 0     |
    | Handler_read_next     | 0     |
    | Handler_read_prev     | 0     |
    | Handler_read_rnd      | 0     |
    | Handler_read_rnd_next | 0     |
    +-----------------------+-------+
    
    上記のいくつかの値が全部0であることが見られます。exminedも0で、slow logsに記録されません。
    3.1インデックスを集めて並行して読むことについては、ここでMySQL 8.0のもう一つの新しい特性を紹介します。8.0.14バージョンからパラメータinnodb_が追加されました。parallel_read_.threadsは、集合インデックスの並列スキャンをサポートし、以下のいくつかの条件を満たす必要があります。
    パラメータinnodb_parallel_read_.threads値>0
    統合された索引のみをサポートします。
    ロックなしのクエリーのみ対応しています。
    INSERTではないです。SELECTで調べます。
    主に次の2つのシーンを加速するために使用されます。
    CHECK TABLE操作
    WHERE条件を持たない全表COUNT(*)
    そのため、COUNT(*)もインデックスを集めて並列に読むことができ、error logsからは以下のような情報が見られます。
    [Note] [MY-011825] [InnoDB] Parallel scan: 4
    [Note] [MY-011825] [InnoDB] ranges: 130 max_threads: 4 split: 128 depth: 1
    [Note] [MY-011825] [InnoDB] n: 20914
    [Note] [MY-011825] [InnoDB] n: 18066
    [Note] [MY-011825] [InnoDB] n: 4482
    
    上記のログから何時まで見られますか?
    最大4つの平行スレッドが設置されています。
    実際に3つのスレッドを並行して、実際の並列数は1~4から、毎回最高並列を走るとは限りません。
    それぞれの走査行数は20914、18066、4482であり、COUNT()の結果の総数は43462である。
    t 1表に補助インデックスを追加して、下記のCOUNT(*)を見てください。
    #              
    [[email protected]]>desc select count(*) from t1\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: t1
       partitions: NULL
             type: index
    possible_keys: NULL
              key: k1
          key_len: 5
              ref: NULL
             rows: 42760
         filtered: 100.00
            Extra: Using index
    #       
    [[email protected]]>select count(*) from t1;
    +----------+
    | count(*) |
    +----------+
    |    43462 |
    +----------+
    1 row in set (0.01 sec)
    #    Handler_read_%      0
    
    [[email protected]]>show status like 'handler%read%';
    +-----------------------+-------+
    | Variable_name         | Value |
    +-----------------------+-------+
    | Handler_read_first    | 0     |
    | Handler_read_key      | 0     |
    | Handler_read_last     | 0     |
    | Handler_read_next     | 0     |
    | Handler_read_prev     | 0     |
    | Handler_read_rnd      | 0     |
    | Handler_read_rnd_next | 0     |
    +-----------------------+-------+        
    
    また、この時もerror logsには並列スキャンの記録があります。
    [Note] [MY-011825] [InnoDB] Parallel scan: 4
    [Note] [MY-011825] [InnoDB] ranges: 91 max_threads: 4 split: 88 depth: 1
    [Note] [MY-011825] [InnoDB] n: 21493
    [Note] [MY-011825] [InnoDB] n: 21486
    [Note] [MY-011825] [InnoDB] n: 483
    
    見ましたか?実際には集合インデックスの並列スキャン特性を使って加速します。
    注意:上記のerror logsに記録されたパラレルスキャン統合インデックス情報の機能は8.0.20でまた削除されました。このログが見られたのは、私が調べてから8.0.19バージョンに戻したからです。ちょっと大変です。8.0.20 Release Notesを見てください。
    InnoDB: Unnecessary messages about parallel scans were printed to the error log. (Bug #30330448)
    
    実は残しておいていいじゃないですか?毛を取るということが分かりません。。。。
    サービス紹介
  • トンボエージェント
  • ipエージェント
  • プロキシip
  • ipプロキシ
  • 国内のipエージェント
  • プロキシサービスip
  • 最新のプロキシサーバ
  • エージェントipネットワーク
  • 中国代理サーバ
  • 有料代理店
  • 企業級ip
  • 企業級代理ip
  • 中国代理ip
  • 最新プロキシip