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 件のコメント :
コメントを投稿