Skip to content

Commit 01466ad

Browse files
committed
MDEV-32841 Provide Innodb async IO statistics
Provide some statistics about asynchronous IO reads and writes: - number of pending operations - number of completion callbacks that are currently being executed - number of completion callbacks that are currently queued (due to restriction on number of IO threads) - total number of IOs finished - total time to wait for free IO slot - total number of completions that were queued. Also revert tpool InnoDB perfschema instrumentation (MDEV-31048) That instrumentation of cache mutex did not bring any revelation ( the mutex is taken for a couple of instructions), and made it impossible to use tpool outside of the server (e.g in mariadbimport/dump)
1 parent 374783c commit 01466ad

File tree

9 files changed

+190
-55
lines changed

9 files changed

+190
-55
lines changed

mysql-test/suite/innodb/r/innodb_status_variables.result

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ AND variable_name NOT IN
55
'INNODB_MEM_ADAPTIVE_HASH',
66
'INNODB_BUFFERED_AIO_SUBMITTED','INNODB_BUFFER_POOL_PAGES_LATCHED');
77
variable_name
8+
INNODB_ASYNC_READS_PENDING
9+
INNODB_ASYNC_READS_TASKS_RUNNING
10+
INNODB_ASYNC_READS_TOTAL_COUNT
11+
INNODB_ASYNC_READS_TOTAL_ENQUEUES
12+
INNODB_ASYNC_READS_QUEUE_SIZE
13+
INNODB_ASYNC_READS_WAIT_SLOT_SEC
14+
INNODB_ASYNC_WRITES_PENDING
15+
INNODB_ASYNC_WRITES_TASKS_RUNNING
16+
INNODB_ASYNC_WRITES_TOTAL_COUNT
17+
INNODB_ASYNC_WRITES_TOTAL_ENQUEUES
18+
INNODB_ASYNC_WRITES_QUEUE_SIZE
19+
INNODB_ASYNC_WRITES_WAIT_SLOT_SEC
820
INNODB_BACKGROUND_LOG_SYNC
921
INNODB_BUFFER_POOL_DUMP_STATUS
1022
INNODB_BUFFER_POOL_LOAD_STATUS

storage/innobase/handler/ha_innodb.cc

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,6 @@ mysql_pfs_key_t trx_pool_manager_mutex_key;
526526
mysql_pfs_key_t lock_wait_mutex_key;
527527
mysql_pfs_key_t trx_sys_mutex_key;
528528
mysql_pfs_key_t srv_threads_mutex_key;
529-
mysql_pfs_key_t tpool_cache_mutex_key;
530529

531530
/* all_innodb_mutexes array contains mutexes that are
532531
performance schema instrumented if "UNIV_PFS_MUTEX"
@@ -558,7 +557,6 @@ static PSI_mutex_info all_innodb_mutexes[] = {
558557
PSI_KEY(rtr_match_mutex),
559558
PSI_KEY(rtr_path_mutex),
560559
PSI_KEY(trx_sys_mutex),
561-
PSI_KEY(tpool_cache_mutex),
562560
};
563561
# endif /* UNIV_PFS_MUTEX */
564562

