InnoDB disk IO counters in SHOW STATUS

By Mark Callaghan on Saturday, October 23, 2010 at 7:23pm

There are several counters for disk IO in SHOW STATUS output. I want to know what causes IO to be done by InnoDB. I know the IO rate for my server from iostat output. How do I map that back to features in InnoDB?

 

The SHOW STATUS counters for InnoDB include:

* Innodb_data_fsyncs - number of fsync calls for data and log files

* Innodb_log_writes - number of writes to the log file. These use buffered IO.

* Innodb_os_log_fsyncs - number of fsync calls for the log file

 * Innodb_pages_read - number of reads done for data files. These include single-page reads done by per-connection threads, reads done by the main background thread during purge and insert buffer merges and reads done by the background IO threads.

* Innodb_data_reads - number of reads for data and log files. Per-connection threads and the main background thread do reads in the size of an InnoDB page. The background IO threads can merge adjacent requests and do reads that are a multiple of the InnoDB page size.

* Innodb_data_writes - number of writes for data and log files. Most writes are done by the background IO threads but in some cases per-connection threads request these writes and then block until they are done. This counter is incremented for the cheap log file writes that are a multiple of 512 bytes and use buffered IO, the frequently large writes to the double write buffer and the writes to the database files. The database file writes are a multiple of the InnoDB page size as write merging can be done. Some of the functions that request writes for dirty pages make it likely that write requests can be merged. Because this counter includes so many different types of writes it is not easy to reason about the amount of disk IO that is done for a given value.

* Innodb_pages_written - the number of data file pages written. Writes to adjacent pages may be merged into a larger disk IO request.

* Innodb_dblwr_pages_written - the number of data file pages written to the double write buffer.

* Innodb_dblwr_writes - the number of disk writes done for the double write buffer. When the double write buffer is enabled, pages are first written to it sequentially using one large IO request before being updated in place.

 

The InnoDB glossary is a great place to start if you don't understand some of the terms used here.

 

How can you use this data? I don't think that any of the stats are sufficient. There are a few stats that can be used in conjunction with iostat output to determine the sources of disk IO.

 

Innodb_data_writes describes the number of writes done but all writes are not created equal. Writes to the log file are small and use buffered IO. These are much less expensive that writes done to update InnoDB pages in place. But Innodb_data_writes does not distinguish between them. Innodb_os_log_fsyncs might be a better proxy for the amount of disk IO done for the InnoDB transaction log.

 

Innodb_pages_written describes the number of pages written but not the number of IO requests used to write them as write requests for adjacent pages can be merged and some of the code that schedules pages to be written attempts to find adjacent dirty pages to be written at the same time. 

 

Innodb_dblwr_pages_written describes the number of writes done for the doublewrite buffer. Therefore, the number of writes done for data files is (Innodb_data_writes - Innodb_log_writes - Innodb_dblwr_writes). On a production server the result from this formula is 304158461 and the value of Innodb_pages_written is 377672999 so the number of pages per disk write is 1.24. I have begun to use this script to check the sources of IO.

select variable_value into @data_writes from global_status

    where variable_name = "Innodb_data_writes";

select variable_value into @log_writes from global_status

    where variable_name = "Innodb_log_writes";

select variable_value into @dblwr_writes from global_status

    where variable_name = "Innodb_dblwr_writes";

select variable_value into @pages_written from global_status

    where variable_name = "Innodb_pages_written";

select variable_value into @data_reads from global_status

    where variable_name = "Innodb_data_reads";

select variable_value into @pages_read from global_status

    where variable_name = "Innodb_pages_read";

 

select @data_writes as Data_writes,

           @log_writes as Log_writes,

           @dblwr_writes as Dblwr_writes,

          (@data_writes - @log_writes - @dblwr_writes) as Page_disk_writes,

          @pages_written as Pages_written,

          @data_reads as Data_reads,

          @pages_read as Pages_read,      

          @data_reads / (@data_writes - @log_writes - @dblwr_writes) as

              Page_reads_per_write\G