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