@@ -886,6 +884,32 @@ static SHOW_VAR innodb_status_variables[]= {
886884
{"adaptive_hash_non_hash_searches",
887885
&export_vars.innodb_ahi_miss, SHOW_SIZE_T},
888886
#endif
887+
{"async_reads_pending",
888+
&export_vars.async_read_stats.pending_ops, SHOW_SIZE_T},
889+
{"async_reads_tasks_running",
890+
&export_vars.async_read_stats.completion_stats.tasks_running, SHOW_SIZE_T},
891+
{"async_reads_total_count",
892+
&export_vars.async_read_stats.completion_stats.total_tasks_executed,SHOW_ULONGLONG},
893+
{"async_reads_total_enqueues",
894+
&export_vars.async_read_stats.completion_stats.total_tasks_enqueued,SHOW_ULONGLONG},
895+
{"async_reads_queue_size",
896+
&export_vars.async_read_stats.completion_stats.queue_size, SHOW_SIZE_T},
897+
{"async_reads_wait_slot_sec",
898+
&export_vars.async_read_stats.slot_wait_time_sec, SHOW_DOUBLE},
899+
900+
{"async_writes_pending",
901+
&export_vars.async_write_stats.pending_ops,SHOW_SIZE_T},
902+
{"async_writes_tasks_running",
903+
&export_vars.async_write_stats.completion_stats.tasks_running, SHOW_SIZE_T},
904+
{"async_writes_total_count",
905+
&export_vars.async_write_stats.completion_stats.total_tasks_executed, SHOW_ULONGLONG},
906+
{"async_writes_total_enqueues",
907+
&export_vars.async_write_stats.completion_stats.total_tasks_enqueued, SHOW_ULONGLONG},
908+
{"async_writes_queue_size",
909+
&export_vars.async_write_stats.completion_stats.queue_size, SHOW_SIZE_T},
910+
{"async_writes_wait_slot_sec",
911+
&export_vars.async_write_stats.slot_wait_time_sec, SHOW_DOUBLE},
912+
889913
{"background_log_sync", &srv_log_writes_and_flush, SHOW_SIZE_T},
890914
{"buffer_pool_dump_status",
891915
(char*) &export_vars.innodb_buffer_pool_dump_status, SHOW_CHAR},

storage/innobase/include/os0file.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,4 +1187,44 @@ inline bool is_absolute_path(const char *path)
11871187

11881188
#include "os0file.inl"
11891189

1190+
/**
1191+
Structure used for async io statistics
1192+
There is one instance of this structure for each operation type
1193+
(read or write)
1194+
*/
1195+
struct innodb_async_io_stats_t
1196+
{
1197+
/**
1198+
Current of submitted and not yet finished IOs.
1199+
IO is considered finished when it finished in the OS
1200+
*and* the completion callback has been called
1201+
*/
1202+
size_t pending_ops;
1203+
/**
1204+
Time, in seconds, spent waiting for a slot to become
1205+
available. There is a limited number of slots for async IO
1206+
operations. If all slots are in use, the IO submission has
1207+
to wait.
1208+
*/
1209+
double slot_wait_time_sec;
1210+
1211+
/**
1212+
Information related to IO completion callbacks.
1213+
1214+
- number of tasks currently running (<= innodb_read/write_io_threads)
1215+
- total number of tasks that have been completed
1216+
- current task queue size . Queueing happens if running tasks is
1217+
maxed out (equal to innodb_read/write_io_threads)
1218+
- total number of tasks that have been queued
1219+
*/
1220+
tpool::group_stats completion_stats;
1221+
};
1222+
1223+
/**
1224+
Statistics for asynchronous I/O
1225+
@param[in] op operation - aio_opcode::AIO_PREAD or aio_opcode::AIO_PWRITE
1226+
@param[in] stats - structure to fill
1227+
*/
1228+
extern void innodb_io_slots_stats(tpool::aio_opcode op,
1229+
innodb_async_io_stats_t *stats);
11901230
#endif /* os0file_h */

storage/innobase/include/srv0srv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,8 @@ struct export_var_t{
573573
ulint innodb_ahi_hit;
574574
ulint innodb_ahi_miss;
575575
#endif /* BTR_CUR_HASH_ADAPT */
576+
innodb_async_io_stats_t async_read_stats;
577+
innodb_async_io_stats_t async_write_stats;
576578
char innodb_buffer_pool_dump_status[OS_FILE_MAX_PATH + 128];/*!< Buf pool dump status */
577579
char innodb_buffer_pool_load_status[OS_FILE_MAX_PATH + 128];/*!< Buf pool load status */
578580
char innodb_buffer_pool_resize_status[512];/*!< Buf pool resize status */

storage/innobase/os/os0file.cc

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Created 10/21/1995 Heikki Tuuri
8080
class io_slots
8181
{
8282
private:
83-
tpool::cache<tpool::aiocb> m_cache;
83+
tpool::cache<tpool::aiocb, true> m_cache;
8484
tpool::task_group m_group;
8585
int m_max_aio;
8686
public:
@@ -106,9 +106,9 @@ class io_slots
106106
}
107107

108108
/* Wait for completions of all AIO operations */
109-
void wait(mysql_mutex_t &m)
109+
void wait(std::unique_lock<std::mutex> &lk)
110110
{
111-
m_cache.wait(m);
111+
m_cache.wait(lk);
112112
}
113113

114114
void wait()
@@ -121,6 +121,11 @@ class io_slots
121121
return m_cache.pos();
122122
}
123123

124+
std::chrono::duration<float> wait_time()
125+
{
126+
return m_cache.wait_time();
127+
}
128+
124129
tpool::task_group* get_task_group()
125130
{
126131
return &m_group;
@@ -131,7 +136,7 @@ class io_slots
131136
wait();
132137
}
133138

134-
mysql_mutex_t& mutex()
139+
std::mutex &mutex()
135140
{
136141
return m_cache.mutex();
137142
}
@@ -152,6 +157,22 @@ class io_slots
152157
static io_slots *read_slots;
153158
static io_slots *write_slots;
154159

160+
/**
161+
Statistics for asynchronous I/O
162+
@param[in] op operation type (aio_opcode::AIO_PREAD or aio_opcode::AIO_PWRITE)
163+
@param[in] stats pointer to the structure to fill
164+
*/
165+
void innodb_io_slots_stats(tpool::aio_opcode op, innodb_async_io_stats_t *stats)
166+
{
167+
io_slots *slots= op == tpool::aio_opcode::AIO_PREAD? read_slots : write_slots;
168+
169+
stats->pending_ops = slots->pending_io_count();
170+
stats->slot_wait_time_sec=
171+
std::chrono::duration_cast<std::chrono::duration<float>>(
172+
slots->wait_time()).count();
173+
slots->task_group().get_stats(&stats->completion_stats);
174+
}
175+
155176
/** Number of retries for partial I/O's */
156177
constexpr ulint NUM_RETRIES_ON_PARTIAL_IO = 10;
157178

@@ -3623,9 +3644,8 @@ more concurrent threads via thread_group setting.
36233644
int os_aio_resize(ulint n_reader_threads, ulint n_writer_threads)
36243645
{
36253646
/* Lock the slots, and wait until all current IOs finish.*/
3626-
auto &lk_read= read_slots->mutex(), &lk_write= write_slots->mutex();
3627-
mysql_mutex_lock(&lk_read);
3628-
mysql_mutex_lock(&lk_write);
3647+
std::unique_lock<std::mutex> lk_read(read_slots->mutex()),
3648+
lk_write(write_slots->mutex());
36293649

36303650
read_slots->wait(lk_read);
36313651
write_slots->wait(lk_write);
@@ -3653,9 +3673,6 @@ int os_aio_resize(ulint n_reader_threads, ulint n_writer_threads)
36533673
read_slots->resize(max_read_events, static_cast<int>(n_reader_threads));
36543674
write_slots->resize(max_write_events, static_cast<int>(n_writer_threads));
36553675
}
3656-
3657-
mysql_mutex_unlock(&lk_read);
3658-
mysql_mutex_unlock(&lk_write);
36593676
return ret;
36603677
}
36613678

