So what does the DATA_FREE column tell us? It tells us the free data within InnoDB for that particular table. A table can share a tablespace with multiple other tables.
 
The tablespace which is used by a table depends on whether the innodb_file_per_table was enabled during table creation and/or at the last time the table was rebuild (e.g. by OPTIMIZE TABLE).
 
If innodb_file_per_table was always disabled then this query probably reports the correct free space:
SELECT DATA_FREE FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE='InnoDB' LIMIT 1;
This is because all tables will share 1 tablespace.
 
If innodb_file_per_table was always enabled (new default for 5.6!) then this would report the free space:
SELECT SUM(DATA_FREE) FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE='InnoDB';
This is because eache table will have it's own tablespace.
 
But how could we combine these two?
 
To give this a try I create a MySQL 5.6.10 sandbox with 4 tables of which only 2 have their own tablespace.
 
My first try is to use my udf_fileexists UDF:
 
mysql> SELECT TABLE_NAME,DATA_FREE, -> udf_fileexists(CONCAT(TABLE_SCHEMA,'/',TABLE_NAME,'.ibd')) AS ibd_file -> FROM information_schema.tables WHERE ENGINE='InnoDB'; +----------------------+-----------+----------+ | TABLE_NAME | DATA_FREE | ibd_file | +----------------------+-----------+----------+ | t1 | 4194304 | 1 | | t2 | 4194304 | 1 | | t3 | 66060288 | 0 | | t4 | 66060288 | 0 | | innodb_index_stats | 0 | 1 | | innodb_table_stats | 0 | 1 | | slave_master_info | 0 | 1 | | slave_relay_log_info | 0 | 1 | | slave_worker_info | 0 | 1 | +----------------------+-----------+----------+ 9 rows in set (0.02 sec) mysql> SELECT (SELECT DATA_FREE FROM INFORMATION_SCHEMA.TABLES WHERE -> NOT udf_fileexists(CONCAT(TABLE_SCHEMA,'/',TABLE_NAME,'.ibd')) -> AND ENGINE='InnoDB' LIMIT 1) + (SELECT SUM(DATA_FREE) -> FROM INFORMATION_SCHEMA.TABLES WHERE -> udf_fileexists(CONCAT(TABLE_SCHEMA,'/',TABLE_NAME,'.ibd')) -> AND ENGINE='InnoDB') AS TOTAL_DATA_FREE; +-----------------+ | TOTAL_DATA_FREE | +-----------------+ | 74448896 | +-----------------+ 1 row in set (0.03 sec)
 
So that works, but it requires loading a UDF. Luckily it's also possible to only use INFORMATION_SCHEMA by using the INNODB_SYS_TABLES table. This works only for 5.6 and newer.
 
mysql> SELECT T.TABLE_SCHEMA,T.TABLE_NAME,T.DATA_FREE,ST.SPACE,SD.PATH -> FROM INFORMATION_SCHEMA.TABLES T LEFT JOIN INFORMATION_SCHEMA.INNODB_SYS_TABLES ST -> ON ST.NAME=CONCAT(T.TABLE_SCHEMA,'/',T.TABLE_NAME) -> LEFT JOIN INFORMATION_SCHEMA.INNODB_SYS_DATAFILES SD ON SD.SPACE=ST.SPACE WHERE T.ENGINE='InnoDB'; +---------------------+----------------------+-----------+-------+----------------------------------+ | TABLE_SCHEMA | TABLE_NAME | DATA_FREE | SPACE | PATH | +---------------------+----------------------+-----------+-------+----------------------------------+ | mysql | innodb_table_stats | 0 | 1 | ./mysql/innodb_table_stats.ibd | | mysql | innodb_index_stats | 0 | 2 | ./mysql/innodb_index_stats.ibd | | mysql | slave_relay_log_info | 0 | 3 | ./mysql/slave_relay_log_info.ibd | | mysql | slave_master_info | 0 | 4 | ./mysql/slave_master_info.ibd | | mysql | slave_worker_info | 0 | 5 | ./mysql/slave_worker_info.ibd | | innodbfreespacetest | t1 | 4194304 | 6 | ./innodbfreespacetest/t1.ibd | | innodbfreespacetest | t2 | 4194304 | 7 | ./innodbfreespacetest/t2.ibd | | innodbfreespacetest | t3 | 66060288 | 0 | NULL | | innodbfreespacetest | t4 | 66060288 | 0 | NULL | +---------------------+----------------------+-----------+-------+----------------------------------+ 9 rows in set (0.02 sec) mysql> SELECT (SELECT SUM(DATA_FREE) FROM INFORMATION_SCHEMA.TABLES T -> LEFT JOIN INFORMATION_SCHEMA.INNODB_SYS_TABLES ST ON -> ST.NAME=CONCAT(T.TABLE_SCHEMA,'/',T.TABLE_NAME) WHERE SPACE<>0) + -> (SELECT DATA_FREE FROM INFORMATION_SCHEMA.TABLES T -> LEFT JOIN INFORMATION_SCHEMA.INNODB_SYS_TABLES ST -> ON ST.NAME=CONCAT(T.TABLE_SCHEMA,'/',T.TABLE_NAME) -> WHERE SPACE=0 LIMIT 1) AS INNODB_FREE_GLOBAL; +--------------------+ | INNODB_FREE_GLOBAL | +--------------------+ | 74448896 | +--------------------+ 1 row in set (0.04 sec)Calculating the free space can be useful if you don't have autoextend enabled on your datafiles (requires innodb_file_per_table to be disabled). It can also be useful to calculate how much space could be freed by running OPTIMIZE TABLE (requires innodb_file_per_table to be enabled).
Conclusion:
If you're on 5.6 you can use INFORMATION_SCHEMA and for 5.5 and earlier you have to use a UDF or have (a script) to peek in the MySQL datadir.
wow
ReplyDeleteYes, it is WOW indeed.
ReplyDeleteHey Daniel, what's up with the line 7 in the table?
Cheers,
Dmitry
http://www.scnsoft.com/
This comment has been removed by the author.
ReplyDeleteThis comment has been removed by a blog administrator.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteGreat Article Daniel. Hope to read more soon !!
ReplyDeletecheers!
https://www.startechup.com
Good way to calculate free space .... Nice Article ....Good way to explain the whole process..
ReplyDeleteTarika
http://www.tarikatechnologies.com
tarika technologies
DeleteHello Daniela,
ReplyDeleteThanks for your informative clarification. At our Adoriasoft courses we are required to do the similar thing. Thanks to your post, now I know all the pecularities.
Good for us, cose we get a lot of useful things
ReplyDeleteGood blog on free space.Thanks for share.
ReplyDeletevertexplus
This comment has been removed by the author.
ReplyDeleteNice write up about data. Using big data in the freemium stage allows game developers to accurately measure, predict, and track player behavior to optimize the experience, increasing the likelihood users converting to a paid model. Innovecs Games development share their experience
ReplyDeleteMany thanks for sharing the great content related to Innodb with us.
ReplyDeleteHey Daniel, wish you have a merry Christmas and happy new year ahead.
Thanks & Regards,
Suman
https://www.speranzainc.com
This comment has been removed by the author.
ReplyDelete