innnodb 线程在做什么?

1.master 线程的主代码位于 
   (1)storage/innobase/srv/srv0srv.cc
   (2)storage/innobase/buf/buf0flu.cc

   srv_master_do_idle_task()每10秒中的操作。
   srv_master_do_active_tasks()每秒中的操作。

  1秒中的算法做的事情(主循环):
    (1)日志刷新到磁盘,即使这个事务没有提交(总是)
    (2)合并插入缓冲(可能)
       //(3)至多刷新innodb_io_capacity个innodb的缓冲池中的脏页到磁盘(可能) ---innodb新版本中已经交给了page_cleaner线程
    (4)如果没有用户活动,切换到background loop(可能)
  10秒中的算法做的事情(主循环):
    (1)刷新innodb_io_capacity个脏页到从盘(可能)
    (2)合并至多innodb_io_capacity*5%个插入缓冲(总是)
    (3)将日志缓冲刷新到磁盘(总是)
      //(4)刷新100个或者10个脏页到磁盘(总是)---innodb新版本中已经交给了page_cleaner线程
  切换到background loop做的事情:
    (1)删除innodb_purge_batch_size个无用的undo页(总是)
    (2)合并innodb_io_capacity*5%个插入缓冲(总是)
    (3)跳回到主循环(总是)
      //(4)不断刷新innodb_io_capacity个页直到符合条件---innodb新版本中已经交给了page_cleaner线程
    
mysql5.6后台线程:
+-----------+----------------------------------------+------------+
| thread_id | name                                   | type       |
+-----------+----------------------------------------+------------+
|         1 | thread/sql/main                        | BACKGROUND |
|         2 | thread/innodb/io_handler_thread        | BACKGROUND |
|         3 | thread/innodb/io_handler_thread        | BACKGROUND |
|         4 | thread/innodb/io_handler_thread        | BACKGROUND |
|         5 | thread/innodb/io_handler_thread        | BACKGROUND |
|         6 | thread/innodb/io_handler_thread        | BACKGROUND |
|         7 | thread/innodb/io_handler_thread        | BACKGROUND |
|         8 | thread/innodb/io_handler_thread        | BACKGROUND |
|         9 | thread/innodb/io_handler_thread        | BACKGROUND |
|        10 | thread/innodb/io_handler_thread        | BACKGROUND |
|        11 | thread/innodb/io_handler_thread        | BACKGROUND |
|        14 | thread/innodb/srv_master_thread        | BACKGROUND |
|        15 | thread/innodb/srv_monitor_thread       | BACKGROUND |
|        16 | thread/innodb/srv_purge_thread         | BACKGROUND | --undo页回收线程
|        17 | thread/innodb/srv_error_monitor_thread | BACKGROUND |
|        18 | thread/innodb/srv_lock_timeout_thread  | BACKGROUND |
|        19 | thread/innodb/page_cleaner_thread      | BACKGROUND | --刷新脏页线程
|        20 | thread/sql/signal_handler              | BACKGROUND |
+-----------+----------------------------------------+------------+
storage/innobase/buf/buf0flu.cc:

/******************************************************************//**
page_cleaner thread tasked with flushing dirty pages from the buffer
pools. As of now we'll have only one instance of this thread.
@return a dummy parameter */
extern "C" UNIV_INTERN
os_thread_ret_t
DECLARE_THREAD(buf_flush_page_cleaner_thread)(
/*==========================================*/
    void*    arg __attribute__((unused)))
            /*!< in: a dummy parameter required by
            os_thread_create */
{
    ulint    next_loop_time = ut_time_ms() + 1000;
    ulint    n_flushed = 0;
    ulint    last_activity = srv_get_activity_count();

    ut_ad(!srv_read_only_mode);

#ifdef UNIV_PFS_THREAD
    pfs_register_thread(buf_page_cleaner_thread_key);
#endif /* UNIV_PFS_THREAD */

#ifdef UNIV_DEBUG_THREAD_CREATION
    fprintf(stderr, "InnoDB: page_cleaner thread running, id %lu\n",
        os_thread_pf(os_thread_get_curr_id()));
#endif /* UNIV_DEBUG_THREAD_CREATION */

    buf_page_cleaner_is_active = TRUE;

    while (srv_shutdown_state == SRV_SHUTDOWN_NONE) {

        /* The page_cleaner skips sleep if the server is
        idle and there are no pending IOs in the buffer pool
        and there is work to do. */
        if (srv_check_activity(last_activity)
            || buf_get_n_pending_read_ios()
            || n_flushed == 0) {
            page_cleaner_sleep_if_needed(next_loop_time);
        }

        next_loop_time = ut_time_ms() + 1000;

        if (srv_check_activity(last_activity)) {
            last_activity = srv_get_activity_count();

            /* Flush pages from end of LRU if required */
            n_flushed = buf_flush_LRU_tail();

            /* Flush pages from flush_list if required */
            n_flushed += page_cleaner_flush_pages_if_needed();
        } else {
            n_flushed = page_cleaner_do_flush_batch(
                            PCT_IO(100),
                            LSN_MAX);

            if (n_flushed) {
                MONITOR_INC_VALUE_CUMULATIVE(
                    MONITOR_FLUSH_BACKGROUND_TOTAL_PAGE,
                    MONITOR_FLUSH_BACKGROUND_COUNT,
                    MONITOR_FLUSH_BACKGROUND_PAGES,
                    n_flushed);
            }
        }
    }

    ut_ad(srv_shutdown_state > 0);
    if (srv_fast_shutdown == 2) {
        /* In very fast shutdown we simulate a crash of
        buffer pool. We are not required to do any flushing */
        goto thread_exit;
    }

    /* In case of normal and slow shutdown the page_cleaner thread
    must wait for all other activity in the server to die down.
    Note that we can start flushing the buffer pool as soon as the
    server enters shutdown phase but we must stay alive long enough
    to ensure that any work done by the master or purge threads is
    also flushed.
    During shutdown we pass through two stages. In the first stage,
    when SRV_SHUTDOWN_CLEANUP is set other threads like the master
    and the purge threads may be working as well. We start flushing
    the buffer pool but can't be sure that no new pages are being
    dirtied until we enter SRV_SHUTDOWN_FLUSH_PHASE phase. */

    do {
        n_flushed = page_cleaner_do_flush_batch(PCT_IO(100), LSN_MAX);

        /* We sleep only if there are no pages to flush */
        if (n_flushed == 0) {
            os_thread_sleep(100000);
        }
    } while (srv_shutdown_state == SRV_SHUTDOWN_CLEANUP);

    /* At this point all threads including the master and the purge
    thread must have been suspended. */
    ut_a(srv_get_active_thread_type() == SRV_NONE);
    ut_a(srv_shutdown_state == SRV_SHUTDOWN_FLUSH_PHASE);

    /* We can now make a final sweep on flushing the buffer pool
    and exit after we have cleaned the whole buffer pool.
    It is important that we wait for any running batch that has
    been triggered by us to finish. Otherwise we can end up
    considering end of that batch as a finish of our final
    sweep and we'll come out of the loop leaving behind dirty pages
    in the flush_list */
    buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST);
    buf_flush_wait_LRU_batch_end();

    bool    success;

    do {

        success = buf_flush_list(PCT_IO(100), LSN_MAX, &n_flushed);
        buf_flush_wait_batch_end(NULL, BUF_FLUSH_LIST);

    } while (!success || n_flushed > 0);

    /* Some sanity checks */
    ut_a(srv_get_active_thread_type() == SRV_NONE);
    ut_a(srv_shutdown_state == SRV_SHUTDOWN_FLUSH_PHASE);
    for (ulint i = 0; i < srv_buf_pool_instances; i++) {
        buf_pool_t* buf_pool = buf_pool_from_array(i);
        ut_a(UT_LIST_GET_LEN(buf_pool->flush_list) == 0);
    }

    /* We have lived our life. Time to die. */