@@ -3693,10 +3710,8 @@ void os_aio_wait_until_no_pending_writes(bool declare)
36933710
/** @return number of pending reads */
36943711
size_t os_aio_pending_reads()
36953712
{
3696-
mysql_mutex_lock(&read_slots->mutex());
3697-
size_t pending= read_slots->pending_io_count();
3698-
mysql_mutex_unlock(&read_slots->mutex());
3699-
return pending;
3713+
std::lock_guard<std::mutex> lock(read_slots->mutex());
3714+
return read_slots->pending_io_count();
37003715
}
37013716

37023717
/** @return approximate number of pending reads */
@@ -3708,10 +3723,8 @@ size_t os_aio_pending_reads_approx()
37083723
/** @return number of pending writes */
37093724
size_t os_aio_pending_writes()
37103725
{
3711-
mysql_mutex_lock(&write_slots->mutex());
3712-
size_t pending= write_slots->pending_io_count();
3713-
mysql_mutex_unlock(&write_slots->mutex());
3714-
return pending;
3726+
std::lock_guard<std::mutex> lock(write_slots->mutex());
3727+
return write_slots->pending_io_count();
37153728
}
37163729

37173730
/** Wait until all pending asynchronous reads have completed.

storage/innobase/srv/srv0srv.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,9 @@ srv_printf_innodb_monitor(
813813
return(ret);
814814
}
815815

816+
void innodb_io_slots_stats(tpool::aio_opcode op,
817+
innodb_async_io_stats_t *stats);
818+
816819
/******************************************************************//**
817820
Function to pass InnoDB status variables to MySQL */
818821
void
@@ -824,6 +827,10 @@ srv_export_innodb_status(void)
824827
if (!srv_read_only_mode) {
825828
fil_crypt_total_stat(&crypt_stat);
826829
}
830+
innodb_io_slots_stats(tpool::aio_opcode::AIO_PREAD,
831+
&export_vars.async_read_stats);
832+
innodb_io_slots_stats(tpool::aio_opcode::AIO_PWRITE,
833+
&export_vars.async_write_stats);
827834

