• 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
    ",
            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
    ",
            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 */
    }
  • 相关阅读:
    .NET:CLR via C# The CLR’s Execution Model
    VisualStudio:WEB 性能测试和负载测试 入门
    Maven:Maven 入门
    技术人生:东莞之行
    技术人生:新的生活计划
    Java:使用 Java 开发的一个异常处理框架
    .NET:命令行解析器介绍
    技术人生:希望有生之年开发一个“自己的解释语言”
    .NET:异常处理的两条“黄金定律”,求批!
    FAQ:Domain Event 和 C# 中的 Event 有啥区别?
  • 原文地址:https://www.cnblogs.com/innobase/p/4584316.html
Copyright © 2020-2023  润新知