thread_exit:
    buf_page_cleaner_is_active = FALSE;

    /* We count the number of threads in os_thread_exit(). A created
    thread should always use that to exit and not use return() to exit. */
    os_thread_exit(NULL);

    OS_THREAD_DUMMY_RETURN;
}


/*********************************************************************//**
This function is called approximately once every second by the
page_cleaner thread. Based on various factors it decides if there is a
need to do flushing. If flushing is needed it is performed and the
number of pages flushed is returned.
@return number of pages flushed */
static
ulint
page_cleaner_flush_pages_if_needed(void)
/*====================================*/
{
    static    lsn_t        lsn_avg_rate = 0;
    static    lsn_t        prev_lsn = 0;
    static    lsn_t        last_lsn = 0;
    static    ulint        sum_pages = 0;
    static    ulint        last_pages = 0;
    static    ulint        prev_pages = 0;
    static    ulint        avg_page_rate = 0;
    static    ulint        n_iterations = 0;
    lsn_t            oldest_lsn;
    lsn_t            cur_lsn;
    lsn_t            age;
    lsn_t            lsn_rate;
    ulint            n_pages = 0;
    ulint            pct_for_dirty = 0;
    ulint            pct_for_lsn = 0;
    ulint            pct_total = 0;
    int            age_factor = 0;

    cur_lsn = log_get_lsn();

    if (prev_lsn == 0) {
        /* First time around. */
        prev_lsn = cur_lsn;
        return(0);
    }

    if (prev_lsn == cur_lsn) {
        return(0);
    }

    /* We update our variables every srv_flushing_avg_loops
    iterations to smooth out transition in workload. */
    if (++n_iterations >= srv_flushing_avg_loops) {

        avg_page_rate = ((sum_pages / srv_flushing_avg_loops)
                 + avg_page_rate) / 2;

        /* How much LSN we have generated since last call. */
        lsn_rate = (cur_lsn - prev_lsn) / srv_flushing_avg_loops;

        lsn_avg_rate = (lsn_avg_rate + lsn_rate) / 2;

        prev_lsn = cur_lsn;

        n_iterations = 0;

        sum_pages = 0;
    }

    oldest_lsn = buf_pool_get_oldest_modification();

    ut_ad(oldest_lsn <= log_get_lsn());

    age = cur_lsn > oldest_lsn ? cur_lsn - oldest_lsn : 0;

    pct_for_dirty = af_get_pct_for_dirty();
    pct_for_lsn = af_get_pct_for_lsn(age);

    pct_total = ut_max(pct_for_dirty, pct_for_lsn);

    /* Cap the maximum IO capacity that we are going to use by
    max_io_capacity. */
    n_pages = (PCT_IO(pct_total) + avg_page_rate) / 2;

    if (n_pages > srv_max_io_capacity) {
        n_pages = srv_max_io_capacity;
    }

    if (last_pages && cur_lsn - last_lsn > lsn_avg_rate / 2) {
        age_factor = prev_pages / last_pages;
    }

    MONITOR_SET(MONITOR_FLUSH_N_TO_FLUSH_REQUESTED, n_pages);

    prev_pages = n_pages;
    n_pages = page_cleaner_do_flush_batch(
        n_pages, oldest_lsn + lsn_avg_rate * (age_factor + 1));

    last_lsn= cur_lsn;
    last_pages= n_pages + 1;

    MONITOR_SET(MONITOR_FLUSH_AVG_PAGE_RATE, avg_page_rate);
    MONITOR_SET(MONITOR_FLUSH_LSN_AVG_RATE, lsn_avg_rate);
    MONITOR_SET(MONITOR_FLUSH_PCT_FOR_DIRTY, pct_for_dirty);
    MONITOR_SET(MONITOR_FLUSH_PCT_FOR_LSN, pct_for_lsn);

    if (n_pages) {
        MONITOR_INC_VALUE_CUMULATIVE(
            MONITOR_FLUSH_ADAPTIVE_TOTAL_PAGE,
            MONITOR_FLUSH_ADAPTIVE_COUNT,
            MONITOR_FLUSH_ADAPTIVE_PAGES,
            n_pages);

        sum_pages += n_pages;
    }

    return(n_pages);
}


