mysql优化器在统计全表扫描的代价时的方法

innodb 的聚集索引 的叶子结点 存放的 是 索引值以及数据页的偏移量

那么在计算全表扫描的代价是怎么计算的呢?

我们知道代价 为 cpu代价+io代价

cpu代价 就是 每5条记录比对 计算一个代价 (这里的记录并不是我们数据记录,而是索引记录) 是数据记录个数

又是如何取出全表的总记录呢 (即全表的总索引记录)

具体方法是 通过索引能拿到叶子结点的page数,page页默认16K ,那么总容量为 leaf page num * 16k

再计算这个索引的长度,因为索引可能是由多个字段构成,因此要遍历,假设为 m

 total_records = leaf page num * 16k /m 就是 索引记录个数了, 一条聚焦索引记录对应一条数据记录,所以这里是总的记录数

还是有问题 这个leaf page是数据页,而m是主键的长度,上面的total_records计算出来的结果 并不是准确的记录个数,按理说m为一条记录的长度,但代码里是主键的长度

那么cpu cost 就是 total_records/5+1

  io cost 就是  (double) (prebuilt->table->stat_clustered_index_size(聚簇索引叶页面数);

/******************************************************************//**
Calculate the time it takes to read a set of ranges through an index
This enables us to optimise reads for clustered indexes.
@return    estimated time measured in disk seeks */
UNIV_INTERN
double
ha_innobase::read_time(
/*===================*/
    uint    index,    /*!< in: key number */
    uint    ranges,    /*!< in: how many ranges */
    ha_rows rows)    /*!< in: estimated number of rows in the ranges */
{
    ha_rows total_rows;
    double    time_for_scan;

    if (index != table->s->primary_key) {
        /* Not clustered */
        return(handler::read_time(index, ranges, rows));
    }

    if (rows <= 2) {

        return((double) rows);
    }

    /* Assume that the read time is proportional to the scan time for all
    rows + at most one seek per range. */

    time_for_scan = scan_time();
    
    //estimate_rows_upper_bound这里就是计算全表总记录的函数
    if ((total_rows = estimate_rows_upper_bound()) < rows) {

        return(time_for_scan);
    }

    return(ranges + (double) rows / (double) total_rows * time_for_scan);
}

/*********************************************************************//**
Gives an UPPER BOUND to the number of rows in a table. This is used in
filesort.cc.
@return    upper bound of rows */
UNIV_INTERN
ha_rows
ha_innobase::estimate_rows_upper_bound(void)
/*======================================*/
{
    dict_index_t*    index;
    ulonglong    estimate;
    ulonglong    local_data_file_length;
    ulint        stat_n_leaf_pages;


    //取得该表的第一个索引,就是聚集索引
    index = dict_table_get_first_index(prebuilt->table);
    
    //聚焦索引的叶子结点个数
    stat_n_leaf_pages = index->stat_n_leaf_pages;

    //大小为 叶子结点个数*16k
    local_data_file_length = ((ulonglong) stat_n_leaf_pages) * UNIV_PAGE_SIZE;


    /* Calculate a minimum length for a clustered index record and from
    that an upper bound for the number of rows. Since we only calculate
    new statistics in row0mysql.c when a table has grown by a threshold
    factor, we must add a safety factor 2 in front of the formula below. */
    
    //计算这个聚集索引的大小
    // 2* 总叶子个数*16K / 聚焦索引大小 得到聚集索引记录个数
    estimate = 2 * local_data_file_length /
                     dict_index_calc_min_rec_len(index);

    DBUG_RETURN((ha_rows) estimate);
}

/*********************************************************************//**
Calculates the minimum record length in an index. */
UNIV_INTERN
ulint
dict_index_calc_min_rec_len(
/*========================*/
    const dict_index_t*    index)    /*!< in: index */
{
    ulint    sum    = 0;
    ulint    i;

    //记录为compack 紧凑模式,因为有可能这个索引是由多个字段组成,要遍历,求出总字节数
    ulint    comp    = dict_table_is_comp(index->table);

    if (comp) {
        ulint nullable = 0;
        sum = REC_N_NEW_EXTRA_BYTES;
        for (i = 0; i < dict_index_get_n_fields(index); i++) {
            const dict_col_t*    col
                = dict_index_get_nth_col(index, i);
            ulint    size = dict_col_get_fixed_size(col, comp);
            sum += size;
            if (!size) {
                size = col->len;
                sum += size < 128 ? 1 : 2;
            }
            if (!(col->prtype & DATA_NOT_NULL)) {
                nullable++;
            }
        }

        /* round the NULL flags up to full bytes */
        sum += UT_BITS_IN_BYTES(nullable);

        return(sum);
    }
}

 

结构体dict_index_t

/** InnoDB B-tree index */
typedef struct dict_index_struct dict_index_t;

/** Data structure for an index.  Most fields will be
initialized to 0, NULL or FALSE in dict_mem_index_create(). */
struct dict_index_struct{
    index_id_t    id;    /*!< id of the index */
    mem_heap_t*    heap;    /*!< memory heap */
    const char*    name;    /*!< index name */
    const char*    table_name;/*!< table name */
    dict_table_t*    table;    /*!< back pointer to table */ //
#ifndef UNIV_HOTBACKUP
    unsigned    space:32;
                /*!< space where the index tree is placed */
    unsigned    page:32;/*!< index tree root page number */
#endif /* !UNIV_HOTBACKUP */
    unsigned    type:DICT_IT_BITS;
                /*!< index type (DICT_CLUSTERED, DICT_UNIQUE,
                DICT_UNIVERSAL, DICT_IBUF, DICT_CORRUPT) */
#define MAX_KEY_LENGTH_BITS 12
    unsigned    trx_id_offset:MAX_KEY_LENGTH_BITS;
                /*!< position of the trx id column
                in a clustered index record, if the fields
                before it are known to be of a fixed size,
                0 otherwise */
#if (1<<MAX_KEY_LENGTH_BITS) < MAX_KEY_LENGTH
# error (1<<MAX_KEY_LENGTH_BITS) < MAX_KEY_LENGTH
#endif
    unsigned    n_user_defined_cols:10;
                /*!< number of columns the user defined to
                be in the index: in the internal
                representation we add more columns */
    unsigned    n_uniq:10;/*!< number of fields from the beginning
                which are enough to determine an index
                entry uniquely */
    unsigned    n_def:10;/*!< number of fields defined so far */
    unsigned    n_fields:10;/*!< number of fields in the index */
    unsigned    n_nullable:10;/*!< number of nullable fields */
    unsigned    cached:1;/*!< TRUE if the index object is in the
                dictionary cache */
    unsigned    to_be_dropped:1;
                /*!< TRUE if this index is marked to be
                dropped in ha_innobase::prepare_drop_index(),
                otherwise FALSE. Protected by
                dict_sys->mutex, dict_operation_lock and
                index->lock.*/
    dict_field_t*    fields;    /*!< array of field descriptions */
#ifndef UNIV_HOTBACKUP
    UT_LIST_NODE_T(dict_index_t)
            indexes;/*!< list of indexes of the table */
    btr_search_t*    search_info; /*!< info used in optimistic searches */
    /*----------------------*/
    /** Statistics for query optimization */
    /* @{ */
    ib_int64_t*    stat_n_diff_key_vals;
                /*!< approximate number of different
                key values for this index, for each
                n-column prefix where n <=
                dict_get_n_unique(index); we
                periodically calculate new
                estimates */
    ib_int64_t*    stat_n_non_null_key_vals;
                /* approximate number of non-null key values
                for this index, for each column where
                n < dict_get_n_unique(index); This
                is used when innodb_stats_method is
                "nulls_ignored". */
    ulint        stat_index_size;
                /*!< approximate index size in
                database pages */
    ulint        stat_n_leaf_pages;
                /*!< approximate number of leaf pages in the
                index tree */
    /* @} */
    rw_lock_t    lock;    /*!< read-write lock protecting the
                upper levels of the index tree */
    trx_id_t    trx_id; /*!< id of the transaction that created this
                index, or 0 if the index existed
                when InnoDB was started up */
#endif /* !UNIV_HOTBACKUP */
#ifdef UNIV_BLOB_DEBUG
    mutex_t        blobs_mutex;
                /*!< mutex protecting blobs */
    void*        blobs;    /*!< map of (page_no,heap_no,field_no)
                to first_blob_page_no; protected by
                blobs_mutex; @see btr_blob_dbg_t */
#endif /* UNIV_BLOB_DEBUG */
#ifdef UNIV_DEBUG
    ulint        magic_n;/*!< magic number */
/** Value of dict_index_struct::magic_n */
# define DICT_INDEX_MAGIC_N    76789786
#endif
};

 

posted @ 2017-03-31 19:02  taek  阅读(1603)  评论(0编辑  收藏  举报