GA

2022/07/01

どうしてもSELECT COUNT(*) せずに正しいレコードの数を数えたい(が、そんなに都合の良いことはなかった)

 

TL;DR

  • innochecksumを使って強引に数えてみた
  • こんなことをするくらいならおとなしくCOUNT(*) した方がマシ

SHOW TABLE STATUS や information_schema.tables は統計情報ベースなので正確な値ではない、でも COUNT(*) はしたくない、という時。

mysql> SHOW TABLE STATUS LIKE 't1'\G
*************************** 1. row ***************************
           Name: t1
         Engine: InnoDB
        Version: 10
     Row_format: Dynamic
           Rows: 94295380
 Avg_row_length: 37
    Data_length: 3515875328
Max_data_length: 0
   Index_length: 3868196864
      Data_free: 6291456
 Auto_increment: NULL
    Create_time: 2022-05-23 21:43:56
    Update_time: 2022-05-23 22:43:29
     Check_time: NULL
      Collation: utf8mb4_bin
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.00 sec)

mysql> SELECT COUNT(*) FROM t1;
+-----------+
| COUNT(*)  |
+-----------+
| 100000000 |
+-----------+
1 row in set (8.92 sec)

innochecksum ならそれができるかもなあと思った。

innochecksumは -D オプションでファイル名を指定すると、ibdファイルの中身をページ単位でドバドバ吐き出すことができる。 ( -D /dev/stdout でターミナルに吐ける)

ただしこの時、mysqldが.ibdファイルを掴んでいると↓のようなエラーになるので

$ innochecksum -D /dev/stdout t1.ibd
Error: Unable to lock file:: t1.ibd
fcntl: Resource temporarily unavailable

どうせ「その瞬間のページの状態」しか認識できないんだからと割り切ってどっかにibdファイルをコピーしてそれを掴ませるのをよく(?)やってる。

このコピーの負荷や時間を考えるとCOUNT(*)の方がたぶんマシ。

$ cp -ip t1.ibd ~
$ innochecksum -D /dev/stdout ~/t1.ibd ### コピーしたibdの方にinnochecksumをかける

Filename::/home/yoku0825/t1.ibd
==============================================================================
        PAGE_NO         |               PAGE_TYPE                       |       EXTRA INFO
==============================================================================
#::       0             |               File Space Header               |       -
#::       1             |               Insert Buffer Bitmap            |       -
#::       2             |               Inode page                      |       -
#::       3             |               SDI Index page                  |       index id=18446744073709551615, page level=0, No. of records=2, garbage=0, -
#::       4             |               Index page                      |       index id=145, page level=2, No. of records=189, garbage=0, -
#::       5             |               Index page                      |       index id=146, page level=2, No. of records=91, garbage=0, -
#::       6             |               Index page                      |       index id=147, page level=2, No. of records=91, garbage=0, -
#::       7             |               Index page                      |       index id=148, page level=2, No. of records=91, garbage=0, -
#::       8             |               Index page                      |       index id=145, page level=0, No. of records=220, garbage=7514, -
#::       9             |               Index page                      |       index id=145, page level=0, No. of records=441, garbage=0, -
#::      10             |               Index page                      |       index id=145, page level=0, No. of records=441, garbage=0, -
#::      11             |               Index page                      |       index id=145, page level=0, No. of records=441, garbage=0, -
#::      12             |               Index page                      |       index id=146, page level=0, No. of records=601, garbage=7826, -
#::      13             |               Index page                      |       index id=146, page level=0, No. of records=1203, garbage=0, -
#::      14             |               Index page                      |       index id=147, page level=0, No. of records=601, garbage=7826, -
#::      15             |               Index page                      |       index id=147, page level=0, No. of records=1203, garbage=0, -
#::      16             |               Index page                      |       index id=148, page level=0, No. of records=1202, garbage=0, -
#::      17             |               Index page                      |       index id=148, page level=0, No. of records=601, garbage=7826, -
#::      18             |               Index page                      |       index id=145, page level=0, No. of records=441, garbage=0, -
#::      19             |               Index page                      |       index id=146, page level=0, No. of records=1203, garbage=0, -
#::      20             |               Index page                      |       index id=147, page level=0, No. of records=1203, garbage=0, -
#::      21             |               Index page                      |       index id=148, page level=0, No. of records=1203, garbage=0, -

..

index id は information_schema.innodb_indexes から引ける ( innodb_tables とJOINするとテーブルで絞りやすい )

mysql> SELECT * FROM information_schema.innodb_indexes JOIN information_schema.innodb_tables USING(table_id) WHERE innodb_tables.name = 'd1/t1';
+----------+----------+---------+------+----------+---------+-------+-----------------+-------+------+--------+-------+------------+---------------+------------+--------------+
| TABLE_ID | INDEX_ID | NAME    | TYPE | N_FIELDS | PAGE_NO | SPACE | MERGE_THRESHOLD | NAME  | FLAG | N_COLS | SPACE | ROW_FORMAT | ZIP_PAGE_SIZE | SPACE_TYPE | INSTANT_COLS |
+----------+----------+---------+------+----------+---------+-------+-----------------+-------+------+--------+-------+------------+---------------+------------+--------------+
|     1061 |      145 | PRIMARY |    3 |        6 |       4 |     4 |              50 | d1/t1 |   33 |      7 |     4 | Dynamic    |             0 | Single     |            0 |
|     1061 |      146 | c2      |    0 |        2 |       5 |     4 |              50 | d1/t1 |   33 |      7 |     4 | Dynamic    |             0 | Single     |            0 |
|     1061 |      147 | c3      |    0 |        2 |       6 |     4 |              50 | d1/t1 |   33 |      7 |     4 | Dynamic    |             0 | Single     |            0 |
|     1061 |      148 | c4      |    0 |        2 |       7 |     4 |              50 | d1/t1 |   33 |      7 |     4 | Dynamic    |             0 | Single     |            0 |
+----------+----------+---------+------+----------+---------+-------+-----------------+-------+------+--------+-------+------------+---------------+------------+--------------+
4 rows in set (0.00 sec)

なんか gargage=? の値を引いたりとかいろいろしてみたけど引かない方が合ってるっぽい。 page level=0 (これがリーフノードらしい) の数だけを数えるのが正解っぽい?

$ innochecksum -D /dev/stdout ~/t1.ibd | grep 'Index page' | grep -v 'SDI' | grep 'page level=0' | perl -MData::Dumper -nlE 'BEGIN { $counter }; /index id=(\d+).*records=(\d+)/; $counter->{$1} += $2; END { print Dumper $counter }'
$VAR1 = {
          '147' => '100000000',
          '145' => '100000000',
          '148' => '100000601',
          '146' => '100000000'
        };

なんで INDEX c4(c4) だけズレるのかがわからない…。

0 件のコメント :

コメントを投稿