storage/innobase/srv/srv0srv.c:

/*********************************************************************//**
Perform the tasks that the master thread is supposed to do when the
server is active. There are two types of tasks. The first category is
of such tasks which are performed at each inovcation of this function.
We assume that this function is called roughly every second when the
server is active. The second category is of such tasks which are
performed at some interval e.g.: purge, dict_LRU cleanup etc. */
static
void
srv_master_do_active_tasks(void)
/*============================*/
{
    ib_time_t    cur_time = ut_time();
    ullint        counter_time = ut_time_us(NULL);

    /* First do the tasks that we are suppose to do at each
    invocation of this function. */

    ++srv_main_active_loops;

    MONITOR_INC(MONITOR_MASTER_ACTIVE_LOOPS);

    /* ALTER TABLE in MySQL requires on Unix that the table handler
    can drop tables lazily after there no longer are SELECT
    queries to them. */
    srv_main_thread_op_info = "doing background drop tables";
    row_drop_tables_for_mysql_in_background();
    MONITOR_INC_TIME_IN_MICRO_SECS(
        MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND, counter_time);

    if (srv_shutdown_state > 0) {
        return;
    }

    /* make sure that there is enough reusable space in the redo
    log files */
    srv_main_thread_op_info = "checking free log space";
    log_free_check();

    /* Do an ibuf merge */
    srv_main_thread_op_info = "doing insert buffer merge";
    counter_time = ut_time_us(NULL);
    ibuf_contract_in_background(0, FALSE);
    MONITOR_INC_TIME_IN_MICRO_SECS(
        MONITOR_SRV_IBUF_MERGE_MICROSECOND, counter_time);

    /* Flush logs if needed */
    srv_main_thread_op_info = "flushing log";
    srv_sync_log_buffer_in_background();
    MONITOR_INC_TIME_IN_MICRO_SECS(
        MONITOR_SRV_LOG_FLUSH_MICROSECOND, counter_time);

    /* Now see if various tasks that are performed at defined
    intervals need to be performed. */

#ifdef MEM_PERIODIC_CHECK
    /* Check magic numbers of every allocated mem block once in
    SRV_MASTER_MEM_VALIDATE_INTERVAL seconds */
    if (cur_time % SRV_MASTER_MEM_VALIDATE_INTERVAL == 0) {
        mem_validate_all_blocks();
        MONITOR_INC_TIME_IN_MICRO_SECS(
            MONITOR_SRV_MEM_VALIDATE_MICROSECOND, counter_time);
    }
#endif
    if (srv_shutdown_state > 0) {
        return;
    }

    if (srv_shutdown_state > 0) {
        return;
    }

    if (cur_time % SRV_MASTER_DICT_LRU_INTERVAL == 0) {
        srv_main_thread_op_info = "enforcing dict cache limit";
        srv_master_evict_from_table_cache(50);
        MONITOR_INC_TIME_IN_MICRO_SECS(
            MONITOR_SRV_DICT_LRU_MICROSECOND, counter_time);
    }

    if (srv_shutdown_state > 0) {
        return;
    }

    /* Make a new checkpoint */
    if (cur_time % SRV_MASTER_CHECKPOINT_INTERVAL == 0) {
        srv_main_thread_op_info = "making checkpoint";
        log_checkpoint(TRUE, FALSE);
        MONITOR_INC_TIME_IN_MICRO_SECS(
            MONITOR_SRV_CHECKPOINT_MICROSECOND, counter_time);
    }
}


