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

    自适应哈希索引KEY和VALUE

    在MySQL中,对于不同对象,哈希索引键值的计算各不相同,基本思路都是对查询的键进行fold,然后通过hash_calc_hash来计算对应所处哈希表中哈希槽(cell)。

    对于自适应哈希索引,KEY对应的是逻辑记录经过fold后的值,VALUE对应的是记录所在的页编号和页偏移量。

    自适应哈希索引更新

    当插入新记录时更新AHI函数为btr_search_update_hash_node_on_insert:

    /** Updates the page hash index when a single record is inserted on a page.
    @param[in]    cursor    cursor which was positioned to the place to insert
                            using btr_cur_search_, and the new record has been
                            inserted next to the cursor. */
    void btr_search_update_hash_node_on_insert(btr_cur_t *cursor);
    
    /** Updates the page hash index when a single record is inserted on a page.
    @param[in]    cursor    cursor which was positioned to the place to insert
                            using btr_cur_search_, and the new record has been
                            inserted next to the cursor. */
    void btr_search_update_hash_node_on_insert(btr_cur_t *cursor) {
      hash_table_t *table;
      buf_block_t *block;
      dict_index_t *index;
      rec_t *rec;
    
      if (cursor->index->disable_ahi || !btr_search_enabled) {
        return;
      }
    
      rec = btr_cur_get_rec(cursor);
    
      block = btr_cur_get_block(cursor);
    
      ut_ad(rw_lock_own(&(block->lock), RW_LOCK_X));
    
      index = block->index;
    
      if (!index) {
        return;
      }
    
      ut_a(cursor->index == index);
      ut_a(!dict_index_is_ibuf(index));
    
      btr_search_x_lock(index);
    
      if (!block->index) {
        goto func_exit;
      }
    
      ut_a(block->index == index);
    
      if ((cursor->flag == BTR_CUR_HASH) &&
          (cursor->n_fields == block->curr_n_fields) &&
          (cursor->n_bytes == block->curr_n_bytes) && !block->curr_left_side) {
        table = btr_get_search_table(index);
    
        if (ha_search_and_update_if_found(table, cursor->fold, rec, block,
                                          page_rec_get_next(rec))) {
          MONITOR_INC(MONITOR_ADAPTIVE_HASH_ROW_UPDATED);
        }
    
      func_exit:
        assert_block_ahi_valid(block);
        btr_search_x_unlock(index);
      } else {
        btr_search_x_unlock(index);
    
        btr_search_update_hash_on_insert(cursor);
      }
    }

    其中通过ha_search_and_update_if_found来查找和更新AHI,ha_search_and_update_if_found等价于ha_search_and_update_if_found_func:

    #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
    /** Looks for an element when we know the pointer to the data and
    updates the pointer to data if found.
    @param table in/out: hash table
    @param fold in: folded value of the searched data
    @param data in: pointer to the data
    @param new_block in: block containing new_data
    @param new_data in: new pointer to the data */
    #define ha_search_and_update_if_found(table, fold, data, new_block, new_data) 
      ha_search_and_update_if_found_func(table, fold, data, new_block, new_data)
    #else /* UNIV_AHI_DEBUG || UNIV_DEBUG */
    /** Looks for an element when we know the pointer to the data and
    updates the pointer to data if found.
    @param table in/out: hash table
    @param fold in: folded value of the searched data
    @param data in: pointer to the data
    @param new_block ignored: block containing new_data
    @param new_data in: new pointer to the data */
    #define ha_search_and_update_if_found(table, fold, data, new_block, new_data) 
      ha_search_and_update_if_found_func(table, fold, data, new_data)
    #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
    
    
    /** Looks for an element when we know the pointer to the data, and updates
     the pointer to data, if found.
     @return true if found */
    ibool ha_search_and_update_if_found_func(
        hash_table_t *table, /*!< in/out: hash table */
        ulint fold,          /*!< in: folded value of the searched data */
        const rec_t *data,   /*!< in: pointer to the data */
    #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
        buf_block_t *new_block, /*!< in: block containing new_data */
    #endif                      /* UNIV_AHI_DEBUG || UNIV_DEBUG */
        const rec_t *new_data)  /*!< in: new pointer to the data */
    {
      ha_node_t *node;
    
      ut_ad(table);
      ut_ad(table->magic_n == HASH_TABLE_MAGIC_N);
      hash_assert_can_modify(table, fold);
    #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
      ut_a(new_block->frame == page_align(new_data));
    #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
    
      ut_d(ha_btr_search_latch_x_locked(table));
    
      if (!btr_search_enabled) {
        return (FALSE);
      }
    
      node = ha_search_with_data(table, fold, data);
    
      if (node) {
    #if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG
        if (table->adaptive) {
          ut_a(node->block->n_pointers.fetch_sub(1) - 1 < MAX_N_POINTERS);
          ut_a(new_block->n_pointers.fetch_add(1) + 1 < MAX_N_POINTERS);
        }
    
        node->block = new_block;
    #endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */
        node->data = new_data;
    
        return (TRUE);
      }
    
      return (FALSE);
    }

    自适应哈希索引键的封装

    在函数btr_search_guess_on_hash中如如下代码:

    /** Tries to guess the right search position based on the hash search info
    of the index. Note that if mode is PAGE_CUR_LE, which is used in inserts,
    and the function returns TRUE, then cursor->up_match and cursor->low_match
    both have sensible values.
    @param[in,out]    index        index
    @param[in,out]    info        index search info
    @param[in]    tuple        logical record
    @param[in]    mode        PAGE_CUR_L, ....
    @param[in]    latch_mode    BTR_SEARCH_LEAF, ...;
                    NOTE that only if has_search_latch is 0, we will
                    have a latch set on the cursor page, otherwise
                    we assume the caller uses his search latch
                    to protect the record!
    @param[out]    cursor        tree cursor
    @param[in]    has_search_latch
                    latch mode the caller currently has on
                    search system: RW_S/X_LATCH or 0
    @param[in]    mtr        mini transaction
    @return TRUE if succeeded */
    ibool
    btr_search_guess_on_hash(
        dict_index_t*    index,
        btr_search_t*    info,
        const dtuple_t*    tuple,
        ulint        mode,
        ulint        latch_mode,
        btr_cur_t*    cursor,
        ulint        has_search_latch,
        mtr_t*        mtr)
    {
        const rec_t*    rec;
        ulint        fold;
        index_id_t    index_id;
        ......
        cursor->n_fields = info->n_fields;
        cursor->n_bytes = info->n_bytes;
        fold = dtuple_fold(tuple, cursor->n_fields, cursor->n_bytes, index_id);
        cursor->fold = fold;
        cursor->flag = BTR_CUR_HASH;
        rec = (rec_t*) ha_search_and_get_data(
                btr_get_search_table(index), fold);
    
    }

    方法调用dtuple_fold来对逻辑记录tuple进行封装:

    /** Fold a prefix given as the number of fields of a tuple.
    @param[in]    tuple        index record
    @param[in]    n_fields    number of complete fields to fold
    @param[in]    n_bytes        number of bytes to fold in the last field
    @param[in]    index_id    index tree ID
    @return the folded value */
    UNIV_INLINE
    ulint
    dtuple_fold(
        const dtuple_t*    tuple,
        ulint        n_fields,
        ulint        n_bytes,
        index_id_t    tree_id)
    {
        const dfield_t*    field;
        ulint        i;
        const byte*    data;
        ulint        len;
        ulint        fold;
    
        ut_ad(tuple);
        ut_ad(tuple->magic_n == DATA_TUPLE_MAGIC_N);
        ut_ad(dtuple_check_typed(tuple));
    
        fold = ut_fold_ull(tree_id);
    
        for (i = 0; i < n_fields; i++) {
            field = dtuple_get_nth_field(tuple, i);
    
            data = (const byte*) dfield_get_data(field);
            len = dfield_get_len(field);
    
            if (len != UNIV_SQL_NULL) {
                fold = ut_fold_ulint_pair(fold,
                              ut_fold_binary(data, len));
            }
        }
    
        if (n_bytes > 0) {
            field = dtuple_get_nth_field(tuple, i);
    
            data = (const byte*) dfield_get_data(field);
            len = dfield_get_len(field);
    
            if (len != UNIV_SQL_NULL) {
                if (len > n_bytes) {
                    len = n_bytes;
                }
    
                fold = ut_fold_ulint_pair(fold,
                              ut_fold_binary(data, len));
            }
        }
    
        return(fold);
    }
    
    /*************************************************************//**
    Folds a 64-bit integer.
    @return folded value */
    UNIV_INLINE
    ulint
    ut_fold_ull(
    /*========*/
        ib_uint64_t    d)    /*!< in: 64-bit integer */
    {
        return(ut_fold_ulint_pair((ulint) d & ULINT32_MASK,
                      (ulint) (d >> 32)));
    }
    
    
    /*************************************************************//**
    Folds a pair of ulints.
    @return folded value */
    UNIV_INLINE
    ulint
    ut_fold_ulint_pair(
    /*===============*/
        ulint    n1,    /*!< in: ulint */
        ulint    n2)    /*!< in: ulint */
    {
        return(((((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1)
            ^ UT_HASH_RANDOM_MASK) + n2);
    }
    
    
    /*************************************************************//**
    Folds a binary string.
    @return folded value */
    UNIV_INLINE
    ulint
    ut_fold_binary(
    /*===========*/
        const byte*    str,    /*!< in: string of bytes */
        ulint        len)    /*!< in: length */
    {
        ulint        fold = 0;
        const byte*    str_end    = str + (len & 0xFFFFFFF8);
    
        ut_ad(str || !len);
    
        while (str < str_end) {
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
        }
    
        switch (len & 0x7) {
        case 7:
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            // Fall through.
        case 6:
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            // Fall through.
        case 5:
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            // Fall through.
        case 4:
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            // Fall through.
        case 3:
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            // Fall through.
        case 2:
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
            // Fall through.
        case 1:
            fold = ut_fold_ulint_pair(fold, (ulint)(*str++));
        }
    
        return(fold);
    }

    可以看出,在封装KEY的过程中,使用到:

    • 用于标识索引的index_id
    • 用于标识查询模式的n_fields和n_bytes
    • 用于标识索引记录的tuple

    自适应哈希索引值的封装

    在调用ha_search_and_update_if_found中,使用page_rec_get_next(rec)来获取记录的页信息,并作为*new_data参数的值传递进去。

    /** Gets the pointer to the next record on the page.
     @return pointer to next record */
    UNIV_INLINE
    const rec_t *page_rec_get_next_low(
        const rec_t *rec, /*!< in: pointer to record */
        ulint comp)       /*!< in: nonzero=compact page layout */
    {
      ulint offs;
      const page_t *page;
    
      ut_ad(page_rec_check(rec));
    
      page = page_align(rec);
    
      offs = rec_get_next_offs(rec, comp);
    
      if (offs >= UNIV_PAGE_SIZE) {
        fprintf(stderr,
                "InnoDB: Next record offset is nonsensical %lu"
                " in record at offset %lu
    "
                "InnoDB: rec address %p, space id %lu, page %lu
    ",
                (ulong)offs, (ulong)page_offset(rec), (void *)rec,
                (ulong)page_get_space_id(page), (ulong)page_get_page_no(page));
        ut_error;
      } else if (offs == 0) {
        return (nullptr);
      }
    
      return (page + offs);
    }
    
    /** Gets the pointer to the next record on the page.
     @return pointer to next record */
    UNIV_INLINE
    rec_t *page_rec_get_next(rec_t *rec) /*!< in: pointer to record */
    {
      return ((rec_t *)page_rec_get_next_low(rec, page_rec_is_comp(rec)));
    }

    函数page_rec_get_next调用page_rec_get_next_low函数,而page_rec_get_next_low返回的值为(page + offs),即记录所在页编号和页偏移量。

    索引记录向左和向右查找

    在保存索引信息的btr_search_t对象和保存数据页信息的buf_block_t对象中,都有left_side对象用来标识索引记录查找方向。

    在创建自适应哈希索引时,不会对数据页中所有记录进行索引,而是根据访问模式来决定对相同键值的记录如何进行索引。

    假设有索引idx_c1,其记录集合为[1,2,2,3,3,3,3,4],如对于c1=3的索引记录有3条:

    • 对于查询模式为PAGE_CUR_LE或PAGE_CUR_G,对相同记录取最左的记录进行索引
    • 对于查询模式为PAGE_CUR_GE或PAGE_CUR_L,对相同记录取最右的记录进行索引

    当找到最左或最右记录后,再按照left_side值进行向左或向右的查询。

    PS:在每条物理记录的record header部分,有18bit用来存放下一条记录的位置,那么如何向左查找记录呢?

  • 相关阅读:
    shell字符串截取
    QT,QT SDK, QT Creator 区别
    linux -- 扩容 /home 空间( xfs文件系统分区扩容指定挂载点)
    条件变量与互斥量
    越努力越幸运--2018年7月22日周记
    越努力越幸运--动态数组vector
    越努力越幸运--3-日常bug修复
    越努力越幸运--2-LD_PRELOAD, fork ,僵尸进程
    越努力越幸运--1
    makefile--回顾基础篇
  • 原文地址:https://www.cnblogs.com/gaogao67/p/14992717.html
Copyright © 2011-2022 走看看