828835
#ifdef BTR_CUR_HASH_ADAPT
829836
export_vars.innodb_ahi_hit = btr_cur_n_sea;

tpool/task_group.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ namespace tpool
4242
:
4343
m_queue(8),
4444
m_mtx(),
45+
m_total_tasks(0),
46+
m_total_enqueues(0),
4547
m_tasks_running(),
4648
m_max_concurrent_tasks(max_concurrency),
4749
m_enable_task_release(enable_task_release)
@@ -59,6 +61,7 @@ namespace tpool
5961
{
6062
/* Queue for later execution by another thread.*/
6163
m_queue.push(t);
64+
m_total_enqueues++;
6265
return;
6366
}
6467
m_tasks_running++;
@@ -72,7 +75,7 @@ namespace tpool
7275
t->release();
7376
}
7477
lk.lock();
75-
78+
m_total_tasks++;
7679
if (m_queue.empty())
7780
break;
7881
t = m_queue.front();
@@ -96,6 +99,15 @@ namespace tpool
9699
}
97100
}
98101

102+
void task_group::get_stats(group_stats *stats)
103+
{
104+
std::lock_guard<std::mutex> lk(m_mtx);
105+
stats->tasks_running= m_tasks_running;
106+
stats->queue_size= m_queue.size();
107+
stats->total_tasks_executed= m_total_tasks;
108+
stats->total_tasks_enqueued= m_total_enqueues;
109+
}
110+
99111
task_group::~task_group()
100112
{
101113
std::unique_lock<std::mutex> lk(m_mtx);

tpool/tpool.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ typedef void (*callback_func)(void *);
5757
typedef void (*callback_func_np)(void);
5858
class task;
5959

60+
struct group_stats
61+
{
62+
/** Current number of running tasks*/
63+
size_t tasks_running;
64+
/** Current number of tasks in the queue*/
65+
size_t queue_size;
66+
/** Total number of tasks executed */
67+
unsigned long long total_tasks_executed;
68+
/** Total number of tasks enqueued */
69+
unsigned long long total_tasks_enqueued;
70+
};
71+
6072
/** A class that can be used e.g. for
6173
restricting concurrency for specific class of tasks. */
6274

@@ -66,6 +78,8 @@ class task_group
6678
circular_queue<task*> m_queue;
6779
std::mutex m_mtx;
6880
std::condition_variable m_cv;
81+
unsigned long long m_total_tasks;
82+
unsigned long long m_total_enqueues;
6983
unsigned int m_tasks_running;
7084
unsigned int m_max_concurrent_tasks;
7185
const bool m_enable_task_release;
@@ -75,6 +89,7 @@ class task_group
7589
void set_max_tasks(unsigned int max_concurrent_tasks);
7690
void execute(task* t);
7791
void cancel_pending(task *t);
92+
void get_stats(group_stats* stats);
7893
~task_group();
7994
};
8095

0 commit comments

Comments
 (0)