/*********************************************************************//**
Perform the tasks that the master thread is supposed to do whenever the
server is idle. We do check for the server state during this function
and if the server has entered the shutdown phase we may return from
the function without completing the required tasks.
Note that the server can move to active state when we are executing this
function but we don't check for that as we are suppose to perform more
or less same tasks when server is active. */
static
void
srv_master_do_idle_tasks(void)
/*==========================*/
{
    ullint    counter_time;

    ++srv_main_idle_loops;

    MONITOR_INC(MONITOR_MASTER_IDLE_LOOPS);


    /* ALTER TABLE in MySQL requires on Unix that the table handler
    can drop tables lazily after there no longer are SELECT
    queries to them. */
    counter_time = ut_time_us(NULL);
    srv_main_thread_op_info = "doing background drop tables";
    row_drop_tables_for_mysql_in_background();
    MONITOR_INC_TIME_IN_MICRO_SECS(
        MONITOR_SRV_BACKGROUND_DROP_TABLE_MICROSECOND,
             counter_time);

    if (srv_shutdown_state > 0) {
        return;
    }

    /* make sure that there is enough reusable space in the redo
    log files */
    srv_main_thread_op_info = "checking free log space";
    log_free_check();

    /* Do an ibuf merge */
    counter_time = ut_time_us(NULL);
    srv_main_thread_op_info = "doing insert buffer merge";
    ibuf_contract_in_background(0, TRUE);
    MONITOR_INC_TIME_IN_MICRO_SECS(
        MONITOR_SRV_IBUF_MERGE_MICROSECOND, counter_time);

    if (srv_shutdown_state > 0) {
        return;
    }

    srv_main_thread_op_info = "enforcing dict cache limit";
    srv_master_evict_from_table_cache(100);
    MONITOR_INC_TIME_IN_MICRO_SECS(
        MONITOR_SRV_DICT_LRU_MICROSECOND, counter_time);

    /* Flush logs if needed */
    srv_sync_log_buffer_in_background();
    MONITOR_INC_TIME_IN_MICRO_SECS(
        MONITOR_SRV_LOG_FLUSH_MICROSECOND, counter_time);

    if (srv_shutdown_state > 0) {
        return;
    }

    /* Make a new checkpoint */
    srv_main_thread_op_info = "making checkpoint";
    log_checkpoint(TRUE, FALSE);
    MONITOR_INC_TIME_IN_MICRO_SECS(MONITOR_SRV_CHECKPOINT_MICROSECOND,
                       counter_time);
}


/*********************************************************************//**
Perform the tasks during shutdown. The tasks that we do at shutdown
depend on srv_fast_shutdown:
2 => very fast shutdown => do no book keeping
1 => normal shutdown => clear drop table queue and make checkpoint
0 => slow shutdown => in addition to above do complete purge and ibuf
merge
@return TRUE if some work was done. FALSE otherwise */
static
ibool
srv_master_do_shutdown_tasks(
/*=========================*/
    ib_time_t*    last_print_time)/*!< last time the function
                    print the message */
{
    ulint        n_bytes_merged = 0;
    ulint        n_tables_to_drop = 0;

    ut_ad(!srv_read_only_mode);

    ++srv_main_shutdown_loops;

    ut_a(srv_shutdown_state > 0);

    /* In very fast shutdown none of the following is necessary */
    if (srv_fast_shutdown == 2) {
        return(FALSE);
    }

    /* ALTER TABLE in MySQL requires on Unix that the table handler
    can drop tables lazily after there no longer are SELECT
    queries to them. */
    srv_main_thread_op_info = "doing background drop tables";
    n_tables_to_drop = row_drop_tables_for_mysql_in_background();

    /* make sure that there is enough reusable space in the redo
    log files */
    srv_main_thread_op_info = "checking free log space";
    log_free_check();

    /* In case of normal shutdown we don't do ibuf merge or purge */
    if (srv_fast_shutdown == 1) {
        goto func_exit;
    }

    /* Do an ibuf merge */
    srv_main_thread_op_info = "doing insert buffer merge";
    n_bytes_merged = ibuf_contract_in_background(0, TRUE);

    /* Flush logs if needed */
    srv_sync_log_buffer_in_background();

func_exit:
    /* Make a new checkpoint about once in 10 seconds */
    srv_main_thread_op_info = "making checkpoint";
    log_checkpoint(TRUE, FALSE);

    /* Print progress message every 60 seconds during shutdown */
    if (srv_shutdown_state > 0 && srv_print_verbose_log) {
        srv_shutdown_print_master_pending(
            last_print_time, n_tables_to_drop, n_bytes_merged);
    }

    return(n_bytes_merged || n_tables_to_drop);
}

