zoukankan      html  css  js  c++  java
  • MySQL InnoDB Engine--自适应哈希索引代码瞎猜02

    自适应哈希索引数据结构

    对B+树的数据查找,需要遍历从根节点到叶子节点之间的每一层的节点,因此B+树的树高过高时,会影响B+树的查找效率。

    Innodb存储引擎根据查询模式,对活跃的数据页中的记录进行哈希索引,以实现快速查找,解决B+树树高问题。

    为标识查询模式和热点页,需要在索引和数据页上有相应的数据结构来存放访问信息。

    • 每个索引对象有一个btr_search_t对象来保持索引的访问模式和相关信息。
    • 每个数据页对象有一个buf_block_t对象来保持数据页的访问模式和相关信息。

    btr_search_t对象

    btr_search_t对象来保持索引的访问模式以及相关AHI信息:

    • hash_analysis:通过索引访问的次数,如果通过索引访问次数超过BTR_SEARCH_HASH_ANALYSIS(默认17)次数时,则开始对索引上的访问模式进行统计。
    • n_hash_potential:按照连续查询模式成功次数,当成功次数超过BTR_SEARCH_BUILD_LIMIT(默认100)次数时,则开始对索引中记录创建HASH索引。
    • n_fields:自适应哈希索引前缀列的数量
    • n_bytes:自适应哈希索引的字节数(非完整列)
    • left_side:访问数据方向,用于判断对重复列进行查找的方向(向左还是向右
    struct btr_search_t{
        ulint    ref_count;    /*!< Number of blocks in this index tree
                    that have search index built
                    i.e. block->index points to this index.
                    Protected by search latch except
                    when during initialization in
                    btr_search_info_create(). */
    
        /* @{ The following fields are not protected by any latch.
        Unfortunately, this means that they must be aligned to
        the machine word, i.e., they cannot be turned into bit-fields. */
        buf_block_t* root_guess;/*!< the root page frame when it was last time
                    fetched, or NULL */
        ulint    withdraw_clock;    /*!< the withdraw clock value of the buffer
                    pool when root_guess was stored */
        ulint    hash_analysis;    /*!< when this exceeds
                    BTR_SEARCH_HASH_ANALYSIS, the hash
                    analysis starts; this is reset if no
                    success noticed */
        ibool    last_hash_succ;    /*!< TRUE if the last search would have
                    succeeded, or did succeed, using the hash
                    index; NOTE that the value here is not exact:
                    it is not calculated for every search, and the
                    calculation itself is not always accurate! */
        ulint    n_hash_potential;
                    /*!< number of consecutive searches
                    which would have succeeded, or did succeed,
                    using the hash index;
                    the range is 0 .. BTR_SEARCH_BUILD_LIMIT + 5 */
        /* @} */
        /*---------------------- @{ */
        ulint    n_fields;    /*!< recommended prefix length for hash search:
                    number of full fields */
        ulint    n_bytes;    /*!< recommended prefix: number of bytes in
                    an incomplete field
                    @see BTR_PAGE_MAX_REC_SIZE */
        ibool    left_side;    /*!< TRUE or FALSE, depending on whether
                    the leftmost record of several records with
                    the same prefix should be indexed in the
                    hash index */
        /*---------------------- @} */
    #ifdef UNIV_SEARCH_PERF_STAT
        ulint    n_hash_succ;    /*!< number of successful hash searches thus
                    far */
        ulint    n_hash_fail;    /*!< number of failed hash searches */
        ulint    n_patt_succ;    /*!< number of successful pattern searches thus
                    far */
        ulint    n_searches;    /*!< number of searches */
    #endif /* UNIV_SEARCH_PERF_STAT */
    #ifdef UNIV_DEBUG
        ulint    magic_n;    /*!< magic number @see BTR_SEARCH_MAGIC_N */
    /** value of btr_search_t::magic_n, used in assertions */
    # define BTR_SEARCH_MAGIC_N    1112765
    #endif /* UNIV_DEBUG */
    };

    buf_block_t 对象

    每个数据页有一个buf_block_t对象用来存放页的访问信息,判断该数据页是否需要对该页记录建立自适应哈希索引。

    • n_hash_helps:用来控制是否创建哈希索引的计数器
    • n_fields:自适应哈希索引前缀列的数量
    • n_bytes:自适应哈希索引的字节数(非完整列)
    /** The buffer control block structure */
    
    struct buf_block_t{
    
        /** @name General fields */
        /* @{ */
    
        buf_page_t    page;        /*!< page information; this must
                        be the first field, so that
                        buf_pool->page_hash can point
                        to buf_page_t or buf_block_t */
        byte*        frame;        /*!< pointer to buffer frame which
                        is of size UNIV_PAGE_SIZE, and
                        aligned to an address divisible by
                        UNIV_PAGE_SIZE */
    #ifndef UNIV_HOTBACKUP
        BPageLock    lock;        /*!< read-write lock of the buffer
                        frame */
        UT_LIST_NODE_T(buf_block_t) unzip_LRU;
                        /*!< node of the decompressed LRU list;
                        a block is in the unzip_LRU list
                        if page.state == BUF_BLOCK_FILE_PAGE
                        and page.zip.data != NULL */
    #ifdef UNIV_DEBUG
        ibool        in_unzip_LRU_list;/*!< TRUE if the page is in the
                        decompressed LRU list;
                        used in debugging */
        ibool        in_withdraw_list;
    #endif /* UNIV_DEBUG */
        unsigned    lock_hash_val:32;/*!< hashed value of the page address
                        in the record lock hash table;
                        protected by buf_block_t::lock
                        (or buf_block_t::mutex, buf_pool->mutex
                            in buf_page_get_gen(),
                        buf_page_init_for_read()
                        and buf_page_create()) */
        /* @} */
        /** @name Optimistic search field */
        /* @{ */
    
        ib_uint64_t    modify_clock;    /*!< this clock is incremented every
                        time a pointer to a record on the
                        page may become obsolete; this is
                        used in the optimistic cursor
                        positioning: if the modify clock has
                        not changed, we know that the pointer
                        is still valid; this field may be
                        changed if the thread (1) owns the
                        pool mutex and the page is not
                        bufferfixed, or (2) the thread has an
                        x-latch on the block */
        /* @} */
        /** @name Hash search fields (unprotected)
        NOTE that these fields are NOT protected by any semaphore! */
        /* @{ */
    
        ulint        n_hash_helps;    /*!< counter which controls building
                        of a new hash index for the page */
        volatile ulint    n_bytes;    /*!< recommended prefix length for hash
                        search: number of bytes in
                        an incomplete last field */
        volatile ulint    n_fields;    /*!< recommended prefix length for hash
                        search: number of full fields */
        volatile bool    left_side;    /*!< true or false, depending on
                        whether the leftmost record of several
                        records with the same prefix should be
                        indexed in the hash index */
        /* @} */
    
        /** @name Hash search fields
        These 5 fields may only be modified when:
        we are holding the appropriate x-latch in btr_search_latches[], and
        one of the following holds:
        (1) the block state is BUF_BLOCK_FILE_PAGE, and
        we are holding an s-latch or x-latch on buf_block_t::lock, or
        (2) buf_block_t::buf_fix_count == 0, or
        (3) the block state is BUF_BLOCK_REMOVE_HASH.
    
        An exception to this is when we init or create a page
        in the buffer pool in buf0buf.cc.
    
        Another exception for buf_pool_clear_hash_index() is that
        assigning block->index = NULL (and block->n_pointers = 0)
        is allowed whenever btr_search_own_all(RW_LOCK_X).
    
        Another exception is that ha_insert_for_fold_func() may
        decrement n_pointers without holding the appropriate latch
        in btr_search_latches[]. Thus, n_pointers must be
        protected by atomic memory access.
    
        This implies that the fields may be read without race
        condition whenever any of the following hold:
        - the btr_search_latches[] s-latch or x-latch is being held, or
        - the block state is not BUF_BLOCK_FILE_PAGE or BUF_BLOCK_REMOVE_HASH,
        and holding some latch prevents the state from changing to that.
    
        Some use of assert_block_ahi_empty() or assert_block_ahi_valid()
        is prone to race conditions while buf_pool_clear_hash_index() is
        executing (the adaptive hash index is being disabled). Such use
        is explicitly commented. */
    
        /* @{ */
    
    #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
        ulint        n_pointers;    /*!< used in debugging: the number of
                        pointers in the adaptive hash index
                        pointing to this frame;
                        protected by atomic memory access
                        or btr_search_own_all(). */
    # define assert_block_ahi_empty(block)                    
        ut_a(os_atomic_increment_ulint(&(block)->n_pointers, 0) == 0)
    # define assert_block_ahi_empty_on_init(block) do {            
        UNIV_MEM_VALID(&(block)->n_pointers, sizeof (block)->n_pointers); 
        assert_block_ahi_empty(block);                    
    } while (0)
    # define assert_block_ahi_valid(block)                    
        ut_a((block)->index                        
             || os_atomic_increment_ulint(&(block)->n_pointers, 0) == 0)
    #else /* UNIV_AHI_DEBUG || UNIV_DEBUG */
    # define assert_block_ahi_empty(block) /* nothing */
    # define assert_block_ahi_empty_on_init(block) /* nothing */
    # define assert_block_ahi_valid(block) /* nothing */
    #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
        unsigned    curr_n_fields:10;/*!< prefix length for hash indexing:
                        number of full fields */
        unsigned    curr_n_bytes:15;/*!< number of bytes in hash
                        indexing */
        unsigned    curr_left_side:1;/*!< TRUE or FALSE in hash indexing */
        dict_index_t*    index;        /*!< Index for which the
                        adaptive hash index has been
                        created, or NULL if the page
                        does not exist in the
                        index. Note that it does not
                        guarantee that the index is
                        complete, though: there may
                        have been hash collisions,
                        record deletions, etc. */
        /* @} */
        bool        made_dirty_with_no_latch;
                        /*!< true if block has been made dirty
                        without acquiring X/SX latch as the
                        block belongs to temporary tablespace
                        and block is always accessed by a
                        single thread. */
        bool        skip_flush_check;
                        /*!< Skip check in buf_dblwr_check_block
                        during bulk load, protected by lock.*/
    # ifdef UNIV_DEBUG
        /** @name Debug fields */
        /* @{ */
        rw_lock_t    debug_latch;    /*!< in the debug version, each thread
                        which bufferfixes the block acquires
                        an s-latch here; so we can use the
                        debug utilities in sync0rw */
        /* @} */
    # endif
        BPageMutex    mutex;        /*!< mutex protecting this block:
                        state (also protected by the buffer
                        pool mutex), io_fix, buf_fix_count,
                        and accessed; we introduce this new
                        mutex in InnoDB-5.1 to relieve
                        contention on the buffer pool mutex */
    #endif /* !UNIV_HOTBACKUP */
    };

    判断索引对象是否满足自适应哈希索引

    在判断是否需要对索引对象创建自适应哈希索引前,需要先判断索引是否被访问17次,btr_search_t对象中hash_analysis统计索引使用次数


    /*
    * Updates the search info. */ UNIV_INLINE void btr_search_info_update( dict_index_t *index, /*!< in: index of the cursor */ btr_cur_t *cursor) /*!< in: cursor which was just positioned */ { ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_S)); ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); if (dict_index_is_spatial(index) || !btr_search_enabled) { return; } btr_search_t *info; info = btr_search_get_info(index); info->hash_analysis++; if (info->hash_analysis < BTR_SEARCH_HASH_ANALYSIS) { /* Do nothing */ return; } ut_ad(cursor->flag != BTR_CUR_HASH); btr_search_info_update_slow(info, cursor); }

    BTR_SEARCH_HASH_ANALYSIS被硬编码在代码中:

    /** After change in n_fields or n_bytes in info, this many rounds are waited
    before starting the hash analysis again: this is to save CPU time when there
    is no hope in building a hash index. */
    #define BTR_SEARCH_HASH_ANALYSIS 17

    判断数据页对象是否满足自适应哈希索引

    当索引被访问17次后,则开始对索引上的数据页进行访问模式统计,当满足下列条件时则对相应的数据页创建自适应哈希索引:

    • 索引上按照相同查询模式连续访问100次
    • 数据页上按照相同查询模式访问超过"当前数据页记录数的1/16" 次
    /** The global limit for consecutive potentially successful hash searches,
    before hash index building is started */
    #define BTR_SEARCH_BUILD_LIMIT 100
    
    /** If the number of records on the page divided by this parameter
    would have been successfully accessed using a hash index, the index
    is then built on the page, assuming the global limit has been reached */
    #define BTR_SEARCH_PAGE_BUILD_LIMIT 16
    
    /** Update the block search info on hash successes. NOTE that info and
    block->n_hash_helps, n_fields, n_bytes, left_side are NOT protected by any
    semaphore, to save CPU time! Do not assume the fields are consistent.
    @return true if building a (new) hash index on the block is recommended
    @param[in,out]    info    search info
    @param[in,out]    block    buffer block
    @param[in]    cursor    cursor */
    static ibool btr_search_update_block_hash_info(btr_search_t *info,
                                                   buf_block_t *block,
                                                   const btr_cur_t *cursor) {
      ut_ad(!rw_lock_own(btr_get_search_latch(cursor->index), RW_LOCK_S));
      ut_ad(!rw_lock_own(btr_get_search_latch(cursor->index), RW_LOCK_X));
      ut_ad(rw_lock_own(&block->lock, RW_LOCK_S) ||
            rw_lock_own(&block->lock, RW_LOCK_X));
    
      info->last_hash_succ = FALSE;
    
      ut_a(buf_block_state_valid(block));
      ut_ad(info->magic_n == BTR_SEARCH_MAGIC_N);
    
      if ((block->n_hash_helps > 0) && (info->n_hash_potential > 0) &&
          (block->n_fields == info->n_fields) &&
          (block->n_bytes == info->n_bytes) &&
          (block->left_side == !!info->left_side)) {
        if ((block->index) && (block->curr_n_fields == info->n_fields) &&
            (block->curr_n_bytes == info->n_bytes) &&
            (block->curr_left_side == info->left_side)) {
          /* The search would presumably have succeeded using
          the hash index */
    
          info->last_hash_succ = TRUE;
        }
    
        block->n_hash_helps++;
      } else {
        block->n_hash_helps = 1;
        block->n_fields = info->n_fields;
        block->n_bytes = info->n_bytes;
        block->left_side = info->left_side;
      }
    
    #ifdef UNIV_DEBUG
      if (cursor->index->table->does_not_fit_in_memory) {
        block->n_hash_helps = 0;
      }
    #endif /* UNIV_DEBUG */
    
      if ((block->n_hash_helps >
           page_get_n_recs(block->frame) / BTR_SEARCH_PAGE_BUILD_LIMIT) &&
          (info->n_hash_potential >= BTR_SEARCH_BUILD_LIMIT)) {
        if ((!block->index) ||
            (block->n_hash_helps > 2 * page_get_n_recs(block->frame)) ||
            (block->n_fields != block->curr_n_fields) ||
            (block->n_bytes != block->curr_n_bytes) ||
            (block->left_side != block->curr_left_side)) {
          /* Build a new hash index on the page */
    
          return (TRUE);
        }
      }
    
      return (FALSE);
    }
  • 相关阅读:
    我的Android学习路线(二)
    利用python3.x实现小爬虫下载贴吧内图片
    我的Android学习路线(一)
    根网科技面试题
    sql语句的执行顺序
    英文面试常见问题汇总
    关于oracle、sqlserver、mysql查询前N条数据的实现
    LinqToSql(一)
    关于一些概念的问题,命名空间,程序集,解决方案,项目
    索引器
  • 原文地址:https://www.cnblogs.com/gaogao67/p/14991710.html
Copyright © 2011-2022 走看看