| Bug #56276 | Re-architect index->lock mutex for less contention to improve scaling | ||
|---|---|---|---|
| Submitted: | 26 Aug 2010 2:34 | Modified: | 26 Aug 2010 2:37 |
| Reporter: | Shannon Wade | Email Updates: | |
| Status: | Verified | Impact on me: | |
| Category: | MySQL Server: InnoDB storage engine | Severity: | S4 (Feature request) |
| Version: | OS: | Any | |
| Assigned to: | CPU Architecture: | Any | |
[26 Aug 2010 2:34]
Shannon Wade
[26 Aug 2010 18:29]
Ben Krug
The following may be an example, from an affected customer:
We have a case where trx_purge has a lock that blocks other threads. The basic problem is that the thread running trx_purge holds the dict lock for the table/index in X mode
while it is blocked on IO (calling pread64). That limits throughput and hurts response time. Why does btr_cur_search_to_nth_level need this code?
if (latch_mode == BTR_MODIFY_TREE) {
mtr_x_lock(dict_index_get_lock(index), mtr);
trx_purge:
pread64,os_file_pread,os_file_read,os_aio,_fil_io,buf_read_page_low,buf_read_page,buf_page_get_gen,
btr_cur_latch_leaves,btr_cur_search_to_nth_level,row_search_index_entry,
row_purge_remove_sec_if_poss_low,row_purge_step,que_run_threads,trx_purge,srv_master_thread,start_thread,clone
blocked thread:
pthread_cond_wait@@GLIBC_2.3.2,os_event_wait_low,sync_array_wait_event,rw_lock_s_lock_spin,
btr_cur_search_to_nth_level,row_ins_index_entry_low,row_ins_index_entry,row_ins_step,
row_insert_for_mysql,ha_innobase::write_row,handler::ha_write_row,write_record,
mysql_insert,mysql_execute_command,mysql_parse,Query_log_event::do_apply_event,
apply_event_and_update_pos,handle_slave_sql,start_thread,clone
I think this comes from this code in btr_cur_search_to_nth_level
if (latch_mode == BTR_MODIFY_TREE) {
mtr_x_lock(dict_index_get_lock(index), mtr);
} else if (latch_mode == BTR_CONT_MODIFY_TREE) {
/* Do nothing */
ut_ad(mtr_memo_contains(mtr, dict_index_get_lock(index),
MTR_MEMO_X_LOCK));
} else {
mtr_s_lock(dict_index_get_lock(index), mtr);
}
And trx_purge did mtr_x_lock because of:
row_purge_remove_sec_if_poss(
/*=========================*/
purge_node_t* node, /*!< in: row purge node */
dict_index_t* index, /*!< in: index */
dtuple_t* entry) /*!< in: index entry */
{
ibool success;
ulint n_tries = 0;
/* fputs("Purge: Removing secondary record\n", stderr); */
success = row_purge_remove_sec_if_poss_low(node, index, entry,
BTR_MODIFY_LEAF);
if (success) {
return;
}
retry:
success = row_purge_remove_sec_if_poss_low(node, index, entry,
BTR_MODIFY_TREE);
Which means that the first call to row_purge_remove_sec_if_poss_low(..., BTR_MODIFY_LEAF) failed and then it was called again with BTR_MODIFY_TREE. It can fail when
row_purge_remove_sec_if_poss_low returns FALSE here:
if (!success || !old_has) {
/* Remove the index record */
if (mode == BTR_MODIFY_LEAF) {
success = btr_cur_optimistic_delete(btr_cur, &mtr);
} else {
ut_ad(mode == BTR_MODIFY_TREE);
btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
RB_NONE, &mtr);
success = err == DB_SUCCESS;
ut_a(success || err == DB_OUT_OF_FILE_SPACE);
}
}
btr_pcur_close(&pcur);
mtr_commit(&mtr);
return(success);
And for that to return false, btr_cur_pessimistic_delete must return false and that returns the value of:
no_compress_needed = !rec_offs_any_extern(offsets)
&& btr_cur_can_delete_without_compress(
cursor, rec_offs_size(offsets), mtr);
[27 Aug 2014 13:02]
Mark Callaghan
This can be updated and maybe closed based on work in 5.7