/*********************************************************************//**
Puts master thread to sleep. At this point we are using polling to
service various activities. Master thread sleeps for one second before
checking the state of the server again */
static
void
srv_master_sleep(void)
/*==================*/
{
    srv_main_thread_op_info = "sleeping";
    os_thread_sleep(1000000);
    srv_main_thread_op_info = "";
}


/*********************************************************************//**
The master thread controlling the server.
@return    a dummy parameter */
extern "C" UNIV_INTERN
os_thread_ret_t
DECLARE_THREAD(srv_master_thread)(
/*==============================*/
    void*    arg __attribute__((unused)))
            /*!< in: a dummy parameter required by
            os_thread_create */
{
    srv_slot_t*    slot;
    ulint        old_activity_count = srv_get_activity_count();
    ib_time_t    last_print_time;

    ut_ad(!srv_read_only_mode);

#ifdef UNIV_DEBUG_THREAD_CREATION
    fprintf(stderr, "Master thread starts, id %lu\n",
        os_thread_pf(os_thread_get_curr_id()));
#endif /* UNIV_DEBUG_THREAD_CREATION */

#ifdef UNIV_PFS_THREAD
    pfs_register_thread(srv_master_thread_key);
#endif /* UNIV_PFS_THREAD */

    srv_main_thread_process_no = os_proc_get_number();
    srv_main_thread_id = os_thread_pf(os_thread_get_curr_id());

    slot = srv_reserve_slot(SRV_MASTER);
    ut_a(slot == srv_sys->sys_threads);

    last_print_time = ut_time();
loop:
    if (srv_force_recovery >= SRV_FORCE_NO_BACKGROUND) {
        goto suspend_thread;
    }

    while (srv_shutdown_state == SRV_SHUTDOWN_NONE) {

        srv_master_sleep();

        MONITOR_INC(MONITOR_MASTER_THREAD_SLEEP);

        if (srv_check_activity(old_activity_count)) {
            old_activity_count = srv_get_activity_count();
            srv_master_do_active_tasks();
        } else {
            srv_master_do_idle_tasks();
        }
    }

    while (srv_master_do_shutdown_tasks(&last_print_time)) {

        /* Shouldn't loop here in case of very fast shutdown */
        ut_ad(srv_fast_shutdown < 2);
    }

suspend_thread:
    srv_main_thread_op_info = "suspending";

    srv_suspend_thread(slot);

    /* DO NOT CHANGE THIS STRING. innobase_start_or_create_for_mysql()
    waits for database activity to die down when converting < 4.1.x
    databases, and relies on this string being exactly as it is. InnoDB
    manual also mentions this string in several places. */
    srv_main_thread_op_info = "waiting for server activity";

    os_event_wait(slot->event);

    if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) {
        os_thread_exit(NULL);
    }

    goto loop;

    OS_THREAD_DUMMY_RETURN;    /* Not reached, avoid compiler warning */
}

 

你可能感兴趣的:(数据库源码原理)